Passed
Push — master ( dcb10e...d87ef3 )
by Adrien
12:23
created

Chart::parseRichText()   F

Complexity

Conditions 37
Paths > 20000

Size

Total Lines 211
Code Lines 135

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 125
CRAP Score 37.0052

Importance

Changes 0
Metric Value
eloc 135
dl 0
loc 211
ccs 125
cts 127
cp 0.9843
rs 0
c 0
b 0
f 0
cc 37
nc 118610019
nop 1
crap 37.0052

How to fix   Long Method    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\Calculation\Information\ExcelError;
6
use PhpOffice\PhpSpreadsheet\Chart\Axis;
7
use PhpOffice\PhpSpreadsheet\Chart\ChartColor;
8
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
9
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
10
use PhpOffice\PhpSpreadsheet\Chart\GridLines;
11
use PhpOffice\PhpSpreadsheet\Chart\Layout;
12
use PhpOffice\PhpSpreadsheet\Chart\Legend;
13
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
14
use PhpOffice\PhpSpreadsheet\Chart\Properties as ChartProperties;
15
use PhpOffice\PhpSpreadsheet\Chart\Title;
16
use PhpOffice\PhpSpreadsheet\Chart\TrendLine;
17
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
18
use PhpOffice\PhpSpreadsheet\RichText\RichText;
19
use PhpOffice\PhpSpreadsheet\Style\Font;
20
use SimpleXMLElement;
21
22
class Chart
23
{
24
    /** @var string */
25
    private $cNamespace;
26
27
    /** @var string */
28
    private $aNamespace;
29
30 46
    public function __construct(string $cNamespace = Namespaces::CHART, string $aNamespace = Namespaces::DRAWINGML)
31
    {
32 46
        $this->cNamespace = $cNamespace;
33 46
        $this->aNamespace = $aNamespace;
34
    }
35
36
    /**
37
     * @param string $name
38
     * @param string $format
39
     *
40
     * @return null|bool|float|int|string
41
     */
42 46
    private static function getAttribute(SimpleXMLElement $component, $name, $format)
43
    {
44 46
        $attributes = $component->attributes();
45 46
        if (@isset($attributes[$name])) {
46 46
            if ($format == 'string') {
47 46
                return (string) $attributes[$name];
48 46
            } elseif ($format == 'integer') {
49 46
                return (int) $attributes[$name];
50 45
            } elseif ($format == 'boolean') {
51 45
                $value = (string) $attributes[$name];
52
53 45
                return $value === 'true' || $value === '1';
54
            }
55
56 43
            return (float) $attributes[$name];
57
        }
58
59 46
        return null;
60
    }
61
62
    /**
63
     * @param string $chartName
64
     *
65
     * @return \PhpOffice\PhpSpreadsheet\Chart\Chart
66
     */
67 46
    public function readChart(SimpleXMLElement $chartElements, $chartName)
68
    {
69 46
        $chartElementsC = $chartElements->children($this->cNamespace);
70
71 46
        $XaxisLabel = $YaxisLabel = $legend = $title = null;
72 46
        $dispBlanksAs = $plotVisOnly = null;
73 46
        $plotArea = null;
74 46
        $rotX = $rotY = $rAngAx = $perspective = null;
75 46
        $xAxis = new Axis();
76 46
        $yAxis = new Axis();
77 46
        $autoTitleDeleted = null;
78 46
        $chartNoFill = false;
79 46
        $gradientArray = [];
80 46
        $gradientLin = null;
81 46
        $roundedCorners = false;
82 46
        foreach ($chartElementsC as $chartElementKey => $chartElement) {
83
            switch ($chartElementKey) {
84 46
                case 'spPr':
85 6
                    $possibleNoFill = $chartElementsC->spPr->children($this->aNamespace);
86 6
                    if (isset($possibleNoFill->noFill)) {
87 3
                        $chartNoFill = true;
88
                    }
89
90 6
                    break;
91 46
                case 'roundedCorners':
92
                    /** @var bool */
93 44
                    $roundedCorners = self::getAttribute($chartElementsC->roundedCorners, 'val', 'boolean');
94
95 44
                    break;
96 46
                case 'chart':
97 46
                    foreach ($chartElement as $chartDetailsKey => $chartDetails) {
98 46
                        $chartDetails = Xlsx::testSimpleXml($chartDetails);
99
                        switch ($chartDetailsKey) {
100 46
                            case 'autoTitleDeleted':
101
                                /** @var bool */
102 44
                                $autoTitleDeleted = self::getAttribute($chartElementsC->chart->autoTitleDeleted, 'val', 'boolean');
103
104 44
                                break;
105 46
                            case 'view3D':
106 29
                                $rotX = self::getAttribute($chartDetails->rotX, 'val', 'integer');
107 29
                                $rotY = self::getAttribute($chartDetails->rotY, 'val', 'integer');
108 29
                                $rAngAx = self::getAttribute($chartDetails->rAngAx, 'val', 'integer');
109 29
                                $perspective = self::getAttribute($chartDetails->perspective, 'val', 'integer');
110
111 29
                                break;
112 46
                            case 'plotArea':
113 46
                                $plotAreaLayout = $XaxisLabel = $YaxisLabel = null;
114 46
                                $plotSeries = $plotAttributes = [];
115 46
                                $catAxRead = false;
116 46
                                $plotNoFill = false;
117 46
                                foreach ($chartDetails as $chartDetailKey => $chartDetail) {
118 46
                                    $chartDetail = Xlsx::testSimpleXml($chartDetail);
119
                                    switch ($chartDetailKey) {
120 46
                                        case 'spPr':
121 15
                                            $possibleNoFill = $chartDetails->spPr->children($this->aNamespace);
122 15
                                            if (isset($possibleNoFill->noFill)) {
123 14
                                                $plotNoFill = true;
124
                                            }
125 15
                                            if (isset($possibleNoFill->gradFill->gsLst)) {
126 3
                                                foreach ($possibleNoFill->gradFill->gsLst->gs as $gradient) {
127 3
                                                    $gradient = Xlsx::testSimpleXml($gradient);
128
                                                    /** @var float */
129 3
                                                    $pos = self::getAttribute($gradient, 'pos', 'float');
130 3
                                                    $gradientArray[] = [
131
                                                        $pos / ChartProperties::PERCENTAGE_MULTIPLIER,
132 3
                                                        new ChartColor($this->readColor($gradient)),
133
                                                    ];
134
                                                }
135
                                            }
136 15
                                            if (isset($possibleNoFill->gradFill->lin)) {
137 3
                                                $gradientLin = ChartProperties::XmlToAngle((string) self::getAttribute($possibleNoFill->gradFill->lin, 'ang', 'string'));
138
                                            }
139
140 15
                                            break;
141 46
                                        case 'layout':
142 45
                                            $plotAreaLayout = $this->chartLayoutDetails($chartDetail);
143
144 45
                                            break;
145 4
                                        case Axis::AXIS_TYPE_CATEGORY:
146 4
                                        case Axis::AXIS_TYPE_DATE:
147 40
                                            $catAxRead = true;
148 40
                                            if (isset($chartDetail->title)) {
149 4
                                                $XaxisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace));
150
                                            }
151 40
                                            $xAxis->setAxisType($chartDetailKey);
152 40
                                            $this->readEffects($chartDetail, $xAxis);
153 40
                                            $this->readLineStyle($chartDetail, $xAxis);
154 40
                                            if (isset($chartDetail->spPr)) {
155 27
                                                $sppr = $chartDetail->spPr->children($this->aNamespace);
156 27
                                                if (isset($sppr->solidFill)) {
157 4
                                                    $axisColorArray = $this->readColor($sppr->solidFill);
158 4
                                                    $xAxis->setFillParameters($axisColorArray['value'], $axisColorArray['alpha'], $axisColorArray['type']);
159
                                                }
160
                                            }
161 40
                                            if (isset($chartDetail->majorGridlines)) {
162 5
                                                $majorGridlines = new GridLines();
163 5
                                                if (isset($chartDetail->majorGridlines->spPr)) {
164 5
                                                    $this->readEffects($chartDetail->majorGridlines, $majorGridlines);
165 5
                                                    $this->readLineStyle($chartDetail->majorGridlines, $majorGridlines);
166
                                                }
167 5
                                                $xAxis->setMajorGridlines($majorGridlines);
168
                                            }
169 40
                                            if (isset($chartDetail->minorGridlines)) {
170 4
                                                $minorGridlines = new GridLines();
171 4
                                                $minorGridlines->activateObject();
172 4
                                                if (isset($chartDetail->minorGridlines->spPr)) {
173 4
                                                    $this->readEffects($chartDetail->minorGridlines, $minorGridlines);
174 4
                                                    $this->readLineStyle($chartDetail->minorGridlines, $minorGridlines);
175
                                                }
176 4
                                                $xAxis->setMinorGridlines($minorGridlines);
177
                                            }
178 40
                                            $this->setAxisProperties($chartDetail, $xAxis);
179
180 40
                                            break;
181 4
                                        case Axis::AXIS_TYPE_VALUE:
182 44
                                            $whichAxis = null;
183 44
                                            $axPos = null;
184 44
                                            if (isset($chartDetail->axPos)) {
185 44
                                                $axPos = self::getAttribute($chartDetail->axPos, 'val', 'string');
186
                                            }
187 44
                                            if ($catAxRead) {
188 40
                                                $whichAxis = $yAxis;
189 40
                                                $yAxis->setAxisType($chartDetailKey);
190 21
                                            } elseif (!empty($axPos)) {
191
                                                switch ($axPos) {
192 21
                                                    case 't':
193 21
                                                    case 'b':
194 20
                                                        $whichAxis = $xAxis;
195 20
                                                        $xAxis->setAxisType($chartDetailKey);
196
197 20
                                                        break;
198 21
                                                    case 'r':
199 21
                                                    case 'l':
200 21
                                                        $whichAxis = $yAxis;
201 21
                                                        $yAxis->setAxisType($chartDetailKey);
202
203 21
                                                        break;
204
                                                }
205
                                            }
206 44
                                            if (isset($chartDetail->title)) {
207 19
                                                $axisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace));
208
209
                                                switch ($axPos) {
210 19
                                                    case 't':
211 19
                                                    case 'b':
212 4
                                                        $XaxisLabel = $axisLabel;
213
214 4
                                                        break;
215 19
                                                    case 'r':
216 19
                                                    case 'l':
217 19
                                                        $YaxisLabel = $axisLabel;
218
219 19
                                                        break;
220
                                                }
221
                                            }
222 44
                                            $this->readEffects($chartDetail, $whichAxis);
223 44
                                            $this->readLineStyle($chartDetail, $whichAxis);
224 44
                                            if ($whichAxis !== null && isset($chartDetail->spPr)) {
225 31
                                                $sppr = $chartDetail->spPr->children($this->aNamespace);
226 31
                                                if (isset($sppr->solidFill)) {
227 4
                                                    $axisColorArray = $this->readColor($sppr->solidFill);
228 4
                                                    $whichAxis->setFillParameters($axisColorArray['value'], $axisColorArray['alpha'], $axisColorArray['type']);
229
                                                }
230
                                            }
231 44
                                            if ($whichAxis !== null && isset($chartDetail->majorGridlines)) {
232 34
                                                $majorGridlines = new GridLines();
233 34
                                                if (isset($chartDetail->majorGridlines->spPr)) {
234 21
                                                    $this->readEffects($chartDetail->majorGridlines, $majorGridlines);
235 21
                                                    $this->readLineStyle($chartDetail->majorGridlines, $majorGridlines);
236
                                                }
237 34
                                                $whichAxis->setMajorGridlines($majorGridlines);
238
                                            }
239 44
                                            if ($whichAxis !== null && isset($chartDetail->minorGridlines)) {
240 7
                                                $minorGridlines = new GridLines();
241 7
                                                $minorGridlines->activateObject();
242 7
                                                if (isset($chartDetail->minorGridlines->spPr)) {
243 7
                                                    $this->readEffects($chartDetail->minorGridlines, $minorGridlines);
244 7
                                                    $this->readLineStyle($chartDetail->minorGridlines, $minorGridlines);
245
                                                }
246 7
                                                $whichAxis->setMinorGridlines($minorGridlines);
247
                                            }
248 44
                                            $this->setAxisProperties($chartDetail, $whichAxis);
249
250 44
                                            break;
251 46
                                        case 'barChart':
252 42
                                        case 'bar3DChart':
253 20
                                            $barDirection = self::getAttribute($chartDetail->barDir, 'val', 'string');
254 20
                                            $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
255 20
                                            $plotSer->setPlotDirection("$barDirection");
256 20
                                            $plotSeries[] = $plotSer;
257 20
                                            $plotAttributes = $this->readChartAttributes($chartDetail);
258
259 20
                                            break;
260 42
                                        case 'lineChart':
261 35
                                        case 'line3DChart':
262 12
                                            $plotSeries[] = $this->chartDataSeries($chartDetail, $chartDetailKey);
263 12
                                            $plotAttributes = $this->readChartAttributes($chartDetail);
264
265 12
                                            break;
266 35
                                        case 'areaChart':
267 25
                                        case 'area3DChart':
268 12
                                            $plotSeries[] = $this->chartDataSeries($chartDetail, $chartDetailKey);
269 12
                                            $plotAttributes = $this->readChartAttributes($chartDetail);
270
271 12
                                            break;
272 25
                                        case 'doughnutChart':
273 25
                                        case 'pieChart':
274 22
                                        case 'pie3DChart':
275 5
                                            $explosion = self::getAttribute($chartDetail->ser->explosion, 'val', 'string');
276 5
                                            $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
277 5
                                            $plotSer->setPlotStyle("$explosion");
278 5
                                            $plotSeries[] = $plotSer;
279 5
                                            $plotAttributes = $this->readChartAttributes($chartDetail);
280
281 5
                                            break;
282 22
                                        case 'scatterChart':
283
                                            /** @var string */
284 20
                                            $scatterStyle = self::getAttribute($chartDetail->scatterStyle, 'val', 'string');
285 20
                                            $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
286 20
                                            $plotSer->setPlotStyle($scatterStyle);
287 20
                                            $plotSeries[] = $plotSer;
288 20
                                            $plotAttributes = $this->readChartAttributes($chartDetail);
289
290 20
                                            break;
291 4
                                        case 'bubbleChart':
292 3
                                            $bubbleScale = self::getAttribute($chartDetail->bubbleScale, 'val', 'integer');
293 3
                                            $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
294 3
                                            $plotSer->setPlotStyle("$bubbleScale");
295 3
                                            $plotSeries[] = $plotSer;
296 3
                                            $plotAttributes = $this->readChartAttributes($chartDetail);
297
298 3
                                            break;
299 3
                                        case 'radarChart':
300
                                            /** @var string */
301 2
                                            $radarStyle = self::getAttribute($chartDetail->radarStyle, 'val', 'string');
302 2
                                            $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
303 2
                                            $plotSer->setPlotStyle($radarStyle);
304 2
                                            $plotSeries[] = $plotSer;
305 2
                                            $plotAttributes = $this->readChartAttributes($chartDetail);
306
307 2
                                            break;
308 3
                                        case 'surfaceChart':
309 3
                                        case 'surface3DChart':
310 2
                                            $wireFrame = self::getAttribute($chartDetail->wireframe, 'val', 'boolean');
311 2
                                            $plotSer = $this->chartDataSeries($chartDetail, $chartDetailKey);
312 2
                                            $plotSer->setPlotStyle("$wireFrame");
313 2
                                            $plotSeries[] = $plotSer;
314 2
                                            $plotAttributes = $this->readChartAttributes($chartDetail);
315
316 2
                                            break;
317 3
                                        case 'stockChart':
318 3
                                            $plotSeries[] = $this->chartDataSeries($chartDetail, $chartDetailKey);
319 3
                                            $plotAttributes = $this->readChartAttributes($chartDetail);
320
321 3
                                            break;
322
                                    }
323
                                }
324 46
                                if ($plotAreaLayout == null) {
325 40
                                    $plotAreaLayout = new Layout();
326
                                }
327 46
                                $plotArea = new PlotArea($plotAreaLayout, $plotSeries);
328 46
                                $this->setChartAttributes($plotAreaLayout, $plotAttributes);
329 46
                                if ($plotNoFill) {
330 14
                                    $plotArea->setNoFill(true);
331
                                }
332 46
                                if (!empty($gradientArray)) {
333 3
                                    $plotArea->setGradientFillProperties($gradientArray, $gradientLin);
334
                                }
335
336 46
                                break;
337 46
                            case 'plotVisOnly':
338 46
                                $plotVisOnly = self::getAttribute($chartDetails, 'val', 'string');
339
340 46
                                break;
341 46
                            case 'dispBlanksAs':
342 45
                                $dispBlanksAs = self::getAttribute($chartDetails, 'val', 'string');
343
344 45
                                break;
345 46
                            case 'title':
346 45
                                $title = $this->chartTitle($chartDetails);
347
348 45
                                break;
349 46
                            case 'legend':
350 42
                                $legendPos = 'r';
351 42
                                $legendLayout = null;
352 42
                                $legendOverlay = false;
353 42
                                foreach ($chartDetails as $chartDetailKey => $chartDetail) {
354 42
                                    $chartDetail = Xlsx::testSimpleXml($chartDetail);
355
                                    switch ($chartDetailKey) {
356 42
                                        case 'legendPos':
357 42
                                            $legendPos = self::getAttribute($chartDetail, 'val', 'string');
358
359 42
                                            break;
360 41
                                        case 'overlay':
361 41
                                            $legendOverlay = self::getAttribute($chartDetail, 'val', 'boolean');
362
363 41
                                            break;
364 41
                                        case 'layout':
365 38
                                            $legendLayout = $this->chartLayoutDetails($chartDetail);
366
367 38
                                            break;
368
                                    }
369
                                }
370 42
                                $legend = new Legend("$legendPos", $legendLayout, (bool) $legendOverlay);
371
372 42
                                break;
373
                        }
374
                    }
