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