Skip to content

Commit fe2fc36

Browse files
committed
Do Not Output Alignment and Protection for Conditional Formats
Fix #4025. With Conditional Formatting, Excel can change Font, Fill, Borders, and NumberFormat. It does not support Alignment nor Protection, at least not natively. This PR removes all code supporting Conditional Alignment or Protection from Xlsx and Xls Writer. Attached to the issue report is a spreadsheet where a conditionally formatted merged cell shows its value twice when Alignment is included with the Conditional Style; it is fixed by this PR. It is not entirely certain which particular combination of CF, Alignment, Merge, AutoFilter, and right-to-left sheet alignment trigger this problem. I am sure that it shows up for Vertical Alignment Center, and I am sure that Xls Writer (which is somewhat buggy anyhow regarding CF support - see issue #3403) has the same problem. The now-unused Xls code is commented out rather than deleted, for documentation purposes.
1 parent 563de5f commit fe2fc36

File tree

3 files changed

+207
-71
lines changed

3 files changed

+207
-71
lines changed

src/PhpSpreadsheet/Writer/Xls/Worksheet.php

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2802,7 +2802,7 @@ private function writeCFRule(
28022802

28032803
// $flags : Option flags
28042804
// Alignment
2805-
$bAlignHz = ($conditional->getStyle()->getAlignment()->getHorizontal() === null ? 1 : 0);
2805+
/*$bAlignHz = ($conditional->getStyle()->getAlignment()->getHorizontal() === null ? 1 : 0);
28062806
$bAlignVt = ($conditional->getStyle()->getAlignment()->getVertical() === null ? 1 : 0);
28072807
$bAlignWrapTx = ($conditional->getStyle()->getAlignment()->getWrapText() === false ? 1 : 0);
28082808
$bTxRotation = ($conditional->getStyle()->getAlignment()->getTextRotation() === null ? 1 : 0);
@@ -2812,15 +2812,15 @@ private function writeCFRule(
28122812
$bFormatAlign = 1;
28132813
} else {
28142814
$bFormatAlign = 0;
2815-
}
2815+
}*/
28162816
// Protection
2817-
$bProtLocked = ($conditional->getStyle()->getProtection()->getLocked() === null ? 1 : 0);
2817+
/*$bProtLocked = ($conditional->getStyle()->getProtection()->getLocked() === null ? 1 : 0);
28182818
$bProtHidden = ($conditional->getStyle()->getProtection()->getHidden() === null ? 1 : 0);
28192819
if ($bProtLocked == 0 || $bProtHidden == 0) {
28202820
$bFormatProt = 1;
28212821
} else {
28222822
$bFormatProt = 0;
2823-
}
2823+
}*/
28242824
// Border
28252825
$bBorderLeft = ($conditional->getStyle()->getBorders()->getLeft()->getBorderStyle() !== Border::BORDER_OMIT) ? 1 : 0;
28262826
$bBorderRight = ($conditional->getStyle()->getBorders()->getRight()->getBorderStyle() !== Border::BORDER_OMIT) ? 1 : 0;
@@ -2858,19 +2858,19 @@ private function writeCFRule(
28582858
}
28592859
// Alignment
28602860
$flags = 0;
2861-
$flags |= (1 == $bAlignHz ? 0x00000001 : 0);
2862-
$flags |= (1 == $bAlignVt ? 0x00000002 : 0);
2863-
$flags |= (1 == $bAlignWrapTx ? 0x00000004 : 0);
2864-
$flags |= (1 == $bTxRotation ? 0x00000008 : 0);
2861+
//$flags |= (1 == $bAlignHz ? 0x00000001 : 0);
2862+
//$flags |= (1 == $bAlignVt ? 0x00000002 : 0);
2863+
//$flags |= (1 == $bAlignWrapTx ? 0x00000004 : 0);
2864+
//$flags |= (1 == $bTxRotation ? 0x00000008 : 0);
28652865
// Justify last line flag
28662866
$flags |= (1 == self::$always1 ? 0x00000010 : 0);
2867-
$flags |= (1 == $bIndent ? 0x00000020 : 0);
2868-
$flags |= (1 == $bShrinkToFit ? 0x00000040 : 0);
2867+
//$flags |= (1 == $bIndent ? 0x00000020 : 0);
2868+
//$flags |= (1 == $bShrinkToFit ? 0x00000040 : 0);
28692869
// Default
28702870
$flags |= (1 == self::$always1 ? 0x00000080 : 0);
28712871
// Protection
2872-
$flags |= (1 == $bProtLocked ? 0x00000100 : 0);
2873-
$flags |= (1 == $bProtHidden ? 0x00000200 : 0);
2872+
//$flags |= (1 == $bProtLocked ? 0x00000100 : 0);
2873+
//$flags |= (1 == $bProtHidden ? 0x00000200 : 0);
28742874
// Border
28752875
$flags |= (1 == $bBorderLeft ? 0x00000400 : 0);
28762876
$flags |= (1 == $bBorderRight ? 0x00000800 : 0);
@@ -2886,13 +2886,13 @@ private function writeCFRule(
28862886
// Font
28872887
$flags |= (1 == $bFormatFont ? 0x04000000 : 0);
28882888
// Alignment:
2889-
$flags |= (1 == $bFormatAlign ? 0x08000000 : 0);
2889+
//$flags |= (1 == $bFormatAlign ? 0x08000000 : 0);
28902890
// Border
28912891
$flags |= (1 == $bFormatBorder ? 0x10000000 : 0);
28922892
// Pattern
28932893
$flags |= (1 == $bFormatFill ? 0x20000000 : 0);
28942894
// Protection
2895-
$flags |= (1 == $bFormatProt ? 0x40000000 : 0);
2895+
//$flags |= (1 == $bFormatProt ? 0x40000000 : 0);
28962896
// Text direction
28972897
$flags |= (1 == self::$always0 ? 0x80000000 : 0);
28982898

@@ -2998,7 +2998,7 @@ private function writeCFRule(
29982998
// Always
29992999
$dataBlockFont .= pack('v', 0x0001);
30003000
}
3001-
if ($bFormatAlign === 1) {
3001+
/*if ($bFormatAlign === 1) {
30023002
// Alignment and text break
30033003
$blockAlign = Style\CellAlignment::horizontal($conditional->getStyle()->getAlignment());
30043004
$blockAlign |= Style\CellAlignment::wrap($conditional->getStyle()->getAlignment()) << 3;
@@ -3021,7 +3021,7 @@ private function writeCFRule(
30213021
$blockIndentRelative = 255;
30223022
30233023
$dataBlockAlign = pack('CCvvv', $blockAlign, $blockRotation, $blockIndent, $blockIndentRelative, 0x0000);
3024-
}
3024+
}*/
30253025
if ($bFormatBorder === 1) {
30263026
$blockLineStyle = Style\CellBorder::style($conditional->getStyle()->getBorders()->getLeft());
30273027
$blockLineStyle |= Style\CellBorder::style($conditional->getStyle()->getBorders()->getRight()) << 4;
@@ -3055,18 +3055,18 @@ private function writeCFRule(
30553055
if ($bFormatFont === 1) { // Block Formatting : OK
30563056
$data .= $dataBlockFont;
30573057
}
3058-
if ($bFormatAlign === 1) {
3059-
$data .= $dataBlockAlign;
3060-
}
3058+
//if ($bFormatAlign === 1) {
3059+
// $data .= $dataBlockAlign;
3060+
//}
30613061
if ($bFormatBorder === 1) {
30623062
$data .= $dataBlockBorder;
30633063
}
30643064
if ($bFormatFill === 1) { // Block Formatting : OK
30653065
$data .= $dataBlockFill;
30663066
}
3067-
if ($bFormatProt == 1) {
3068-
$data .= $this->getDataBlockProtection($conditional);
3069-
}
3067+
//if ($bFormatProt == 1) {
3068+
// $data .= $this->getDataBlockProtection($conditional);
3069+
//}
30703070
if ($operand1 !== null) {
30713071
$data .= $operand1;
30723072
}
@@ -3130,7 +3130,7 @@ private function writeCFHeader(string $cellCoordinate, array $conditionalStyles)
31303130
return true;
31313131
}
31323132

3133-
private function getDataBlockProtection(Conditional $conditional): int
3133+
/*private function getDataBlockProtection(Conditional $conditional): int
31343134
{
31353135
$dataBlockProtection = 0;
31363136
if ($conditional->getStyle()->getProtection()->getLocked() == Protection::PROTECTION_PROTECTED) {
@@ -3141,5 +3141,5 @@ private function getDataBlockProtection(Conditional $conditional): int
31413141
}
31423142
31433143
return $dataBlockProtection;
3144-
}
3144+
}*/
31453145
}

src/PhpSpreadsheet/Writer/Xlsx/Style.php

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -506,56 +506,9 @@ private function writeCellStyleDxf(XMLWriter $objWriter, \PhpOffice\PhpSpreadshe
506506
// fill
507507
$this->writeFill($objWriter, $style->getFill());
508508

509-
// alignment
510-
$horizontal = Alignment::HORIZONTAL_ALIGNMENT_FOR_XLSX[$style->getAlignment()->getHorizontal()] ?? '';
511-
$vertical = Alignment::VERTICAL_ALIGNMENT_FOR_XLSX[$style->getAlignment()->getVertical()] ?? '';
512-
$rotation = $style->getAlignment()->getTextRotation();
513-
if ($horizontal || $vertical || $rotation !== null) {
514-
$objWriter->startElement('alignment');
515-
if ($horizontal) {
516-
$objWriter->writeAttribute('horizontal', $horizontal);
517-
}
518-
if ($vertical) {
519-
$objWriter->writeAttribute('vertical', $vertical);
520-
}
521-
522-
if ($rotation !== null) {
523-
if ($rotation >= 0) {
524-
$textRotation = $rotation;
525-
} else {
526-
$textRotation = 90 - $rotation;
527-
}
528-
$objWriter->writeAttribute('textRotation', (string) $textRotation);
529-
}
530-
$objWriter->endElement();
531-
}
532-
533509
// border
534510
$this->writeBorder($objWriter, $style->getBorders());
535511

536-
// protection
537-
if ((!empty($style->getProtection()->getLocked())) || (!empty($style->getProtection()->getHidden()))) {
538-
if (
539-
$style->getProtection()->getLocked() !== Protection::PROTECTION_INHERIT
540-
|| $style->getProtection()->getHidden() !== Protection::PROTECTION_INHERIT
541-
) {
542-
$objWriter->startElement('protection');
543-
if (
544-
($style->getProtection()->getLocked() !== null)
545-
&& ($style->getProtection()->getLocked() !== Protection::PROTECTION_INHERIT)
546-
) {
547-
$objWriter->writeAttribute('locked', ($style->getProtection()->getLocked() == Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
548-
}
549-
if (
550-
($style->getProtection()->getHidden() !== null)
551-
&& ($style->getProtection()->getHidden() !== Protection::PROTECTION_INHERIT)
552-
) {
553-
$objWriter->writeAttribute('hidden', ($style->getProtection()->getHidden() == Protection::PROTECTION_PROTECTED ? 'true' : 'false'));
554-
}
555-
$objWriter->endElement();
556-
}
557-
}
558-
559512
$objWriter->endElement();
560513
}
561514

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
6+
7+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
8+
use PhpOffice\PhpSpreadsheet\Style\Alignment;
9+
use PhpOffice\PhpSpreadsheet\Style\Conditional;
10+
use PhpOffice\PhpSpreadsheet\Style\Fill;
11+
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
12+
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
13+
use PHPUnit\Framework\TestCase;
14+
15+
class Issue4025Test extends TestCase
16+
{
17+
private static function merge(Worksheet $sheet, string $start, string $end): void
18+
{
19+
$sheet->mergeCells("$start:$end");
20+
$sheet->getStyle($start)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
21+
$sheet->getStyle($start)->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);
22+
}
23+
24+
public function testIssue4025(): void
25+
{
26+
// Writing alignment in conditional style caused weirdness
27+
$spreadsheet = new Spreadsheet();
28+
$sheet = $spreadsheet->getActiveSheet();
29+
$sheet->setRightToLeft(true);
30+
$sheet->setCellValue('E1', 'אושר עד');
31+
$sheet->setCellValue('M1', 'ויקטורי');
32+
$sheet->setCellValue('E2', '2024-05-01');
33+
$sheet->setCellValue('H2', '2024-05-12');
34+
$sheet->setCellValue('M2', '2024-05-01');
35+
$sheet->setCellValue('P2', '2024-05-12');
36+
$sheet->setCellValue('A3', 'ברקוד');
37+
$sheet->setCellValue('B3', 'תיאור מוצר');
38+
$sheet->setCellValue('C3', 'קטגוריה');
39+
$sheet->setCellValue('D3', 'יצרן');
40+
$sheet->setCellValue('E3', 'מחיר');
41+
$sheet->setCellValue('F3', 'מבצע');
42+
$sheet->setCellValue('G3', 'קובע');
43+
$sheet->setCellValue('H3', 'מחיר');
44+
$sheet->setCellValue('I3', 'מבצע');
45+
$sheet->setCellValue('J3', 'קובע');
46+
$sheet->setCellValue('K3', 'הפרש ב-₪');
47+
$sheet->setCellValue('L3', 'הפרש ב-₪');
48+
$sheet->setCellValue('M3', 'מחיר');
49+
$sheet->setCellValue('N3', 'מבצע');
50+
$sheet->setCellValue('O3', 'קובע');
51+
$sheet->setCellValue('P3', 'מחיר');
52+
$sheet->setCellValue('Q3', 'מבצע');
53+
$sheet->setCellValue('R3', 'קובע');
54+
$sheet->setCellValue('S3', 'הפרש ב-₪');
55+
$sheet->setCellValue('T3', 'הפרש ב-₪');
56+
$sheet->setCellValue('A4', '7290010945306');
57+
$sheet->setCellValue('B4', 'חלב טרי בקרטון 3% בד"צ טרה 2 ליטר');
58+
$sheet->setCellValue('C4', 'חלב טרי');
59+
$sheet->setCellValue('D4', 'החברה המרכזית ל.מ.ק - טרה');
60+
61+
$sheet->setCellValue('E4', 13.50);
62+
$sheet->setCellValue('F4', '-');
63+
//$sheet->setCellValue('G4', 13.50);
64+
$sheet->setCellValue('G4', 14.70);
65+
$sheet->setCellValue('H4', 14.20);
66+
$sheet->setCellValue('I4', '-');
67+
$sheet->setCellValue('J4', 14.20);
68+
$sheet->setCellValue('K4', 0.70);
69+
$sheet->setCellValue('L4', 0.0519);
70+
$sheet->setCellValue('M4', 13.62);
71+
$sheet->setCellValue('N4', '-');
72+
$sheet->setCellValue('O4', 13.62);
73+
$sheet->setCellValue('P4', 14.22);
74+
$sheet->setCellValue('Q4', '-');
75+
$sheet->setCellValue('R4', 14.22);
76+
$sheet->setCellValue('S4', 0.60);
77+
$sheet->setCellValue('T4', 0.0441);
78+
79+
$sheet->setCellValue('E10', '=SUM(G4:G10)');
80+
$sheet->setCellValue('H10', '=SUM(J4:J10)');
81+
$sheet->setCellValue('K10', '=COUNTIF(K4:K9,">0")');
82+
$sheet->setCellValue('M10', '=SUM(O4:O10)');
83+
$sheet->setCellValue('P10', '=SUM(R4:R10)');
84+
$sheet->setCellValue('S10', '=COUNTIF(S4:S9,">0")');
85+
$sheet->setCellValue('H11', '=(SUM(J4:J10) / SUM(G4:G10) - 1)');
86+
$sheet->setCellValue('K11', '=COUNTIF(K4:K9,"<0")');
87+
$sheet->setCellValue('P11', '=(SUM(R4:R10) / SUM(O4:O10) - 1)');
88+
$sheet->setCellValue('S11', '=COUNTIF(S4:S9,"<0")');
89+
90+
$sheet->setAutoFilter('A3:T3');
91+
self::merge($sheet, 'E1', 'L1');
92+
self::merge($sheet, 'E2', 'G2');
93+
self::merge($sheet, 'H2', 'J2');
94+
self::merge($sheet, 'K2', 'L2');
95+
self::merge($sheet, 'M1', 'T1');
96+
self::merge($sheet, 'M2', 'O2');
97+
self::merge($sheet, 'P2', 'R2');
98+
self::merge($sheet, 'S2', 'T2');
99+
self::merge($sheet, 'E10', 'G10');
100+
self::merge($sheet, 'H10', 'J10');
101+
self::merge($sheet, 'H11', 'J11');
102+
self::merge($sheet, 'M10', 'O10');
103+
self::merge($sheet, 'P10', 'R10');
104+
self::merge($sheet, 'P11', 'R11');
105+
self::merge($sheet, 'K10', 'L10');
106+
self::merge($sheet, 'K11', 'L11');
107+
self::merge($sheet, 'S10', 'T10');
108+
self::merge($sheet, 'S11', 'T11');
109+
110+
$sheet->getStyle('E4:K8')->getNumberFormat()->setFormatCode('0.00');
111+
$sheet->getStyle('L4:L8')->getNumberFormat()->setFormatCode('0.00%');
112+
$sheet->getStyle('M4:S8')->getNumberFormat()->setFormatCode('0.00');
113+
$sheet->getStyle('T4:T8')->getNumberFormat()->setFormatCode('0.00%');
114+
$sheet->getStyle('P10')->getNumberFormat()->setFormatCode('0.00');
115+
$sheet->getStyle('P11')->getNumberFormat()->setFormatCode('0.00%');
116+
$sheet->getStyle('H1')->getNumberFormat()->setFormatCode('0.00');
117+
$sheet->getStyle('H11')->getNumberFormat()->setFormatCode('0.00%');
118+
119+
$center = [
120+
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER],
121+
];
122+
$pink = [
123+
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER],
124+
'font' => ['color' => ['rgb' => 'FF0000']],
125+
'fill' => ['fillType' => Fill::FILL_SOLID, 'endColor' => ['argb' => 'FFFFE5E8'], 'startColor' => ['argb' => 'FFFFE5E8']],
126+
];
127+
$green = [
128+
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER],
129+
'font' => ['color' => ['rgb' => '339966']],
130+
'fill' => ['fillType' => Fill::FILL_SOLID, 'endColor' => ['argb' => 'ffccffcc'], 'startColor' => ['argb' => 'ffccffcc']],
131+
];
132+
//$sheet->getStyle('E4:T8')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
133+
$sheet->getStyle('E4:T8')->applyFromArray($center);
134+
$sheet->getStyle('H4')->applyFromArray($pink);
135+
$sheet->getStyle('J4:L4')->applyFromArray($pink);
136+
$sheet->getStyle('P4')->applyFromArray($pink);
137+
$sheet->getStyle('R4:T4')->applyFromArray($pink);
138+
$sheet->getStyle('K10')->applyFromArray($pink);
139+
$sheet->getStyle('S10')->applyFromArray($pink);
140+
$sheet->getStyle('K11')->applyFromArray($green);
141+
$sheet->getStyle('S11')->applyFromArray($green);
142+
143+
$condition1a = new Conditional();
144+
$condition1a->setConditionType(Conditional::CONDITION_EXPRESSION);
145+
$condition1a->addCondition('$H$10 < $E$10');
146+
$condition1a->getStyle()->applyFromArray($green);
147+
$condition1b = new Conditional();
148+
$condition1b->setConditionType(Conditional::CONDITION_EXPRESSION);
149+
$condition1b->addCondition('$H$10 > $E$10');
150+
$condition1b->getStyle()->applyFromArray($pink);
151+
$conditionalStyles = [$condition1a, $condition1b];
152+
$sheet->getStyle('H10:H11')->setConditionalStyles($conditionalStyles);
153+
154+
$condition2a = new Conditional();
155+
$condition2a->setConditionType(Conditional::CONDITION_EXPRESSION);
156+
$condition2a->addCondition('$P$10 < $M$10');
157+
$condition2a->getStyle()->applyFromArray($green);
158+
$condition2b = new Conditional();
159+
$condition2b->setConditionType(Conditional::CONDITION_EXPRESSION);
160+
$condition2b->addCondition('$P$10 > $M$10');
161+
$condition2b->getStyle()->applyFromArray($pink);
162+
$conditionalStyles2 = [$condition2a, $condition2b];
163+
$sheet->getStyle('P10:P11')->setConditionalStyles($conditionalStyles2);
164+
165+
$sheet->setSelectedCells('A1');
166+
$oufil = 'issue.4025b.xlsx';
167+
$writer = new XlsxWriter($spreadsheet);
168+
169+
$writer->getStylesConditionalHashTable()->addFromSource($writer->getWriterPartStyle()->allConditionalStyles($spreadsheet));
170+
$writerStyle = new XlsxWriter\Style($writer);
171+
$data = $writerStyle->writeStyles($spreadsheet);
172+
$spreadsheet->disconnectWorksheets();
173+
174+
$preg = preg_match('~<dxfs.*</dxfs>~ms', $data, $matches);
175+
if ($preg !== 1) {
176+
self::fail('preg failed');
177+
} else {
178+
self::assertStringContainsString('<font', $matches[0]);
179+
self::assertStringContainsString('<fill', $matches[0]);
180+
self::assertStringNotContainsString('<align', $matches[0]);
181+
}
182+
}
183+
}

0 commit comments

Comments
 (0)