375
            }
376
        }
377 46
        $chart = new \PhpOffice\PhpSpreadsheet\Chart\Chart($chartName, $title, $legend, $plotArea, $plotVisOnly, (string) $dispBlanksAs, $XaxisLabel, $YaxisLabel, $xAxis, $yAxis);
378 46
        if ($chartNoFill) {
379 3
            $chart->setNoFill(true);
380
        }
381 46
        $chart->setRoundedCorners($roundedCorners);
382 46
        if (is_bool($autoTitleDeleted)) {
383 44
            $chart->setAutoTitleDeleted($autoTitleDeleted);
384
        }
385 46
        if (is_int($rotX)) {
386 2
            $chart->setRotX($rotX);
387
        }
388 46
        if (is_int($rotY)) {
389 2
            $chart->setRotY($rotY);
390
        }
391 46
        if (is_int($rAngAx)) {
392 2
            $chart->setRAngAx($rAngAx);
393
        }
394 46
        if (is_int($perspective)) {
395 2
            $chart->setPerspective($perspective);
396
        }
397
398 46
        return $chart;
399
    }
400
401 45
    private function chartTitle(SimpleXMLElement $titleDetails): Title
402
    {
403 45
        $caption = [];
404 45
        $titleLayout = null;
405 45
        foreach ($titleDetails as $titleDetailKey => $chartDetail) {
406 45
            $chartDetail = Xlsx::testSimpleXml($chartDetail);
407
            switch ($titleDetailKey) {
408 45
                case 'tx':
409 45
                    if (isset($chartDetail->rich)) {
410 44
                        $titleDetails = $chartDetail->rich->children($this->aNamespace);
411 44
                        foreach ($titleDetails as $titleKey => $titleDetail) {
412 44
                            $titleDetail = Xlsx::testSimpleXml($titleDetail);
413
                            switch ($titleKey) {
414 44
                                case 'p':
415 44
                                    $titleDetailPart = $titleDetail->children($this->aNamespace);
416 44
                                    $caption[] = $this->parseRichText($titleDetailPart);
417
                            }
418
                        }
419 1
                    } elseif (isset($chartDetail->strRef->strCache)) {
420 1
                        foreach ($chartDetail->strRef->strCache->pt as $pt) {
421 1
                            if (isset($pt->v)) {
422 1
                                $caption[] = (string) $pt->v;
423
                            }
424
                        }
425
                    }
426
427 45
                    break;
428 45
                case 'layout':
429 42
                    $titleLayout = $this->chartLayoutDetails($chartDetail);
430
431 42
                    break;
432
            }
433
        }
434
435 45
        return new Title($caption, $titleLayout);
436
    }
