Styles::readFontStyle()   F
last analyzed

Complexity

Conditions 18
Paths 2160

Size

Total Lines 50
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 35
CRAP Score 18

Importance

Changes 0
Metric Value
eloc 35
dl 0
loc 50
ccs 35
cts 35
cp 1
rs 0.7
c 0
b 0
f 0
cc 18
nc 2160
nop 2
crap 18

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