Skip to content

Commit 540b315

Browse files
authored
[BUGFIX] Don't render rgb colors with % values as hex (#803)
The only percentage values that could be reliably converted to hex notation are 0%, 20%, 40%, etc. It's beyond the scope of this library to do that. Also add additional tests to confirm parsing of percentage values. Some of these are commented out, because the input data would result in rendering in an invalid format. (The "legacy" syntax does not allow a mixture of `percentage`s and `number`s, so it would be necessary to implement rendering in the "modern" syntax to resolve those cases, which is beyond the scope of this PR.) Co-authored-by: Jake Hotson <[email protected]>
1 parent 98c8bb8 commit 540b315

File tree

3 files changed

+91
-2
lines changed

3 files changed

+91
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ Please also have a look at our
4545

4646
### Fixed
4747

48+
- Don't render `rgb` colors with percentage values using hex notation (#803)
4849
- Parse `@font-face` `src` property as comma-delimited list (#790)
4950
- Fix type errors in PHP strict mode (#664)
5051
- Fix undefined local variable in `CalcFunction::parse()` (#593)

src/Value/Color.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,11 @@ public function __toString(): string
225225
public function render(OutputFormat $outputFormat): string
226226
{
227227
// Shorthand RGB color values
228-
if ($outputFormat->getRGBHashNotation() && \implode('', \array_keys($this->aComponents)) === 'rgb') {
228+
if (
229+
$outputFormat->getRGBHashNotation()
230+
&& \implode('', \array_keys($this->aComponents)) === 'rgb'
231+
&& $this->allComponentsAreNumbers()
232+
) {
229233
$result = \sprintf(
230234
'%02x%02x%02x',
231235
$this->aComponents['r']->getSize(),
@@ -237,4 +241,19 @@ public function render(OutputFormat $outputFormat): string
237241
}
238242
return parent::render($outputFormat);
239243
}
244+
245+
/**
246+
* Test whether all color components are absolute numbers (CSS type `number`), not percentages or anything else.
247+
* If any component is not an instance of `Size`, the method will also return `false`.
248+
*/
249+
private function allComponentsAreNumbers(): bool
250+
{
251+
foreach ($this->aComponents as $component) {
252+
if (!$component instanceof Size || $component->getUnit() !== null) {
253+
return false;
254+
}
255+
}
256+
257+
return true;
258+
}
240259
}

tests/Unit/Value/ColorTest.php

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ public static function provideValidColorAndExpectedRendering(): array
5454
'rgb(0, 118, 0)',
5555
'#007600',
5656
],
57+
'legacy rgb with percentage components' => [
58+
'rgb(0%, 60%, 0%)',
59+
'rgb(0%,60%,0%)',
60+
],
5761
'legacy rgba with fractional alpha' => [
5862
'rgba(0, 119, 0, 0.5)',
5963
'rgba(0,119,0,.5)',
@@ -62,6 +66,14 @@ public static function provideValidColorAndExpectedRendering(): array
6266
'rgba(0, 119, 0, 50%)',
6367
'rgba(0,119,0,50%)',
6468
],
69+
'legacy rgba with percentage components and fractional alpha' => [
70+
'rgba(0%, 60%, 0%, 0.5)',
71+
'rgba(0%,60%,0%,.5)',
72+
],
73+
'legacy rgba with percentage components and percentage alpha' => [
74+
'rgba(0%, 60%, 0%, 50%)',
75+
'rgba(0%,60%,0%,50%)',
76+
],
6577
'legacy rgb as rgba' => [
6678
'rgba(0, 119, 0)',
6779
'#070',
@@ -74,16 +86,73 @@ public static function provideValidColorAndExpectedRendering(): array
7486
'rgb(0 119 0)',
7587
'#070',
7688
],
89+
// The "legacy" syntax currently used for rendering does not allow a mixture of percentages and numbers.
90+
/*
91+
'modern rgb with percentage R' => [
92+
'rgb(0% 119 0)',
93+
'rgb(0% 119 0)',
94+
],
95+
'modern rgb with percentage G' => [
96+
'rgb(0 60% 0)',
97+
'rgb(0 60% 0)',
98+
],
99+
'modern rgb with percentage B' => [
100+
'rgb(0 119 0%)',
101+
'rgb(0 119 0%)',
102+
],
103+
'modern rgb with percentage R&G' => [
104+
'rgb(0% 60% 0)',
105+
'rgb(0% 60% 0)',
106+
],
107+
'modern rgb with percentage R&B' => [
108+
'rgb(0% 119 0%)',
109+
'rgb(0% 119 0%)',
110+
],
111+
'modern rgb with percentage G&B' => [
112+
'rgb(0 60% 0%)',
113+
'rgb(0 60% 0%)',
114+
],
115+
//*/
116+
'modern rgb with percentage components' => [
117+
'rgb(0% 60% 0%)',
118+
'rgb(0%,60%,0%)',
119+
],
77120
/*
78121
'modern rgb with none' => [
79122
'rgb(none 119 0)',
80123
'rgb(none 119 0)',
81124
],
82125
//*/
83-
'modern rgba' => [
126+
'modern rgba with fractional alpha' => [
84127
'rgb(0 119 0 / 0.5)',
85128
'rgba(0,119,0,.5)',
86129
],
130+
'modern rgba with percentage alpha' => [
131+
'rgb(0 119 0 / 50%)',
132+
'rgba(0,119,0,50%)',
133+
],
134+
/*
135+
'modern rgba with percentage R' => [
136+
'rgb(0% 119 0 / 0.5)',
137+
'rgba(0% 119 0/.5)',
138+
],
139+
'modern rgba with percentage G' => [
140+
'rgb(0 60% 0 / 0.5)',
141+
'rgba(0 60% 0/.5)',
142+
],
143+
'modern rgba with percentage B' => [
144+
'rgb(0 119 0% / 0.5)',
145+
'rgba(0 119 0%/.5)',
146+
],
147+
//*/
148+
'modern rgba with percentage RGB' => [
149+
'rgb(0% 60% 0% / 0.5)',
150+
'rgba(0%,60%,0%,.5)',
151+
],
152+
'modern rgba with percentage components' => [
153+
'rgb(0% 60% 0% / 50%)',
154+
'rgba(0%,60%,0%,50%)',
155+
],
87156
/*
88157
'modern rgba with none as alpha' => [
89158
'rgb(0 119 0 / none)',

0 commit comments

Comments
 (0)