437
438 45
    private function chartLayoutDetails(SimpleXMLElement $chartDetail): ?Layout
439
    {
440 45
        if (!isset($chartDetail->manualLayout)) {
441 41
            return null;
442
        }
443 28
        $details = $chartDetail->manualLayout->children($this->cNamespace);
444 28
        if ($details === null) {
445
            return null;
446
        }
447 28
        $layout = [];
448 28
        foreach ($details as $detailKey => $detail) {
449 18
            $detail = Xlsx::testSimpleXml($detail);
450 18
            $layout[$detailKey] = self::getAttribute($detail, 'val', 'string');
451
        }
452
453 28
        return new Layout($layout);
454
    }
455
456 46
    private function chartDataSeries(SimpleXMLElement $chartDetail, string $plotType): DataSeries
457
    {
458 46
        $multiSeriesType = null;
459 46
        $smoothLine = false;
460 46
        $seriesLabel = $seriesCategory = $seriesValues = $plotOrder = $seriesBubbles = [];
461
462 46
        $seriesDetailSet = $chartDetail->children($this->cNamespace);
463 46
        foreach ($seriesDetailSet as $seriesDetailKey => $seriesDetails) {
464
            switch ($seriesDetailKey) {
465 46
                case 'grouping':
466 39
                    $multiSeriesType = self::getAttribute($chartDetail->grouping, 'val', 'string');
467
468 39
                    break;
469 46
                case 'ser':
470 46
                    $marker = null;
471 46
                    $seriesIndex = '';
472 46
                    $fillColor = null;
473 46
                    $pointSize = null;
474 46
                    $noFill = false;
475 46
                    $bubble3D = false;
476 46
                    $dptColors = [];
477 46
                    $markerFillColor = null;
478 46
                    $markerBorderColor = null;
479 46
                    $lineStyle = null;
480 46
                    $labelLayout = null;
481 46
                    $trendLines = [];
482 46
                    foreach ($seriesDetails as $seriesKey => $seriesDetail) {
483 46
                        $seriesDetail = Xlsx::testSimpleXml($seriesDetail);
484
                        switch ($seriesKey) {
485 46
                            case 'idx':
486 46
                                $seriesIndex = self::getAttribute($seriesDetail, 'val', 'integer');
487
488 46
                                break;
489 46
                            case 'order':
490 46
                                $seriesOrder = self::getAttribute($seriesDetail, 'val', 'integer');
491 46
                                $plotOrder[$seriesIndex] = $seriesOrder;
492
493 46
                                break;
494 46
                            case 'tx':
495 46
                                $seriesLabel[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail);
496
497 46
                                break;
498 46
                            case 'spPr':
499 44
                                $children = $seriesDetail->children($this->aNamespace);
500 44
                                if (isset($children->ln)) {
501 44
                                    $ln = $children->ln;
502 44
                                    if (is_countable($ln->noFill) && count($ln->noFill) === 1) {
503 19
                                        $noFill = true;
504
                                    }
505 44
                                    $lineStyle = new GridLines();
506 44
                                    $this->readLineStyle($seriesDetails, $lineStyle);
507
                                }
508 44
                                if (isset($children->effectLst)) {
509 5
                                    if ($lineStyle === null) {
510 3
                                        $lineStyle = new GridLines();
511
                                    }
512 5
                                    $this->readEffects($seriesDetails, $lineStyle);
513
                                }
514 44
                                if (isset($children->solidFill)) {
515 14
                                    $fillColor = new ChartColor($this->readColor($children->solidFill));
516
                                }
517
518 44
                                break;
519 46
                            case 'dPt':
520 6
                                $dptIdx = (int) self::getAttribute($seriesDetail->idx, 'val', 'string');
521 6
                                if (isset($seriesDetail->spPr)) {
522 6
                                    $children = $seriesDetail->spPr->children($this->aNamespace);
523 6
                                    if (isset($children->solidFill)) {
524 6
                                        $arrayColors = $this->readColor($children->solidFill);
525 6
                                        $dptColors[$dptIdx] = new ChartColor($arrayColors);
526
                                    }
527
                                }
528
529 6
                                break;
530 46
                            case 'trendline':
531 5
                                $trendLine = new TrendLine();
532 5
                                $this->readLineStyle($seriesDetail, $trendLine);
533
                                /** @var ?string */
534 5
                                $trendLineType = self::getAttribute($seriesDetail->trendlineType, 'val', 'string');
535
                                /** @var ?bool */
536 5
                                $dispRSqr = self::getAttribute($seriesDetail->dispRSqr, 'val', 'boolean');
537
                                /** @var ?bool */
538 5
                                $dispEq = self::getAttribute($seriesDetail->dispEq, 'val', 'boolean');
539
                                /** @var ?int */
540 5
                                $order = self::getAttribute($seriesDetail->order, 'val', 'integer');
541
                                /** @var ?int */
542 5
                                $period = self::getAttribute($seriesDetail->period, 'val', 'integer');
543
                                /** @var ?float */
544 5
                                $forward = self::getAttribute($seriesDetail->forward, 'val', 'float');
545
                                /** @var ?float */
546 5
                                $backward = self::getAttribute($seriesDetail->backward, 'val', 'float');
547
                                /** @var ?float */
548 5
                                $intercept = self::getAttribute($seriesDetail->intercept, 'val', 'float');
549
                                /** @var ?string */
550 5
                                $name = (string) $seriesDetail->name;
551 5
                                $trendLine->setTrendLineProperties(
552
                                    $trendLineType,
553
                                    $order,
554
                                    $period,
555
                                    $dispRSqr,
556
                                    $dispEq,
557
                                    $backward,
558
                                    $forward,
559
                                    $intercept,
560
                                    $name
561
                                );
562 5
                                $trendLines[] = $trendLine;
563
564 5
                                break;
565 46
                            case 'marker':
566 19
                                $marker = self::getAttribute($seriesDetail->symbol, 'val', 'string');
567 19
                                $pointSize = self::getAttribute($seriesDetail->size, 'val', 'string');
568 19
                                $pointSize = is_numeric($pointSize) ? ((int) $pointSize) : null;
569 19
                                if (isset($seriesDetail->spPr)) {
570 19
                                    $children = $seriesDetail->spPr->children($this->aNamespace);
571 19
                                    if (isset($children->solidFill)) {
572 17
                                        $markerFillColor = $this->readColor($children->solidFill);
573
                                    }
574 19
                                    if (isset($children->ln->solidFill)) {
575 9
                                        $markerBorderColor = $this->readColor($children->ln->solidFill);
576
                                    }
577
                                }
578
579 19
                                break;
580 46
                            case 'smooth':
581 24
                                $smoothLine = self::getAttribute($seriesDetail, 'val', 'boolean');
582
583 24
                                break;
584 46
                            case 'cat':
585 34
                                $seriesCategory[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail);
586
587 34
                                break;
588 46
                            case 'val':
589 41
                                $seriesValues[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
590
591 41
                                break;
592 31
                            case 'xVal':
593 21
                                $seriesCategory[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
594
595 21
                                break;
596 31
                            case 'yVal':
597 21
                                $seriesValues[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
598
599 21
                                break;
600 29
                            case 'bubbleSize':
601 3
                                $seriesBubbles[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize");
602
603 3
                                break;
604 28
                            case 'bubble3D':
605 2
                                $bubble3D = self::getAttribute($seriesDetail, 'val', 'boolean');
606
607 2
                                break;
608 28
                            case 'dLbls':
609 4
                                $labelLayout = new Layout($this->readChartAttributes($seriesDetails));
610
611 4
                                break;
612
                        }
613
                    }
614 46
                    if ($labelLayout) {
615 4
                        if (isset($seriesLabel[$seriesIndex])) {
616 4
                            $seriesLabel[$seriesIndex]->setLabelLayout($labelLayout);
617
                        }
618 4
                        if (isset($seriesCategory[$seriesIndex])) {
619 4
                            $seriesCategory[$seriesIndex]->setLabelLayout($labelLayout);
620
                        }
621 4
                        if (isset($seriesValues[$seriesIndex])) {
622 4
                            $seriesValues[$seriesIndex]->setLabelLayout($labelLayout);
623
                        }
624
                    }
625 46
                    if ($noFill) {
626 19
                        if (isset($seriesLabel[$seriesIndex])) {
627 19
                            $seriesLabel[$seriesIndex]->setScatterLines(false);
628
                        }
629 19
                        if (isset($seriesCategory[$seriesIndex])) {
630 19
                            $seriesCategory[$seriesIndex]->setScatterLines(false);
631
                        }
632 19
                        if (isset($seriesValues[$seriesIndex])) {
633 19
                            $seriesValues[$seriesIndex]->setScatterLines(false);
634
                        }
635
                    }
636 46
                    if ($lineStyle !== null) {
637 44
                        if (isset($seriesLabel[$seriesIndex])) {
638 44
                            $seriesLabel[$seriesIndex]->copyLineStyles($lineStyle);
639
                        }
640 44
                        if (isset($seriesCategory[$seriesIndex])) {
641 37
                            $seriesCategory[$seriesIndex]->copyLineStyles($lineStyle);
642
                        }
643 44
                        if (isset($seriesValues[$seriesIndex])) {
644 44
                            $seriesValues[$seriesIndex]->copyLineStyles($lineStyle);
645
                        }
646
                    }
647 46
                    if ($bubble3D) {
648 2
                        if (isset($seriesLabel[$seriesIndex])) {
649 2
                            $seriesLabel[$seriesIndex]->setBubble3D($bubble3D);
650
                        }
651 2
                        if (isset($seriesCategory[$seriesIndex])) {
652 2
                            $seriesCategory[$seriesIndex]->setBubble3D($bubble3D);
653
                        }
654 2
                        if (isset($seriesValues[$seriesIndex])) {
655 2
                            $seriesValues[$seriesIndex]->setBubble3D($bubble3D);
656
                        }
657
                    }
658 46
                    if (!empty($dptColors)) {
659 6
                        if (isset($seriesLabel[$seriesIndex])) {
660 6
                            $seriesLabel[$seriesIndex]->setFillColor($dptColors);
661
                        }
662 6
                        if (isset($seriesCategory[$seriesIndex])) {
663 6
                            $seriesCategory[$seriesIndex]->setFillColor($dptColors);
664
                        }
665 6
                        if (isset($seriesValues[$seriesIndex])) {
666 6
                            $seriesValues[$seriesIndex]->setFillColor($dptColors);
667
                        }
668
                    }
669 46
                    if ($markerFillColor !== null) {
670 17
                        if (isset($seriesLabel[$seriesIndex])) {
671 17
                            $seriesLabel[$seriesIndex]->getMarkerFillColor()->setColorPropertiesArray($markerFillColor);
672
                        }
673 17
                        if (isset($seriesCategory[$seriesIndex])) {
674 17
                            $seriesCategory[$seriesIndex]->getMarkerFillColor()->setColorPropertiesArray($markerFillColor);
675
                        }
676 17
                        if (isset($seriesValues[$seriesIndex])) {
677 17
                            $seriesValues[$seriesIndex]->getMarkerFillColor()->setColorPropertiesArray($markerFillColor);
678
                        }
679
                    }
680 46
                    if ($markerBorderColor !== null) {
681 9
                        if (isset($seriesLabel[$seriesIndex])) {
682 9
                            $seriesLabel[$seriesIndex]->getMarkerBorderColor()->setColorPropertiesArray($markerBorderColor);
683
                        }
684 9
                        if (isset($seriesCategory[$seriesIndex])) {
685 9
                            $seriesCategory[$seriesIndex]->getMarkerBorderColor()->setColorPropertiesArray($markerBorderColor);
686
                        }
687 9
                        if (isset($seriesValues[$seriesIndex])) {
688 9
                            $seriesValues[$seriesIndex]->getMarkerBorderColor()->setColorPropertiesArray($markerBorderColor);
689
                        }
690
                    }
691 46
                    if ($smoothLine) {
692 9
                        if (isset($seriesLabel[$seriesIndex])) {
693 9
                            $seriesLabel[$seriesIndex]->setSmoothLine(true);
694
                        }
695 9
                        if (isset($seriesCategory[$seriesIndex])) {
696 9
                            $seriesCategory[$seriesIndex]->setSmoothLine(true);
697
                        }
698 9
                        if (isset($seriesValues[$seriesIndex])) {
699 9
                            $seriesValues[$seriesIndex]->setSmoothLine(true);
700
                        }
701
                    }
702 46
                    if (!empty($trendLines)) {
703 5
                        if (isset($seriesLabel[$seriesIndex])) {
704 5
                            $seriesLabel[$seriesIndex]->setTrendLines($trendLines);
705
                        }
706 5
                        if (isset($seriesCategory[$seriesIndex])) {
707 5
                            $seriesCategory[$seriesIndex]->setTrendLines($trendLines);
708
                        }
709 5
                        if (isset($seriesValues[$seriesIndex])) {
710 5
                            $seriesValues[$seriesIndex]->setTrendLines($trendLines);
711
                        }
712
                    }
713
            }
714
        }
715
        /** @phpstan-ignore-next-line */
716 46
        $series = new DataSeries($plotType, $multiSeriesType, $plotOrder, $seriesLabel, $seriesCategory, $seriesValues, $smoothLine);
717 46
        $series->setPlotBubbleSizes($seriesBubbles);
718
719 46
        return $series;
720
    }
721
722
    /**
723
     * @return mixed
724
     */
725 46
    private function chartDataSeriesValueSet(SimpleXMLElement $seriesDetail, ?string $marker = null, ?ChartColor $fillColor = null, ?string $pointSize = null)
726
    {
727 46
        if (isset($seriesDetail->strRef)) {
728 45
            $seriesSource = (string) $seriesDetail->strRef->f;
729 45
            $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $fillColor, "$pointSize");
730
731 45
            if (isset($seriesDetail->strRef->strCache)) {
732 44
                $seriesData = $this->chartDataSeriesValues($seriesDetail->strRef->strCache->children($this->cNamespace), 's');
733
                $seriesValues
734 44
                    ->setFormatCode($seriesData['formatCode'])
735 44
                    ->setDataValues($seriesData['dataValues']);
736
            }
737
738 45
            return $seriesValues;
739 46
        } elseif (isset($seriesDetail->numRef)) {
740 46
            $seriesSource = (string) $seriesDetail->numRef->f;
741 46
            $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, $seriesSource, null, 0, null, $marker, $fillColor, "$pointSize");
742 46
            if (isset($seriesDetail->numRef->numCache)) {
743 45
                $seriesData = $this->chartDataSeriesValues($seriesDetail->numRef->numCache->children($this->cNamespace));
744
                $seriesValues
745 45
                    ->setFormatCode($seriesData['formatCode'])
746 45
                    ->setDataValues($seriesData['dataValues']);
747
            }
748
749 46
            return $seriesValues;
750 17
        } elseif (isset($seriesDetail->multiLvlStrRef)) {
751 16
            $seriesSource = (string) $seriesDetail->multiLvlStrRef->f;
752 16
            $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $fillColor, "$pointSize");
753
754 16
            if (isset($seriesDetail->multiLvlStrRef->multiLvlStrCache)) {
755 16
                $seriesData = $this->chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlStrRef->multiLvlStrCache->children($this->cNamespace), 's');
756
                $seriesValues
757 16
                    ->setFormatCode($seriesData['formatCode'])
758 16
                    ->setDataValues($seriesData['dataValues']);
759
            }
760
761 16
            return $seriesValues;
762 3
        } elseif (isset($seriesDetail->multiLvlNumRef)) {
763
            $seriesSource = (string) $seriesDetail->multiLvlNumRef->f;
764
            $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $fillColor, "$pointSize");
765
766
            if (isset($seriesDetail->multiLvlNumRef->multiLvlNumCache)) {
767
                $seriesData = $this->chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlNumRef->multiLvlNumCache->children($this->cNamespace), 's');
768
                $seriesValues
769
                    ->setFormatCode($seriesData['formatCode'])
770
                    ->setDataValues($seriesData['dataValues']);
771
            }
