Passed
Pull Request — master (#4426)
by
unknown
13:11
created

Color::getStyleArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 6
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Style;
4
5
class Color extends Supervisor
6
{
7
    const NAMED_COLORS = [
8
        'Black',
9
        'White',
10
        'Red',
11
        'Green',
12
        'Blue',
13
        'Yellow',
14
        'Magenta',
15
        'Cyan',
16
    ];
17
18
    // Colors
19
    const COLOR_BLACK = 'FF000000';
20
    const COLOR_WHITE = 'FFFFFFFF';
21
    const COLOR_RED = 'FFFF0000';
22
    const COLOR_DARKRED = 'FF800000';
23
    const COLOR_BLUE = 'FF0000FF';
24
    const COLOR_DARKBLUE = 'FF000080';
25
    const COLOR_GREEN = 'FF00FF00';
26
    const COLOR_DARKGREEN = 'FF008000';
27
    const COLOR_YELLOW = 'FFFFFF00';
28
    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
    ];
42
43
    const VALIDATE_ARGB_SIZE = 8;
44
    const VALIDATE_RGB_SIZE = 6;
45
    const VALIDATE_COLOR_6 = '/^[A-F0-9]{6}$/i';
46
    const VALIDATE_COLOR_8 = '/^[A-F0-9]{8}$/i';
47
48
    private const INDEXED_COLORS = [
49
        1 => 'FF000000', //  System Colour #1 - Black
50
        2 => 'FFFFFFFF', //  System Colour #2 - White
51
        3 => 'FFFF0000', //  System Colour #3 - Red
52
        4 => 'FF00FF00', //  System Colour #4 - Green
53
        5 => 'FF0000FF', //  System Colour #5 - Blue
54
        6 => 'FFFFFF00', //  System Colour #6 - Yellow
55
        7 => 'FFFF00FF', //  System Colour #7- Magenta
56
        8 => 'FF00FFFF', //  System Colour #8- Cyan
57
        9 => 'FF800000', //  Standard Colour #9
58
        10 => 'FF008000', //  Standard Colour #10
59
        11 => 'FF000080', //  Standard Colour #11
60
        12 => 'FF808000', //  Standard Colour #12
61
        13 => 'FF800080', //  Standard Colour #13
62
        14 => 'FF008080', //  Standard Colour #14
63
        15 => 'FFC0C0C0', //  Standard Colour #15
64
        16 => 'FF808080', //  Standard Colour #16
65
        17 => 'FF9999FF', //  Chart Fill Colour #17
66
        18 => 'FF993366', //  Chart Fill Colour #18
67
        19 => 'FFFFFFCC', //  Chart Fill Colour #19
68
        20 => 'FFCCFFFF', //  Chart Fill Colour #20
69
        21 => 'FF660066', //  Chart Fill Colour #21
70
        22 => 'FFFF8080', //  Chart Fill Colour #22
71
        23 => 'FF0066CC', //  Chart Fill Colour #23
72
        24 => 'FFCCCCFF', //  Chart Fill Colour #24
73
        25 => 'FF000080', //  Chart Line Colour #25
74
        26 => 'FFFF00FF', //  Chart Line Colour #26
75
        27 => 'FFFFFF00', //  Chart Line Colour #27
76
        28 => 'FF00FFFF', //  Chart Line Colour #28
77
        29 => 'FF800080', //  Chart Line Colour #29
78
        30 => 'FF800000', //  Chart Line Colour #30
79
        31 => 'FF008080', //  Chart Line Colour #31
80
        32 => 'FF0000FF', //  Chart Line Colour #32
81
        33 => 'FF00CCFF', //  Standard Colour #33
82
        34 => 'FFCCFFFF', //  Standard Colour #34
83
        35 => 'FFCCFFCC', //  Standard Colour #35
84
        36 => 'FFFFFF99', //  Standard Colour #36
85
        37 => 'FF99CCFF', //  Standard Colour #37
86
        38 => 'FFFF99CC', //  Standard Colour #38
87
        39 => 'FFCC99FF', //  Standard Colour #39
88
        40 => 'FFFFCC99', //  Standard Colour #40
89
        41 => 'FF3366FF', //  Standard Colour #41
90
        42 => 'FF33CCCC', //  Standard Colour #42
91
        43 => 'FF99CC00', //  Standard Colour #43
92
        44 => 'FFFFCC00', //  Standard Colour #44
93
        45 => 'FFFF9900', //  Standard Colour #45
94
        46 => 'FFFF6600', //  Standard Colour #46
95
        47 => 'FF666699', //  Standard Colour #47
96
        48 => 'FF969696', //  Standard Colour #48
97
        49 => 'FF003366', //  Standard Colour #49
98
        50 => 'FF339966', //  Standard Colour #50
99
        51 => 'FF003300', //  Standard Colour #51
100
        52 => 'FF333300', //  Standard Colour #52
101
        53 => 'FF993300', //  Standard Colour #53
102
        54 => 'FF993366', //  Standard Colour #54
103
        55 => 'FF333399', //  Standard Colour #55
104
        56 => 'FF333333', //  Standard Colour #56
105
    ];
106
107
    /**
108
     * ARGB - Alpha RGB.
109
     */
110
    protected ?string $argb = null;
111
112
    private bool $hasChanged = false;
113
114
    /**
115
     * Create a new Color.
116
     *
117
     * @param string $colorValue ARGB value for the colour, or named colour
118
     * @param bool $isSupervisor Flag indicating if this is a supervisor or not
119
     *                                    Leave this value at default unless you understand exactly what
120
     *                                        its ramifications are
121
     * @param bool $isConditional Flag indicating if this is a conditional style or not
122
     *                                    Leave this value at default unless you understand exactly what
123
     *                                        its ramifications are
124
     */
125 10729
    public function __construct(string $colorValue = self::COLOR_BLACK, bool $isSupervisor = false, bool $isConditional = false)
126
    {
127
        //    Supervisor?
128 10729
        parent::__construct($isSupervisor);
129
130
        //    Initialise values
131 10729
        if (!$isConditional) {
132 10729
            $this->argb = $this->validateColor($colorValue) ?: self::COLOR_BLACK;
133
        }
134
    }
135
136
    /**
137
     * Get the shared style component for the currently active cell in currently active sheet.
138
     * Only used for style supervisor.
139
     */
140 47
    public function getSharedComponent(): self
141
    {
142
        /** @var Style $parent */
143 47
        $parent = $this->parent;
144
        /** @var Border|Fill $sharedComponent */
145 47
        $sharedComponent = $parent->getSharedComponent();
146 47
        if ($sharedComponent instanceof Fill) {
147 38
            if ($this->parentPropertyName === 'endColor') {
148 21
                return $sharedComponent->getEndColor();
149
            }
150
151 37
            return $sharedComponent->getStartColor();
152
        }
153
154 30
        return $sharedComponent->getColor();
155
    }
156
157
    /**
158
     * Build style array from subcomponents.
159
     */
160 46
    public function getStyleArray(array $array): array
161
    {
162
        /** @var Style $parent */
163 46
        $parent = $this->parent;
164
165 46
        return $parent->getStyleArray([$this->parentPropertyName => $array]);
166
    }
167
168
    /**
169
     * Apply styles from array.
170
     *
171
     * <code>
172
     * $spreadsheet->getActiveSheet()->getStyle('B2')->getFont()->getColor()->applyFromArray(['rgb' => '808080']);
173
     * </code>
174
     *
175
     * @param array $styleArray Array containing style information
176
     *
177
     * @return $this
178
     */
179 177
    public function applyFromArray(array $styleArray): static
180
    {
181 177
        if ($this->isSupervisor) {
182 1
            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($styleArray));
183
        } else {
184 177
            if (isset($styleArray['rgb'])) {
185 73
                $this->setRGB($styleArray['rgb']);
186
            }
187 177
            if (isset($styleArray['argb'])) {
188 106
                $this->setARGB($styleArray['argb']);
189
            }
190
        }
191
192 177
        return $this;
193
    }
