Styles::readFillStyle()   F
last analyzed

Complexity

Conditions 17
Paths 291

Size

Total Lines 57
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 40
CRAP Score 17.0041

Importance

Changes 0
Metric Value
eloc 43
c 0
b 0
f 0
dl 0
loc 57
ccs 40
cts 41
cp 0.9756
rs 3.2458
cc 17
nc 291
nop 2
crap 17.0041

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
4
5
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
6
use PhpOffice\PhpSpreadsheet\Style\Alignment;
7
use PhpOffice\PhpSpreadsheet\Style\Border;
8
use PhpOffice\PhpSpreadsheet\Style\Borders;
9
use PhpOffice\PhpSpreadsheet\Style\Color;
10
use PhpOffice\PhpSpreadsheet\Style\Fill;
11
use PhpOffice\PhpSpreadsheet\Style\Font;
12
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
13
use PhpOffice\PhpSpreadsheet\Style\Protection;
14
use PhpOffice\PhpSpreadsheet\Style\Style;
15
use PhpOffice\PhpSpreadsheet\Worksheet\Table\TableDxfsStyle;
16
use SimpleXMLElement;
17
use stdClass;
18
19
class Styles extends BaseParserClass
20
{
21
    /**
22
     * Theme instance.
23
     */
24
    private ?Theme $theme = null;
25
26
    /** @var string[] */
27
    private array $workbookPalette = [];
28
29
    /** @var mixed[] */
30
    private array $styles = [];
31
32
    /** @var array<SimpleXMLElement|stdClass> */
33
    private array $cellStyles = [];
34
35
    private SimpleXMLElement $styleXml;
36
37
    private string $namespace = '';
38
39 682
    public function setNamespace(string $namespace): void
40
    {
41 682
        $this->namespace = $namespace;
42
    }
43
44
    /** @param string[] $palette */
45 682
    public function setWorkbookPalette(array $palette): void
46
    {
47 682
        $this->workbookPalette = $palette;
48
    }
49
50 684
    private function getStyleAttributes(SimpleXMLElement $value): SimpleXMLElement
51
    {
52 684
        $attr = $value->attributes('');
53 684
        if ($attr === null || count($attr) === 0) {
54 679
            $attr = $value->attributes($this->namespace);
55
        }
56
57 684
        return Xlsx::testSimpleXml($attr);
58
    }
59
60 682
    public function setStyleXml(SimpleXMLElement $styleXml): void
61
    {
62 682
        $this->styleXml = $styleXml;
63
    }
64
65 662
    public function setTheme(Theme $theme): void
66
    {
67 662
        $this->theme = $theme;
68
    }
69
70
    /**
71
     * @param mixed[] $styles
72
     * @param array<SimpleXMLElement|stdClass> $cellStyles
73
     */
74 682
    public function setStyleBaseData(?Theme $theme = null, array $styles = [], array $cellStyles = []): void
75
    {
76 682
        $this->theme = $theme;
77 682
        $this->styles = $styles;
78 682
        $this->cellStyles = $cellStyles;
79
    }
80
81 679
    public function readFontStyle(Font $fontStyle, SimpleXMLElement $fontStyleXml): void
82
    {
83 679
        if (isset($fontStyleXml->name)) {
84 678
            $attr = $this->getStyleAttributes($fontStyleXml->name);
85 678
            if (isset($attr['val'])) {
86 678
                $fontStyle->setName((string) $attr['val']);
87
            }
88
        }
89 679
        if (isset($fontStyleXml->sz)) {
90 678
            $attr = $this->getStyleAttributes($fontStyleXml->sz);
91 678
            if (isset($attr['val'])) {
92 678
                $fontStyle->setSize((float) $attr['val']);
93
            }
94
        }
95 679
        if (isset($fontStyleXml->b)) {
96 515
            $attr = $this->getStyleAttributes($fontStyleXml->b);
97 515
            $fontStyle->setBold(!isset($attr['val']) || self::boolean((string) $attr['val']));
98
        }
99 679
        if (isset($fontStyleXml->i)) {
100 477
            $attr = $this->getStyleAttributes($fontStyleXml->i);
101 477
            $fontStyle->setItalic(!isset($attr['val']) || self::boolean((string) $attr['val']));
102
        }
103 679
        if (isset($fontStyleXml->strike)) {
104 458
            $attr = $this->getStyleAttributes($fontStyleXml->strike);
105 458
            $fontStyle->setStrikethrough(!isset($attr['val']) || self::boolean((string) $attr['val']));
106
        }
107 679
        $fontStyle->getColor()
108 679
            ->setARGB(
109 679
                $this->readColor($fontStyleXml->color)
110 679
            );
111 679
        $theme = $this->readColorTheme($fontStyleXml->color);
112 679
        if ($theme >= 0) {
113 404
            $fontStyle->getColor()->setTheme($theme);
114
        }
115
116 679
        if (isset($fontStyleXml->u)) {
117 472
            $attr = $this->getStyleAttributes($fontStyleXml->u);
118 472
            if (!isset($attr['val'])) {
119 205
                $fontStyle->setUnderline(Font::UNDERLINE_SINGLE);
120
            } else {
121 452
                $fontStyle->setUnderline((string) $attr['val']);
122
            }
123
        }
124 679
        if (isset($fontStyleXml->vertAlign)) {
125 24
            $attr = $this->getStyleAttributes($fontStyleXml->vertAlign);
126 24
            if (isset($attr['val'])) {
127 24
                $verticalAlign = strtolower((string) $attr['val']);
128 24
                if ($verticalAlign === 'superscript') {
129 2
                    $fontStyle->setSuperscript(true);
130 24
                } elseif ($verticalAlign === 'subscript') {
131 2
                    $fontStyle->setSubscript(true);
132
                }
133
            }
134
        }
135 679
        if (isset($fontStyleXml->scheme)) {
136 398
            $attr = $this->getStyleAttributes($fontStyleXml->scheme);
137 398
            $fontStyle->setScheme((string) $attr['val']);
138
        }
139
    }
140
141 239
    private function readNumberFormat(NumberFormat $numfmtStyle, SimpleXMLElement $numfmtStyleXml): void
142
    {
143 239
        if ((string) $numfmtStyleXml['formatCode'] !== '') {
144
            $numfmtStyle->setFormatCode(self::formatGeneral((string) $numfmtStyleXml['formatCode']));
145
146
            return;
147
        }
148 239
        $numfmt = $this->getStyleAttributes($numfmtStyleXml);
149 239
        if (isset($numfmt['formatCode'])) {
150 197
            $numfmtStyle->setFormatCode(self::formatGeneral((string) $numfmt['formatCode']));
151
        }
152
    }
153
154 679
    public function readFillStyle(Fill $fillStyle, SimpleXMLElement $fillStyleXml): void
155
    {
156 679
        if ($fillStyleXml->gradientFill) {
157
            /** @var SimpleXMLElement $gradientFill */
158 4
            $gradientFill = $fillStyleXml->gradientFill[0];
159 4
            $attr = $this->getStyleAttributes($gradientFill);
160 4
            if (!empty($attr['type'])) {
161 2
                $fillStyle->setFillType((string) $attr['type']);
162
            }
163 4
            $fillStyle->setRotation((float) ($attr['degree']));
164 4
            $gradientFill->registerXPathNamespace('sml', Namespaces::MAIN);
165 4
            $fillStyle->getStartColor()->setARGB($this->readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color)); //* @phpstan-ignore-line
166 4
            $fillStyle->getEndColor()->setARGB($this->readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color)); //* @phpstan-ignore-line
167 679
        } elseif ($fillStyleXml->patternFill) {
168 678
            $defaultFillStyle = ($fillStyle->getFillType() !== null) ? Fill::FILL_NONE : '';
169 678
            $fgFound = false;
170 678
            $bgFound = false;
171 678
            if ($fillStyleXml->patternFill->fgColor) {
172 268
                $fillStyle->getStartColor()->setARGB($this->readColor($fillStyleXml->patternFill->fgColor, true));
173 268
                if ($fillStyle->getFillType() !== null) {
174 84
                    $defaultFillStyle = Fill::FILL_SOLID;
175
                }
176 268
                $fgFound = true;
177
            }
178 678
            if ($fillStyleXml->patternFill->bgColor) {
179 279
                $fillStyle->getEndColor()->setARGB($this->readColor($fillStyleXml->patternFill->bgColor, true));
180 279
                if ($fillStyle->getFillType() !== null) {
181 80
                    $defaultFillStyle = Fill::FILL_SOLID;
182
                }
183 279
                $bgFound = true;
184
            }
185
186 678
            $type = '';
187 678
            if ((string) $fillStyleXml->patternFill['patternType'] !== '') {
188
                $type = (string) $fillStyleXml->patternFill['patternType'];
189
            } else {
190 678
                $attr = $this->getStyleAttributes($fillStyleXml->patternFill);
191 678
                $type = (string) $attr['patternType'];
192
            }
193 678
            $patternType = ($type === '') ? $defaultFillStyle : $type;
194
195 678
            $fillStyle->setFillType($patternType);
196
            if (
197 678
                !$fgFound // no foreground color specified
198 678
                && !in_array($patternType, [Fill::FILL_NONE, Fill::FILL_SOLID], true) // these patterns aren't relevant
199 678
                && $fillStyle->getStartColor()->getARGB() // not conditional
200
            ) {
201 11
                $fillStyle->getStartColor()
202 11
                    ->setARGB('', true);
203
            }
204
            if (
205 678
                !$bgFound // no background color specified
206 678
                && !in_array($patternType, [Fill::FILL_NONE, Fill::FILL_SOLID], true) // these patterns aren't relevant
207 678
                && $fillStyle->getEndColor()->getARGB() // not conditional
208
            ) {
209 11
                $fillStyle->getEndColor()
210 11
                    ->setARGB('', true);
211
            }
212
        }
