Failed Conditions
Push — master ( 9dc82e...e23c98 )
by
unknown
20:43 queued 07:22
created

Styles::styles()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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