194
195 10729
    private function validateColor(?string $colorValue): string
196
    {
197 10729
        if ($colorValue === null || $colorValue === '') {
198 4
            return self::COLOR_BLACK;
199
        }
200 10729
        $named = ucfirst(strtolower($colorValue));
201 10729
        if (array_key_exists($named, self::NAMED_COLOR_TRANSLATIONS)) {
202 2
            return self::NAMED_COLOR_TRANSLATIONS[$named];
203
        }
204 10729
        if (preg_match(self::VALIDATE_COLOR_8, $colorValue) === 1) {
205 10729
            return $colorValue;
206
        }
207 243
        if (preg_match(self::VALIDATE_COLOR_6, $colorValue) === 1) {
208 228
            return 'FF' . $colorValue;
209
        }
210
211 18
        return '';
212
    }
213
214
    /**
215
     * Get ARGB.
216
     */
217 692
    public function getARGB(): ?string
218
    {
219 692
        if ($this->isSupervisor) {
220 35
            return $this->getSharedComponent()->getARGB();
221
        }
222
223 692
        return $this->argb;
224
    }
225
226
    /**
227
     * Set ARGB.
228
     *
229
     * @param ?string $colorValue  ARGB value, or a named color
230
     *
231
     * @return $this
232
     */
233 979
    public function setARGB(?string $colorValue = self::COLOR_BLACK, bool $nullStringOkay = false): static