772
773
            return $seriesValues;
774
        }
775
776 3
        if (isset($seriesDetail->v)) {
777 2
            return new DataSeriesValues(
778
                DataSeriesValues::DATASERIES_TYPE_STRING,
779
                null,
780
                null,
781
                1,
782 2
                [(string) $seriesDetail->v]
783
            );
784
        }
785
786 2
        return null;
787
    }
788
789 45
    private function chartDataSeriesValues(SimpleXMLElement $seriesValueSet, string $dataType = 'n'): array
790
    {
791 45
        $seriesVal = [];
792 45
        $formatCode = '';
793 45
        $pointCount = 0;
794
795 45
        foreach ($seriesValueSet as $seriesValueIdx => $seriesValue) {
796 44
            $seriesValue = Xlsx::testSimpleXml($seriesValue);
797
            switch ($seriesValueIdx) {
798 44
                case 'ptCount':
799 44
                    $pointCount = self::getAttribute($seriesValue, 'val', 'integer');
800
801 44
                    break;
802 44
                case 'formatCode':
803 32
                    $formatCode = (string) $seriesValue;
804
805 32
                    break;
806 44
                case 'pt':
807 44
                    $pointVal = self::getAttribute($seriesValue, 'idx', 'integer');
808 44
                    if ($dataType == 's') {
809 44
                        $seriesVal[$pointVal] = (string) $seriesValue->v;
810 44
                    } elseif ((string) $seriesValue->v === ExcelError::NA()) {
811
                        $seriesVal[$pointVal] = null;
812
                    } else {
813 44
                        $seriesVal[$pointVal] = (float) $seriesValue->v;
814
                    }
815
816 44
                    break;
817
            }
818
        }
819
820
        return [
821 45
            'formatCode' => $formatCode,
822
            'pointCount' => $pointCount,
823
            'dataValues' => $seriesVal,
824
        ];
825
    }
