PluginProbe
TablePress – Tables in WordPress made easy / 3.4
TablePress – Tables in WordPress made easy v3.4
3.4 3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 All 45 releases
tablepress / libraries / vendor / PhpSpreadsheet / Reader / Xlsx / Styles.php

Styles.php in TablePress – Tables in WordPress made easy 3.4, at libraries/vendor/PhpSpreadsheet/Reader/Xlsx/Styles.php

564 lines 16.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace TablePress\PhpOffice\PhpSpreadsheet\Reader\Xlsx;
4
5 use TablePress\PhpOffice\PhpSpreadsheet\Reader\Xlsx;
6 use TablePress\PhpOffice\PhpSpreadsheet\Style\Alignment;
7 use TablePress\PhpOffice\PhpSpreadsheet\Style\Border;
8 use TablePress\PhpOffice\PhpSpreadsheet\Style\Borders;
9 use TablePress\PhpOffice\PhpSpreadsheet\Style\Color;
10 use TablePress\PhpOffice\PhpSpreadsheet\Style\Fill;
11 use TablePress\PhpOffice\PhpSpreadsheet\Style\Font;
12 use TablePress\PhpOffice\PhpSpreadsheet\Style\NumberFormat;
13 use TablePress\PhpOffice\PhpSpreadsheet\Style\Protection;
14 use TablePress\PhpOffice\PhpSpreadsheet\Style\Style;
15 use TablePress\PhpOffice\PhpSpreadsheet\Worksheet\Table\TableDxfsStyle;
16 use SimpleXMLElement;
17 use stdClass;
18
19 class Styles extends BaseParserClass
20 {
21 /**
22 * Theme instance.
23 */
24 private ?Theme $theme = null;
25
26 /** @var string[] */
27 private array $workbookPalette = [];
28
29 /** @var mixed[] */
30 private array $styles = [];
31
32 /** @var array<SimpleXMLElement|stdClass> */
33 private array $cellStyles = [];
34
35 private SimpleXMLElement $styleXml;
36
37 private string $namespace = '';
38
39 /** @var array<string, int> */
40 private array $fontCharsets = [];
41
42 /** @return array<string, int> */
43 public function getFontCharsets(): array
44 {
45 return $this->fontCharsets;
46 }
47
48 public function setNamespace(string $namespace): void
49 {
50 $this->namespace = $namespace;
51 }
52
53 /** @param string[] $palette */
54 public function setWorkbookPalette(array $palette): void
55 {
56 $this->workbookPalette = $palette;
57 }
58
59 private function getStyleAttributes(SimpleXMLElement $value): SimpleXMLElement
60 {
61 // TablePress: Added check for $value being falsy, which is needed here for compatibility with PHP < 8.
62 $attr = null;
63 if ( $value ) {
64 $attr = $value->attributes('');
65 if ($attr === null || count($attr) === 0) {
66 $attr = $value->attributes($this->namespace);
67 }
68 }
69
70 return Xlsx::testSimpleXml($attr);
71 }
72
73 public function setStyleXml(SimpleXMLElement $styleXml): void
74 {
75 $this->styleXml = $styleXml;
76 }
77
78 public function setTheme(Theme $theme): void
79 {
80 $this->theme = $theme;
81 }
82
83 /**
84 * @param mixed[] $styles
85 * @param array<SimpleXMLElement|stdClass> $cellStyles
86 */
87 public function setStyleBaseData(?Theme $theme = null, array $styles = [], array $cellStyles = []): void
88 {
89 $this->theme = $theme;
90 $this->styles = $styles;
91 $this->cellStyles = $cellStyles;
92 }
93
94 public function readFontStyle(Font $fontStyle, SimpleXMLElement $fontStyleXml): void
95 {
96 if (isset($fontStyleXml->name)) {
97 $attr = $this->getStyleAttributes($fontStyleXml->name);
98 if (isset($attr['val'])) {
99 $fontStyle->setName((string) $attr['val']);
100 }
101 if (isset($fontStyleXml->charset)) {
102 $charsetAttr = $this->getStyleAttributes($fontStyleXml->charset);
103 if (isset($charsetAttr['val'])) {
104 $charsetVal = (int) $charsetAttr['val'];
105 $this->fontCharsets[$fontStyle->getName()] = $charsetVal;
106 }
107 }
108 }
109 if (isset($fontStyleXml->sz)) {
110 $attr = $this->getStyleAttributes($fontStyleXml->sz);
111 if (isset($attr['val'])) {
112 $fontStyle->setSize((float) $attr['val']);
113 }
114 }
115 if (isset($fontStyleXml->b)) {
116 $attr = $this->getStyleAttributes($fontStyleXml->b);
117 $fontStyle->setBold(!isset($attr['val']) || self::boolean((string) $attr['val']));
118 }
119 if (isset($fontStyleXml->i)) {
120 $attr = $this->getStyleAttributes($fontStyleXml->i);
121 $fontStyle->setItalic(!isset($attr['val']) || self::boolean((string) $attr['val']));
122 }
123 if (isset($fontStyleXml->strike)) {
124 $attr = $this->getStyleAttributes($fontStyleXml->strike);
125 $fontStyle->setStrikethrough(!isset($attr['val']) || self::boolean((string) $attr['val']));
126 }
127 $fontStyle->getColor()
128 ->setARGB(
129 $this->readColor($fontStyleXml->color)
130 );
131 $theme = $this->readColorTheme($fontStyleXml->color);
132 if ($theme >= 0) {
133 $fontStyle->getColor()->setTheme($theme);
134 }
135
136 if (isset($fontStyleXml->u)) {
137 $attr = $this->getStyleAttributes($fontStyleXml->u);
138 if (!isset($attr['val'])) {
139 $fontStyle->setUnderline(Font::UNDERLINE_SINGLE);
140 } else {
141 $fontStyle->setUnderline((string) $attr['val']);
142 }
143 }
144 if (isset($fontStyleXml->vertAlign)) {
145 $attr = $this->getStyleAttributes($fontStyleXml->vertAlign);
146 if (isset($attr['val'])) {
147 $verticalAlign = strtolower((string) $attr['val']);
148 if ($verticalAlign === 'superscript') {
149 $fontStyle->setSuperscript(true);
150 } elseif ($verticalAlign === 'subscript') {
151 $fontStyle->setSubscript(true);
152 }
153 }
154 }
155 if (isset($fontStyleXml->scheme)) {
156 $attr = $this->getStyleAttributes($fontStyleXml->scheme);
157 $fontStyle->setScheme((string) $attr['val']);
158 }
159 if (isset($fontStyleXml->auto)) {
160 $attr = $this->getStyleAttributes($fontStyleXml->auto);
161 if (isset($attr['val'])) {
162 $fontStyle->setAutoColor(self::boolean((string) $attr['val']));
163 }
164 }
165 }
166
167 private function readNumberFormat(NumberFormat $numfmtStyle, SimpleXMLElement $numfmtStyleXml): void
168 {
169 if ((string) $numfmtStyleXml['formatCode'] !== '') {
170 $numfmtStyle->setFormatCode(self::formatGeneral((string) $numfmtStyleXml['formatCode']));
171
172 return;
173 }
174 $numfmt = $this->getStyleAttributes($numfmtStyleXml);
175 if (isset($numfmt['formatCode'])) {
176 $numfmtStyle->setFormatCode(self::formatGeneral((string) $numfmt['formatCode']));
177 }
178 }
179
180 public function readFillStyle(Fill $fillStyle, SimpleXMLElement $fillStyleXml): void
181 {
182 if ($fillStyleXml->gradientFill) {
183 /** @var SimpleXMLElement $gradientFill */
184 $gradientFill = $fillStyleXml->gradientFill[0];
185 $attr = $this->getStyleAttributes($gradientFill);
186 if (!empty($attr['type'])) {
187 $fillStyle->setFillType((string) $attr['type']);
188 }
189 $fillStyle->setRotation((float) ($attr['degree']));
190 $gradientFill->registerXPathNamespace('sml', Namespaces::MAIN);
191 $fillStyle->getStartColor()->setARGB($this->readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color)); //* @phpstan-ignore property.nonObject (xpath may return null)
192 $fillStyle->getEndColor()->setARGB($this->readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color)); //* @phpstan-ignore property.nonObject (xpath may return null)
193 } elseif ($fillStyleXml->patternFill) {
194 $defaultFillStyle = ($fillStyle->getFillType() !== null) ? Fill::FILL_NONE : '';
195 $fgFound = false;
196 $bgFound = false;
197 if ($fillStyleXml->patternFill->fgColor) {
198 $fillStyle->getStartColor()->setARGB($this->readColor($fillStyleXml->patternFill->fgColor, true));
199 if ($fillStyle->getFillType() !== null) {
200 $defaultFillStyle = Fill::FILL_SOLID;
201 }
202 $fgFound = true;
203 }
204 if ($fillStyleXml->patternFill->bgColor) {
205 $fillStyle->getEndColor()->setARGB($this->readColor($fillStyleXml->patternFill->bgColor, true));
206 if ($fillStyle->getFillType() !== null) {
207 $defaultFillStyle = Fill::FILL_SOLID;
208 }
209 $bgFound = true;
210 }
211
212 $type = '';
213 if ((string) $fillStyleXml->patternFill['patternType'] !== '') {
214 $type = (string) $fillStyleXml->patternFill['patternType'];
215 } else {
216 $attr = $this->getStyleAttributes($fillStyleXml->patternFill);
217 $type = (string) $attr['patternType'];
218 }
219 $patternType = ($type === '') ? $defaultFillStyle : $type;
220
221 $fillStyle->setFillType($patternType);
222 if (
223 !$fgFound // no foreground color specified
224 && !in_array($patternType, [Fill::FILL_NONE, Fill::FILL_SOLID], true) // these patterns aren't relevant
225 && $fillStyle->getStartColor()->getARGB() // not conditional
226 ) {
227 $fillStyle->getStartColor()
228 ->setARGB('', true);
229 }
230 if (
231 !$bgFound // no background color specified
232 && !in_array($patternType, [Fill::FILL_NONE, Fill::FILL_SOLID], true) // these patterns aren't relevant
233 && $fillStyle->getEndColor()->getARGB() // not conditional
234 ) {
235 $fillStyle->getEndColor()
236 ->setARGB('', true);
237 }
238 }
239 }
240
241 public function readBorderStyle(Borders $borderStyle, SimpleXMLElement $borderStyleXml): void
242 {
243 $diagonalUp = $this->getAttribute($borderStyleXml, 'diagonalUp');
244 $diagonalUp = self::boolean($diagonalUp);
245 $diagonalDown = $this->getAttribute($borderStyleXml, 'diagonalDown');
246 $diagonalDown = self::boolean($diagonalDown);
247 if ($diagonalUp === false) {
248 if ($diagonalDown === false) {
249 $borderStyle->setDiagonalDirection(Borders::DIAGONAL_NONE);
250 } else {
251 $borderStyle->setDiagonalDirection(Borders::DIAGONAL_DOWN);
252 }
253 } elseif ($diagonalDown === false) {
254 $borderStyle->setDiagonalDirection(Borders::DIAGONAL_UP);
255 } else {
256 $borderStyle->setDiagonalDirection(Borders::DIAGONAL_BOTH);
257 }
258
259 if (isset($borderStyleXml->left)) {
260 $this->readBorder($borderStyle->getLeft(), $borderStyleXml->left);
261 }
262 if (isset($borderStyleXml->right)) {
263 $this->readBorder($borderStyle->getRight(), $borderStyleXml->right);
264 }
265 if (isset($borderStyleXml->top)) {
266 $this->readBorder($borderStyle->getTop(), $borderStyleXml->top);
267 }
268 if (isset($borderStyleXml->bottom)) {
269 $this->readBorder($borderStyle->getBottom(), $borderStyleXml->bottom);
270 }
271 if (isset($borderStyleXml->diagonal)) {
272 $this->readBorder($borderStyle->getDiagonal(), $borderStyleXml->diagonal);
273 }
274 }
275
276 private function getAttribute(SimpleXMLElement $xml, string $attribute): string
277 {
278 $style = '';
279 if ((string) $xml[$attribute] !== '') {
280 $style = (string) $xml[$attribute];
281 } else {
282 $attr = $this->getStyleAttributes($xml);
283 if (isset($attr[$attribute])) {
284 $style = (string) $attr[$attribute];
285 }
286 }
287
288 return $style;
289 }
290
291 private function readBorder(Border $border, SimpleXMLElement $borderXml): void
292 {
293 $style = $this->getAttribute($borderXml, 'style');
294 if ($style !== '') {
295 $border->setBorderStyle((string) $style);
296 } else {
297 $border->setBorderStyle(Border::BORDER_NONE);
298 }
299 if (isset($borderXml->color)) {
300 $border->getColor()->setARGB($this->readColor($borderXml->color));
301 }
302 }
303
304 public function readAlignmentStyle(Alignment $alignment, SimpleXMLElement $alignmentXml): void
305 {
306 $horizontal = (string) $this->getAttribute($alignmentXml, 'horizontal');
307 if ($horizontal !== '') {
308 $alignment->setHorizontal($horizontal);
309 }
310 $justifyLastLine = (string) $this->getAttribute($alignmentXml, 'justifyLastLine');
311 if ($justifyLastLine !== '') {
312 $alignment->setJustifyLastLine(
313 self::boolean($justifyLastLine)
314 );
315 }
316 $vertical = (string) $this->getAttribute($alignmentXml, 'vertical');
317 if ($vertical !== '') {
318 $alignment->setVertical($vertical);
319 }
320
321 $textRotation = (int) $this->getAttribute($alignmentXml, 'textRotation');
322 if ($textRotation > 90) {
323 $textRotation = 90 - $textRotation;
324 }
325 $alignment->setTextRotation($textRotation);
326
327 $wrapText = $this->getAttribute($alignmentXml, 'wrapText');
328 $alignment->setWrapText(self::boolean((string) $wrapText));
329 $shrinkToFit = $this->getAttribute($alignmentXml, 'shrinkToFit');
330 $alignment->setShrinkToFit(self::boolean((string) $shrinkToFit));
331 $indent = (int) $this->getAttribute($alignmentXml, 'indent');
332 $alignment->setIndent(max($indent, 0));
333 $readingOrder = (int) $this->getAttribute($alignmentXml, 'readingOrder');
334 $alignment->setReadOrder(max($readingOrder, 0));
335 }
336
337 private static function formatGeneral(string $formatString): string
338 {
339 if ($formatString === 'GENERAL') {
340 $formatString = NumberFormat::FORMAT_GENERAL;
341 }
342
343 return $formatString;
344 }
345
346 /**
347 * Read style.
348 * @param \SimpleXMLElement|\stdClass $style
349 */
350 public function readStyle(Style $docStyle, $style): void
351 {
352 if ($style instanceof SimpleXMLElement) {
353 $this->readNumberFormat($docStyle->getNumberFormat(), $style->numFmt);
354 } else {
355 /** @var SimpleXMLElement */
356 $temp = $style->numFmt;
357 $docStyle->getNumberFormat()->setFormatCode(self::formatGeneral((string) $temp));
358 }
359
360 /** @var SimpleXMLElement $style */
361 if (isset($style->font)) {
362 $this->readFontStyle($docStyle->getFont(), $style->font);
363 }
364
365 if (isset($style->fill)) {
366 $this->readFillStyle($docStyle->getFill(), $style->fill);
367 }
368
369 if (isset($style->border)) {
370 $this->readBorderStyle($docStyle->getBorders(), $style->border);
371 }
372
373 if (isset($style->alignment)) {
374 $this->readAlignmentStyle($docStyle->getAlignment(), $style->alignment);
375 }
376
377 // protection
378 if (isset($style->protection)) {
379 $this->readProtectionLocked($docStyle, $style->protection);
380 $this->readProtectionHidden($docStyle, $style->protection);
381 }
382
383 // top-level style settings
384 if (isset($style->quotePrefix)) {
385 $docStyle->setQuotePrefix((bool) $style->quotePrefix);
386 }
387 }
388
389 /**
390 * Read protection locked attribute.
391 */
392 public function readProtectionLocked(Style $docStyle, SimpleXMLElement $style): void
393 {
394 $locked = '';
395 if ((string) $style['locked'] !== '') {
396 $locked = (string) $style['locked'];
397 } else {
398 $attr = $this->getStyleAttributes($style);
399 if (isset($attr['locked'])) {
400 $locked = (string) $attr['locked'];
401 }
402 }
403 if ($locked !== '') {
404 if (self::boolean($locked)) {
405 $docStyle->getProtection()->setLocked(Protection::PROTECTION_PROTECTED);
406 } else {
407 $docStyle->getProtection()->setLocked(Protection::PROTECTION_UNPROTECTED);
408 }
409 }
410 }
411
412 /**
413 * Read protection hidden attribute.
414 */
415 public function readProtectionHidden(Style $docStyle, SimpleXMLElement $style): void
416 {
417 $hidden = '';
418 if ((string) $style['hidden'] !== '') {
419 $hidden = (string) $style['hidden'];
420 } else {
421 $attr = $this->getStyleAttributes($style);
422 if (isset($attr['hidden'])) {
423 $hidden = (string) $attr['hidden'];
424 }
425 }
426 if ($hidden !== '') {
427 if (self::boolean((string) $hidden)) {
428 $docStyle->getProtection()->setHidden(Protection::PROTECTION_PROTECTED);
429 } else {
430 $docStyle->getProtection()->setHidden(Protection::PROTECTION_UNPROTECTED);
431 }
432 }
433 }
434
435 public function readColorTheme(SimpleXMLElement $color): int
436 {
437 $attr = $this->getStyleAttributes($color);
438 $retVal = -1;
439 if (isset($attr['theme']) && is_numeric((string) $attr['theme']) && !isset($attr['tint'])) {
440 $retVal = (int) $attr['theme'];
441 }
442
443 return $retVal;
444 }
445
446 public function readColor(SimpleXMLElement $color, bool $background = false): string
447 {
448 $attr = $this->getStyleAttributes($color);
449 if (isset($attr['rgb'])) {
450 return (string) $attr['rgb'];
451 }
452 if (isset($attr['indexed'])) {
453 $indexedColor = (int) $attr['indexed'];
454 if ($indexedColor >= count($this->workbookPalette)) {
455 return Color::indexedColor($indexedColor - 7, $background)->getARGB() ?? '';
456 }
457
458 return Color::indexedColor($indexedColor, $background, $this->workbookPalette)->getARGB() ?? '';
459 }
460 if (isset($attr['theme'])) {
461 if ($this->theme !== null) {
462 $returnColour = $this->theme->getColourByIndex((int) $attr['theme']);
463 if (isset($attr['tint'])) {
464 $tintAdjust = (float) $attr['tint'];
465 $returnColour = Color::changeBrightness($returnColour ?? '', $tintAdjust);
466 }
467
468 return 'FF' . $returnColour;
469 }
470 }
471
472 return ($background) ? 'FFFFFFFF' : 'FF000000';
473 }
474
475 /** @return Style[] */
476 public function dxfs(bool $readDataOnly = false): array
477 {
478 $dxfs = [];
479 if (!$readDataOnly && $this->styleXml) {
480 // Conditional Styles
481 if ($this->styleXml->dxfs) {
482 foreach ($this->styleXml->dxfs->dxf as $dxf) {
483 $style = new Style(false, true);
484 $this->readStyle($style, $dxf);
485 $dxfs[] = $style;
486 }
487 }
488 // Cell Styles
489 if ($this->styleXml->cellStyles) {
490 foreach ($this->styleXml->cellStyles->cellStyle as $cellStylex) {
491 $cellStyle = Xlsx::getAttributes($cellStylex);
492 if ((int) ($cellStyle['builtinId']) == 0) {
493 if (isset($this->cellStyles[(int) ($cellStyle['xfId'])])) {
494 // Set default style
495 $style = new Style();
496 $this->readStyle($style, $this->cellStyles[(int) ($cellStyle['xfId'])]);
497
498 // normal style, currently not using it for anything
499 }
500 }
501 }
502 }
503 }
504
505 return $dxfs;
506 }
507
508 /** @return TableDxfsStyle[] */
509 public function tableStyles(bool $readDataOnly = false): array
510 {
511 $tableStyles = [];
512 if (!$readDataOnly && $this->styleXml) {
513 // Conditional Styles
514 if ($this->styleXml->tableStyles) {
515 foreach ($this->styleXml->tableStyles->tableStyle as $s) {
516 $attrs = Xlsx::getAttributes($s);
517 if (isset($attrs['name'][0])) {
518 $style = new TableDxfsStyle((string) ($attrs['name'][0]));
519 foreach ($s->tableStyleElement as $e) {
520 $a = Xlsx::getAttributes($e);
521 if (isset($a['dxfId'][0], $a['type'][0])) {
522 switch ($a['type'][0]) {
523 case 'headerRow':
524 $style->setHeaderRow((int) ($a['dxfId'][0]));
525
526 break;
527 case 'firstRowStripe':
528 $style->setFirstRowStripe((int) ($a['dxfId'][0]));
529
530 break;
531 case 'secondRowStripe':
532 $style->setSecondRowStripe((int) ($a['dxfId'][0]));
533
534 break;
535 default:
536 }
537 }
538 }
539 $tableStyles[] = $style;
540 }
541 }
542 }
543 }
544
545 return $tableStyles;
546 }
547
548 /** @return mixed[] */
549 public function styles(): array
550 {
551 return $this->styles;
552 }
553
554 /**
555 * Get array item.
556 *
557 * @param mixed $array (usually array, in theory can be false)
558 */
559 private static function getArrayItem($array): ?SimpleXMLElement
560 {
561 return is_array($array) ? ($array[0] ?? null) : null;
562 }
563 }
564