Skip to content

Commit 2d389f5

Browse files
committed
Restore RGB/ARGB Interchangeability
Fix #2494. Apparently EPPlus outputs fill colors as `<fgColor rgb="BFBFBF">` while most output fill colors as `<fgColor rgb="FFBFBFBF">`. EPPlus actually makes more sense. Regardless, validating length of rgb/argb is a recent development for PhpSpreadsheet, under the assumption that an incorrect length is a user error. This development invalidates that assumption, so restore the previous behavior. In addition, a comment in Colors.php says that the supplied color is "the ARGB value for the colour, or named colour". However, although named colors are accepted, nothing sensible is done with them - they are passed unchanged to the ARGB value, where Excel treats them as black. The routine should either reject the named color, or convert it to the appropriate ARGB value. This change implements the latter.
1 parent d6622c1 commit 2d389f5

File tree

5 files changed

+84
-24
lines changed

5 files changed

+84
-24
lines changed

src/PhpSpreadsheet/Style/Color.php

+35-23
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,24 @@ class Color extends Supervisor
2626
const COLOR_DARKGREEN = 'FF008000';
2727
const COLOR_YELLOW = 'FFFFFF00';
2828
const COLOR_DARKYELLOW = 'FF808000';
29+
const COLOR_MAGENTA = 'FFFF00FF';
30+
const COLOR_CYAN = 'FF00FFFF';
31+
32+
const NAMED_COLOR_TRANSLATIONS = [
33+
'Black' => self::COLOR_BLACK,
34+
'White' => self::COLOR_WHITE,
35+
'Red' => self::COLOR_RED,
36+
'Green' => self::COLOR_GREEN,
37+
'Blue' => self::COLOR_BLUE,
38+
'Yellow' => self::COLOR_YELLOW,
39+
'Magenta' => self::COLOR_MAGENTA,
40+
'Cyan' => self::COLOR_CYAN,
41+
];
2942

3043
const VALIDATE_ARGB_SIZE = 8;
3144
const VALIDATE_RGB_SIZE = 6;
32-
const VALIDATE_COLOR_VALUE = '/^[A-F0-9]{%d}$/i';
45+
const VALIDATE_COLOR_6 = '/^[A-F0-9]{6}$/i';
46+
const VALIDATE_COLOR_8 = '/^[A-F0-9]{8}$/i';
3347

3448
/**
3549
* Indexed colors array.
@@ -66,7 +80,7 @@ public function __construct($colorValue = self::COLOR_BLACK, $isSupervisor = fal
6680

6781
// Initialise values
6882
if (!$isConditional) {
69-
$this->argb = $this->validateColor($colorValue, self::VALIDATE_ARGB_SIZE) ? $colorValue : self::COLOR_BLACK;
83+
$this->argb = $this->validateColor($colorValue) ?: self::COLOR_BLACK;
7084
}
7185
}
7286

@@ -135,10 +149,23 @@ public function applyFromArray(array $styleArray)
135149
return $this;
136150
}
137151

138-
private function validateColor(string $colorValue, int $size): bool
152+
private function validateColor(?string $colorValue): string
139153
{
140-
return in_array(ucfirst(strtolower($colorValue)), self::NAMED_COLORS) ||
141-
preg_match(sprintf(self::VALIDATE_COLOR_VALUE, $size), $colorValue);
154+
if ($colorValue === null || $colorValue === '') {
155+
return self::COLOR_BLACK;
156+
}
157+
$named = ucfirst(strtolower($colorValue));
158+
if (array_key_exists($named, self::NAMED_COLOR_TRANSLATIONS)) {
159+
return self::NAMED_COLOR_TRANSLATIONS[$named];
160+
}
161+
if (preg_match(self::VALIDATE_COLOR_8, $colorValue) === 1) {
162+
return $colorValue;
163+
}
164+
if (preg_match(self::VALIDATE_COLOR_6, $colorValue) === 1) {
165+
return 'FF' . $colorValue;
166+
}
167+
168+
return '';
142169
}
143170

144171
/**
@@ -163,9 +190,8 @@ public function getARGB(): ?string
163190
public function setARGB(?string $colorValue = self::COLOR_BLACK)
164191
{
165192
$this->hasChanged = true;
166-
if ($colorValue === '' || $colorValue === null) {
167-
$colorValue = self::COLOR_BLACK;
168-
} elseif (!$this->validateColor($colorValue, self::VALIDATE_ARGB_SIZE)) {
193+
$colorValue = $this->validateColor($colorValue);
194+
if ($colorValue === '') {
169195
return $this;
170196
}
171197

@@ -200,21 +226,7 @@ public function getRGB(): string
200226
*/
201227
public function setRGB(?string $colorValue = self::COLOR_BLACK)
202228
{
203-
$this->hasChanged = true;
204-
if ($colorValue === '' || $colorValue === null) {
205-
$colorValue = '000000';
206-
} elseif (!$this->validateColor($colorValue, self::VALIDATE_RGB_SIZE)) {
207-
return $this;
208-
}
209-
210-
if ($this->isSupervisor) {
211-
$styleArray = $this->getStyleArray(['argb' => 'FF' . $colorValue]);
212-
$this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
213-
} else {
214-
$this->argb = 'FF' . $colorValue;
215-
}
216-
217-
return $this;
229+
return $this->setARGB($colorValue);
218230
}
219231