826
827 16
    private function chartDataSeriesValuesMultiLevel(SimpleXMLElement $seriesValueSet, string $dataType = 'n'): array
828
    {
829 16
        $seriesVal = [];
830 16
        $formatCode = '';
831 16
        $pointCount = 0;
832
833 16
        foreach ($seriesValueSet->lvl as $seriesLevelIdx => $seriesLevel) {
834 16
            foreach ($seriesLevel as $seriesValueIdx => $seriesValue) {
835 16
                $seriesValue = Xlsx::testSimpleXml($seriesValue);
836
                switch ($seriesValueIdx) {
837 16
                    case 'ptCount':
838
                        $pointCount = self::getAttribute($seriesValue, 'val', 'integer');
839
840
                        break;
841 16
                    case 'formatCode':
842
                        $formatCode = (string) $seriesValue;
843
844
                        break;
845 16
                    case 'pt':
846 16
                        $pointVal = self::getAttribute($seriesValue, 'idx', 'integer');
847 16
                        if ($dataType == 's') {
848 16
                            $seriesVal[$pointVal][] = (string) $seriesValue->v;
849
                        } elseif ((string) $seriesValue->v === ExcelError::NA()) {
850
                            $seriesVal[$pointVal] = null;
851
                        } else {
852
                            $seriesVal[$pointVal][] = (float) $seriesValue->v;
853
                        }
854
855 16
                        break;
856
                }
857
            }
858
        }
859
860
        return [
861 16
            'formatCode' => $formatCode,
862
            'pointCount' => $pointCount,
863
            'dataValues' => $seriesVal,
864
        ];
865
    }
866
867 44
    private function parseRichText(SimpleXMLElement $titleDetailPart): RichText
