Skip to content

Commit 67bf45d

Browse files
authored
Fill Pattern Start and End Colors (#2444)
* Fill Pattern Start and End Colors Fix #2441. The Fill constructor sets start color to white and end color to black and the Xlsx writer writes these values to the output file. This appears to be the wrong setting for all 7 LIGHT* pattern types, 2 of the 7 DARK* patterns (DARKGRAY and DARKTRELLIS), and 1 of the 3 GRAY patterns (GRAY0625). When the wrong colors are written at save time, those patterns are not as expected. Xls writer does not appear to have the same problem. The XML does not require either a start or end color, and the omission of these colors in the file being read was responsible for the problem. The code is changed to mimic that behavior by omitting the color tags at write time if they have not changed from when they were created by the Fill constructor (they will be written for gradient or solid patterns regardless). This is another change which is easier to confirm via samples rather than tests. There are separate samples for Xlsx and Xls; as Excel will be quick to warn you, Xls is not as fully functional as Xlsx with respect to fill patterns. The samples do include a cell where one of the cells (LightGrid in C11) explicitly specifies the "default" colors. * Scrutinizer It somehow ascribed to me a problem in code which was unchanged by this PR. Correct it anyhow, along with some Phpstan fixes (errors now ignored because of change). * Added Tests Also corrected some docBlock problems with Style/*/parent and getSharedComponent. * Create 2 Abstract Methods Scrutinizer complained that 2 methods found in all Supervisor sub-types were not defined in Supervisor. Add abstract methods to satisfy it. * Scrutinizer Ignoring Typehints Try this instead. * Slight Improvement Better handling of Style->getParent().
1 parent a7f687f commit 67bf45d

17 files changed

+269
-136
lines changed

phpstan-baseline.neon

-105
Original file line numberDiff line numberDiff line change
@@ -5510,66 +5510,6 @@ parameters:
55105510
count: 1
55115511
path: src/PhpSpreadsheet/Spreadsheet.php
55125512

5513-
-
5514-
message: "#^Call to an undefined method PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\|PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Style\\:\\:getSharedComponent\\(\\)\\.$#"
5515-
count: 1
5516-
path: src/PhpSpreadsheet/Style/Alignment.php
5517-
5518-
-
5519-
message: "#^Call to an undefined method PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\|PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Style\\:\\:getSharedComponent\\(\\)\\.$#"
5520-
count: 1
5521-
path: src/PhpSpreadsheet/Style/Border.php
5522-
5523-
-
5524-
message: "#^Call to an undefined method PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\|PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Style\\:\\:getStyleArray\\(\\)\\.$#"
5525-
count: 1
5526-
path: src/PhpSpreadsheet/Style/Border.php
5527-
5528-
-
5529-
message: "#^Parameter \\#1 \\$parent of method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Supervisor\\:\\:bindParent\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\|PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Style, \\$this\\(PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Border\\) given\\.$#"
5530-
count: 1
5531-
path: src/PhpSpreadsheet/Style/Border.php
5532-
5533-
-
5534-
message: "#^Call to an undefined method PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\|PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Style\\:\\:getSharedComponent\\(\\)\\.$#"
5535-
count: 1
5536-
path: src/PhpSpreadsheet/Style/Borders.php
5537-
5538-
-
5539-
message: "#^Parameter \\#1 \\$parent of method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Supervisor\\:\\:bindParent\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\|PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Style, \\$this\\(PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Borders\\) given\\.$#"
5540-
count: 10
5541-
path: src/PhpSpreadsheet/Style/Borders.php
5542-
5543-
-
5544-
message: "#^Call to an undefined method PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\|PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Style\\:\\:getSharedComponent\\(\\)\\.$#"
5545-
count: 1
5546-
path: src/PhpSpreadsheet/Style/Color.php
5547-
5548-
-
5549-
message: "#^Call to an undefined method PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\|PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Style\\:\\:getStyleArray\\(\\)\\.$#"
5550-
count: 1
5551-
path: src/PhpSpreadsheet/Style/Color.php
5552-
5553-
-
5554-
message: "#^Call to an undefined method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Border\\|PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Fill\\:\\:getColor\\(\\)\\.$#"
5555-
count: 1
5556-
path: src/PhpSpreadsheet/Style/Color.php
5557-
5558-
-
5559-
message: "#^Call to an undefined method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Border\\|PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Fill\\:\\:getEndColor\\(\\)\\.$#"
5560-
count: 1
5561-
path: src/PhpSpreadsheet/Style/Color.php
5562-
5563-
-
5564-
message: "#^Call to an undefined method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Border\\|PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Fill\\:\\:getStartColor\\(\\)\\.$#"
5565-
count: 1
5566-
path: src/PhpSpreadsheet/Style/Color.php
5567-
5568-
-
5569-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Color\\:\\:getColourComponent\\(\\) should return int\\|string but returns float\\|int\\|string\\.$#"
5570-
count: 1
5571-
path: src/PhpSpreadsheet/Style/Color.php
5572-
55735513
-
55745514
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Conditional\\:\\:\\$condition \\(array\\<string\\>\\) does not accept array\\<bool\\|float\\|int\\|string\\>\\.$#"
55755515
count: 1
@@ -5740,36 +5680,6 @@ parameters:
57405680
count: 1
57415681
path: src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php
57425682

5743-
-
5744-
message: "#^Call to an undefined method PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\|PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Style\\:\\:getSharedComponent\\(\\)\\.$#"
5745-
count: 1
5746-
path: src/PhpSpreadsheet/Style/Fill.php
5747-
5748-
-
5749-
message: "#^Parameter \\#1 \\$parent of method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Supervisor\\:\\:bindParent\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\|PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Style, \\$this\\(PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Fill\\) given\\.$#"
5750-
count: 2
5751-
path: src/PhpSpreadsheet/Style/Fill.php
5752-
5753-
-
5754-
message: "#^Call to an undefined method PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\|PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Style\\:\\:getSharedComponent\\(\\)\\.$#"
5755-
count: 1
5756-
path: src/PhpSpreadsheet/Style/Font.php
5757-
5758-
-
5759-
message: "#^Parameter \\#1 \\$parent of method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Supervisor\\:\\:bindParent\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\|PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Style, \\$this\\(PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\) given\\.$#"
5760-
count: 1
5761-
path: src/PhpSpreadsheet/Style/Font.php
5762-
5763-
-
5764-
message: "#^Call to an undefined method PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\|PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Style\\:\\:getSharedComponent\\(\\)\\.$#"
5765-
count: 1
5766-
path: src/PhpSpreadsheet/Style/NumberFormat.php
5767-
5768-
-
5769-
message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\NumberFormat\\:\\:\\$builtInFormatCode \\(int\\|false\\) does not accept bool\\|int\\.$#"
5770-
count: 1
5771-
path: src/PhpSpreadsheet/Style/NumberFormat.php
5772-
57735683
-
57745684
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\NumberFormat\\\\DateFormatter\\:\\:escapeQuotesCallback\\(\\) has parameter \\$matches with no type specified\\.$#"
57755685
count: 1
@@ -5900,21 +5810,6 @@ parameters:
59005810
count: 1
59015811
path: src/PhpSpreadsheet/Style/NumberFormat/PercentageFormatter.php
59025812

5903-
-
5904-
message: "#^Call to an undefined method PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\|PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Style\\:\\:getSharedComponent\\(\\)\\.$#"
5905-
count: 1
5906-
path: src/PhpSpreadsheet/Style/Protection.php
5907-
5908-
-
5909-
message: "#^Call to an undefined method PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\|PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Style\\:\\:getCellXfByIndex\\(\\)\\.$#"
5910-
count: 1
5911-
path: src/PhpSpreadsheet/Style/Style.php
5912-
5913-
-
5914-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Style\\:\\:getParent\\(\\) should return PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet but returns PhpOffice\\\\PhpSpreadsheet\\\\Spreadsheet\\|PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Style\\.$#"
5915-
count: 1
5916-
path: src/PhpSpreadsheet/Style/Style.php
5917-
59185813
-
59195814
message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#"
59205815
count: 1

samples/Basic/47_xlsfill.php

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
require __DIR__ . '/../Header.php';
4+
5+
use PhpOffice\PhpSpreadsheet\Reader\Xls;
6+
7+
$helper->log('Read spreadsheet');
8+
$reader = new Xls();
9+
$spreadsheet = $reader->load(__DIR__ . '/../templates/47_xlsfill.xls');
10+
11+
// Save
12+
$helper->write($spreadsheet, __FILE__, ['Xls']);
13+
$spreadsheet->disconnectWorksheets();

samples/Basic/47_xlsxfill.php

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
require __DIR__ . '/../Header.php';
4+
5+
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
6+
7+
$helper->log('Read spreadsheet');
8+
$reader = new Xlsx();
9+
$spreadsheet = $reader->load(__DIR__ . '/../templates/47_xlsxfill.xlsx');
10+
11+
// Save
12+
$helper->write($spreadsheet, __FILE__, ['Xlsx']);
13+
$spreadsheet->disconnectWorksheets();

samples/templates/47_xlsfill.xls

33 KB
Binary file not shown.

samples/templates/47_xlsxfill.xlsx

10.4 KB
Binary file not shown.

src/PhpSpreadsheet/Style/Alignment.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,10 @@ public function __construct($isSupervisor = false, $isConditional = false)
111111
*/
112112
public function getSharedComponent()
113113
{
114-
return $this->parent->getSharedComponent()->getAlignment();
114+
/** @var Style */
115+
$parent = $this->parent;
116+
117+
return $parent->getSharedComponent()->getAlignment();
115118
}
116119

117120
/**

src/PhpSpreadsheet/Style/Border.php

+8-2
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,11 @@ public function __construct($isSupervisor = false)
7070
*/
7171
public function getSharedComponent()
7272
{
73+
/** @var Style */
74+
$parent = $this->parent;
75+
7376
/** @var Borders $sharedComponent */
74-
$sharedComponent = $this->parent->getSharedComponent();
77+
$sharedComponent = $parent->getSharedComponent();
7578
switch ($this->parentPropertyName) {
7679
case 'bottom':
7780
return $sharedComponent->getBottom();
@@ -97,7 +100,10 @@ public function getSharedComponent()
97100
*/
98101
public function getStyleArray($array)
99102
{
100-
return $this->parent->getStyleArray([$this->parentPropertyName => $array]);
103+
/** @var Style */
104+
$parent = $this->parent;
105+
106+
return $parent->getStyleArray([$this->parentPropertyName => $array]);
101107
}
102108

103109
/**

src/PhpSpreadsheet/Style/Borders.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,10 @@ public function __construct($isSupervisor = false)
140140
*/
141141
public function getSharedComponent()
142142
{
143-
return $this->parent->getSharedComponent()->getBorders();
143+
/** @var Style */
144+
$parent = $this->parent;
145+
146+
return $parent->getSharedComponent()->getBorders();
144147
}
145148

146149
/**

src/PhpSpreadsheet/Style/Color.php

+27-7
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ class Color extends Supervisor
4545
*/
4646
protected $argb;
4747

48+
/** @var bool */
49+
private $hasChanged = false;
50+
4851
/**
4952
* Create a new Color.
5053
*
@@ -75,12 +78,15 @@ public function __construct($colorValue = self::COLOR_BLACK, $isSupervisor = fal
7578
*/
7679
public function getSharedComponent()
7780
{
81+
/** @var Style */
82+
$parent = $this->parent;
7883
/** @var Border|Fill $sharedComponent */
79-
$sharedComponent = $this->parent->getSharedComponent();
80-
if ($this->parentPropertyName === 'endColor') {
81-
return $sharedComponent->getEndColor();
82-
}
83-
if ($this->parentPropertyName === 'startColor') {
84+
$sharedComponent = $parent->getSharedComponent();
85+
if ($sharedComponent instanceof Fill) {
86+
if ($this->parentPropertyName === 'endColor') {
87+
return $sharedComponent->getEndColor();
88+
}
89+
8490
return $sharedComponent->getStartColor();
8591
}
8692

@@ -96,7 +102,10 @@ public function getSharedComponent()
96102
*/
97103
public function getStyleArray($array)
98104
{
99-
return $this->parent->getStyleArray([$this->parentPropertyName => $array]);
105+
/** @var Style */
106+
$parent = $this->parent;
107+
108+
return $parent->getStyleArray([$this->parentPropertyName => $array]);
100109
}
101110

102111
/**
@@ -153,6 +162,7 @@ public function getARGB(): ?string
153162
*/
154163
public function setARGB(?string $colorValue = self::COLOR_BLACK)
155164
{
165+
$this->hasChanged = true;
156166
if ($colorValue === '' || $colorValue === null) {
157167
$colorValue = self::COLOR_BLACK;
158168
} elseif (!$this->validateColor($colorValue, self::VALIDATE_ARGB_SIZE)) {
@@ -190,6 +200,7 @@ public function getRGB(): string
190200
*/
191201
public function setRGB(?string $colorValue = self::COLOR_BLACK)
192202
{
203+
$this->hasChanged = true;
193204
if ($colorValue === '' || $colorValue === null) {
194205
$colorValue = '000000';
195206
} elseif (!$this->validateColor($colorValue, self::VALIDATE_RGB_SIZE)) {
@@ -220,7 +231,7 @@ private static function getColourComponent($rgbValue, $offset, $hex = true)
220231
{
221232
$colour = substr($rgbValue, $offset, 2);
222233

223-
return ($hex) ? $colour : hexdec($colour);
234+
return ($hex) ? $colour : (int) hexdec($colour);
224235
}
225236

226237
/**
@@ -410,4 +421,13 @@ protected function exportArray1(): array
410421

411422
return $exportedArray;
412423
}
424+
425+
public function getHasChanged(): bool
426+
{
427+
if ($this->isSupervisor) {
428+
return $this->getSharedComponent()->hasChanged;
429+
}
430+
431+
return $this->hasChanged;
432+
}
413433
}

src/PhpSpreadsheet/Style/Fill.php

+27-5
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class Fill extends Supervisor
4949
*
5050
* @var float
5151
*/
52-
protected $rotation = 0;
52+
protected $rotation = 0.0;
5353

5454
/**
5555
* Start color.
@@ -65,6 +65,9 @@ class Fill extends Supervisor
6565
*/
6666
protected $endColor;
6767

68+
/** @var bool */
69+
private $colorChanged = false;
70+
6871
/**
6972
* Create a new Fill.
7073
*
@@ -102,7 +105,10 @@ public function __construct($isSupervisor = false, $isConditional = false)
102105
*/
103106
public function getSharedComponent()
104107
{
105-
return $this->parent->getSharedComponent()->getFill();
108+
/** @var Style */
109+
$parent = $this->parent;
110+
111+
return $parent->getSharedComponent()->getFill();
106112
}
107113

108114
/**
@@ -124,7 +130,7 @@ public function getStyleArray($array)
124130
* $spreadsheet->getActiveSheet()->getStyle('B2')->getFill()->applyFromArray(
125131
* [
126132
* 'fillType' => Fill::FILL_GRADIENT_LINEAR,
127-
* 'rotation' => 0,
133+
* 'rotation' => 0.0,
128134
* 'startColor' => [
129135
* 'rgb' => '000000'
130136
* ],
@@ -248,6 +254,7 @@ public function getStartColor()
248254
*/
249255
public function setStartColor(Color $color)
250256
{
257+
$this->colorChanged = true;
251258
// make sure parameter is a real color and not a supervisor
252259
$color = $color->getIsSupervisor() ? $color->getSharedComponent() : $color;
253260

@@ -278,6 +285,7 @@ public function getEndColor()
278285
*/
279286
public function setEndColor(Color $color)
280287
{
288+
$this->colorChanged = true;
281289
// make sure parameter is a real color and not a supervisor
282290
$color = $color->getIsSupervisor() ? $color->getSharedComponent() : $color;
283291

@@ -291,6 +299,17 @@ public function setEndColor(Color $color)
291299
return $this;
292300
}
293301

302+
public function getColorsChanged(): bool
303+
{
304+
if ($this->isSupervisor) {
305+
$changed = $this->getSharedComponent()->colorChanged;
306+
} else {
307+
$changed = $this->colorChanged;
308+
}
309+
310+
return $changed || $this->startColor->getHasChanged() || $this->endColor->getHasChanged();
311+
}
312+
294313
/**
295314
* Get hash code.
296315
*
@@ -308,17 +327,20 @@ public function getHashCode()
308327
$this->getRotation() .
309328
($this->getFillType() !== self::FILL_NONE ? $this->getStartColor()->getHashCode() : '') .
310329
($this->getFillType() !== self::FILL_NONE ? $this->getEndColor()->getHashCode() : '') .
330+
((string) $this->getColorsChanged()) .
311331
__CLASS__
312332
);
313333
}
314334

315335
protected function exportArray1(): array
316336
{
317337
$exportedArray = [];
318-
$this->exportArray2($exportedArray, 'endColor', $this->getEndColor());
319338
$this->exportArray2($exportedArray, 'fillType', $this->getFillType());
320339
$this->exportArray2($exportedArray, 'rotation', $this->getRotation());
321-
$this->exportArray2($exportedArray, 'startColor', $this->getStartColor());
340+
if ($this->getColorsChanged()) {
341+
$this->exportArray2($exportedArray, 'endColor', $this->getEndColor());
342+
$this->exportArray2($exportedArray, 'startColor', $this->getStartColor());
343+
}
322344

323345
return $exportedArray;
324346
}

src/PhpSpreadsheet/Style/Font.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,10 @@ public function __construct($isSupervisor = false, $isConditional = false)
122122
*/
123123
public function getSharedComponent()
124124
{
125-
return $this->parent->getSharedComponent()->getFont();
125+
/** @var Style */
126+
$parent = $this->parent;
127+
128+
return $parent->getSharedComponent()->getFont();
126129
}
127130

128131
/**

0 commit comments

Comments
 (0)