Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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