868
    {
869 44
        $value = new RichText();
870 44
        $defaultFontSize = null;
871 44
        $defaultBold = null;
872 44
        $defaultItalic = null;
873 44
        $defaultUnderscore = null;
874 44
        $defaultStrikethrough = null;
875 44
        $defaultBaseline = null;
876 44
        $defaultFontName = null;
877 44
        $defaultLatin = null;
878 44
        $defaultEastAsian = null;
879 44
        $defaultComplexScript = null;
880 44
        $defaultFontColor = null;
881 44
        if (isset($titleDetailPart->pPr->defRPr)) {
882
            /** @var ?int */
883 44
            $defaultFontSize = self::getAttribute($titleDetailPart->pPr->defRPr, 'sz', 'integer');
884
            /** @var ?bool */
885 44
            $defaultBold = self::getAttribute($titleDetailPart->pPr->defRPr, 'b', 'boolean');
886
            /** @var ?bool */
887 44
            $defaultItalic = self::getAttribute($titleDetailPart->pPr->defRPr, 'i', 'boolean');
888
            /** @var ?string */
889 44
            $defaultUnderscore = self::getAttribute($titleDetailPart->pPr->defRPr, 'u', 'string');
890
            /** @var ?string */
891 44
            $defaultStrikethrough = self::getAttribute($titleDetailPart->pPr->defRPr, 'strike', 'string');
892
            /** @var ?int */
893 44
            $defaultBaseline = self::getAttribute($titleDetailPart->pPr->defRPr, 'baseline', 'integer');
894 44
            if (isset($titleDetailPart->defRPr->rFont['val'])) {
895
                $defaultFontName = (string) $titleDetailPart->defRPr->rFont['val'];
896
            }
897 44
            if (isset($titleDetailPart->pPr->defRPr->latin)) {
898
                /** @var ?string */
899 25
                $defaultLatin = self::getAttribute($titleDetailPart->pPr->defRPr->latin, 'typeface', 'string');
900
            }
901 44
            if (isset($titleDetailPart->pPr->defRPr->ea)) {
902
                /** @var ?string */
903 23
                $defaultEastAsian = self::getAttribute($titleDetailPart->pPr->defRPr->ea, 'typeface', 'string');
904
            }
905 44
            if (isset($titleDetailPart->pPr->defRPr->cs)) {
906
                /** @var ?string */
907 23
                $defaultComplexScript = self::getAttribute($titleDetailPart->pPr->defRPr->cs, 'typeface', 'string');
908
            }
909 44
            if (isset($titleDetailPart->pPr->defRPr->solidFill)) {
910 25
                $defaultFontColor = $this->readColor($titleDetailPart->pPr->defRPr->solidFill);
911
            }
912
        }
913 44
        foreach ($titleDetailPart as $titleDetailElementKey => $titleDetailElement) {
914
            if (
915
                (string) $titleDetailElementKey !== 'r'
916 44
                || !isset($titleDetailElement->t)
917
            ) {
918 44
                continue;
919
            }
920 44
            $objText = $value->createTextRun((string) $titleDetailElement->t);
921 44
            if ($objText->getFont() === null) {
922
                // @codeCoverageIgnoreStart
923
                continue;
924
                // @codeCoverageIgnoreEnd
925
            }
926 44
            $fontSize = null;
927 44
            $bold = null;
928 44
            $italic = null;
929 44
            $underscore = null;
930 44
            $strikethrough = null;
931 44
            $baseline = null;
932 44
            $fontName = null;
933 44
            $latinName = null;
934 44
            $eastAsian = null;
935 44
            $complexScript = null;
936 44
            $fontColor = null;
937 44
            $underlineColor = null;
938 44
            if (isset($titleDetailElement->rPr)) {
939
                // not used now, not sure it ever was, grandfathering
940 31
                if (isset($titleDetailElement->rPr->rFont['val'])) {
941
                    // @codeCoverageIgnoreStart
942
                    $fontName = (string) $titleDetailElement->rPr->rFont['val'];
943
                    // @codeCoverageIgnoreEnd
944
                }
945 31
                if (isset($titleDetailElement->rPr->latin)) {
946
                    /** @var ?string */
947 17
                    $latinName = self::getAttribute($titleDetailElement->rPr->latin, 'typeface', 'string');
948
                }
949 31
                if (isset($titleDetailElement->rPr->ea)) {
950
                    /** @var ?string */
951 7
                    $eastAsian = self::getAttribute($titleDetailElement->rPr->ea, 'typeface', 'string');
952
                }
953 31
                if (isset($titleDetailElement->rPr->cs)) {
954
                    /** @var ?string */
955 9
                    $complexScript = self::getAttribute($titleDetailElement->rPr->cs, 'typeface', 'string');
956
                }
957
                /** @var ?int */
958 31
                $fontSize = self::getAttribute($titleDetailElement->rPr, 'sz', 'integer');
959
960
                // not used now, not sure it ever was, grandfathering
961 31
                if (isset($titleDetailElement->rPr->solidFill)) {
962 16
                    $fontColor = $this->readColor($titleDetailElement->rPr->solidFill);
963
                }
964
965
                /** @var ?bool */
966 31
                $bold = self::getAttribute($titleDetailElement->rPr, 'b', 'boolean');
967
968
                /** @var ?bool */
969 31
                $italic = self::getAttribute($titleDetailElement->rPr, 'i', 'boolean');
970
971
                /** @var ?int */
972 31
                $baseline = self::getAttribute($titleDetailElement->rPr, 'baseline', 'integer');
973
974
                /** @var ?string */
975 31
                $underscore = self::getAttribute($titleDetailElement->rPr, 'u', 'string');
976 31
                if (isset($titleDetailElement->rPr->uFill->solidFill)) {
977 2
                    $underlineColor = $this->readColor($titleDetailElement->rPr->uFill->solidFill);
978
                }
979
980
                /** @var ?string */
981 31
                $strikethrough = self::getAttribute($titleDetailElement->rPr, 'strike', 'string');
982
            }
983
984 44
            $fontFound = false;
985 44
            $latinName = $latinName ?? $defaultLatin;
986 44
            if ($latinName !== null) {
987 26
                $objText->getFont()->setLatin($latinName);
988 26
                $fontFound = true;
989
            }
990 44
            $eastAsian = $eastAsian ?? $defaultEastAsian;
991 44
            if ($eastAsian !== null) {
992 23
                $objText->getFont()->setEastAsian($eastAsian);
993 23
                $fontFound = true;
994
            }
995 44
            $complexScript = $complexScript ?? $defaultComplexScript;
996 44
            if ($complexScript !== null) {
997 23
                $objText->getFont()->setComplexScript($complexScript);
998 23
                $fontFound = true;
999
            }
1000 44
            $fontName = $fontName ?? $defaultFontName;
1001 44
            if ($fontName !== null) {
1002
                // @codeCoverageIgnoreStart
1003
                $objText->getFont()->setName($fontName);
1004
                $fontFound = true;
1005
                // @codeCoverageIgnoreEnd
1006
            }
1007
1008 44
            $fontSize = $fontSize ?? $defaultFontSize;
1009 44
            if (is_int($fontSize)) {
1010 24
                $objText->getFont()->setSize(floor($fontSize / 100));
1011 24
                $fontFound = true;
1012
            } else {
1013 22
                $objText->getFont()->setSize(null, true);
1014
            }
1015
1016 44
            $fontColor = $fontColor ?? $defaultFontColor;
1017 44
            if (!empty($fontColor)) {
1018 25
                $objText->getFont()->setChartColor($fontColor);
1019 25
                $fontFound = true;
1020
            }
1021
1022 44
            $bold = $bold ?? $defaultBold;
1023 44
            if ($bold !== null) {
1024 27
                $objText->getFont()->setBold($bold);
1025 27
                $fontFound = true;
1026
            }
1027
1028 44
            $italic = $italic ?? $defaultItalic;
1029 44
            if ($italic !== null) {
1030 26
                $objText->getFont()->setItalic($italic);
1031 26
                $fontFound = true;
1032
            }
1033
1034 44
            $baseline = $baseline ?? $defaultBaseline;
1035 44
            if ($baseline !== null) {
1036 23
                $objText->getFont()->setBaseLine($baseline);
1037 23
                if ($baseline > 0) {
1038 2
                    $objText->getFont()->setSuperscript(true);
1039 23
                } elseif ($baseline < 0) {
1040 2
                    $objText->getFont()->setSubscript(true);
1041
                }
1042 23
                $fontFound = true;
1043
            }
1044
1045 44
            $underscore = $underscore ?? $defaultUnderscore;
1046 44
            if ($underscore !== null) {
1047 26
                if ($underscore == 'sng') {
1048 3
                    $objText->getFont()->setUnderline(Font::UNDERLINE_SINGLE);
1049 26
                } elseif ($underscore == 'dbl') {
1050 2
                    $objText->getFont()->setUnderline(Font::UNDERLINE_DOUBLE);
1051 26
                } elseif ($underscore !== '') {
1052 26
                    $objText->getFont()->setUnderline($underscore);
1053
                } else {
1054
                    $objText->getFont()->setUnderline(Font::UNDERLINE_NONE);
1055
                }
1056 26
                $fontFound = true;
1057 26
                if ($underlineColor) {
1058 2
                    $objText->getFont()->setUnderlineColor($underlineColor);
1059
                }
1060
            }
1061
1062 44
            $strikethrough = $strikethrough ?? $defaultStrikethrough;
1063 44
            if ($strikethrough !== null) {
1064 26
                $objText->getFont()->setStrikeType($strikethrough);
1065 26
                if ($strikethrough == 'noStrike') {
1066 26
                    $objText->getFont()->setStrikethrough(false);
1067
                } else {
1068 2
                    $objText->getFont()->setStrikethrough(true);
1069
                }
1070 26
                $fontFound = true;
1071
            }
1072 44
            if ($fontFound === false) {
1073 21
                $objText->setFont(null);
1074
            }
1075
        }
1076
1077 44
        return $value;
1078
    }
1079
1080
    /**
1081
     * @param ?SimpleXMLElement $chartDetail
1082
     */
1083 46
    private function readChartAttributes($chartDetail): array