213
    }
214
215 679
    public function readBorderStyle(Borders $borderStyle, SimpleXMLElement $borderStyleXml): void
216
    {
217 679
        $diagonalUp = $this->getAttribute($borderStyleXml, 'diagonalUp');
218 679
        $diagonalUp = self::boolean($diagonalUp);
219 679
        $diagonalDown = $this->getAttribute($borderStyleXml, 'diagonalDown');
220 679
        $diagonalDown = self::boolean($diagonalDown);
221 679
        if ($diagonalUp === false) {
222 679
            if ($diagonalDown === false) {
223 679
                $borderStyle->setDiagonalDirection(Borders::DIAGONAL_NONE);
224
            } else {
225 2
                $borderStyle->setDiagonalDirection(Borders::DIAGONAL_DOWN);
226
            }
227 3
        } elseif ($diagonalDown === false) {
228 2
            $borderStyle->setDiagonalDirection(Borders::DIAGONAL_UP);
229
        } else {
230 1
            $borderStyle->setDiagonalDirection(Borders::DIAGONAL_BOTH);
231
        }
232
233 679
        if (isset($borderStyleXml->left)) {
234 660
            $this->readBorder($borderStyle->getLeft(), $borderStyleXml->left);
235
        }
236 679
        if (isset($borderStyleXml->right)) {
237 659
            $this->readBorder($borderStyle->getRight(), $borderStyleXml->right);
238
        }
239 679
        if (isset($borderStyleXml->top)) {
240 661
            $this->readBorder($borderStyle->getTop(), $borderStyleXml->top);
241
        }
242 679
        if (isset($borderStyleXml->bottom)) {
243 660
            $this->readBorder($borderStyle->getBottom(), $borderStyleXml->bottom);
244
        }
245 679
        if (isset($borderStyleXml->diagonal)) {
246 657
            $this->readBorder($borderStyle->getDiagonal(), $borderStyleXml->diagonal);
247
        }
248
    }