234
    {
235 979
        $this->hasChanged = true;
236 979
        if (!$nullStringOkay || $colorValue !== '') {
237 979
            $colorValue = $this->validateColor($colorValue);
238 979
            if ($colorValue === '') {
239 18
                return $this;
240
            }
241
        }
242
243 977
        if ($this->isSupervisor) {
244 25
            $styleArray = $this->getStyleArray(['argb' => $colorValue]);
245 25
            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
246
        } else {
247 977
            $this->argb = $colorValue;
248
        }
249
        $this->updateHashBeforeUse();
250 977
251
        return $this;
252
    }
253
254
    /**
255
     * Get RGB.
256 728
     */
257
    public function getRGB(): string
258 728
    {
259 20
        if ($this->isSupervisor) {
260
            return $this->getSharedComponent()->getRGB();
261
        }
262 728
263
        return substr($this->argb ?? '', 2);
264
    }
265
266
    /**
267
     * Set RGB.
268
     *
269
     * @param ?string $colorValue RGB value, or a named color
270
     *
271
     * @return $this
272 245
     */
273
    public function setRGB(?string $colorValue = self::COLOR_BLACK): static
274 245
    {
275
        return $this->setARGB($colorValue);
276
    }
277
278
    /**
279
     * Get a specified colour component of an RGB value.
280
     *
281
     * @param string $rgbValue The colour as an RGB value (e.g. FF00CCCC or CCDDEE
282
     * @param int $offset Position within the RGB value to extract
283
     * @param bool $hex Flag indicating whether the component should be returned as a hex or a
284
     *                                    decimal value
285
     *
286
     * @return int|string The extracted colour component
287 245
     */
288
    private static function getColourComponent(string $rgbValue, int $offset, bool $hex = true): string|int
289 245
    {
290 245
        $colour = substr($rgbValue, $offset, 2) ?: '';
291 1
        if (preg_match('/^[0-9a-f]{2}$/i', $colour) !== 1) {
292
            $colour = '00';
293
        }
294 245
295
        return ($hex) ? $colour : (int) hexdec($colour);
296
    }
297
298
    /**
299
     * Get the red colour component of an RGB value.
300
     *
301
     * @param string $rgbValue The colour as an RGB value (e.g. FF00CCCC or CCDDEE
302
     * @param bool $hex Flag indicating whether the component should be returned as a hex or a
303
     *                                    decimal value
304
     *
305
     * @return int|string The red colour component
306 232
     */
307
    public static function getRed(string $rgbValue, bool $hex = true)
308 232
    {
309
        return self::getColourComponent($rgbValue, strlen($rgbValue) - 6, $hex);
310
    }
311
312
    /**
313
     * Get the green colour component of an RGB value.
314
     *
315
     * @param string $rgbValue The colour as an RGB value (e.g. FF00CCCC or CCDDEE
316
     * @param bool $hex Flag indicating whether the component should be returned as a hex or a
317
     *                                    decimal value
318
     *
319
     * @return int|string The green colour component
320 232
     */
