Passed
Pull Request — master (#3468)
by Mark
17:10 queued 07:39
created

Styles::readProtectionHidden()   A

Complexity

Conditions 5
Paths 9

Size

Total Lines 16
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5.0187

Importance

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