249
250 679
    private function getAttribute(SimpleXMLElement $xml, string $attribute): string
251
    {
252 679
        $style = '';
253 679
        if ((string) $xml[$attribute] !== '') {
254
            $style = (string) $xml[$attribute];
255
        } else {
256 679
            $attr = $this->getStyleAttributes($xml);
257 679
            if (isset($attr[$attribute])) {
258 351
                $style = (string) $attr[$attribute];
259
            }
260
        }
261
262 679
        return $style;
263
    }
264
265 661
    private function readBorder(Border $border, SimpleXMLElement $borderXml): void
266
    {
267 661
        $style = $this->getAttribute($borderXml, 'style');
268 661
        if ($style !== '') {
269 83
            $border->setBorderStyle((string) $style);
270
        } else {
271 660
            $border->setBorderStyle(Border::BORDER_NONE);
272
        }
273 661
        if (isset($borderXml->color)) {
274 81
            $border->getColor()->setARGB($this->readColor($borderXml->color));
275
        }
276
    }
277
278 679
    public function readAlignmentStyle(Alignment $alignment, SimpleXMLElement $alignmentXml): void
279
    {
280 679
        $horizontal = (string) $this->getAttribute($alignmentXml, 'horizontal');
281 679
        if ($horizontal !== '') {
282 315
            $alignment->setHorizontal($horizontal);
283
        }
284 679
        $justifyLastLine = (string) $this->getAttribute($alignmentXml, 'justifyLastLine');
285 679
        if ($justifyLastLine !== '') {
286 20
            $alignment->setJustifyLastLine(
287 20
                self::boolean($justifyLastLine)
288 20
            );
289
        }
290 679
        $vertical = (string) $this->getAttribute($alignmentXml, 'vertical');
291 679
        if ($vertical !== '') {
292 300
            $alignment->setVertical($vertical);
293
        }
294
295 679
        $textRotation = (int) $this->getAttribute($alignmentXml, 'textRotation');
296 679
        if ($textRotation > 90) {
297 2
            $textRotation = 90 - $textRotation;
298
        }
299 679
        $alignment->setTextRotation($textRotation);
300
301 679
        $wrapText = $this->getAttribute($alignmentXml, 'wrapText');
302 679
        $alignment->setWrapText(self::boolean((string) $wrapText));
303 679
        $shrinkToFit = $this->getAttribute($alignmentXml, 'shrinkToFit');
304 679
        $alignment->setShrinkToFit(self::boolean((string) $shrinkToFit));
305 679
        $indent = (int) $this->getAttribute($alignmentXml, 'indent');
306 679
        $alignment->setIndent(max($indent, 0));
307 679
        $readingOrder = (int) $this->getAttribute($alignmentXml, 'readingOrder');
308 679
        $alignment->setReadOrder(max($readingOrder, 0));
309
    }
