Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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