1084
    {
1085 46
        $plotAttributes = [];
1086 46
        if (isset($chartDetail->dLbls)) {
1087 34
            if (isset($chartDetail->dLbls->dLblPos)) {
1088 3
                $plotAttributes['dLblPos'] = self::getAttribute($chartDetail->dLbls->dLblPos, 'val', 'string');
1089
            }
1090 34
            if (isset($chartDetail->dLbls->numFmt)) {
1091 2
                $plotAttributes['numFmtCode'] = self::getAttribute($chartDetail->dLbls->numFmt, 'formatCode', 'string');
1092 2
                $plotAttributes['numFmtLinked'] = self::getAttribute($chartDetail->dLbls->numFmt, 'sourceLinked', 'boolean');
1093
            }
1094 34
            if (isset($chartDetail->dLbls->showLegendKey)) {
1095 32
                $plotAttributes['showLegendKey'] = self::getAttribute($chartDetail->dLbls->showLegendKey, 'val', 'string');
1096
            }
1097 34
            if (isset($chartDetail->dLbls->showVal)) {
1098 34
                $plotAttributes['showVal'] = self::getAttribute($chartDetail->dLbls->showVal, 'val', 'string');
1099
            }
1100 34
            if (isset($chartDetail->dLbls->showCatName)) {
1101 32
                $plotAttributes['showCatName'] = self::getAttribute($chartDetail->dLbls->showCatName, 'val', 'string');
1102
            }
1103 34
            if (isset($chartDetail->dLbls->showSerName)) {
1104 32
                $plotAttributes['showSerName'] = self::getAttribute($chartDetail->dLbls->showSerName, 'val', 'string');
1105
            }
1106 34
            if (isset($chartDetail->dLbls->showPercent)) {
1107 34
                $plotAttributes['showPercent'] = self::getAttribute($chartDetail->dLbls->showPercent, 'val', 'string');
1108
            }
1109 34
            if (isset($chartDetail->dLbls->showBubbleSize)) {
1110 31
                $plotAttributes['showBubbleSize'] = self::getAttribute($chartDetail->dLbls->showBubbleSize, 'val', 'string');
1111
            }
1112 34
            if (isset($chartDetail->dLbls->showLeaderLines)) {
1113 4
                $plotAttributes['showLeaderLines'] = self::getAttribute($chartDetail->dLbls->showLeaderLines, 'val', 'string');
1114
            }
1115 34
            if (isset($chartDetail->dLbls->spPr)) {
1116 3
                $sppr = $chartDetail->dLbls->spPr->children($this->aNamespace);
1117 3
                if (isset($sppr->solidFill)) {
1118 3
                    $plotAttributes['labelFillColor'] = new ChartColor($this->readColor($sppr->solidFill));
1119
                }
1120 3
                if (isset($sppr->ln->solidFill)) {
1121 3
                    $plotAttributes['labelBorderColor'] = new ChartColor($this->readColor($sppr->ln->solidFill));
1122
                }
1123
            }
1124 34
            if (isset($chartDetail->dLbls->txPr)) {
1125 4
                $txpr = $chartDetail->dLbls->txPr->children($this->aNamespace);
1126 4
                if (isset($txpr->p->pPr->defRPr->solidFill)) {
1127 3
                    $plotAttributes['labelFontColor'] = new ChartColor($this->readColor($txpr->p->pPr->defRPr->solidFill));
1128
                }
1129
            }
1130
        }
1131
1132 46
        return $plotAttributes;
1133
    }
1134
1135
    /**
1136
     * @param mixed $plotAttributes
1137
     */
1138 46
    private function setChartAttributes(Layout $plotArea, $plotAttributes): void
1139
    {
1140 46
        foreach ($plotAttributes as $plotAttributeKey => $plotAttributeValue) {
1141
            switch ($plotAttributeKey) {
1142 33
                case 'showLegendKey':
1143 31
                    $plotArea->setShowLegendKey($plotAttributeValue);
1144
1145 31
                    break;
1146 33
                case 'showVal':
1147 33
                    $plotArea->setShowVal($plotAttributeValue);
1148
1149 33
                    break;
1150 33
                case 'showCatName':
1151 31
                    $plotArea->setShowCatName($plotAttributeValue);
1152
1153 31
                    break;
1154 33
                case 'showSerName':
1155 31
                    $plotArea->setShowSerName($plotAttributeValue);
1156
1157 31
                    break;
1158 33
                case 'showPercent':
1159 33
                    $plotArea->setShowPercent($plotAttributeValue);
1160
1161 33
                    break;
1162 31
                case 'showBubbleSize':
1163 31
                    $plotArea->setShowBubbleSize($plotAttributeValue);
1164
1165 31
                    break;
1166 2
                case 'showLeaderLines':
1167 2
                    $plotArea->setShowLeaderLines($plotAttributeValue);
1168
1169 2
                    break;
1170
            }
1171
        }
1172
    }
1173
1174 44
    private function readEffects(SimpleXMLElement $chartDetail, ?ChartProperties $chartObject): void
1175
    {
1176 44
        if (!isset($chartObject, $chartDetail->spPr)) {
1177 27
            return;
1178
        }
1179 31
        $sppr = $chartDetail->spPr->children($this->aNamespace);
1180
1181 31
        if (isset($sppr->effectLst->glow)) {
1182 6
            $axisGlowSize = (float) self::getAttribute($sppr->effectLst->glow, 'rad', 'integer') / ChartProperties::POINTS_WIDTH_MULTIPLIER;
1183 6
            if ($axisGlowSize != 0.0) {
1184 6
                $colorArray = $this->readColor($sppr->effectLst->glow);
1185 6
                $chartObject->setGlowProperties($axisGlowSize, $colorArray['value'], $colorArray['alpha'], $colorArray['type']);
1186
            }
1187
        }
1188
1189 31
        if (isset($sppr->effectLst->softEdge)) {
1190
            /** @var string */
1191 2
            $softEdgeSize = self::getAttribute($sppr->effectLst->softEdge, 'rad', 'string');
1192 2
            if (is_numeric($softEdgeSize)) {
1193 2
                $chartObject->setSoftEdges((float) ChartProperties::xmlToPoints($softEdgeSize));
1194
            }
1195
        }
1196
1197 31
        $type = '';
1198 31
        foreach (self::SHADOW_TYPES as $shadowType) {
1199 31
            if (isset($sppr->effectLst->$shadowType)) {
1200 2
                $type = $shadowType;
1201
1202 2
                break;
1203
            }
1204
        }
1205 31
        if ($type !== '') {
1206
            /** @var string */
1207 2
            $blur = self::getAttribute($sppr->effectLst->$type, 'blurRad', 'string');
1208 2
            $blur = is_numeric($blur) ? ChartProperties::xmlToPoints($blur) : null;
1209
            /** @var string */
1210 2
            $dist = self::getAttribute($sppr->effectLst->$type, 'dist', 'string');
1211 2
            $dist = is_numeric($dist) ? ChartProperties::xmlToPoints($dist) : null;
1212
            /** @var string */
1213 2
            $direction = self::getAttribute($sppr->effectLst->$type, 'dir', 'string');
1214 2
            $direction = is_numeric($direction) ? ChartProperties::xmlToAngle($direction) : null;
1215 2
            $algn = self::getAttribute($sppr->effectLst->$type, 'algn', 'string');
1216 2
            $rot = self::getAttribute($sppr->effectLst->$type, 'rotWithShape', 'string');
1217 2
            $size = [];
1218 2
            foreach (['sx', 'sy'] as $sizeType) {
1219 2
                $sizeValue = self::getAttribute($sppr->effectLst->$type, $sizeType, 'string');
1220 2
                if (is_numeric($sizeValue)) {
1221 1
                    $size[$sizeType] = ChartProperties::xmlToTenthOfPercent((string) $sizeValue);
1222
                } else {
1223 2
                    $size[$sizeType] = null;
1224
                }
1225
            }
1226 2
            foreach (['kx', 'ky'] as $sizeType) {
1227 2
                $sizeValue = self::getAttribute($sppr->effectLst->$type, $sizeType, 'string');
1228 2
                if (is_numeric($sizeValue)) {
1229 1
                    $size[$sizeType] = ChartProperties::xmlToAngle((string) $sizeValue);
1230
                } else {
1231 2
                    $size[$sizeType] = null;
1232
                }
1233
            }
1234 2
            $colorArray = $this->readColor($sppr->effectLst->$type);
1235
            $chartObject
1236 2
                ->setShadowProperty('effect', $type)
1237 2
                ->setShadowProperty('blur', $blur)
1238 2
                ->setShadowProperty('direction', $direction)
1239 2
                ->setShadowProperty('distance', $dist)
1240 2
                ->setShadowProperty('algn', $algn)
1241 2
                ->setShadowProperty('rotWithShape', $rot)
1242 2
                ->setShadowProperty('size', $size)
1243 2
                ->setShadowProperty('color', $colorArray);
1244
        }
1245
    }
1246
1247
    private const SHADOW_TYPES = [
1248
        'outerShdw',
1249
        'innerShdw',
1250
    ];
1251
1252 46
    private function readColor(SimpleXMLElement $colorXml): array
