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