From c22c6df5b526c6d794cfd552a31df2bdd4615702 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Mon, 4 Jul 2022 08:43:54 -0700 Subject: [PATCH] Charts Additional Support for Layout and DataSeriesValues (#2922) * Charts Additional Support for Layout and DataSeriesValues The dLbls tag in more or less the Xml equivalent of the Layout class. It is currently read and written only for the Chart as a whole. It can, however, also be applied to DataSeriesValues. Further it has properties which are currently ignored, namely label fill, border, and font colors. All of these omissions are handled by this PR. There are other properties which can be applied to the labels, but, for now, only the 3 colors are added. DataSeriesValues can have effects (like glow). Since DSV now descends from Properties, these are already supported, but support needs to be added to the Reader and Writer to handle them. This PR adds the support. * Add Unit Tests Based on new samples. * Minor Improvements Slight increase to coverage. --- CHANGELOG.md | 2 +- phpstan-baseline.neon | 95 ------ samples/Chart/33_Chart_create_bubble.php | 3 +- samples/templates/32readwriteBarChart4.xlsx | Bin 0 -> 11688 bytes samples/templates/32readwriteLineChart4.xlsx | Bin 0 -> 13474 bytes src/PhpSpreadsheet/Chart/DataSeriesValues.php | 15 + src/PhpSpreadsheet/Chart/Layout.php | 182 +++++------ src/PhpSpreadsheet/Chart/Properties.php | 32 +- src/PhpSpreadsheet/Reader/Xlsx/Chart.php | 42 ++- src/PhpSpreadsheet/Writer/Xlsx/Chart.php | 283 +++++++++--------- .../Chart/Charts32DsvGlowTest.php | 58 ++++ .../Chart/Charts32DsvLabelsTest.php | 73 +++++ .../Chart/GridlinesShadowGlowTest.php | 5 + .../PhpSpreadsheetTests/Chart/LayoutTest.php | 34 +++ 14 files changed, 493 insertions(+), 331 deletions(-) create mode 100644 samples/templates/32readwriteBarChart4.xlsx create mode 100644 samples/templates/32readwriteLineChart4.xlsx create mode 100644 tests/PhpSpreadsheetTests/Chart/Charts32DsvGlowTest.php create mode 100644 tests/PhpSpreadsheetTests/Chart/Charts32DsvLabelsTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index b7d55bbe..6e3a4db1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,7 +49,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Time interval formatting [Issue #2768](https://github.com/PHPOffice/PhpSpreadsheet/issues/2768) [PR #2772](https://github.com/PHPOffice/PhpSpreadsheet/pull/2772) - Copy from Xls(x) to Html/Pdf loses drawings [PR #2788](https://github.com/PHPOffice/PhpSpreadsheet/pull/2788) - Html Reader converting cell containing 0 to null string [Issue #2810](https://github.com/PHPOffice/PhpSpreadsheet/issues/2810) [PR #2813](https://github.com/PHPOffice/PhpSpreadsheet/pull/2813) -- Many fixes for Charts, especially, but not limited to, Scatter, Bubble, and Surface charts. [Issue #2762](https://github.com/PHPOffice/PhpSpreadsheet/issues/2762) [Issue #2299](https://github.com/PHPOffice/PhpSpreadsheet/issues/2299) [Issue #2700](https://github.com/PHPOffice/PhpSpreadsheet/issues/2700) [Issue #2817](https://github.com/PHPOffice/PhpSpreadsheet/issues/2817) [Issue #2763](https://github.com/PHPOffice/PhpSpreadsheet/issues/2763) [Issue #2219](https://github.com/PHPOffice/PhpSpreadsheet/issues/2219) [Issue #2863](https://github.com/PHPOffice/PhpSpreadsheet/issues/2863) [PR #2828](https://github.com/PHPOffice/PhpSpreadsheet/pull/2828) [PR #2841](https://github.com/PHPOffice/PhpSpreadsheet/pull/2841) [PR #2846](https://github.com/PHPOffice/PhpSpreadsheet/pull/2846) [PR #2852](https://github.com/PHPOffice/PhpSpreadsheet/pull/2852) [PR #2856](https://github.com/PHPOffice/PhpSpreadsheet/pull/2856) [PR #2865](https://github.com/PHPOffice/PhpSpreadsheet/pull/2865) [PR #2872](https://github.com/PHPOffice/PhpSpreadsheet/pull/2872) [PR #2879](https://github.com/PHPOffice/PhpSpreadsheet/pull/2879) [PR #2898](https://github.com/PHPOffice/PhpSpreadsheet/pull/2898) [PR #2906](https://github.com/PHPOffice/PhpSpreadsheet/pull/2906) +- Many fixes for Charts, especially, but not limited to, Scatter, Bubble, and Surface charts. [Issue #2762](https://github.com/PHPOffice/PhpSpreadsheet/issues/2762) [Issue #2299](https://github.com/PHPOffice/PhpSpreadsheet/issues/2299) [Issue #2700](https://github.com/PHPOffice/PhpSpreadsheet/issues/2700) [Issue #2817](https://github.com/PHPOffice/PhpSpreadsheet/issues/2817) [Issue #2763](https://github.com/PHPOffice/PhpSpreadsheet/issues/2763) [Issue #2219](https://github.com/PHPOffice/PhpSpreadsheet/issues/2219) [Issue #2863](https://github.com/PHPOffice/PhpSpreadsheet/issues/2863) [PR #2828](https://github.com/PHPOffice/PhpSpreadsheet/pull/2828) [PR #2841](https://github.com/PHPOffice/PhpSpreadsheet/pull/2841) [PR #2846](https://github.com/PHPOffice/PhpSpreadsheet/pull/2846) [PR #2852](https://github.com/PHPOffice/PhpSpreadsheet/pull/2852) [PR #2856](https://github.com/PHPOffice/PhpSpreadsheet/pull/2856) [PR #2865](https://github.com/PHPOffice/PhpSpreadsheet/pull/2865) [PR #2872](https://github.com/PHPOffice/PhpSpreadsheet/pull/2872) [PR #2879](https://github.com/PHPOffice/PhpSpreadsheet/pull/2879) [PR #2898](https://github.com/PHPOffice/PhpSpreadsheet/pull/2898) [PR #2906](https://github.com/PHPOffice/PhpSpreadsheet/pull/2906) [PR #2922](https://github.com/PHPOffice/PhpSpreadsheet/pull/2922) - Calculating Engine regexp for Column/Row references when there are multiple quoted worksheet references in the formula [Issue #2874](https://github.com/PHPOffice/PhpSpreadsheet/issues/2874) [PR #2899](https://github.com/PHPOffice/PhpSpreadsheet/pull/2899) ## 1.23.0 - 2022-04-24 diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 04bdf273..108836bc 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1185,46 +1185,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Chart/PlotArea.php - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Properties\\:\\:getArrayElementsValue\\(\\) has no return type specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Chart/Properties.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Properties\\:\\:getArrayElementsValue\\(\\) has parameter \\$elements with no type specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Chart/Properties.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Properties\\:\\:getArrayElementsValue\\(\\) has parameter \\$properties with no type specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Chart/Properties.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Properties\\:\\:getLineStyleArrowSize\\(\\) has no return type specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Chart/Properties.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Properties\\:\\:getLineStyleArrowSize\\(\\) has parameter \\$arrayKaySelector with no type specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Chart/Properties.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Properties\\:\\:getLineStyleArrowSize\\(\\) has parameter \\$arraySelector with no type specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Chart/Properties.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Properties\\:\\:getShadowPresetsMap\\(\\) has no return type specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Chart/Properties.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Properties\\:\\:getShadowPresetsMap\\(\\) has parameter \\$presetsOption with no type specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Chart/Properties.php - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Title\\:\\:\\$layout \\(PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Layout\\) does not accept PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Layout\\|null\\.$#" count: 1 @@ -4020,61 +3980,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Writer/Xlsx.php - - - message: "#^Parameter \\#1 \\$plotSeriesValues of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Chart\\:\\:writeBubbles\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeriesValues\\|null, PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeriesValues\\|false given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php - - - - message: "#^Parameter \\#1 \\$rawTextData of method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\XMLWriter\\:\\:writeRawData\\(\\) expects array\\\\|string\\|null, int given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php - - - - message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, float given\\.$#" - count: 6 - path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php - - - - message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, float\\|int given\\.$#" - count: 4 - path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php - - - - message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, int given\\.$#" - count: 41 - path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php - - - - message: "#^Parameter \\#6 \\$yAxis of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Chart\\:\\:writeCategoryAxis\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Axis, PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Axis\\|null given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php - - - - message: "#^Parameter \\#7 \\$xAxis of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Chart\\:\\:writeValueAxis\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Axis, PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Axis\\|null given\\.$#" - count: 2 - path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php - - - - message: "#^Parameter \\#8 \\$majorGridlines of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Chart\\:\\:writeValueAxis\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\GridLines, PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\GridLines\\|null given\\.$#" - count: 2 - path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php - - - - message: "#^Parameter \\#9 \\$minorGridlines of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Chart\\:\\:writeValueAxis\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\GridLines, PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\GridLines\\|null given\\.$#" - count: 2 - path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Chart\\:\\:\\$calculateCellValues has no type specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php - - - - message: "#^Strict comparison using \\=\\=\\= between PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\PlotArea and null will always evaluate to false\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php - - message: "#^Parameter \\#1 \\$string of function substr expects string, int given\\.$#" count: 1 diff --git a/samples/Chart/33_Chart_create_bubble.php b/samples/Chart/33_Chart_create_bubble.php index 33feea62..5efcda71 100644 --- a/samples/Chart/33_Chart_create_bubble.php +++ b/samples/Chart/33_Chart_create_bubble.php @@ -89,7 +89,8 @@ $series = new DataSeries( $series->setPlotBubbleSizes($dataSeriesBubbles); // Set the series in the plot area -$plotArea = new PlotArea(null, [$series]); +$plotArea = new PlotArea(); +$plotArea->setPlotSeries([$series]); // Set the chart legend $legend = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false); diff --git a/samples/templates/32readwriteBarChart4.xlsx b/samples/templates/32readwriteBarChart4.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..c78572957769be806273bd25daa4150f418ae7b5 GIT binary patch literal 11688 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 Ie*E?S09k(oaR2}S literal 0 HcmV?d00001 diff --git a/samples/templates/32readwriteLineChart4.xlsx b/samples/templates/32readwriteLineChart4.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..eb09c784e280fb27da5705b25a21f0deef57b981 GIT binary patch literal 13474 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 + } + + public function setLabelLayout(?Layout $labelLayout): self + { + $this->labelLayout = $labelLayout; + + return $this; + } } diff --git a/src/PhpSpreadsheet/Chart/Layout.php b/src/PhpSpreadsheet/Chart/Layout.php index cea96557..03a0ca3e 100644 --- a/src/PhpSpreadsheet/Chart/Layout.php +++ b/src/PhpSpreadsheet/Chart/Layout.php @@ -57,7 +57,7 @@ class Layout * show legend key * Specifies that legend keys should be shown in data labels. * - * @var bool + * @var ?bool */ private $showLegendKey; @@ -65,7 +65,7 @@ class Layout * show value * Specifies that the value should be shown in a data label. * - * @var bool + * @var ?bool */ private $showVal; @@ -73,7 +73,7 @@ class Layout * show category name * Specifies that the category name should be shown in the data label. * - * @var bool + * @var ?bool */ private $showCatName; @@ -81,7 +81,7 @@ class Layout * show data series name * Specifies that the series name should be shown in the data label. * - * @var bool + * @var ?bool */ private $showSerName; @@ -89,14 +89,14 @@ class Layout * show percentage * Specifies that the percentage should be shown in the data label. * - * @var bool + * @var ?bool */ private $showPercent; /** * show bubble size. * - * @var bool + * @var ?bool */ private $showBubbleSize; @@ -104,10 +104,19 @@ class Layout * show leader lines * Specifies that leader lines should be shown for the data label. * - * @var bool + * @var ?bool */ private $showLeaderLines; + /** @var ?ChartColor */ + private $labelFillColor; + + /** @var ?ChartColor */ + private $labelBorderColor; + + /** @var ?ChartColor */ + private $labelFontColor; + /** * Create a new Layout. */ @@ -134,6 +143,30 @@ class Layout if (isset($layout['h'])) { $this->height = (float) $layout['h']; } + $this->initBoolean($layout, 'showLegendKey'); + $this->initBoolean($layout, 'showVal'); + $this->initBoolean($layout, 'showCatName'); + $this->initBoolean($layout, 'showSerName'); + $this->initBoolean($layout, 'showPercent'); + $this->initBoolean($layout, 'showBubbleSize'); + $this->initBoolean($layout, 'showLeaderLines'); + $this->initColor($layout, 'labelFillColor'); + $this->initColor($layout, 'labelBorderColor'); + $this->initColor($layout, 'labelFontColor'); + } + + private function initBoolean(array $layout, string $name): void + { + if (isset($layout[$name])) { + $this->$name = (bool) $layout[$name]; + } + } + + private function initColor(array $layout, string $name): void + { + if (isset($layout[$name]) && $layout[$name] instanceof ChartColor) { + $this->$name = $layout[$name]; + } } /** @@ -304,12 +337,7 @@ class Layout return $this; } - /** - * Get show legend key. - * - * @return bool - */ - public function getShowLegendKey() + public function getShowLegendKey(): ?bool { return $this->showLegendKey; } @@ -317,24 +345,15 @@ class Layout /** * Set show legend key * Specifies that legend keys should be shown in data labels. - * - * @param bool $showLegendKey Show legend key - * - * @return $this */ - public function setShowLegendKey($showLegendKey) + public function setShowLegendKey(?bool $showLegendKey): self { $this->showLegendKey = $showLegendKey; return $this; } - /** - * Get show value. - * - * @return bool - */ - public function getShowVal() + public function getShowVal(): ?bool { return $this->showVal; } @@ -342,24 +361,15 @@ class Layout /** * Set show val * Specifies that the value should be shown in data labels. - * - * @param bool $showDataLabelValues Show val - * - * @return $this */ - public function setShowVal($showDataLabelValues) + public function setShowVal(?bool $showDataLabelValues): self { $this->showVal = $showDataLabelValues; return $this; } - /** - * Get show category name. - * - * @return bool - */ - public function getShowCatName() + public function getShowCatName(): ?bool { return $this->showCatName; } @@ -367,115 +377,111 @@ class Layout /** * Set show cat name * Specifies that the category name should be shown in data labels. - * - * @param bool $showCategoryName Show cat name - * - * @return $this */ - public function setShowCatName($showCategoryName) + public function setShowCatName(?bool $showCategoryName): self { $this->showCatName = $showCategoryName; return $this; } - /** - * Get show data series name. - * - * @return bool - */ - public function getShowSerName() + public function getShowSerName(): ?bool { return $this->showSerName; } /** - * Set show ser name + * Set show data series name. * Specifies that the series name should be shown in data labels. - * - * @param bool $showSeriesName Show series name - * - * @return $this */ - public function setShowSerName($showSeriesName) + public function setShowSerName(?bool $showSeriesName): self { $this->showSerName = $showSeriesName; return $this; } - /** - * Get show percentage. - * - * @return bool - */ - public function getShowPercent() + public function getShowPercent(): ?bool { return $this->showPercent; } /** - * Set show percentage + * Set show percentage. * Specifies that the percentage should be shown in data labels. - * - * @param bool $showPercentage Show percentage - * - * @return $this */ - public function setShowPercent($showPercentage) + public function setShowPercent(?bool $showPercentage): self { $this->showPercent = $showPercentage; return $this; } - /** - * Get show bubble size. - * - * @return bool - */ - public function getShowBubbleSize() + public function getShowBubbleSize(): ?bool { return $this->showBubbleSize; } /** - * Set show bubble size + * Set show bubble size. * Specifies that the bubble size should be shown in data labels. - * - * @param bool $showBubbleSize Show bubble size - * - * @return $this */ - public function setShowBubbleSize($showBubbleSize) + public function setShowBubbleSize(?bool $showBubbleSize): self { $this->showBubbleSize = $showBubbleSize; return $this; } - /** - * Get show leader lines. - * - * @return bool - */ - public function getShowLeaderLines() + public function getShowLeaderLines(): ?bool { return $this->showLeaderLines; } /** - * Set show leader lines + * Set show leader lines. * Specifies that leader lines should be shown in data labels. - * - * @param bool $showLeaderLines Show leader lines - * - * @return $this */ - public function setShowLeaderLines($showLeaderLines) + public function setShowLeaderLines(?bool $showLeaderLines): self { $this->showLeaderLines = $showLeaderLines; return $this; } + + public function getLabelFillColor(): ?ChartColor + { + return $this->labelFillColor; + } + + public function setLabelFillColor(?ChartColor $chartColor): self + { + $this->labelFillColor = $chartColor; + + return $this; + } + + public function getLabelBorderColor(): ?ChartColor + { + return $this->labelBorderColor; + } + + public function setLabelBorderColor(?ChartColor $chartColor): self + { + $this->labelBorderColor = $chartColor; + + return $this; + } + + public function getLabelFontColor(): ?ChartColor + { + return $this->labelFontColor; + } + + public function setLabelFontColor(?ChartColor $chartColor): self + { + $this->labelFontColor = $chartColor; + + return $this; + } } diff --git a/src/PhpSpreadsheet/Chart/Properties.php b/src/PhpSpreadsheet/Chart/Properties.php index fdc3c12b..2ee6572a 100644 --- a/src/PhpSpreadsheet/Chart/Properties.php +++ b/src/PhpSpreadsheet/Chart/Properties.php @@ -421,11 +421,19 @@ abstract class Properties ], ]; - protected function getShadowPresetsMap($presetsOption) + protected function getShadowPresetsMap(int $presetsOption): array { return self::PRESETS_OPTIONS[$presetsOption] ?? self::PRESETS_OPTIONS[0]; } + /** + * Get value of array element. + * + * @param mixed $properties + * @param mixed $elements + * + * @return mixed + */ protected function getArrayElementsValue($properties, $elements) { $reference = &$properties; @@ -718,6 +726,16 @@ abstract class Properties return $this->getArrayElementsValue($this->shadowProperties, $elements); } + public function getShadowArray(): array + { + $array = $this->shadowProperties; + if ($this->getShadowColorObject()->isUsable()) { + $array['color'] = $this->getShadowProperty('color'); + } + + return $array; + } + /** @var ChartColor */ protected $lineColor; @@ -748,6 +766,10 @@ abstract class Properties { $this->lineStyleProperties = $otherProperties->lineStyleProperties; $this->lineColor = $otherProperties->lineColor; + $this->glowSize = $otherProperties->glowSize; + $this->glowColor = $otherProperties->glowColor; + $this->softEdges = $otherProperties->softEdges; + $this->shadowProperties = $otherProperties->shadowProperties; } public function getLineColor(): ChartColor @@ -875,6 +897,14 @@ abstract class Properties 9 => ['w' => 'lg', 'len' => 'lg'], ]; + /** + * Get Line Style Arrow Size. + * + * @param int $arraySelector + * @param string $arrayKaySelector + * + * @return string + */ protected function getLineStyleArrowSize($arraySelector, $arrayKaySelector) { return self::ARROW_SIZES[$arraySelector][$arrayKaySelector] ?? ''; diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php index df25dac7..fd156f13 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php @@ -389,6 +389,7 @@ class Chart $markerFillColor = null; $markerBorderColor = null; $lineStyle = null; + $labelLayout = null; foreach ($seriesDetails as $seriesKey => $seriesDetail) { switch ($seriesKey) { case 'idx': @@ -415,6 +416,12 @@ class Chart $lineStyle = new GridLines(); $this->readLineStyle($seriesDetails, $lineStyle); } + if (isset($children->effectLst)) { + if ($lineStyle === null) { + $lineStyle = new GridLines(); + } + $this->readEffects($seriesDetails, $lineStyle); + } if (isset($children->solidFill)) { $fillColor = new ChartColor($this->readColor($children->solidFill)); } @@ -474,6 +481,21 @@ class Chart $bubble3D = self::getAttribute($seriesDetail, 'val', 'boolean'); break; + case 'dLbls': + $labelLayout = new Layout($this->readChartAttributes($seriesDetails)); + + break; + } + } + if ($labelLayout) { + if (isset($seriesLabel[$seriesIndex])) { + $seriesLabel[$seriesIndex]->setLabelLayout($labelLayout); + } + if (isset($seriesCategory[$seriesIndex])) { + $seriesCategory[$seriesIndex]->setLabelLayout($labelLayout); + } + if (isset($seriesValues[$seriesIndex])) { + $seriesValues[$seriesIndex]->setLabelLayout($labelLayout); } } if ($noFill) { @@ -947,6 +969,21 @@ class Chart if (isset($chartDetail->dLbls->showLeaderLines)) { $plotAttributes['showLeaderLines'] = self::getAttribute($chartDetail->dLbls->showLeaderLines, 'val', 'string'); } + if (isset($chartDetail->dLbls->spPr)) { + $sppr = $chartDetail->dLbls->spPr->children($this->aNamespace); + if (isset($sppr->solidFill)) { + $plotAttributes['labelFillColor'] = new ChartColor($this->readColor($sppr->solidFill)); + } + if (isset($sppr->ln->solidFill)) { + $plotAttributes['labelBorderColor'] = new ChartColor($this->readColor($sppr->ln->solidFill)); + } + } + if (isset($chartDetail->dLbls->txPr)) { + $txpr = $chartDetail->dLbls->txPr->children($this->aNamespace); + if (isset($txpr->p->pPr->defRPr->solidFill)) { + $plotAttributes['labelFontColor'] = new ChartColor($this->readColor($txpr->p->pPr->defRPr->solidFill)); + } + } } return $plotAttributes; @@ -991,10 +1028,7 @@ class Chart } } - /** - * @param null|Axis|GridLines $chartObject may be extended to include other types - */ - private function readEffects(SimpleXMLElement $chartDetail, $chartObject): void + private function readEffects(SimpleXMLElement $chartDetail, ?Properties $chartObject): void { if (!isset($chartObject, $chartDetail->spPr)) { return; diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php index d242e602..876e5f06 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php @@ -17,8 +17,6 @@ use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException; class Chart extends WriterPart { - protected $calculateCellValues; - /** * @var int */ @@ -33,8 +31,6 @@ class Chart extends WriterPart */ public function writeChart(\PhpOffice\PhpSpreadsheet\Chart\Chart $chart, $calculateCellValues = true) { - $this->calculateCellValues = $calculateCellValues; - // Create XML writer $objWriter = null; if ($this->getParentWriter()->getUseDiskCaching()) { @@ -43,7 +39,7 @@ class Chart extends WriterPart $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY); } // Ensure that data series values are up-to-date before we save - if ($this->calculateCellValues) { + if ($calculateCellValues) { $chart->refresh(); } @@ -57,13 +53,13 @@ class Chart extends WriterPart $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); $objWriter->startElement('c:date1904'); - $objWriter->writeAttribute('val', 0); + $objWriter->writeAttribute('val', '0'); $objWriter->endElement(); $objWriter->startElement('c:lang'); $objWriter->writeAttribute('val', 'en-GB'); $objWriter->endElement(); $objWriter->startElement('c:roundedCorners'); - $objWriter->writeAttribute('val', 0); + $objWriter->writeAttribute('val', '0'); $objWriter->endElement(); $this->writeAlternateContent($objWriter); @@ -73,7 +69,7 @@ class Chart extends WriterPart $this->writeTitle($objWriter, $chart->getTitle()); $objWriter->startElement('c:autoTitleDeleted'); - $objWriter->writeAttribute('val', 0); + $objWriter->writeAttribute('val', '0'); $objWriter->endElement(); $objWriter->startElement('c:view3D'); @@ -108,7 +104,7 @@ class Chart extends WriterPart $this->writeLegend($objWriter, $chart->getLegend()); $objWriter->startElement('c:plotVisOnly'); - $objWriter->writeAttribute('val', (int) $chart->getPlotVisibleOnly()); + $objWriter->writeAttribute('val', (string) (int) $chart->getPlotVisibleOnly()); $objWriter->endElement(); $objWriter->startElement('c:dispBlanksAs'); @@ -116,7 +112,7 @@ class Chart extends WriterPart $objWriter->endElement(); $objWriter->startElement('c:showDLblsOverMax'); - $objWriter->writeAttribute('val', 0); + $objWriter->writeAttribute('val', '0'); $objWriter->endElement(); $objWriter->endElement(); @@ -167,7 +163,7 @@ class Chart extends WriterPart $this->writeLayout($objWriter, $title->getLayout()); $objWriter->startElement('c:overlay'); - $objWriter->writeAttribute('val', 0); + $objWriter->writeAttribute('val', '0'); $objWriter->endElement(); $objWriter->endElement(); @@ -203,7 +199,7 @@ class Chart extends WriterPart $objWriter->startElement('a:p'); $objWriter->startElement('a:pPr'); - $objWriter->writeAttribute('rtl', 0); + $objWriter->writeAttribute('rtl', '0'); $objWriter->startElement('a:defRPr'); $objWriter->endElement(); @@ -222,7 +218,7 @@ class Chart extends WriterPart /** * Write Chart Plot Area. */ - private function writePlotArea(XMLWriter $objWriter, PlotArea $plotArea, ?Title $xAxisLabel = null, ?Title $yAxisLabel = null, ?Axis $xAxis = null, ?Axis $yAxis = null, ?GridLines $majorGridlines = null, ?GridLines $minorGridlines = null): void + private function writePlotArea(XMLWriter $objWriter, ?PlotArea $plotArea, ?Title $xAxisLabel = null, ?Title $yAxisLabel = null, ?Axis $xAxis = null, ?Axis $yAxis = null, ?GridLines $majorGridlines = null, ?GridLines $minorGridlines = null): void { if ($plotArea === null) { return; @@ -273,16 +269,16 @@ class Chart extends WriterPart if ($chartType === DataSeries::TYPE_LINECHART && $plotGroup) { // Line only, Line3D can't be smoothed $objWriter->startElement('c:smooth'); - $objWriter->writeAttribute('val', (int) $plotGroup->getSmoothLine()); + $objWriter->writeAttribute('val', (string) (int) $plotGroup->getSmoothLine()); $objWriter->endElement(); } elseif (($chartType === DataSeries::TYPE_BARCHART) || ($chartType === DataSeries::TYPE_BARCHART_3D)) { $objWriter->startElement('c:gapWidth'); - $objWriter->writeAttribute('val', 150); + $objWriter->writeAttribute('val', '150'); $objWriter->endElement(); if ($plotGroupingType == 'percentStacked' || $plotGroupingType == 'stacked') { $objWriter->startElement('c:overlap'); - $objWriter->writeAttribute('val', 100); + $objWriter->writeAttribute('val', '100'); $objWriter->endElement(); } } elseif ($chartType === DataSeries::TYPE_BUBBLECHART) { @@ -294,7 +290,7 @@ class Chart extends WriterPart } $objWriter->startElement('c:showNegBubbles'); - $objWriter->writeAttribute('val', 0); + $objWriter->writeAttribute('val', '0'); $objWriter->endElement(); } elseif ($chartType === DataSeries::TYPE_STOCKCHART) { $objWriter->startElement('c:hiLowLines'); @@ -303,7 +299,7 @@ class Chart extends WriterPart $objWriter->startElement('c:upDownBars'); $objWriter->startElement('c:gapWidth'); - $objWriter->writeAttribute('val', 300); + $objWriter->writeAttribute('val', '300'); $objWriter->endElement(); $objWriter->startElement('c:upBars'); @@ -334,12 +330,12 @@ class Chart extends WriterPart } } else { $objWriter->startElement('c:firstSliceAng'); - $objWriter->writeAttribute('val', 0); + $objWriter->writeAttribute('val', '0'); $objWriter->endElement(); if ($chartType === DataSeries::TYPE_DONUTCHART) { $objWriter->startElement('c:holeSize'); - $objWriter->writeAttribute('val', 50); + $objWriter->writeAttribute('val', '50'); $objWriter->endElement(); } } @@ -349,12 +345,12 @@ class Chart extends WriterPart if (($chartType !== DataSeries::TYPE_PIECHART) && ($chartType !== DataSeries::TYPE_PIECHART_3D) && ($chartType !== DataSeries::TYPE_DONUTCHART)) { if ($chartType === DataSeries::TYPE_BUBBLECHART) { - $this->writeValueAxis($objWriter, $xAxisLabel, $chartType, $id2, $id1, $catIsMultiLevelSeries, $xAxis, $majorGridlines, $minorGridlines); + $this->writeValueAxis($objWriter, $xAxisLabel, $chartType, $id2, $id1, $catIsMultiLevelSeries, $xAxis ?? new Axis(), $majorGridlines, $minorGridlines); } else { - $this->writeCategoryAxis($objWriter, $xAxisLabel, $id1, $id2, $catIsMultiLevelSeries, $xAxis); + $this->writeCategoryAxis($objWriter, $xAxisLabel, $id1, $id2, $catIsMultiLevelSeries, $xAxis ?? new Axis()); } - $this->writeValueAxis($objWriter, $yAxisLabel, $chartType, $id1, $id2, $valIsMultiLevelSeries, $yAxis, $majorGridlines, $minorGridlines); + $this->writeValueAxis($objWriter, $yAxisLabel, $chartType, $id1, $id2, $valIsMultiLevelSeries, $yAxis ?? new Axis(), $majorGridlines, $minorGridlines); if ($chartType === DataSeries::TYPE_SURFACECHART_3D || $chartType === DataSeries::TYPE_SURFACECHART) { $this->writeSerAxis($objWriter, $id2, $id3); } @@ -363,49 +359,75 @@ class Chart extends WriterPart $objWriter->endElement(); } + private function writeDataLabelsBool(XMLWriter $objWriter, string $name, ?bool $value): void + { + if ($value !== null) { + $objWriter->startElement("c:$name"); + $objWriter->writeAttribute('val', $value ? '1' : '0'); + $objWriter->endElement(); + } + } + /** * Write Data Labels. */ private function writeDataLabels(XMLWriter $objWriter, ?Layout $chartLayout = null): void { + if (!isset($chartLayout)) { + return; + } $objWriter->startElement('c:dLbls'); - $objWriter->startElement('c:showLegendKey'); - $showLegendKey = (empty($chartLayout)) ? 0 : $chartLayout->getShowLegendKey(); - $objWriter->writeAttribute('val', ((empty($showLegendKey)) ? 0 : 1)); - $objWriter->endElement(); + $fillColor = $chartLayout->getLabelFillColor(); + $borderColor = $chartLayout->getLabelBorderColor(); + if ($fillColor && $fillColor->isUsable()) { + $objWriter->startElement('c:spPr'); + $this->writeColor($objWriter, $fillColor); + if ($borderColor && $borderColor->isUsable()) { + $objWriter->startElement('a:ln'); + $this->writeColor($objWriter, $borderColor); + $objWriter->endElement(); // a:ln + } + $objWriter->endElement(); // c:spPr + } + $fontColor = $chartLayout->getLabelFontColor(); + if ($fontColor && $fontColor->isUsable()) { + $objWriter->startElement('c:txPr'); - $objWriter->startElement('c:showVal'); - $showVal = (empty($chartLayout)) ? 0 : $chartLayout->getShowVal(); - $objWriter->writeAttribute('val', ((empty($showVal)) ? 0 : 1)); - $objWriter->endElement(); + $objWriter->startElement('a:bodyPr'); + $objWriter->writeAttribute('wrap', 'square'); + $objWriter->writeAttribute('lIns', '38100'); + $objWriter->writeAttribute('tIns', '19050'); + $objWriter->writeAttribute('rIns', '38100'); + $objWriter->writeAttribute('bIns', '19050'); + $objWriter->writeAttribute('anchor', 'ctr'); + $objWriter->startElement('a:spAutoFit'); + $objWriter->endElement(); // a:spAutoFit + $objWriter->endElement(); // a:bodyPr - $objWriter->startElement('c:showCatName'); - $showCatName = (empty($chartLayout)) ? 0 : $chartLayout->getShowCatName(); - $objWriter->writeAttribute('val', ((empty($showCatName)) ? 0 : 1)); - $objWriter->endElement(); + $objWriter->startElement('a:lstStyle'); + $objWriter->endElement(); // a:lstStyle - $objWriter->startElement('c:showSerName'); - $showSerName = (empty($chartLayout)) ? 0 : $chartLayout->getShowSerName(); - $objWriter->writeAttribute('val', ((empty($showSerName)) ? 0 : 1)); - $objWriter->endElement(); + $objWriter->startElement('a:p'); + $objWriter->startElement('a:pPr'); + $objWriter->startElement('a:defRPr'); + $this->writeColor($objWriter, $fontColor); + $objWriter->endElement(); // a:defRPr + $objWriter->endElement(); // a:pPr + $objWriter->endElement(); // a:p - $objWriter->startElement('c:showPercent'); - $showPercent = (empty($chartLayout)) ? 0 : $chartLayout->getShowPercent(); - $objWriter->writeAttribute('val', ((empty($showPercent)) ? 0 : 1)); - $objWriter->endElement(); + $objWriter->endElement(); // c:txPr + } - $objWriter->startElement('c:showBubbleSize'); - $showBubbleSize = (empty($chartLayout)) ? 0 : $chartLayout->getShowBubbleSize(); - $objWriter->writeAttribute('val', ((empty($showBubbleSize)) ? 0 : 1)); - $objWriter->endElement(); + $this->writeDataLabelsBool($objWriter, 'showLegendKey', $chartLayout->getShowLegendKey()); + $this->writeDataLabelsBool($objWriter, 'showVal', $chartLayout->getShowVal()); + $this->writeDataLabelsBool($objWriter, 'showCatName', $chartLayout->getShowCatName()); + $this->writeDataLabelsBool($objWriter, 'showSerName', $chartLayout->getShowSerName()); + $this->writeDataLabelsBool($objWriter, 'showPercent', $chartLayout->getShowPercent()); + $this->writeDataLabelsBool($objWriter, 'showBubbleSize', $chartLayout->getShowBubbleSize()); + $this->writeDataLabelsBool($objWriter, 'showLeaderLines', $chartLayout->getShowLeaderLines()); - $objWriter->startElement('c:showLeaderLines'); - $showLeaderLines = (empty($chartLayout)) ? 1 : $chartLayout->getShowLeaderLines(); - $objWriter->writeAttribute('val', ((empty($showLeaderLines)) ? 0 : 1)); - $objWriter->endElement(); - - $objWriter->endElement(); + $objWriter->endElement(); // c:dLbls } /** @@ -452,7 +474,7 @@ class Chart extends WriterPart $objWriter->endElement(); // c:scaling $objWriter->startElement('c:delete'); - $objWriter->writeAttribute('val', 0); + $objWriter->writeAttribute('val', '0'); $objWriter->endElement(); $objWriter->startElement('c:axPos'); @@ -486,7 +508,7 @@ class Chart extends WriterPart $this->writeLayout($objWriter, $layout); $objWriter->startElement('c:overlay'); - $objWriter->writeAttribute('val', 0); + $objWriter->writeAttribute('val', '0'); $objWriter->endElement(); $objWriter->endElement(); @@ -517,12 +539,7 @@ class Chart extends WriterPart $objWriter->startElement('c:spPr'); $this->writeColor($objWriter, $yAxis->getFillColorObject()); - - $objWriter->startElement('a:effectLst'); - $this->writeGlow($objWriter, $yAxis); - $this->writeShadow($objWriter, $yAxis); - $this->writeSoftEdge($objWriter, $yAxis); - $objWriter->endElement(); // effectLst + $this->writeEffects($objWriter, $yAxis); $objWriter->endElement(); // spPr if ($yAxis->getAxisOptionsProperty('major_unit') !== null) { @@ -550,7 +567,7 @@ class Chart extends WriterPart } $objWriter->startElement('c:auto'); - $objWriter->writeAttribute('val', 1); + $objWriter->writeAttribute('val', '1'); $objWriter->endElement(); $objWriter->startElement('c:lblAlgn'); @@ -558,12 +575,12 @@ class Chart extends WriterPart $objWriter->endElement(); $objWriter->startElement('c:lblOffset'); - $objWriter->writeAttribute('val', 100); + $objWriter->writeAttribute('val', '100'); $objWriter->endElement(); if ($isMultiLevelSeries) { $objWriter->startElement('c:noMultiLvlLbl'); - $objWriter->writeAttribute('val', 0); + $objWriter->writeAttribute('val', '0'); $objWriter->endElement(); } $objWriter->endElement(); @@ -577,7 +594,7 @@ class Chart extends WriterPart * @param string $id2 * @param bool $isMultiLevelSeries */ - private function writeValueAxis(XMLWriter $objWriter, ?Title $yAxisLabel, $groupType, $id1, $id2, $isMultiLevelSeries, Axis $xAxis, GridLines $majorGridlines, GridLines $minorGridlines): void + private function writeValueAxis(XMLWriter $objWriter, ?Title $yAxisLabel, $groupType, $id1, $id2, $isMultiLevelSeries, Axis $xAxis, ?GridLines $majorGridlines, ?GridLines $minorGridlines): void { $objWriter->startElement('c:valAx'); @@ -610,39 +627,27 @@ class Chart extends WriterPart $objWriter->endElement(); // c:scaling $objWriter->startElement('c:delete'); - $objWriter->writeAttribute('val', 0); + $objWriter->writeAttribute('val', '0'); $objWriter->endElement(); $objWriter->startElement('c:axPos'); $objWriter->writeAttribute('val', 'l'); $objWriter->endElement(); - $objWriter->startElement('c:majorGridlines'); - $objWriter->startElement('c:spPr'); + if ($majorGridlines !== null) { + $objWriter->startElement('c:majorGridlines'); + $objWriter->startElement('c:spPr'); + $this->writeLineStyles($objWriter, $majorGridlines); + $this->writeEffects($objWriter, $majorGridlines); + $objWriter->endElement(); //end spPr + $objWriter->endElement(); //end majorGridLines + } - $this->writeLineStyles($objWriter, $majorGridlines); - - $objWriter->startElement('a:effectLst'); - $this->writeGlow($objWriter, $majorGridlines); - $this->writeShadow($objWriter, $majorGridlines); - $this->writeSoftEdge($objWriter, $majorGridlines); - - $objWriter->endElement(); //end effectLst - $objWriter->endElement(); //end spPr - $objWriter->endElement(); //end majorGridLines - - if ($minorGridlines->getObjectState()) { + if ($minorGridlines !== null && $minorGridlines->getObjectState()) { $objWriter->startElement('c:minorGridlines'); $objWriter->startElement('c:spPr'); - $this->writeLineStyles($objWriter, $minorGridlines); - - $objWriter->startElement('a:effectLst'); - $this->writeGlow($objWriter, $minorGridlines); - $this->writeShadow($objWriter, $minorGridlines); - $this->writeSoftEdge($objWriter, $minorGridlines); - $objWriter->endElement(); //end effectLst - + $this->writeEffects($objWriter, $minorGridlines); $objWriter->endElement(); //end spPr $objWriter->endElement(); //end minorGridLines } @@ -676,7 +681,7 @@ class Chart extends WriterPart } $objWriter->startElement('c:overlay'); - $objWriter->writeAttribute('val', 0); + $objWriter->writeAttribute('val', '0'); $objWriter->endElement(); $objWriter->endElement(); @@ -706,17 +711,9 @@ class Chart extends WriterPart } $objWriter->startElement('c:spPr'); - $this->writeColor($objWriter, $xAxis->getFillColorObject()); - $this->writeLineStyles($objWriter, $xAxis); - - $objWriter->startElement('a:effectLst'); - $this->writeGlow($objWriter, $xAxis); - $this->writeShadow($objWriter, $xAxis); - $this->writeSoftEdge($objWriter, $xAxis); - $objWriter->endElement(); //effectList - + $this->writeEffects($objWriter, $xAxis); $objWriter->endElement(); //end spPr if ($id1 !== '0') { @@ -760,7 +757,7 @@ class Chart extends WriterPart if ($isMultiLevelSeries) { if ($groupType !== DataSeries::TYPE_BUBBLECHART) { $objWriter->startElement('c:noMultiLvlLbl'); - $objWriter->writeAttribute('val', 0); + $objWriter->writeAttribute('val', '0'); $objWriter->endElement(); } } @@ -852,11 +849,11 @@ class Chart extends WriterPart $objWriter->startElement('c:dPt'); $objWriter->startElement('c:idx'); - $objWriter->writeAttribute('val', $val); + $objWriter->writeAttribute('val', "$val"); $objWriter->endElement(); // c:idx $objWriter->startElement('c:bubble3D'); - $objWriter->writeAttribute('val', 0); + $objWriter->writeAttribute('val', '0'); $objWriter->endElement(); // c:bubble3D $objWriter->startElement('c:spPr'); @@ -901,11 +898,11 @@ class Chart extends WriterPart if ($groupType !== DataSeries::TYPE_LINECHART) { if (($groupType == DataSeries::TYPE_PIECHART) || ($groupType == DataSeries::TYPE_PIECHART_3D) || ($groupType == DataSeries::TYPE_DONUTCHART) || ($plotSeriesCount > 1)) { $objWriter->startElement('c:varyColors'); - $objWriter->writeAttribute('val', 1); + $objWriter->writeAttribute('val', '1'); $objWriter->endElement(); } else { $objWriter->startElement('c:varyColors'); - $objWriter->writeAttribute('val', 0); + $objWriter->writeAttribute('val', '0'); $objWriter->endElement(); } } @@ -916,11 +913,11 @@ class Chart extends WriterPart $objWriter->startElement('c:ser'); $objWriter->startElement('c:idx'); - $objWriter->writeAttribute('val', $this->seriesIndex + $plotSeriesIdx); + $objWriter->writeAttribute('val', (string) ($this->seriesIndex + $plotSeriesIdx)); $objWriter->endElement(); $objWriter->startElement('c:order'); - $objWriter->writeAttribute('val', $this->seriesIndex + $plotSeriesRef); + $objWriter->writeAttribute('val', (string) ($this->seriesIndex + $plotSeriesRef)); $objWriter->endElement(); $plotLabel = $plotGroup->getPlotLabelByIndex($plotSeriesIdx); @@ -949,6 +946,9 @@ class Chart extends WriterPart } } } + if ($plotSeriesValues !== false && $plotSeriesValues->getLabelLayout()) { + $this->writeDataLabels($objWriter, $plotSeriesValues->getLabelLayout()); + } // Labels $plotSeriesLabel = $plotGroup->getPlotLabelByIndex($plotSeriesIdx); @@ -980,6 +980,7 @@ class Chart extends WriterPart $nofill = $groupType == DataSeries::TYPE_STOCKCHART || ($groupType === DataSeries::TYPE_SCATTERCHART && !$plotSeriesValues->getScatterLines()); if ($callLineStyles) { $this->writeLineStyles($objWriter, $plotSeriesValues, $nofill); + $this->writeEffects($objWriter, $plotSeriesValues); } $objWriter->endElement(); // c:spPr } @@ -1018,7 +1019,7 @@ class Chart extends WriterPart if (($groupType === DataSeries::TYPE_BARCHART) || ($groupType === DataSeries::TYPE_BARCHART_3D) || ($groupType === DataSeries::TYPE_BUBBLECHART)) { $objWriter->startElement('c:invertIfNegative'); - $objWriter->writeAttribute('val', 0); + $objWriter->writeAttribute('val', '0'); $objWriter->endElement(); } @@ -1032,7 +1033,7 @@ class Chart extends WriterPart $plotStyle = $plotGroup->getPlotStyle(); if ($plotStyle) { $objWriter->startElement('c:explosion'); - $objWriter->writeAttribute('val', 25); + $objWriter->writeAttribute('val', '25'); $objWriter->endElement(); } } @@ -1089,7 +1090,7 @@ class Chart extends WriterPart $objWriter->writeAttribute('val', $plotSeriesValues->getBubble3D() ? '1' : '0'); $objWriter->endElement(); } - } else { + } elseif ($plotSeriesValues !== false) { $this->writeBubbles($plotSeriesValues, $objWriter); } } @@ -1115,7 +1116,7 @@ class Chart extends WriterPart $objWriter->startElement('c:strCache'); $objWriter->startElement('c:ptCount'); - $objWriter->writeAttribute('val', $plotSeriesLabel->getPointCount()); + $objWriter->writeAttribute('val', (string) $plotSeriesLabel->getPointCount()); $objWriter->endElement(); foreach ($plotSeriesLabel->getDataValues() as $plotLabelKey => $plotLabelValue) { @@ -1154,7 +1155,7 @@ class Chart extends WriterPart $objWriter->startElement('c:multiLvlStrCache'); $objWriter->startElement('c:ptCount'); - $objWriter->writeAttribute('val', $plotSeriesValues->getPointCount()); + $objWriter->writeAttribute('val', (string) $plotSeriesValues->getPointCount()); $objWriter->endElement(); for ($level = 0; $level < $levelCount; ++$level) { @@ -1200,7 +1201,7 @@ class Chart extends WriterPart } $objWriter->startElement('c:ptCount'); - $objWriter->writeAttribute('val', $plotSeriesValues->getPointCount()); + $objWriter->writeAttribute('val', (string) $plotSeriesValues->getPointCount()); $objWriter->endElement(); $dataValues = $plotSeriesValues->getDataValues(); @@ -1250,7 +1251,7 @@ class Chart extends WriterPart $objWriter->endElement(); $objWriter->startElement('c:ptCount'); - $objWriter->writeAttribute('val', $plotSeriesValues->getPointCount()); + $objWriter->writeAttribute('val', (string) $plotSeriesValues->getPointCount()); $objWriter->endElement(); $dataValues = $plotSeriesValues->getDataValues(); @@ -1260,7 +1261,7 @@ class Chart extends WriterPart $objWriter->startElement('c:pt'); $objWriter->writeAttribute('idx', $plotSeriesKey); $objWriter->startElement('c:v'); - $objWriter->writeRawData(1); + $objWriter->writeRawData('1'); $objWriter->endElement(); $objWriter->endElement(); } @@ -1309,28 +1310,28 @@ class Chart extends WriterPart $x = $layout->getXPosition(); if ($x !== null) { $objWriter->startElement('c:x'); - $objWriter->writeAttribute('val', $x); + $objWriter->writeAttribute('val', "$x"); $objWriter->endElement(); } $y = $layout->getYPosition(); if ($y !== null) { $objWriter->startElement('c:y'); - $objWriter->writeAttribute('val', $y); + $objWriter->writeAttribute('val', "$y"); $objWriter->endElement(); } $w = $layout->getWidth(); if ($w !== null) { $objWriter->startElement('c:w'); - $objWriter->writeAttribute('val', $w); + $objWriter->writeAttribute('val', "$w"); $objWriter->endElement(); } $h = $layout->getHeight(); if ($h !== null) { $objWriter->startElement('c:h'); - $objWriter->writeAttribute('val', $h); + $objWriter->writeAttribute('val', "$h"); $objWriter->endElement(); } @@ -1377,12 +1378,12 @@ class Chart extends WriterPart $objWriter->endElement(); $objWriter->startElement('c:pageMargins'); - $objWriter->writeAttribute('footer', 0.3); - $objWriter->writeAttribute('header', 0.3); - $objWriter->writeAttribute('r', 0.7); - $objWriter->writeAttribute('l', 0.7); - $objWriter->writeAttribute('t', 0.75); - $objWriter->writeAttribute('b', 0.75); + $objWriter->writeAttribute('footer', '0.3'); + $objWriter->writeAttribute('header', '0.3'); + $objWriter->writeAttribute('r', '0.7'); + $objWriter->writeAttribute('l', '0.7'); + $objWriter->writeAttribute('t', '0.75'); + $objWriter->writeAttribute('b', '0.75'); $objWriter->endElement(); $objWriter->startElement('c:pageSetup'); @@ -1392,12 +1393,22 @@ class Chart extends WriterPart $objWriter->endElement(); } - /** - * Write shadow properties. - * - * @param Axis|GridLines $xAxis - */ - private function writeShadow(XMLWriter $objWriter, $xAxis): void + private function writeEffects(XMLWriter $objWriter, Properties $yAxis): void + { + if ( + !empty($yAxis->getSoftEdgesSize()) + || !empty($yAxis->getShadowProperty('effect')) + || !empty($yAxis->getGlowProperty('size')) + ) { + $objWriter->startElement('a:effectLst'); + $this->writeGlow($objWriter, $yAxis); + $this->writeShadow($objWriter, $yAxis); + $this->writeSoftEdge($objWriter, $yAxis); + $objWriter->endElement(); // effectLst + } + } + + private function writeShadow(XMLWriter $objWriter, Properties $xAxis): void { if (empty($xAxis->getShadowProperty('effect'))) { return; @@ -1441,12 +1452,7 @@ class Chart extends WriterPart $objWriter->endElement(); } - /** - * Write glow properties. - * - * @param Axis|GridLines $yAxis - */ - private function writeGlow(XMLWriter $objWriter, $yAxis): void + private function writeGlow(XMLWriter $objWriter, Properties $yAxis): void { $size = $yAxis->getGlowProperty('size'); if (empty($size)) { @@ -1458,12 +1464,7 @@ class Chart extends WriterPart $objWriter->endElement(); // glow } - /** - * Write soft edge properties. - * - * @param Axis|GridLines $yAxis - */ - private function writeSoftEdge(XMLWriter $objWriter, $yAxis): void + private function writeSoftEdge(XMLWriter $objWriter, Properties $yAxis): void { $softEdgeSize = $yAxis->getSoftEdgesSize(); if (empty($softEdgeSize)) { diff --git a/tests/PhpSpreadsheetTests/Chart/Charts32DsvGlowTest.php b/tests/PhpSpreadsheetTests/Chart/Charts32DsvGlowTest.php new file mode 100644 index 00000000..da2ad9f6 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Chart/Charts32DsvGlowTest.php @@ -0,0 +1,58 @@ +setIncludeCharts(true); + } + + public function writeCharts(XlsxWriter $writer): void + { + $writer->setIncludeCharts(true); + } + + public function testLine4(): void + { + $file = self::DIRECTORY . '32readwriteLineChart4.xlsx'; + $reader = new XlsxReader(); + $reader->setIncludeCharts(true); + $spreadsheet = $reader->load($file); + $sheet = $spreadsheet->getActiveSheet(); + self::assertSame(1, $sheet->getChartCount()); + /** @var callable */ + $callableReader = [$this, 'readCharts']; + /** @var callable */ + $callableWriter = [$this, 'writeCharts']; + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx', $callableReader, $callableWriter); + $spreadsheet->disconnectWorksheets(); + + $sheet = $reloadedSpreadsheet->getActiveSheet(); + $charts = $sheet->getChartCollection(); + self::assertCount(1, $charts); + $chart = $charts[0]; + self::assertNotNull($chart); + + $plotArea = $chart->getPlotArea(); + $dataSeriesArray = $plotArea->getPlotGroup(); + self::assertCount(1, $dataSeriesArray); + $dataSeries = $dataSeriesArray[0]; + $dataSeriesValuesArray = $dataSeries->getPlotValues(); + self::assertCount(3, $dataSeriesValuesArray); + $dataSeriesValues = $dataSeriesValuesArray[1]; + self::assertEquals(5, $dataSeriesValues->getGlowSize()); + self::assertSame('schemeClr', $dataSeriesValues->getGlowProperty(['color', 'type'])); + self::assertSame('accent2', $dataSeriesValues->getGlowProperty(['color', 'value'])); + self::assertSame(60, $dataSeriesValues->getGlowProperty(['color', 'alpha'])); + + $reloadedSpreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/PhpSpreadsheetTests/Chart/Charts32DsvLabelsTest.php b/tests/PhpSpreadsheetTests/Chart/Charts32DsvLabelsTest.php new file mode 100644 index 00000000..74655a60 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Chart/Charts32DsvLabelsTest.php @@ -0,0 +1,73 @@ +setIncludeCharts(true); + } + + public function writeCharts(XlsxWriter $writer): void + { + $writer->setIncludeCharts(true); + } + + public function testBar4(): void + { + $file = self::DIRECTORY . '32readwriteBarChart4.xlsx'; + $reader = new XlsxReader(); + $reader->setIncludeCharts(true); + $spreadsheet = $reader->load($file); + $sheet = $spreadsheet->getActiveSheet(); + self::assertSame(1, $sheet->getChartCount()); + /** @var callable */ + $callableReader = [$this, 'readCharts']; + /** @var callable */ + $callableWriter = [$this, 'writeCharts']; + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx', $callableReader, $callableWriter); + $spreadsheet->disconnectWorksheets(); + + $sheet = $reloadedSpreadsheet->getActiveSheet(); + $charts = $sheet->getChartCollection(); + self::assertCount(1, $charts); + $chart = $charts[0]; + self::assertNotNull($chart); + + $plotArea = $chart->getPlotArea(); + $dataSeriesArray = $plotArea->getPlotGroup(); + self::assertCount(1, $dataSeriesArray); + $dataSeries = $dataSeriesArray[0]; + $dataSeriesValuesArray = $dataSeries->getPlotValues(); + self::assertCount(1, $dataSeriesValuesArray); + $dataSeriesValues = $dataSeriesValuesArray[0]; + $layout = $dataSeriesValues->getLabelLayout(); + self::assertNotNull($layout); + self::assertTrue($layout->getShowVal()); + $fillColor = $layout->getLabelFillColor(); + self::assertNotNull($fillColor); + self::assertSame('schemeClr', $fillColor->getType()); + self::assertSame('accent1', $fillColor->getValue()); + $borderColor = $layout->getLabelBorderColor(); + self::assertNotNull($borderColor); + self::assertSame('srgbClr', $borderColor->getType()); + self::assertSame('FFC000', $borderColor->getValue()); + $fontColor = $layout->getLabelFontColor(); + self::assertNotNull($fontColor); + self::assertSame('srgbClr', $fontColor->getType()); + self::assertSame('FFFF00', $fontColor->getValue()); + self::assertEquals( + [15, 73, 61, 32], + $dataSeriesValues->getDataValues() + ); + + $reloadedSpreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/PhpSpreadsheetTests/Chart/GridlinesShadowGlowTest.php b/tests/PhpSpreadsheetTests/Chart/GridlinesShadowGlowTest.php index 04ca7c31..26e8f1c6 100644 --- a/tests/PhpSpreadsheetTests/Chart/GridlinesShadowGlowTest.php +++ b/tests/PhpSpreadsheetTests/Chart/GridlinesShadowGlowTest.php @@ -129,6 +129,11 @@ class GridlinesShadowGlowTest extends AbstractFunctional foreach ($expectedShadow as $key => $value) { self::assertEquals($value, $minorGridlines->getShadowProperty($key), $key); } + $testShadow2 = $minorGridlines->getShadowArray(); + self::assertNull($testShadow2['presets']); + self::assertEquals(['sx' => null, 'sy' => null, 'kx' => null, 'ky' => null], $testShadow2['size']); + unset($testShadow2['presets'], $testShadow2['size']); + self::assertEquals($expectedShadow, $testShadow2); // Create the chart $chart = new Chart( diff --git a/tests/PhpSpreadsheetTests/Chart/LayoutTest.php b/tests/PhpSpreadsheetTests/Chart/LayoutTest.php index 8e927985..fbc878e1 100644 --- a/tests/PhpSpreadsheetTests/Chart/LayoutTest.php +++ b/tests/PhpSpreadsheetTests/Chart/LayoutTest.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Chart; +use PhpOffice\PhpSpreadsheet\Chart\ChartColor; use PhpOffice\PhpSpreadsheet\Chart\Layout; use PHPUnit\Framework\TestCase; @@ -27,4 +28,37 @@ class LayoutTest extends TestCase $result = $testInstance->getLayoutTarget(); self::assertEquals($LayoutTargetValue, $result); } + + public function testConstructorVsMethods(): void + { + $fillColor = new ChartColor('FF0000', 20, 'srgbClr'); + $borderColor = new ChartColor('accent1', 20, 'schemeClr'); + $fontColor = new ChartColor('red', 20, 'prstClr'); + $array = [ + 'xMode' => 'factor', + 'yMode' => 'edge', + 'x' => 1.0, + 'y' => 2.0, + 'w' => 3.0, + 'h' => 4.0, + 'showVal' => true, + 'labelFillColor' => $fillColor, + 'labelBorderColor' => $borderColor, + 'labelFontColor' => $fontColor, + ]; + $layout1 = new Layout($array); + $layout2 = new Layout(); + $layout2 + ->setXMode('factor') + ->setYMode('edge') + ->setXposition(1.0) + ->setYposition(2.0) + ->setWidth(3.0) + ->setHeight(4.0) + ->setShowVal(true) + ->setLabelFillColor($fillColor) + ->setLabelBorderColor($borderColor) + ->setLabelFontColor($fontColor); + self::assertEquals($layout1, $layout2); + } }