310
311 679
    private static function formatGeneral(string $formatString): string
312
    {
313 679
        if ($formatString === 'GENERAL') {
314 1
            $formatString = NumberFormat::FORMAT_GENERAL;
315
        }
316
317 679
        return $formatString;
318
    }
319
320
    /**
321
     * Read style.
322
     */
323 679
    public function readStyle(Style $docStyle, SimpleXMLElement|stdClass $style): void
324
    {
325 679
        if ($style instanceof SimpleXMLElement) {
326 239
            $this->readNumberFormat($docStyle->getNumberFormat(), $style->numFmt);
327
        } else {
328
            /** @var SimpleXMLElement */
329 679
            $temp = $style->numFmt;
330 679
            $docStyle->getNumberFormat()->setFormatCode(self::formatGeneral((string) $temp));
331
        }
332
333
        /** @var SimpleXMLElement $style */
334 679
        if (isset($style->font)) {
335 679
            $this->readFontStyle($docStyle->getFont(), $style->font);
336
        }
337
338 679
        if (isset($style->fill)) {
339 679
            $this->readFillStyle($docStyle->getFill(), $style->fill);
340
        }
341
342 679
        if (isset($style->border)) {
343 679
            $this->readBorderStyle($docStyle->getBorders(), $style->border);
344
        }
345
346 679
        if (isset($style->alignment)) {
347 679
            $this->readAlignmentStyle($docStyle->getAlignment(), $style->alignment);
348
        }
349
350
        // protection
351 679
        if (isset($style->protection)) {
352 679
            $this->readProtectionLocked($docStyle, $style->protection);
353 679
            $this->readProtectionHidden($docStyle, $style->protection);
354
        }
355
356
        // top-level style settings
357 679
        if (isset($style->quotePrefix)) {
358 679
            $docStyle->setQuotePrefix((bool) $style->quotePrefix);
359
        }
360
    }
