3 namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx
;
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
;
20 * @param SimpleXMLElement $component
22 * @param string $format
24 * @return null|bool|float|int|string
26 private static function getAttribute(SimpleXMLElement
$component, $name, $format)
28 $attributes = $component->attributes();
29 if (isset($attributes[$name])) {
30 if ($format == 'string') {
31 return (string) $attributes[$name];
32 } elseif ($format == 'integer') {
33 return (int) $attributes[$name];
34 } elseif ($format == 'boolean') {
35 return (bool) ($attributes[$name] === '0' ||
$attributes[$name] !== 'true') ?
false : true;
38 return (float) $attributes[$name];
44 private static function readColor($color, $background = false)
46 if (isset($color['rgb'])) {
47 return (string) $color['rgb'];
48 } elseif (isset($color['indexed'])) {
49 return Color
::indexedColor($color['indexed'] - 7, $background)->getARGB();
54 * @param SimpleXMLElement $chartElements
55 * @param string $chartName
57 * @return \PhpOffice\PhpSpreadsheet\Chart\Chart
59 public static function readChart(SimpleXMLElement
$chartElements, $chartName)
61 $namespacesChartMeta = $chartElements->getNamespaces(true);
62 $chartElementsC = $chartElements->children($namespacesChartMeta['c']);
64 $XaxisLabel = $YaxisLabel = $legend = $title = null;
65 $dispBlanksAs = $plotVisOnly = null;
67 foreach ($chartElementsC as $chartElementKey => $chartElement) {
68 switch ($chartElementKey) {
70 foreach ($chartElement as $chartDetailsKey => $chartDetails) {
71 $chartDetailsC = $chartDetails->children($namespacesChartMeta['c']);
72 switch ($chartDetailsKey) {
74 $plotAreaLayout = $XaxisLable = $YaxisLable = null;
75 $plotSeries = $plotAttributes = [];
76 foreach ($chartDetails as $chartDetailKey => $chartDetail) {
77 switch ($chartDetailKey) {
79 $plotAreaLayout = self
::chartLayoutDetails($chartDetail, $namespacesChartMeta);
83 if (isset($chartDetail->title
)) {
84 $XaxisLabel = self
::chartTitle($chartDetail->title
->children($namespacesChartMeta['c']), $namespacesChartMeta);
89 if (isset($chartDetail->title
)) {
90 $XaxisLabel = self
::chartTitle($chartDetail->title
->children($namespacesChartMeta['c']), $namespacesChartMeta);
95 if (isset($chartDetail->title
)) {
96 $YaxisLabel = self
::chartTitle($chartDetail->title
->children($namespacesChartMeta['c']), $namespacesChartMeta);
102 $barDirection = self
::getAttribute($chartDetail->barDir
, 'val', 'string');
103 $plotSer = self
::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
104 $plotSer->setPlotDirection($barDirection);
105 $plotSeries[] = $plotSer;
106 $plotAttributes = self
::readChartAttributes($chartDetail);
111 $plotSeries[] = self
::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
112 $plotAttributes = self
::readChartAttributes($chartDetail);
117 $plotSeries[] = self
::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
118 $plotAttributes = self
::readChartAttributes($chartDetail);
121 case 'doughnutChart':
124 $explosion = isset($chartDetail->ser
->explosion
);
125 $plotSer = self
::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
126 $plotSer->setPlotStyle($explosion);
127 $plotSeries[] = $plotSer;
128 $plotAttributes = self
::readChartAttributes($chartDetail);
132 $scatterStyle = self
::getAttribute($chartDetail->scatterStyle
, 'val', 'string');
133 $plotSer = self
::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
134 $plotSer->setPlotStyle($scatterStyle);
135 $plotSeries[] = $plotSer;
136 $plotAttributes = self
::readChartAttributes($chartDetail);
140 $bubbleScale = self
::getAttribute($chartDetail->bubbleScale
, 'val', 'integer');
141 $plotSer = self
::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
142 $plotSer->setPlotStyle($bubbleScale);
143 $plotSeries[] = $plotSer;
144 $plotAttributes = self
::readChartAttributes($chartDetail);
148 $radarStyle = self
::getAttribute($chartDetail->radarStyle
, 'val', 'string');
149 $plotSer = self
::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
150 $plotSer->setPlotStyle($radarStyle);
151 $plotSeries[] = $plotSer;
152 $plotAttributes = self
::readChartAttributes($chartDetail);
156 case 'surface3DChart':
157 $wireFrame = self
::getAttribute($chartDetail->wireframe
, 'val', 'boolean');
158 $plotSer = self
::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
159 $plotSer->setPlotStyle($wireFrame);
160 $plotSeries[] = $plotSer;
161 $plotAttributes = self
::readChartAttributes($chartDetail);
165 $plotSeries[] = self
::chartDataSeries($chartDetail, $namespacesChartMeta, $chartDetailKey);
166 $plotAttributes = self
::readChartAttributes($plotAreaLayout);
171 if ($plotAreaLayout == null) {
172 $plotAreaLayout = new Layout();
174 $plotArea = new PlotArea($plotAreaLayout, $plotSeries);
175 self
::setChartAttributes($plotAreaLayout, $plotAttributes);
179 $plotVisOnly = self
::getAttribute($chartDetails, 'val', 'string');
183 $dispBlanksAs = self
::getAttribute($chartDetails, 'val', 'string');
187 $title = self
::chartTitle($chartDetails, $namespacesChartMeta);
192 $legendLayout = null;
193 $legendOverlay = false;
194 foreach ($chartDetails as $chartDetailKey => $chartDetail) {
195 switch ($chartDetailKey) {
197 $legendPos = self
::getAttribute($chartDetail, 'val', 'string');
201 $legendOverlay = self
::getAttribute($chartDetail, 'val', 'boolean');
205 $legendLayout = self
::chartLayoutDetails($chartDetail, $namespacesChartMeta);
210 $legend = new Legend($legendPos, $legendLayout, $legendOverlay);
217 $chart = new \PhpOffice\PhpSpreadsheet\Chart\
Chart($chartName, $title, $legend, $plotArea, $plotVisOnly, $dispBlanksAs, $XaxisLabel, $YaxisLabel);
222 private static function chartTitle(SimpleXMLElement
$titleDetails, array $namespacesChartMeta)
226 foreach ($titleDetails as $titleDetailKey => $chartDetail) {
227 switch ($titleDetailKey) {
229 $titleDetails = $chartDetail->rich
->children($namespacesChartMeta['a']);
230 foreach ($titleDetails as $titleKey => $titleDetail) {
233 $titleDetailPart = $titleDetail->children($namespacesChartMeta['a']);
234 $caption[] = self
::parseRichText($titleDetailPart);
240 $titleLayout = self
::chartLayoutDetails($chartDetail, $namespacesChartMeta);
246 return new Title($caption, $titleLayout);
249 private static function chartLayoutDetails($chartDetail, $namespacesChartMeta)
251 if (!isset($chartDetail->manualLayout
)) {
254 $details = $chartDetail->manualLayout
->children($namespacesChartMeta['c']);
255 if ($details === null) {
259 foreach ($details as $detailKey => $detail) {
260 $layout[$detailKey] = self
::getAttribute($detail, 'val', 'string');
263 return new Layout($layout);
266 private static function chartDataSeries($chartDetail, $namespacesChartMeta, $plotType)
268 $multiSeriesType = null;
270 $seriesLabel = $seriesCategory = $seriesValues = $plotOrder = [];
272 $seriesDetailSet = $chartDetail->children($namespacesChartMeta['c']);
273 foreach ($seriesDetailSet as $seriesDetailKey => $seriesDetails) {
274 switch ($seriesDetailKey) {
276 $multiSeriesType = self
::getAttribute($chartDetail->grouping
, 'val', 'string');
281 foreach ($seriesDetails as $seriesKey => $seriesDetail) {
282 switch ($seriesKey) {
284 $seriesIndex = self
::getAttribute($seriesDetail, 'val', 'integer');
288 $seriesOrder = self
::getAttribute($seriesDetail, 'val', 'integer');
289 $plotOrder[$seriesIndex] = $seriesOrder;
293 $seriesLabel[$seriesIndex] = self
::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta);
297 $marker = self
::getAttribute($seriesDetail->symbol
, 'val', 'string');
301 $smoothLine = self
::getAttribute($seriesDetail, 'val', 'boolean');
305 $seriesCategory[$seriesIndex] = self
::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta);
309 $seriesValues[$seriesIndex] = self
::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker);
313 $seriesCategory[$seriesIndex] = self
::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker);
317 $seriesValues[$seriesIndex] = self
::chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker);
325 return new DataSeries($plotType, $multiSeriesType, $plotOrder, $seriesLabel, $seriesCategory, $seriesValues, $smoothLine);
328 private static function chartDataSeriesValueSet($seriesDetail, $namespacesChartMeta, $marker = null)
330 if (isset($seriesDetail->strRef
)) {
331 $seriesSource = (string) $seriesDetail->strRef
->f
;
332 $seriesData = self
::chartDataSeriesValues($seriesDetail->strRef
->strCache
->children($namespacesChartMeta['c']), 's');
334 return new DataSeriesValues(DataSeriesValues
::DATASERIES_TYPE_STRING
, $seriesSource, $seriesData['formatCode'], $seriesData['pointCount'], $seriesData['dataValues'], $marker);
335 } elseif (isset($seriesDetail->numRef
)) {
336 $seriesSource = (string) $seriesDetail->numRef
->f
;
337 $seriesData = self
::chartDataSeriesValues($seriesDetail->numRef
->numCache
->children($namespacesChartMeta['c']));
339 return new DataSeriesValues(DataSeriesValues
::DATASERIES_TYPE_NUMBER
, $seriesSource, $seriesData['formatCode'], $seriesData['pointCount'], $seriesData['dataValues'], $marker);
340 } elseif (isset($seriesDetail->multiLvlStrRef
)) {
341 $seriesSource = (string) $seriesDetail->multiLvlStrRef
->f
;
342 $seriesData = self
::chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlStrRef
->multiLvlStrCache
->children($namespacesChartMeta['c']), 's');
343 $seriesData['pointCount'] = count($seriesData['dataValues']);
345 return new DataSeriesValues(DataSeriesValues
::DATASERIES_TYPE_STRING
, $seriesSource, $seriesData['formatCode'], $seriesData['pointCount'], $seriesData['dataValues'], $marker);
346 } elseif (isset($seriesDetail->multiLvlNumRef
)) {
347 $seriesSource = (string) $seriesDetail->multiLvlNumRef
->f
;
348 $seriesData = self
::chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlNumRef
->multiLvlNumCache
->children($namespacesChartMeta['c']), 's');
349 $seriesData['pointCount'] = count($seriesData['dataValues']);
351 return new DataSeriesValues(DataSeriesValues
::DATASERIES_TYPE_STRING
, $seriesSource, $seriesData['formatCode'], $seriesData['pointCount'], $seriesData['dataValues'], $marker);
357 private static function chartDataSeriesValues($seriesValueSet, $dataType = 'n')
363 foreach ($seriesValueSet as $seriesValueIdx => $seriesValue) {
364 switch ($seriesValueIdx) {
366 $pointCount = self
::getAttribute($seriesValue, 'val', 'integer');
370 $formatCode = (string) $seriesValue;
374 $pointVal = self
::getAttribute($seriesValue, 'idx', 'integer');
375 if ($dataType == 's') {
376 $seriesVal[$pointVal] = (string) $seriesValue->v
;
377 } elseif ($seriesValue->v
=== Functions
::NA()) {
378 $seriesVal[$pointVal] = null;
380 $seriesVal[$pointVal] = (float) $seriesValue->v
;
388 'formatCode' => $formatCode,
389 'pointCount' => $pointCount,
390 'dataValues' => $seriesVal,
394 private static function chartDataSeriesValuesMultiLevel($seriesValueSet, $dataType = 'n')
400 foreach ($seriesValueSet->lvl
as $seriesLevelIdx => $seriesLevel) {
401 foreach ($seriesLevel as $seriesValueIdx => $seriesValue) {
402 switch ($seriesValueIdx) {
404 $pointCount = self
::getAttribute($seriesValue, 'val', 'integer');
408 $formatCode = (string) $seriesValue;
412 $pointVal = self
::getAttribute($seriesValue, 'idx', 'integer');
413 if ($dataType == 's') {
414 $seriesVal[$pointVal][] = (string) $seriesValue->v
;
415 } elseif ($seriesValue->v
=== Functions
::NA()) {
416 $seriesVal[$pointVal] = null;
418 $seriesVal[$pointVal][] = (float) $seriesValue->v
;
427 'formatCode' => $formatCode,
428 'pointCount' => $pointCount,
429 'dataValues' => $seriesVal,
433 private static function parseRichText(SimpleXMLElement
$titleDetailPart)
435 $value = new RichText();
437 foreach ($titleDetailPart as $titleDetailElementKey => $titleDetailElement) {
438 if (isset($titleDetailElement->t
)) {
439 $objText = $value->createTextRun((string) $titleDetailElement->t
);
441 if (isset($titleDetailElement->rPr
)) {
442 if (isset($titleDetailElement->rPr
->rFont
['val'])) {
443 $objText->getFont()->setName((string) $titleDetailElement->rPr
->rFont
['val']);
446 $fontSize = (self
::getAttribute($titleDetailElement->rPr
, 'sz', 'integer'));
447 if ($fontSize !== null) {
448 $objText->getFont()->setSize(floor($fontSize / 100));
451 $fontColor = (self
::getAttribute($titleDetailElement->rPr
, 'color', 'string'));
452 if ($fontColor !== null) {
453 $objText->getFont()->setColor(new Color(self
::readColor($fontColor)));
456 $bold = self
::getAttribute($titleDetailElement->rPr
, 'b', 'boolean');
457 if ($bold !== null) {
458 $objText->getFont()->setBold($bold);
461 $italic = self
::getAttribute($titleDetailElement->rPr
, 'i', 'boolean');
462 if ($italic !== null) {
463 $objText->getFont()->setItalic($italic);
466 $baseline = self
::getAttribute($titleDetailElement->rPr
, 'baseline', 'integer');
467 if ($baseline !== null) {
469 $objText->getFont()->setSuperscript(true);
470 } elseif ($baseline < 0) {
471 $objText->getFont()->setSubscript(true);
475 $underscore = (self
::getAttribute($titleDetailElement->rPr
, 'u', 'string'));
476 if ($underscore !== null) {
477 if ($underscore == 'sng') {
478 $objText->getFont()->setUnderline(Font
::UNDERLINE_SINGLE
);
479 } elseif ($underscore == 'dbl') {
480 $objText->getFont()->setUnderline(Font
::UNDERLINE_DOUBLE
);
482 $objText->getFont()->setUnderline(Font
::UNDERLINE_NONE
);
486 $strikethrough = (self
::getAttribute($titleDetailElement->rPr
, 's', 'string'));
487 if ($strikethrough !== null) {
488 if ($strikethrough == 'noStrike') {
489 $objText->getFont()->setStrikethrough(false);
491 $objText->getFont()->setStrikethrough(true);
500 private static function readChartAttributes($chartDetail)
502 $plotAttributes = [];
503 if (isset($chartDetail->dLbls
)) {
504 if (isset($chartDetail->dLbls
->howLegendKey
)) {
505 $plotAttributes['showLegendKey'] = self
::getAttribute($chartDetail->dLbls
->showLegendKey
, 'val', 'string');
507 if (isset($chartDetail->dLbls
->showVal
)) {
508 $plotAttributes['showVal'] = self
::getAttribute($chartDetail->dLbls
->showVal
, 'val', 'string');
510 if (isset($chartDetail->dLbls
->showCatName
)) {
511 $plotAttributes['showCatName'] = self
::getAttribute($chartDetail->dLbls
->showCatName
, 'val', 'string');
513 if (isset($chartDetail->dLbls
->showSerName
)) {
514 $plotAttributes['showSerName'] = self
::getAttribute($chartDetail->dLbls
->showSerName
, 'val', 'string');
516 if (isset($chartDetail->dLbls
->showPercent
)) {
517 $plotAttributes['showPercent'] = self
::getAttribute($chartDetail->dLbls
->showPercent
, 'val', 'string');
519 if (isset($chartDetail->dLbls
->showBubbleSize
)) {
520 $plotAttributes['showBubbleSize'] = self
::getAttribute($chartDetail->dLbls
->showBubbleSize
, 'val', 'string');
522 if (isset($chartDetail->dLbls
->showLeaderLines
)) {
523 $plotAttributes['showLeaderLines'] = self
::getAttribute($chartDetail->dLbls
->showLeaderLines
, 'val', 'string');
527 return $plotAttributes;
531 * @param Layout $plotArea
532 * @param mixed $plotAttributes
534 private static function setChartAttributes(Layout
$plotArea, $plotAttributes)
536 foreach ($plotAttributes as $plotAttributeKey => $plotAttributeValue) {
537 switch ($plotAttributeKey) {
538 case 'showLegendKey':
539 $plotArea->setShowLegendKey($plotAttributeValue);
543 $plotArea->setShowVal($plotAttributeValue);
547 $plotArea->setShowCatName($plotAttributeValue);
551 $plotArea->setShowSerName($plotAttributeValue);
555 $plotArea->setShowPercent($plotAttributeValue);
558 case 'showBubbleSize':
559 $plotArea->setShowBubbleSize($plotAttributeValue);
562 case 'showLeaderLines':
563 $plotArea->setShowLeaderLines($plotAttributeValue);