Skip to content

Commit 011f9cd

Browse files
authored
Xlsx Writer Honor Alignment in Default Font (#3459)
* Xlsx Writer Honor Alignment in Default Font Fix #3443. A mysterious implementation by Excel. The style tags have an attribute applyAlignment, which nominally says whether or not the style should use its own Alignment. Except ... Excel ignores that attribute and uses the alignment tag if it is supplied ... and, another mystery, uses not the default style for the spreadsheet if not supplied, but rather uses the default alignment style for all spreadsheets even if the spreadsheet's default style uses a non-default alignment. I am changing Xlsx Writer to generate alignment tag unless the alignment matches both the default alignment for the spreadsheet and the default alignment for all spreadsheets (which I expect to happen most of the time). * Improve Performance Also don't change horizontal/vertical on Xlsx Read if they aren't explicitly set.
1 parent 076ef9d commit 011f9cd

File tree

3 files changed

+117
-7
lines changed

3 files changed

+117
-7
lines changed

src/PhpSpreadsheet/Reader/Xlsx/Styles.php

+8-4
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,14 @@ private function readBorder(Border $border, SimpleXMLElement $borderXml): void
253253

254254
public function readAlignmentStyle(Alignment $alignment, SimpleXMLElement $alignmentXml): void
255255
{
256-
$horizontal = $this->getAttribute($alignmentXml, 'horizontal');
257-
$alignment->setHorizontal($horizontal);
258-
$vertical = $this->getAttribute($alignmentXml, 'vertical');
259-
$alignment->setVertical((string) $vertical);
256+
$horizontal = (string) $this->getAttribute($alignmentXml, 'horizontal');
257+
if ($horizontal !== '') {
258+
$alignment->setHorizontal($horizontal);
259+
}
260+
$vertical = (string) $this->getAttribute($alignmentXml, 'vertical');
261+
if ($vertical !== '') {
262+
$alignment->setVertical($vertical);
263+
}
260264

261265
$textRotation = (int) $this->getAttribute($alignmentXml, 'textRotation');
262266
if ($textRotation > 90) {

src/PhpSpreadsheet/Writer/Xlsx/Style.php

+12-3
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,13 @@ public function writeStyles(Spreadsheet $spreadsheet)
112112
$objWriter->writeAttribute('count', (string) count($spreadsheet->getCellXfCollection()));
113113

114114
// xf
115+
$alignment = new Alignment();
116+
$defaultAlignHash = $alignment->getHashCode();
117+
if ($defaultAlignHash !== $spreadsheet->getDefaultStyle()->getAlignment()->getHashCode()) {
118+
$defaultAlignHash = '';
119+
}
115120
foreach ($spreadsheet->getCellXfCollection() as $cellXf) {
116-
$this->writeCellStyleXf($objWriter, $cellXf, $spreadsheet);
121+
$this->writeCellStyleXf($objWriter, $cellXf, $spreadsheet, $defaultAlignHash);
117122
}
118123

119124
$objWriter->endElement();
@@ -400,7 +405,7 @@ private function writeBorder(XMLWriter $objWriter, Borders $borders): void
400405
/**
401406
* Write Cell Style Xf.
402407
*/
403-
private function writeCellStyleXf(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Style $style, Spreadsheet $spreadsheet): void
408+
private function writeCellStyleXf(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\Style\Style $style, Spreadsheet $spreadsheet, string $defaultAlignHash): void
404409
{
405410
// xf
406411
$objWriter->startElement('xf');
@@ -424,7 +429,11 @@ private function writeCellStyleXf(XMLWriter $objWriter, \PhpOffice\PhpSpreadshee
424429
$objWriter->writeAttribute('applyNumberFormat', ($spreadsheet->getDefaultStyle()->getNumberFormat()->getHashCode() != $style->getNumberFormat()->getHashCode()) ? '1' : '0');
425430
$objWriter->writeAttribute('applyFill', ($spreadsheet->getDefaultStyle()->getFill()->getHashCode() != $style->getFill()->getHashCode()) ? '1' : '0');
426431
$objWriter->writeAttribute('applyBorder', ($spreadsheet->getDefaultStyle()->getBorders()->getHashCode() != $style->getBorders()->getHashCode()) ? '1' : '0');
427-
$applyAlignment = ($spreadsheet->getDefaultStyle()->getAlignment()->getHashCode() != $style->getAlignment()->getHashCode()) ? '1' : '0';
432+
if ($defaultAlignHash !== '' && $defaultAlignHash === $style->getAlignment()->getHashCode()) {
433+
$applyAlignment = '0';
434+
} else {
435+
$applyAlignment = '1';
436+
}
428437
$objWriter->writeAttribute('applyAlignment', $applyAlignment);
429438
if ($style->getProtection()->getLocked() != Protection::PROTECTION_INHERIT || $style->getProtection()->getHidden() != Protection::PROTECTION_INHERIT) {
430439
$objWriter->writeAttribute('applyProtection', 'true');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php
2+
3+
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
4+
5+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
6+
use PhpOffice\PhpSpreadsheet\Style\Alignment;
7+
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
8+
9+
class Issue3443Test extends AbstractFunctional
10+
{
11+
public function testNonDefaultAlign(): void
12+
{
13+
// Issue 3443 - default alignment not honored.
14+
$spreadsheet = new Spreadsheet();
15+
$styleArray = [
16+
'alignment' => [
17+
'horizontal' => Alignment::HORIZONTAL_CENTER,
18+
'vertical' => Alignment::VERTICAL_CENTER,
19+
'wrapText' => true,
20+
],
21+
'font' => [
22+
'name' => 'Courier New',
23+
],
24+
];
25+
$spreadsheet->getDefaultStyle()->applyFromArray($styleArray);
26+
$sheet = $spreadsheet->getActiveSheet();
27+
$sheet->getCell('A1')->setValue(1);
28+
$sheet->getCell('A2')->setValue(2);
29+
$sheet->getCell('A3')->setValue(3);
30+
$sheet->getStyle('A2')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
31+
$sheet->getStyle('A3')->getFont()->setName('Tahoma');
32+
33+
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx');
34+
$spreadsheet->disconnectWorksheets();
35+
$rsheet = $reloadedSpreadsheet->getActiveSheet();
36+
$expected1 = [
37+
'horizontal' => 'center',
38+
'indent' => 0,
39+
'readOrder' => 0,
40+
'shrinkToFit' => false,
41+
'textRotation' => 0,
42+
'vertical' => 'center',
43+
'wrapText' => true,
44+
];
45+
self::assertSame($expected1, $rsheet->getStyle('A1')->getAlignment()->exportArray());
46+
$expected2 = $expected1;
47+
$expected2['horizontal'] = 'left';
48+
self::assertSame($expected2, $rsheet->getStyle('A2')->getAlignment()->exportArray());
49+
self::assertSame($expected1, $rsheet->getStyle('A3')->getAlignment()->exportArray());
50+
self::assertSame('Courier New', $rsheet->getStyle('A1')->getFont()->getName());
51+
self::assertSame('Courier New', $rsheet->getStyle('A2')->getFont()->getName());
52+
self::assertSame('Tahoma', $rsheet->getStyle('A3')->getFont()->getName());
53+
54+
$reloadedSpreadsheet->disconnectWorksheets();
55+
}
56+
57+
public function testDefaultAlign(): void
58+
{
59+
// Issue 3443 - default alignment not honored.
60+
$spreadsheet = new Spreadsheet();
61+
$styleArray = [
62+
'font' => [
63+
'name' => 'Courier New',
64+
],
65+
];
66+
$spreadsheet->getDefaultStyle()->applyFromArray($styleArray);
67+
$sheet = $spreadsheet->getActiveSheet();
68+
$sheet->getCell('A1')->setValue(1);
69+
$sheet->getCell('A2')->setValue(2);
70+
$sheet->getCell('A3')->setValue(3);
71+
$sheet->getStyle('A2')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
72+
$sheet->getStyle('A3')->getFont()->setName('Tahoma');
73+
74+
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx');
75+
$spreadsheet->disconnectWorksheets();
76+
$rsheet = $reloadedSpreadsheet->getActiveSheet();
77+
$expected1 = [
78+
'horizontal' => 'general',
79+
'indent' => 0,
80+
'readOrder' => 0,
81+
'shrinkToFit' => false,
82+
'textRotation' => 0,
83+
'vertical' => 'bottom',
84+
'wrapText' => false,
85+
];
86+
self::assertSame($expected1, $rsheet->getStyle('A1')->getAlignment()->exportArray());
87+
$expected2 = $expected1;
88+
$expected2['horizontal'] = 'left';
89+
self::assertSame($expected2, $rsheet->getStyle('A2')->getAlignment()->exportArray());
90+
self::assertSame($expected1, $rsheet->getStyle('A3')->getAlignment()->exportArray());
91+
self::assertSame('Courier New', $rsheet->getStyle('A1')->getFont()->getName());
92+
self::assertSame('Courier New', $rsheet->getStyle('A2')->getFont()->getName());
93+
self::assertSame('Tahoma', $rsheet->getStyle('A3')->getFont()->getName());
94+
95+
$reloadedSpreadsheet->disconnectWorksheets();
96+
}
97+
}

0 commit comments

Comments
 (0)