Completed
Push — master ( a3489b...087532 )
by Mark
33s queued 30s
created

Chart::chartTitle()   B

Complexity

Conditions 11
Paths 7

Size

Total Lines 40
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 11

Importance

Changes 0
Metric Value
eloc 27
dl 0
loc 40
ccs 26
cts 26
cp 1
rs 7.3166
c 0
b 0
f 0
cc 11
nc 7
nop 1
crap 11

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