321
    public static function getGreen(string $rgbValue, bool $hex = true)
322 232
    {
323
        return self::getColourComponent($rgbValue, strlen($rgbValue) - 4, $hex);
324
    }
325
326
    /**
327
     * Get the blue colour component of an RGB value.
328
     *
329
     * @param string $rgbValue The colour as an RGB value (e.g. FF00CCCC or CCDDEE
330
     * @param bool $hex Flag indicating whether the component should be returned as a hex or a
331
     *                                    decimal value
332
     *
333
     * @return int|string The blue colour component
334 233
     */
335
    public static function getBlue(string $rgbValue, bool $hex = true)
336 233
    {
337
        return self::getColourComponent($rgbValue, strlen($rgbValue) - 2, $hex);
338
    }
339
340
    /**
341
     * Adjust the brightness of a color.
342
     *
343
     * @param string $hexColourValue The colour as an RGBA or RGB value (e.g. FF00CCCC or CCDDEE)
344
     * @param float $adjustPercentage The percentage by which to adjust the colour as a float from -1 to 1
345
     *
346
     * @return string The adjusted colour as an RGBA or RGB value (e.g. FF00CCCC or CCDDEE)
347 226
     */
348
    public static function changeBrightness(string $hexColourValue, float $adjustPercentage): string
349 226
    {
350 226
        $rgba = (strlen($hexColourValue) === 8);
351
        $adjustPercentage = max(-1.0, min(1.0, $adjustPercentage));
352
353 226
        /** @var int $red */
354
        $red = self::getRed($hexColourValue, false);
355 226
        /** @var int $green */
356
        $green = self::getGreen($hexColourValue, false);
357 226
        /** @var int $blue */
358
        $blue = self::getBlue($hexColourValue, false);
359 226
360
        return (($rgba) ? 'FF' : '') . RgbTint::rgbAndTintToRgb($red, $green, $blue, $adjustPercentage);
361
    }
362
363
    /**
364
     * Get indexed color.
365
     *
366
     * @param int $colorIndex Index entry point into the colour array
367
     * @param bool $background Flag to indicate whether default background or foreground colour
368
     *                                            should be returned if the indexed colour doesn't exist
369 101
     */
370
    public static function indexedColor(int $colorIndex, bool $background = false, ?array $palette = null): self
371
    {
372 101
        // Clean parameter
373
        $colorIndex = (int) $colorIndex;
374 101
375 98
        if (empty($palette)) {
376 45
            if (isset(self::INDEXED_COLORS[$colorIndex])) {
377
                return new self(self::INDEXED_COLORS[$colorIndex]);
378
            }
379 4
        } else {
380 4
            if (isset($palette[$colorIndex])) {
381
                return new self($palette[$colorIndex]);
382
            }
383
        }
384 67
385
        return ($background) ? new self(self::COLOR_WHITE) : new self(self::COLOR_BLACK);
386
    }
387
388
    /**
389
     * Update Hash when something changes.
390
     */
391
    protected function updateHash(): void
392 1301
    {
393
        $this->md5Sum = md5(
394 1301
            $this->argb
395 1
            . __CLASS__
396
        );
397
        $this->updateMd5Sum = false;
398 1301
    }
399 1301
400 1301
    /**
401 1301
     * Get hash code.
402
     *
403
     * @return string Hash code
404 14
     */
405
    public function getHashCode(): string
406 14
    {
407 14
        if ($this->isSupervisor) {
408
            return $this->getSharedComponent()->getHashCode();
409 14
        }
410
411
        if ($this->updateMd5Sum) {
412 1249
            $this->updateHash();
413
        }
414 1249
415 8
        return $this->md5Sum;
416
    }
417
418 1249
    protected function exportArray1(): array
419
    {
420
        $exportedArray = [];
421
        $this->exportArray2($exportedArray, 'argb', $this->getARGB());
422
423
        return $exportedArray;
424
    }
425
426
    public function getHasChanged(): bool
427
    {
428
        if ($this->isSupervisor) {
429
            return $this->getSharedComponent()->hasChanged;
430
        }
431
432
        return $this->hasChanged;
433
    }
434
}
435