1253
    {
1254
        $result = [
1255
            'type' => null,
1256
            'value' => null,
1257
            'alpha' => null,
1258
            'brightness' => null,
1259
        ];
1260 46
        foreach (ChartColor::EXCEL_COLOR_TYPES as $type) {
1261 46
            if (isset($colorXml->$type)) {
1262 43
                $result['type'] = $type;
1263 43
                $result['value'] = self::getAttribute($colorXml->$type, 'val', 'string');
1264 43
                if (isset($colorXml->$type->alpha)) {
1265
                    /** @var string */
1266 16
                    $alpha = self::getAttribute($colorXml->$type->alpha, 'val', 'string');
1267 16
                    if (is_numeric($alpha)) {
1268 16
                        $result['alpha'] = ChartColor::alphaFromXml($alpha);
1269
                    }
1270
                }
1271 43
                if (isset($colorXml->$type->lumMod)) {
1272
                    /** @var string */
1273 13
                    $brightness = self::getAttribute($colorXml->$type->lumMod, 'val', 'string');
1274 13
                    if (is_numeric($brightness)) {
1275 13
                        $result['brightness'] = ChartColor::alphaFromXml($brightness);
1276
                    }
1277
                }
1278
1279 43
                break;
1280
            }
1281
        }
1282
1283 46
        return $result;
1284
    }
1285
1286 46
    private function readLineStyle(SimpleXMLElement $chartDetail, ?ChartProperties $chartObject): void
1287
    {
1288 46
        if (!isset($chartObject, $chartDetail->spPr)) {
1289 27
            return;
1290
        }
1291 44
        $sppr = $chartDetail->spPr->children($this->aNamespace);
1292
1293 44
        if (!isset($sppr->ln)) {
1294 7
            return;
1295
        }
1296 44
        $lineWidth = null;
1297
        /** @var string */
1298 44
        $lineWidthTemp = self::getAttribute($sppr->ln, 'w', 'string');
1299 44
        if (is_numeric($lineWidthTemp)) {
1300 31
            $lineWidth = ChartProperties::xmlToPoints($lineWidthTemp);
1301
        }
1302
        /** @var string */
1303 44
        $compoundType = self::getAttribute($sppr->ln, 'cmpd', 'string');
1304
        /** @var string */
1305 44
        $dashType = self::getAttribute($sppr->ln->prstDash, 'val', 'string');
1306
        /** @var string */
1307 44
        $capType = self::getAttribute($sppr->ln, 'cap', 'string');
1308 44
        if (isset($sppr->ln->miter)) {
1309 10
            $joinType = ChartProperties::LINE_STYLE_JOIN_MITER;
1310 44
        } elseif (isset($sppr->ln->bevel)) {
1311 4
            $joinType = ChartProperties::LINE_STYLE_JOIN_BEVEL;
1312
        } else {
1313 44
            $joinType = '';
1314
        }
1315 44
        $headArrowSize = '';
1316 44
        $endArrowSize = '';
1317
        /** @var string */
1318 44
        $headArrowType = self::getAttribute($sppr->ln->headEnd, 'type', 'string');
1319
        /** @var string */
1320 44
        $headArrowWidth = self::getAttribute($sppr->ln->headEnd, 'w', 'string');
1321
        /** @var string */
1322 44
        $headArrowLength = self::getAttribute($sppr->ln->headEnd, 'len', 'string');
1323
        /** @var string */
1324 44
        $endArrowType = self::getAttribute($sppr->ln->tailEnd, 'type', 'string');
1325
        /** @var string */
1326 44
        $endArrowWidth = self::getAttribute($sppr->ln->tailEnd, 'w', 'string');
1327
        /** @var string */
1328 44
        $endArrowLength = self::getAttribute($sppr->ln->tailEnd, 'len', 'string');
1329 44
        $chartObject->setLineStyleProperties(
1330
            $lineWidth,
1331
            $compoundType,
1332
            $dashType,
1333
            $capType,
1334
            $joinType,
1335
            $headArrowType,
1336
            $headArrowSize,
1337
            $endArrowType,
1338
            $endArrowSize,
1339
            $headArrowWidth,
1340
            $headArrowLength,
1341
            $endArrowWidth,
1342
            $endArrowLength
1343
        );
1344 44
        $colorArray = $this->readColor($sppr->ln->solidFill);
1345 44
        $chartObject->getLineColor()->setColorPropertiesArray($colorArray);
1346
    }
1347
1348 44
    private function setAxisProperties(SimpleXMLElement $chartDetail, ?Axis $whichAxis): void
1349
    {
1350 44
        if (!isset($whichAxis)) {
1351
            return;
1352
        }
1353 44
        if (isset($chartDetail->delete)) {
1354 43
            $whichAxis->setAxisOption('hidden', (string) self::getAttribute($chartDetail->delete, 'val', 'string'));
1355
        }
1356 44
        if (isset($chartDetail->numFmt)) {
1357 43
            $whichAxis->setAxisNumberProperties(
1358 43
                (string) self::getAttribute($chartDetail->numFmt, 'formatCode', 'string'),
1359
                null,
1360 43
                (int) self::getAttribute($chartDetail->numFmt, 'sourceLinked', 'int')
1361
            );
1362
        }
1363 44
        if (isset($chartDetail->crossBetween)) {
1364 31
            $whichAxis->setCrossBetween((string) self::getAttribute($chartDetail->crossBetween, 'val', 'string'));
1365
        }
1366 44
        if (isset($chartDetail->majorTickMark)) {
1367 44
            $whichAxis->setAxisOption('major_tick_mark', (string) self::getAttribute($chartDetail->majorTickMark, 'val', 'string'));
1368
        }
1369 44
        if (isset($chartDetail->minorTickMark)) {
1370 44
            $whichAxis->setAxisOption('minor_tick_mark', (string) self::getAttribute($chartDetail->minorTickMark, 'val', 'string'));
1371
        }
1372 44
        if (isset($chartDetail->tickLblPos)) {
1373 43
            $whichAxis->setAxisOption('axis_labels', (string) self::getAttribute($chartDetail->tickLblPos, 'val', 'string'));
1374
        }
1375 44
        if (isset($chartDetail->crosses)) {
1376 42
            $whichAxis->setAxisOption('horizontal_crosses', (string) self::getAttribute($chartDetail->crosses, 'val', 'string'));
1377
        }
1378 44
        if (isset($chartDetail->crossesAt)) {
1379
            $whichAxis->setAxisOption('horizontal_crosses_value', (string) self::getAttribute($chartDetail->crossesAt, 'val', 'string'));
1380
        }
1381 44
        if (isset($chartDetail->scaling->orientation)) {
1382 44
            $whichAxis->setAxisOption('orientation', (string) self::getAttribute($chartDetail->scaling->orientation, 'val', 'string'));
1383
        }
1384 44
        if (isset($chartDetail->scaling->max)) {
1385 4
            $whichAxis->setAxisOption('maximum', (string) self::getAttribute($chartDetail->scaling->max, 'val', 'string'));
1386
        }
1387 44
        if (isset($chartDetail->scaling->min)) {
1388 4
            $whichAxis->setAxisOption('minimum', (string) self::getAttribute($chartDetail->scaling->min, 'val', 'string'));
1389
        }
1390 44
        if (isset($chartDetail->scaling->min)) {
1391 4
            $whichAxis->setAxisOption('minimum', (string) self::getAttribute($chartDetail->scaling->min, 'val', 'string'));
1392
        }
1393 44
        if (isset($chartDetail->majorUnit)) {
1394 4
            $whichAxis->setAxisOption('major_unit', (string) self::getAttribute($chartDetail->majorUnit, 'val', 'string'));
1395
        }
1396 44
        if (isset($chartDetail->minorUnit)) {
1397 1
            $whichAxis->setAxisOption('minor_unit', (string) self::getAttribute($chartDetail->minorUnit, 'val', 'string'));
1398
        }
1399 44
        if (isset($chartDetail->baseTimeUnit)) {
1400 3
            $whichAxis->setAxisOption('baseTimeUnit', (string) self::getAttribute($chartDetail->baseTimeUnit, 'val', 'string'));
1401
        }
1402 44
        if (isset($chartDetail->majorTimeUnit)) {
1403 3
            $whichAxis->setAxisOption('majorTimeUnit', (string) self::getAttribute($chartDetail->majorTimeUnit, 'val', 'string'));
1404
        }
1405 44
        if (isset($chartDetail->minorTimeUnit)) {
1406 3
            $whichAxis->setAxisOption('minorTimeUnit', (string) self::getAttribute($chartDetail->minorTimeUnit, 'val', 'string'));
1407
        }
1408 44
        if (isset($chartDetail->txPr)) {
1409 9
            $children = $chartDetail->txPr->children($this->aNamespace);
1410 9
            if (isset($children->bodyPr)) {
1411
                /** @var string */
1412 9
                $textRotation = self::getAttribute($children->bodyPr, 'rot', 'string');
1413 9
                if (is_numeric($textRotation)) {
1414 7
                    $whichAxis->setAxisOption('textRotation', (string) ChartProperties::xmlToAngle($textRotation));
1415
                }
1416
            }
1417
        }
1418
    }
1419
}
1420