361
362
    /**
363
     * Read protection locked attribute.
364
     */
365 679
    public function readProtectionLocked(Style $docStyle, SimpleXMLElement $style): void
366
    {
367 679
        $locked = '';
368 679
        if ((string) $style['locked'] !== '') {
369
            $locked = (string) $style['locked'];
370
        } else {
371 679
            $attr = $this->getStyleAttributes($style);
372 679
            if (isset($attr['locked'])) {
373 40
                $locked = (string) $attr['locked'];
374
            }
375
        }
376 679
        if ($locked !== '') {
377 40
            if (self::boolean($locked)) {
378 17
                $docStyle->getProtection()->setLocked(Protection::PROTECTION_PROTECTED);
379
            } else {
380 23
                $docStyle->getProtection()->setLocked(Protection::PROTECTION_UNPROTECTED);
381
            }
382
        }
383
    }
384
385
    /**
386
     * Read protection hidden attribute.
387
     */
388 679
    public function readProtectionHidden(Style $docStyle, SimpleXMLElement $style): void
389
    {
390 679
        $hidden = '';
391 679
        if ((string) $style['hidden'] !== '') {
392
            $hidden = (string) $style['hidden'];
393
        } else {
394 679
            $attr = $this->getStyleAttributes($style);
395 679
            if (isset($attr['hidden'])) {
396 22
                $hidden = (string) $attr['hidden'];
397
            }
398
        }
399 679
        if ($hidden !== '') {
400 22
            if (self::boolean((string) $hidden)) {
401 1
                $docStyle->getProtection()->setHidden(Protection::PROTECTION_PROTECTED);
402
            } else {
403 21
                $docStyle->getProtection()->setHidden(Protection::PROTECTION_UNPROTECTED);
404
            }
405
        }
406
    }
407
408 679
    public function readColorTheme(SimpleXMLElement $color): int
409
    {
410 679
        $attr = $this->getStyleAttributes($color);
411 679
        $retVal = -1;
412 679
        if (isset($attr['theme']) && is_numeric((string) $attr['theme']) && !isset($attr['tint'])) {
413 404
            $retVal = (int) $attr['theme'];
414
        }
415
416 679
        return $retVal;
417
    }
418
419 684
    public function readColor(SimpleXMLElement $color, bool $background = false): string
420
    {
421 684
        $attr = $this->getStyleAttributes($color);
422 684
        if (isset($attr['rgb'])) {
423 549
            return (string) $attr['rgb'];
424
        }
425 443
        if (isset($attr['indexed'])) {
426 103
            $indexedColor = (int) $attr['indexed'];
427 103
            if ($indexedColor >= count($this->workbookPalette)) {
428 100
                return Color::indexedColor($indexedColor - 7, $background)->getARGB() ?? '';
429
            }
430
431 4
            return Color::indexedColor($indexedColor, $background, $this->workbookPalette)->getARGB() ?? '';
432
        }
433 436
        if (isset($attr['theme'])) {
434 404
            if ($this->theme !== null) {
435 401
                $returnColour = $this->theme->getColourByIndex((int) $attr['theme']);
436 401
                if (isset($attr['tint'])) {
437 213
                    $tintAdjust = (float) $attr['tint'];
438 213
                    $returnColour = Color::changeBrightness($returnColour ?? '', $tintAdjust);
439
                }
440
441 401
                return 'FF' . $returnColour;
442
            }
443
        }
444
445 71
        return ($background) ? 'FFFFFFFF' : 'FF000000';
446
    }
