Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
   4  
   5  use PhpOffice\PhpSpreadsheet\Calculation\Functions;
   6  use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
   7  use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
   8  use PhpOffice\PhpSpreadsheet\Chart\Layout;
   9  use PhpOffice\PhpSpreadsheet\Chart\Legend;
  10  use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
  11  use PhpOffice\PhpSpreadsheet\Chart\Title;
  12  use PhpOffice\PhpSpreadsheet\RichText\RichText;
  13  use PhpOffice\PhpSpreadsheet\Style\Color;
  14  use PhpOffice\PhpSpreadsheet\Style\Font;
  15  use SimpleXMLElement;
  16  
  17  class Chart
  18  {
  19      /**
  20       * @param string $name
  21       * @param string $format
  22       *
  23       * @return null|bool|float|int|string
  24       */
  25      private static function getAttribute(SimpleXMLElement $component, $name, $format)
  26      {
  27          $attributes = $component->attributes();
  28          if (isset($attributes[$name])) {
  29              if ($format == 'string') {
  30                  return (string) $attributes[$name];
  31              } elseif ($format == 'integer') {
  32                  return (int) $attributes[$name];
  33              } elseif ($format == 'boolean') {
  34                  $value = (string) $attributes[$name];
  35  
  36                  return $value === 'true' || $value === '1';
  37              }
  38  
  39              return (float) $attributes[$name];
  40          }
  41  
  42          return null;
  43      }
  44  
  45      private static function readColor($color, $background = false)
  46      {
  47          if (isset($color['rgb'])) {
  48              return (string) $color['rgb'];
  49          } elseif (isset($color['indexed'])) {
  50              return Color::indexedColor($color['indexed'] - 7, $background)->getARGB();
  51          }
  52      }
  53  
  54      /**
  55       * @param string $chartName
  56       *
  57       * @return \PhpOffice\PhpSpreadsheet\Chart\Chart
  58       */
  59      public static function readChart(SimpleXMLElement $chartElements, $chartName)
  60      {
  61          $namespacesChartMeta = $chartElements->getNamespaces(true);
  62          $chartElementsC = $chartElements->children($namespacesChartMeta['c']);
  63  
  64          $XaxisLabel = $YaxisLabel = $legend = $title = null;
  65          $dispBlanksAs = $plotVisOnly = null;
  66          $plotArea = null;
  67          foreach ($chartElementsC as $chartElementKey => $chartElement) {
  68              switch ($chartElementKey) {
  69                  case 'chart':
  70                      foreach ($chartElement as $chartDetailsKey => $chartDetails) {
  71                          $chartDetailsC = $chartDetails->children($namespacesChartMeta['c']);
  72                          switch ($chartDetailsKey) {
  73                              case 'plotArea':
  74                                  $plotAreaLayout = $XaxisLable = $YaxisLable = null;
  75                                  $plotSeries = $plotAttributes = [];
  76                                  foreach ($chartDetails as $chartDetailKey => $chartDetail) {
  77                                      switch ($chartDetailKey) {
  78                                          case 'layout':
  79                                              $plotAreaLayout = self::chartLayoutDetails($chartDetail, $namespacesChartMeta);
  80  
  81                                              break;
  82                                          case 'catAx':
  83                                              if (isset($chartDetail->title)) {
  84                                                  $XaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta);
  85                                              }
  86  
  87                                              break;
  88                                          case 'dateAx':
  89                                              if (isset($chartDetail->title)) {
  90                                                  $XaxisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta);
  91                                              }
  92  
  93                                              break;
  94                                          case 'valAx':
  95                                              if (isset($chartDetail->title, $chartDetail->axPos)) {
  96                                                  $axisLabel = self::chartTitle($chartDetail->title->children($namespacesChartMeta['c']), $namespacesChartMeta);
  97                                                  $axPos = self::getAttribute($chartDetail->axPos, 'val', 'string');
  98  
  99                                                  switch ($axPos) {
 100                                                      case 't':
 101                                                      case 'b':
 102                                                          $XaxisLabel = $axisLabel;
 103  
 104                                                          break;
 105                                                      case 'r':
 106                                                      case 'l':
 107                                                          $YaxisLabel = $axisLabel;
 108  
 109                                                          break;
 110                                                  }
 111                                              }
 112  
 113                                              break;
 114                                          case 'barChart':
 115                                          case 'bar3DChart':
 116                                              $barDirection = self::getAttribute($chartDetail->barDir, 'val', 'string');
 117                                              $plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
 118                                              $plotSer->setPlotDirection($barDirection);
 119                                              $plotSeries[] = $plotSer;
 120                                              $plotAttributes = self::readChartAttributes($chartDetail);
 121  
 122                                              break;
 123                                          case 'lineChart':
 124                                          case 'line3DChart':
 125                                              $plotSeries[] = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
 126                                              $plotAttributes = self::readChartAttributes($chartDetail);
 127  
 128                                              break;
 129                                          case 'areaChart':
 130                                          case 'area3DChart':
 131                                              $plotSeries[] = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
 132                                              $plotAttributes = self::readChartAttributes($chartDetail);
 133  
 134                                              break;
 135                                          case 'doughnutChart':
 136                                          case 'pieChart':
 137                                          case 'pie3DChart':
 138                                              $explosion = isset($chartDetail->ser->explosion);
 139                                              $plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
 140                                              $plotSer->setPlotStyle($explosion);
 141                                              $plotSeries[] = $plotSer;
 142                                              $plotAttributes = self::readChartAttributes($chartDetail);
 143  
 144                                              break;
 145                                          case 'scatterChart':
 146                                              $scatterStyle = self::getAttribute($chartDetail->scatterStyle, 'val', 'string');
 147                                              $plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
 148                                              $plotSer->setPlotStyle($scatterStyle);
 149                                              $plotSeries[] = $plotSer;
 150                                              $plotAttributes = self::readChartAttributes($chartDetail);
 151  
 152                                              break;
 153                                          case 'bubbleChart':
 154                                              $bubbleScale = self::getAttribute($chartDetail->bubbleScale, 'val', 'integer');
 155                                              $plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
 156                                              $plotSer->setPlotStyle($bubbleScale);
 157                                              $plotSeries[] = $plotSer;
 158                                              $plotAttributes = self::readChartAttributes($chartDetail);
 159  
 160                                              break;
 161                                          case 'radarChart':
 162                                              $radarStyle = self::getAttribute($chartDetail->radarStyle, 'val', 'string');
 163                                              $plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
 164                                              $plotSer->setPlotStyle($radarStyle);
 165                                              $plotSeries[] = $plotSer;
 166                                              $plotAttributes = self::readChartAttributes($chartDetail);
 167  
 168                                              break;
 169                                          case 'surfaceChart':
 170                                          case 'surface3DChart':
 171                                              $wireFrame = self::getAttribute($chartDetail->wireframe, 'val', 'boolean');
 172                                              $plotSer = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
 173                                              $plotSer->setPlotStyle($wireFrame);
 174                                              $plotSeries[] = $plotSer;
 175                                              $plotAttributes = self::readChartAttributes($chartDetail);
 176  
 177                                              break;
 178                                          case 'stockChart':
 179                                              $plotSeries[] = self::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
 180                                              $plotAttributes = self::readChartAttributes($plotAreaLayout);
 181  
 182                                              break;
 183                                      }
 184                                  }
 185                                  if ($plotAreaLayout == null) {
 186                                      $plotAreaLayout = new Layout();
 187                                  }
 188                                  $plotArea = new PlotArea($plotAreaLayout, $plotSeries);
 189                                  self::setChartAttributes($plotAreaLayout, $plotAttributes);
 190  
 191                                  break;
 192                              case 'plotVisOnly':
 193                                  $plotVisOnly = self::getAttribute($chartDetails, 'val', 'string');
 194  
 195                                  break;
 196                              case 'dispBlanksAs':
 197                                  $dispBlanksAs = self::getAttribute($chartDetails, 'val', 'string');
 198  
 199                                  break;
 200                              case 'title':
 201                                  $title = self::chartTitle($chartDetails, $namespacesChartMeta);
 202  
 203                                  break;
 204                              case 'legend':
 205                                  $legendPos = 'r';
 206                                  $legendLayout = null;
 207                                  $legendOverlay = false;
 208                                  foreach ($chartDetails as $chartDetailKey => $chartDetail) {
 209                                      switch ($chartDetailKey) {
 210                                          case 'legendPos':
 211                                              $legendPos = self::getAttribute($chartDetail, 'val', 'string');
 212  
 213                                              break;
 214                                          case 'overlay':
 215                                              $legendOverlay = self::getAttribute($chartDetail, 'val', 'boolean');
 216  
 217                                              break;
 218                                          case 'layout':
 219                                              $legendLayout = self::chartLayoutDetails($chartDetail, $namespacesChartMeta);
 220  
 221                                              break;
 222                                      }
 223                                  }
 224                                  $legend = new Legend($legendPos, $legendLayout, $legendOverlay);
 225  
 226                                  break;
 227                          }
 228                      }
 229              }
 230          }
 231          $chart = new \PhpOffice\PhpSpreadsheet\Chart\Chart($chartName, $title, $legend, $plotArea, $plotVisOnly, $dispBlanksAs, $XaxisLabel, $YaxisLabel);
 232  
 233          return $chart;
 234      }
 235  
 236      private static function chartTitle(SimpleXMLElement $titleDetails, array $namespacesChartMeta)
 237      {
 238          $caption = [];
 239          $titleLayout = null;
 240          foreach ($titleDetails as $titleDetailKey => $chartDetail) {
 241              switch ($titleDetailKey) {
 242                  case 'tx':
 243                      $titleDetails = $chartDetail->rich->children($namespacesChartMeta['a']);
 244                      foreach ($titleDetails as $titleKey => $titleDetail) {
 245                          switch ($titleKey) {
 246                              case 'p':
 247                                  $titleDetailPart = $titleDetail->children($namespacesChartMeta['a']);
 248                                  $caption[] = self::parseRichText($titleDetailPart);
 249                          }
 250                      }
 251  
 252                      break;
 253                  case 'layout':
 254                      $titleLayout = self::chartLayoutDetails($chartDetail, $namespacesChartMeta);
 255  
 256                      break;
 257              }
 258          }
 259  
 260          return new Title($caption, $titleLayout);
 261      }
 262  
 263      private static function chartLayoutDetails($chartDetail, $namespacesChartMeta)
 264      {
 265          if (!isset($chartDetail->manualLayout)) {
 266              return null;
 267          }
 268          $details = $chartDetail->manualLayout->children($namespacesChartMeta['c']);
 269          if ($details === null) {
 270              return null;
 271          }
 272          $layout = [];
 273          foreach ($details as $detailKey => $detail) {
 274              $layout[$detailKey] = self::getAttribute($detail, 'val', 'string');
 275          }
 276  
 277          return new Layout($layout);
 278      }
 279  
 280      private static function chartDataSeries($chartDetail, $namespacesChartMeta, $plotType)
 281      {
 282          $multiSeriesType = null;
 283          $smoothLine = false;
 284          $seriesLabel = $seriesCategory = $seriesValues = $plotOrder = [];
 285  
 286          $seriesDetailSet = $chartDetail->children($namespacesChartMeta['c']);
 287          foreach ($seriesDetailSet as $seriesDetailKey => $seriesDetails) {
 288              switch ($seriesDetailKey) {
 289                  case 'grouping':
 290                      $multiSeriesType = self::getAttribute($chartDetail->grouping, 'val', 'string');
 291  
 292                      break;
 293                  case 'ser':
 294                      $marker = null;
 295                      $seriesIndex = '';
 296                      foreach ($seriesDetails as $seriesKey => $seriesDetail) {
 297                          switch ($seriesKey) {
 298                              case 'idx':
 299                                  $seriesIndex = self::getAttribute($seriesDetail, 'val', 'integer');
 300  
 301                                  break;
 302                              case 'order':
 303                                  $seriesOrder = self::getAttribute($seriesDetail, 'val', 'integer');
 304                                  $plotOrder[$seriesIndex] = $seriesOrder;
 305  
 306                                  break;
 307                              case 'tx':
 308                                  $seriesLabel[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta);
 309  
 310                                  break;
 311                              case 'marker':
 312                                  $marker = self::getAttribute($seriesDetail->symbol, 'val', 'string');
 313  
 314                                  break;
 315                              case 'smooth':
 316                                  $smoothLine = self::getAttribute($seriesDetail, 'val', 'boolean');
 317  
 318                                  break;
 319                              case 'cat':
 320                                  $seriesCategory[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta);
 321  
 322                                  break;
 323                              case 'val':
 324                                  $seriesValues[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker);
 325  
 326                                  break;
 327                              case 'xVal':
 328                                  $seriesCategory[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker);
 329  
 330                                  break;
 331                              case 'yVal':
 332                                  $seriesValues[$seriesIndex] = self::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker);
 333  
 334                                  break;
 335                          }
 336                      }
 337              }
 338          }
 339  
 340          return new DataSeries($plotType, $multiSeriesType, $plotOrder, $seriesLabel, $seriesCategory, $seriesValues, $smoothLine);
 341      }
 342  
 343      private static function chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker = null)
 344      {
 345          if (isset($seriesDetail->strRef)) {
 346              $seriesSource = (string) $seriesDetail->strRef->f;
 347              $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, null, null, $marker);
 348  
 349              if (isset($seriesDetail->strRef->strCache)) {
 350                  $seriesData = self::chartDataSeriesValues($seriesDetail->strRef->strCache->children($namespacesChartMeta['c']), 's');
 351                  $seriesValues
 352                      ->setFormatCode($seriesData['formatCode'])
 353                      ->setDataValues($seriesData['dataValues']);
 354              }
 355  
 356              return $seriesValues;
 357          } elseif (isset($seriesDetail->numRef)) {
 358              $seriesSource = (string) $seriesDetail->numRef->f;
 359              $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, $seriesSource, null, null, null, $marker);
 360              if (isset($seriesDetail->numRef->numCache)) {
 361                  $seriesData = self::chartDataSeriesValues($seriesDetail->numRef->numCache->children($namespacesChartMeta['c']));
 362                  $seriesValues
 363                      ->setFormatCode($seriesData['formatCode'])
 364                      ->setDataValues($seriesData['dataValues']);
 365              }
 366  
 367              return $seriesValues;
 368          } elseif (isset($seriesDetail->multiLvlStrRef)) {
 369              $seriesSource = (string) $seriesDetail->multiLvlStrRef->f;
 370              $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, null, null, $marker);
 371  
 372              if (isset($seriesDetail->multiLvlStrRef->multiLvlStrCache)) {
 373                  $seriesData = self::chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlStrRef->multiLvlStrCache->children($namespacesChartMeta['c']), 's');
 374                  $seriesValues
 375                      ->setFormatCode($seriesData['formatCode'])
 376                      ->setDataValues($seriesData['dataValues']);
 377              }
 378  
 379              return $seriesValues;
 380          } elseif (isset($seriesDetail->multiLvlNumRef)) {
 381              $seriesSource = (string) $seriesDetail->multiLvlNumRef->f;
 382              $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, null, null, $marker);
 383  
 384              if (isset($seriesDetail->multiLvlNumRef->multiLvlNumCache)) {
 385                  $seriesData = self::chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlNumRef->multiLvlNumCache->children($namespacesChartMeta['c']), 's');
 386                  $seriesValues
 387                      ->setFormatCode($seriesData['formatCode'])
 388                      ->setDataValues($seriesData['dataValues']);
 389              }
 390  
 391              return $seriesValues;
 392          }
 393  
 394          return null;
 395      }
 396  
 397      private static function chartDataSeriesValues($seriesValueSet, $dataType = 'n')
 398      {
 399          $seriesVal = [];
 400          $formatCode = '';
 401          $pointCount = 0;
 402  
 403          foreach ($seriesValueSet as $seriesValueIdx => $seriesValue) {
 404              switch ($seriesValueIdx) {
 405                  case 'ptCount':
 406                      $pointCount = self::getAttribute($seriesValue, 'val', 'integer');
 407  
 408                      break;
 409                  case 'formatCode':
 410                      $formatCode = (string) $seriesValue;
 411  
 412                      break;
 413                  case 'pt':
 414                      $pointVal = self::getAttribute($seriesValue, 'idx', 'integer');
 415                      if ($dataType == 's') {
 416                          $seriesVal[$pointVal] = (string) $seriesValue->v;
 417                      } elseif ($seriesValue->v === Functions::NA()) {
 418                          $seriesVal[$pointVal] = null;
 419                      } else {
 420                          $seriesVal[$pointVal] = (float) $seriesValue->v;
 421                      }
 422  
 423                      break;
 424              }
 425          }
 426  
 427          return [
 428              'formatCode' => $formatCode,
 429              'pointCount' => $pointCount,
 430              'dataValues' => $seriesVal,
 431          ];
 432      }
 433  
 434      private static function chartDataSeriesValuesMultiLevel($seriesValueSet, $dataType = 'n')
 435      {
 436          $seriesVal = [];
 437          $formatCode = '';
 438          $pointCount = 0;
 439  
 440          foreach ($seriesValueSet->lvl as $seriesLevelIdx => $seriesLevel) {
 441              foreach ($seriesLevel as $seriesValueIdx => $seriesValue) {
 442                  switch ($seriesValueIdx) {
 443                      case 'ptCount':
 444                          $pointCount = self::getAttribute($seriesValue, 'val', 'integer');
 445  
 446                          break;
 447                      case 'formatCode':
 448                          $formatCode = (string) $seriesValue;
 449  
 450                          break;
 451                      case 'pt':
 452                          $pointVal = self::getAttribute($seriesValue, 'idx', 'integer');
 453                          if ($dataType == 's') {
 454                              $seriesVal[$pointVal][] = (string) $seriesValue->v;
 455                          } elseif ($seriesValue->v === Functions::NA()) {
 456                              $seriesVal[$pointVal] = null;
 457                          } else {
 458                              $seriesVal[$pointVal][] = (float) $seriesValue->v;
 459                          }
 460  
 461                          break;
 462                  }
 463              }
 464          }
 465  
 466          return [
 467              'formatCode' => $formatCode,
 468              'pointCount' => $pointCount,
 469              'dataValues' => $seriesVal,
 470          ];
 471      }
 472  
 473      private static function parseRichText(SimpleXMLElement $titleDetailPart)
 474      {
 475          $value = new RichText();
 476          $objText = null;
 477          foreach ($titleDetailPart as $titleDetailElementKey => $titleDetailElement) {
 478              if (isset($titleDetailElement->t)) {
 479                  $objText = $value->createTextRun((string) $titleDetailElement->t);
 480              }
 481              if (isset($titleDetailElement->rPr)) {
 482                  if (isset($titleDetailElement->rPr->rFont['val'])) {
 483                      $objText->getFont()->setName((string) $titleDetailElement->rPr->rFont['val']);
 484                  }
 485  
 486                  $fontSize = (self::getAttribute($titleDetailElement->rPr, 'sz', 'integer'));
 487                  if (is_int($fontSize)) {
 488                      $objText->getFont()->setSize(floor($fontSize / 100));
 489                  }
 490  
 491                  $fontColor = (self::getAttribute($titleDetailElement->rPr, 'color', 'string'));
 492                  if ($fontColor !== null) {
 493                      $objText->getFont()->setColor(new Color(self::readColor($fontColor)));
 494                  }
 495  
 496                  $bold = self::getAttribute($titleDetailElement->rPr, 'b', 'boolean');
 497                  if ($bold !== null) {
 498                      $objText->getFont()->setBold($bold);
 499                  }
 500  
 501                  $italic = self::getAttribute($titleDetailElement->rPr, 'i', 'boolean');
 502                  if ($italic !== null) {
 503                      $objText->getFont()->setItalic($italic);
 504                  }
 505  
 506                  $baseline = self::getAttribute($titleDetailElement->rPr, 'baseline', 'integer');
 507                  if ($baseline !== null) {
 508                      if ($baseline > 0) {
 509                          $objText->getFont()->setSuperscript(true);
 510                      } elseif ($baseline < 0) {
 511                          $objText->getFont()->setSubscript(true);
 512                      }
 513                  }
 514  
 515                  $underscore = (self::getAttribute($titleDetailElement->rPr, 'u', 'string'));
 516                  if ($underscore !== null) {
 517                      if ($underscore == 'sng') {
 518                          $objText->getFont()->setUnderline(Font::UNDERLINE_SINGLE);
 519                      } elseif ($underscore == 'dbl') {
 520                          $objText->getFont()->setUnderline(Font::UNDERLINE_DOUBLE);
 521                      } else {
 522                          $objText->getFont()->setUnderline(Font::UNDERLINE_NONE);
 523                      }
 524                  }
 525  
 526                  $strikethrough = (self::getAttribute($titleDetailElement->rPr, 's', 'string'));
 527                  if ($strikethrough !== null) {
 528                      if ($strikethrough == 'noStrike') {
 529                          $objText->getFont()->setStrikethrough(false);
 530                      } else {
 531                          $objText->getFont()->setStrikethrough(true);
 532                      }
 533                  }
 534              }
 535          }
 536  
 537          return $value;
 538      }
 539  
 540      private static function readChartAttributes($chartDetail)
 541      {
 542          $plotAttributes = [];
 543          if (isset($chartDetail->dLbls)) {
 544              if (isset($chartDetail->dLbls->showLegendKey)) {
 545                  $plotAttributes['showLegendKey'] = self::getAttribute($chartDetail->dLbls->showLegendKey, 'val', 'string');
 546              }
 547              if (isset($chartDetail->dLbls->showVal)) {
 548                  $plotAttributes['showVal'] = self::getAttribute($chartDetail->dLbls->showVal, 'val', 'string');
 549              }
 550              if (isset($chartDetail->dLbls->showCatName)) {
 551                  $plotAttributes['showCatName'] = self::getAttribute($chartDetail->dLbls->showCatName, 'val', 'string');
 552              }
 553              if (isset($chartDetail->dLbls->showSerName)) {
 554                  $plotAttributes['showSerName'] = self::getAttribute($chartDetail->dLbls->showSerName, 'val', 'string');
 555              }
 556              if (isset($chartDetail->dLbls->showPercent)) {
 557                  $plotAttributes['showPercent'] = self::getAttribute($chartDetail->dLbls->showPercent, 'val', 'string');
 558              }
 559              if (isset($chartDetail->dLbls->showBubbleSize)) {
 560                  $plotAttributes['showBubbleSize'] = self::getAttribute($chartDetail->dLbls->showBubbleSize, 'val', 'string');
 561              }
 562              if (isset($chartDetail->dLbls->showLeaderLines)) {
 563                  $plotAttributes['showLeaderLines'] = self::getAttribute($chartDetail->dLbls->showLeaderLines, 'val', 'string');
 564              }
 565          }
 566  
 567          return $plotAttributes;
 568      }
 569  
 570      /**
 571       * @param mixed $plotAttributes
 572       */
 573      private static function setChartAttributes(Layout $plotArea, $plotAttributes): void
 574      {
 575          foreach ($plotAttributes as $plotAttributeKey => $plotAttributeValue) {
 576              switch ($plotAttributeKey) {
 577                  case 'showLegendKey':
 578                      $plotArea->setShowLegendKey($plotAttributeValue);
 579  
 580                      break;
 581                  case 'showVal':
 582                      $plotArea->setShowVal($plotAttributeValue);
 583  
 584                      break;
 585                  case 'showCatName':
 586                      $plotArea->setShowCatName($plotAttributeValue);
 587  
 588                      break;
 589                  case 'showSerName':
 590                      $plotArea->setShowSerName($plotAttributeValue);
 591  
 592                      break;
 593                  case 'showPercent':
 594                      $plotArea->setShowPercent($plotAttributeValue);
 595  
 596                      break;
 597                  case 'showBubbleSize':
 598                      $plotArea->setShowBubbleSize($plotAttributeValue);
 599  
 600                      break;
 601                  case 'showLeaderLines':
 602                      $plotArea->setShowLeaderLines($plotAttributeValue);
 603  
 604                      break;
 605              }
 606          }
 607      }
 608  }