220232
/**

tests/PhpSpreadsheetTests/CommentTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public function testSetFillColor(): void
6565
{
6666
$comment = new Comment();
6767
$comment->setFillColor(new Color('RED'));
68-
self::assertEquals('RED', $comment->getFillColor()->getARGB());
68+
self::assertEquals(Color::COLOR_RED, $comment->getFillColor()->getARGB());
6969
}
7070

7171
public function testSetAlignment(): void
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
4+
5+
use PhpOffice\PhpSpreadsheet\IOFactory;
6+
use PHPUnit\Framework\TestCase;
7+
8+
class Issue2494Test extends TestCase
9+
{
10+
public function testIssue2494(): void
11+
{
12+
// Fill style incorrect.
13+
$filename = 'tests/data/Reader/XLSX/issue.2494.xlsx';
14+
$reader = IOFactory::createReader('Xlsx');
15+
$spreadsheet = $reader->load($filename);
16+
$sheet = $spreadsheet->getActiveSheet();
17+
self::assertTrue($sheet->getCell('C3')->getStyle()->getFont()->getBold());
18+
self::assertSame('FFBFBFBF', $sheet->getCell('D8')->getStyle()->getFill()->getStartColor()->getArgb());
19+
$spreadsheet->disconnectWorksheets();
20+
}
21+
}

tests/PhpSpreadsheetTests/Style/ColorTest.php

+27
Original file line numberDiff line numberDiff line change
@@ -157,4 +157,31 @@ public function testDefaultColor(): void
157157
self::assertEquals(Color::COLOR_BLACK, $color->getARGB());
158158
self::assertEquals('000000', $color->getRGB());
159159
}
160+
161+
public function testNamedColors(): void
162+
{
163+
$color = new Color();
164+
$color->setARGB('Blue');
165+
self::assertEquals(Color::COLOR_BLUE, $color->getARGB());
166+
$color->setARGB('black');
167+
self::assertEquals(Color::COLOR_BLACK, $color->getARGB());
168+
$color->setARGB('wHite');
169+
self::assertEquals(Color::COLOR_WHITE, $color->getARGB());
170+
$color->setRGB('reD');
171+
self::assertEquals(Color::COLOR_RED, $color->getARGB());
172+
$color->setRGB('GREEN');
173+
self::assertEquals(Color::COLOR_GREEN, $color->getARGB());
174+
$color->setRGB('magenta');
175+
self::assertEquals(Color::COLOR_MAGENTA, $color->getARGB());
176+
$color->setRGB('YeLlOw');
177+
self::assertEquals(Color::COLOR_YELLOW, $color->getARGB());
178+
$color->setRGB('CYAN');
179+
self::assertEquals(Color::COLOR_CYAN, $color->getARGB());
180+
$color->setRGB('123456ab');
181+
self::assertEquals('123456ab', $color->getARGB());
182+
self::assertEquals('3456ab', $color->getRGB());
183+
$color->setARGB('3456cd');
184+
self::assertEquals('FF3456cd', $color->getARGB());
185+
self::assertEquals('3456cd', $color->getRGB());
186+
}
160187
}
21.4 KB
Binary file not shown.

0 commit comments

Comments
 (0)