447
448
    /** @return Style[] */
449 682
    public function dxfs(bool $readDataOnly = false): array
450
    {
451 682
        $dxfs = [];
452 682
        if (!$readDataOnly && $this->styleXml) {
453
            //    Conditional Styles
454 679
            if ($this->styleXml->dxfs) {
455 658
                foreach ($this->styleXml->dxfs->dxf as $dxf) {
456 239
                    $style = new Style(false, true);
457 239
                    $this->readStyle($style, $dxf);
458 239
                    $dxfs[] = $style;
459
                }
460
            }
461
            //    Cell Styles
462 679
            if ($this->styleXml->cellStyles) {
463 675
                foreach ($this->styleXml->cellStyles->cellStyle as $cellStylex) {
464 675
                    $cellStyle = Xlsx::getAttributes($cellStylex);
465 675
                    if ((int) ($cellStyle['builtinId']) == 0) {
466 675
                        if (isset($this->cellStyles[(int) ($cellStyle['xfId'])])) {
467
                            // Set default style
468 675
                            $style = new Style();
469 675
                            $this->readStyle($style, $this->cellStyles[(int) ($cellStyle['xfId'])]);
470
471
                            // normal style, currently not using it for anything
472
                        }
473
                    }
474
                }
475
            }
476
        }
477
478 682
        return $dxfs;
479
    }
480
481
    /** @return TableDxfsStyle[] */
482 682
    public function tableStyles(bool $readDataOnly = false): array
483
    {
484 682
        $tableStyles = [];
485 682
        if (!$readDataOnly && $this->styleXml) {
486
            //    Conditional Styles
487 679
            if ($this->styleXml->tableStyles) {
488 652
                foreach ($this->styleXml->tableStyles->tableStyle as $s) {
489 6
                    $attrs = Xlsx::getAttributes($s);
490 6
                    if (isset($attrs['name'][0])) {
491 6
                        $style = new TableDxfsStyle((string) ($attrs['name'][0]));
492 6
                        foreach ($s->tableStyleElement as $e) {
493 6
                            $a = Xlsx::getAttributes($e);
494 6
                            if (isset($a['dxfId'][0], $a['type'][0])) {
495 6
                                switch ($a['type'][0]) {
496 6
                                    case 'headerRow':
497 5
                                        $style->setHeaderRow((int) ($a['dxfId'][0]));
498
499 5
                                        break;
500 6
                                    case 'firstRowStripe':
501 6
                                        $style->setFirstRowStripe((int) ($a['dxfId'][0]));
502
503 6
                                        break;
504 6
                                    case 'secondRowStripe':
505 6
                                        $style->setSecondRowStripe((int) ($a['dxfId'][0]));
506
507 6
                                        break;
508
                                    default:
509
                                }
510
                            }
511
                        }
512 6
                        $tableStyles[] = $style;
513
                    }
514
                }
515
            }
516
        }
517
518 682
        return $tableStyles;
519
    }
520
521
    /** @return mixed[] */
522 682
    public function styles(): array
523
    {
524 682
        return $this->styles;
525
    }
526
527
    /**
528
     * Get array item.
529
     *
530
     * @param false|mixed[] $array (usually array, in theory can be false)
531
     */
532 4
    private static function getArrayItem(mixed $array): ?SimpleXMLElement
533
    {
534 4
        return is_array($array) ? ($array[0] ?? null) : null; // @phpstan-ignore-line
535
    }
536
}
537