Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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