Passed
Pull Request — master (#3481)
by Mark
11:46
created

Chart::readChartAttributes()   F

Complexity

Conditions 16
Paths 7681

Size

Total Lines 50
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 33
CRAP Score 16

Importance

Changes 0
Metric Value
eloc 32
c 0
b 0
f 0
dl 0
loc 50
ccs 33
cts 33
cp 1
rs 1.4
cc 16
nc 7681
nop 1
crap 16

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