From 5608e05eda9430437f9f0168b0d0607ef005bae0 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Fri, 3 Jun 2022 16:47:03 -0700 Subject: [PATCH] Add Support to Chart/Axis for Glow/SoftEdges (#2865) * Add Support to Chart/Axis for Glow/SoftEdges Chart is very under-covered in unit tests. I was hoping to just add some tests and be done with it, but that just won't suffice - many code changes are required. Adding Glow properties for Axis turned out to be a real mixed bag - no support in Xlsx/Reader at all, support in Xlsx/Writer ... for the X-axis only. So we'll just inch forward in many stages. This change does not address other Axis properties (Fill, Shadow, Line, LineStyle, and maybe others, and their sub-properties). On my to-do list. GridLines, and maybe other Chart objects, also should support these properties. This change does not address those. On my to-do list. No support is added for spreadsheet formats other than Xlsx. * Refactoring This should make the code easier to follow, and I hope it will also be extensible to other object types (e.g. GridLines). * More Refactoring Make scheme/srgb easier to handle. * Fix Typo In a very rarely used function. --- phpstan-baseline.neon | 25 -- src/PhpSpreadsheet/Chart/Axis.php | 32 +-- src/PhpSpreadsheet/Chart/Properties.php | 5 +- src/PhpSpreadsheet/Reader/Xlsx/Chart.php | 102 +++++-- src/PhpSpreadsheet/Shared/StringHelper.php | 4 +- src/PhpSpreadsheet/Style/Font.php | 51 +++- src/PhpSpreadsheet/Writer/Xlsx/Chart.php | 25 +- .../Chart/AxisGlowTest.php | 268 ++++++++++++++++++ 8 files changed, 437 insertions(+), 75 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Chart/AxisGlowTest.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 240e7621..33a3ea69 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1110,31 +1110,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Cell/Coordinate.php - - - message: "#^Parameter \\#1 \\$textValue of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\StringHelper\\:\\:substring\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Cell/DataType.php - - - - message: "#^Parameter \\#1 \\$angle of method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Axis\\:\\:setShadowAngle\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Chart/Axis.php - - - - message: "#^Parameter \\#1 \\$blur of method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Axis\\:\\:setShadowBlur\\(\\) expects float, float\\|null given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Chart/Axis.php - - - - message: "#^Parameter \\#1 \\$distance of method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Axis\\:\\:setShadowDistance\\(\\) expects float, float\\|null given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Chart/Axis.php - - - - message: "#^Parameter \\#2 \\$alpha of method PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\Axis\\:\\:setShadowColor\\(\\) expects int, int\\|string given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Chart/Axis.php - - message: "#^Call to an undefined method object\\:\\:render\\(\\)\\.$#" count: 1 diff --git a/src/PhpSpreadsheet/Chart/Axis.php b/src/PhpSpreadsheet/Chart/Axis.php index 089ebdb9..3a72e725 100644 --- a/src/PhpSpreadsheet/Chart/Axis.php +++ b/src/PhpSpreadsheet/Chart/Axis.php @@ -346,17 +346,17 @@ class Axis extends Properties * @param int $shadowPresets * @param string $colorValue * @param string $colorType - * @param string $colorAlpha - * @param float $blur - * @param int $angle - * @param float $distance + * @param null|int|string $colorAlpha + * @param null|float $blur + * @param null|int $angle + * @param null|float $distance */ public function setShadowProperties($shadowPresets, $colorValue = null, $colorType = null, $colorAlpha = null, $blur = null, $angle = null, $distance = null): void { $this->setShadowPresetsProperties((int) $shadowPresets) ->setShadowColor( $colorValue ?? $this->shadowProperties['color']['value'], - $colorAlpha ?? (int) $this->shadowProperties['color']['alpha'], + (int) ($colorAlpha ?? $this->shadowProperties['color']['alpha']), $colorType ?? $this->shadowProperties['color']['type'] ) ->setShadowBlur($blur) @@ -412,9 +412,9 @@ class Axis extends Properties /** * Set Shadow Color. * - * @param string $color - * @param int $alpha - * @param string $alphaType + * @param null|string $color + * @param null|int $alpha + * @param null|string $alphaType * * @return $this */ @@ -428,7 +428,7 @@ class Axis extends Properties /** * Set Shadow Blur. * - * @param float $blur + * @param null|float $blur * * @return $this */ @@ -444,7 +444,7 @@ class Axis extends Properties /** * Set Shadow Angle. * - * @param int $angle + * @param null|int $angle * * @return $this */ @@ -460,7 +460,7 @@ class Axis extends Properties /** * Set Shadow Distance. * - * @param float $distance + * @param null|float $distance * * @return $this */ @@ -489,9 +489,9 @@ class Axis extends Properties * Set Glow Properties. * * @param float $size - * @param string $colorValue - * @param int $colorAlpha - * @param string $colorType + * @param null|string $colorValue + * @param null|int $colorAlpha + * @param null|string $colorType */ public function setGlowProperties($size, $colorValue = null, $colorAlpha = null, $colorType = null): void { @@ -508,7 +508,7 @@ class Axis extends Properties * * @param array|string $property * - * @return string + * @return null|string */ public function getGlowProperty($property) { @@ -555,7 +555,7 @@ class Axis extends Properties public function setSoftEdges($size): void { if ($size !== null) { - $softEdges['size'] = (string) $this->getExcelPointsWidth($size); + $this->softEdges['size'] = (string) $this->getExcelPointsWidth($size); } } diff --git a/src/PhpSpreadsheet/Chart/Properties.php b/src/PhpSpreadsheet/Chart/Properties.php index 9f1a5ce0..800f6aaf 100644 --- a/src/PhpSpreadsheet/Chart/Properties.php +++ b/src/PhpSpreadsheet/Chart/Properties.php @@ -110,6 +110,7 @@ abstract class Properties const SHADOW_PRESETS_PERSPECTIVE_UPPER_LEFT = 21; const SHADOW_PRESETS_PERSPECTIVE_LOWER_RIGHT = 22; const SHADOW_PRESETS_PERSPECTIVE_LOWER_LEFT = 23; + const POINTS_WIDTH_MULTIPLIER = 12700; /** * @param float $width @@ -118,7 +119,7 @@ abstract class Properties */ protected function getExcelPointsWidth($width) { - return $width * 12700; + return $width * self::POINTS_WIDTH_MULTIPLIER; } /** @@ -133,7 +134,7 @@ abstract class Properties protected function getTrueAlpha($alpha) { - return (string) 100 - $alpha . '000'; + return (string) (100 - $alpha) . '000'; } protected function setColorProperties($color, $alpha, $colorType) diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php index 98507af8..67af04b2 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php @@ -3,11 +3,13 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx; use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError; +use PhpOffice\PhpSpreadsheet\Chart\Axis; use PhpOffice\PhpSpreadsheet\Chart\DataSeries; use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues; use PhpOffice\PhpSpreadsheet\Chart\Layout; use PhpOffice\PhpSpreadsheet\Chart\Legend; use PhpOffice\PhpSpreadsheet\Chart\PlotArea; +use PhpOffice\PhpSpreadsheet\Chart\Properties; use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\RichText\RichText; use PhpOffice\PhpSpreadsheet\Style\Color; @@ -67,6 +69,8 @@ class Chart $dispBlanksAs = $plotVisOnly = null; $plotArea = null; $rotX = $rotY = $rAngAx = $perspective = null; + $xAxis = new Axis(); + $yAxis = new Axis(); foreach ($chartElementsC as $chartElementKey => $chartElement) { switch ($chartElementKey) { case 'chart': @@ -93,6 +97,7 @@ class Chart if (isset($chartDetail->title)) { $XaxisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace)); } + $this->readEffects($chartDetail, $xAxis); break; case 'dateAx': @@ -102,6 +107,7 @@ class Chart break; case 'valAx': + $whichAxis = null; if (isset($chartDetail->title, $chartDetail->axPos)) { $axisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace)); $axPos = self::getAttribute($chartDetail->axPos, 'val', 'string'); @@ -110,15 +116,18 @@ class Chart case 't': case 'b': $XaxisLabel = $axisLabel; + $whichAxis = $xAxis; break; case 'r': case 'l': $YaxisLabel = $axisLabel; + $whichAxis = $yAxis; break; } } + $this->readEffects($chartDetail, $whichAxis); break; case 'barChart': @@ -240,7 +249,7 @@ class Chart } } } - $chart = new \PhpOffice\PhpSpreadsheet\Chart\Chart($chartName, $title, $legend, $plotArea, $plotVisOnly, (string) $dispBlanksAs, $XaxisLabel, $YaxisLabel); + $chart = new \PhpOffice\PhpSpreadsheet\Chart\Chart($chartName, $title, $legend, $plotArea, $plotVisOnly, (string) $dispBlanksAs, $XaxisLabel, $YaxisLabel, $xAxis, $yAxis); if (is_int($rotX)) { $chart->setRotX($rotX); } @@ -345,9 +354,8 @@ class Chart if (is_countable($ln->noFill) && count($ln->noFill) === 1) { $noFill = true; } - $sf = $children->solidFill->schemeClr; - if ($sf) { - $schemeClr = self::getAttribute($sf, 'val', 'string'); + if (isset($children->solidFill)) { + $this->readColor($children->solidFill, $srgbClr, $schemeClr); } break; @@ -357,8 +365,8 @@ class Chart $pointSize = is_numeric($pointSize) ? ((int) $pointSize) : null; if (count($seriesDetail->spPr) === 1) { $ln = $seriesDetail->spPr->children($this->aNamespace); - if (count($ln->solidFill) === 1) { - $srgbClr = self::getAttribute($ln->solidFill->srgbClr, 'val', 'string'); + if (isset($ln->solidFill)) { + $this->readColor($ln->solidFill, $srgbClr, $schemeClr); } } @@ -603,7 +611,8 @@ class Chart $defaultLatin = null; $defaultEastAsian = null; $defaultComplexScript = null; - $defaultColor = null; + $defaultSrgbColor = ''; + $defaultSchemeColor = ''; if (isset($titleDetailPart->pPr->defRPr)) { /** @var ?int */ $defaultFontSize = self::getAttribute($titleDetailPart->pPr->defRPr, 'sz', 'integer'); @@ -632,9 +641,8 @@ class Chart /** @var ?string */ $defaultComplexScript = self::getAttribute($titleDetailPart->pPr->defRPr->cs, 'typeface', 'string'); } - if (isset($titleDetailPart->pPr->defRPr->solidFill->srgbClr)) { - /** @var ?string */ - $defaultColor = self::getAttribute($titleDetailPart->pPr->defRPr->solidFill->srgbClr, 'val', 'string'); + if (isset($titleDetailPart->pPr->defRPr->solidFill)) { + $this->readColor($titleDetailPart->pPr->defRPr->solidFill, $defaultSrgbColor, $defaultSchemeClr); } } foreach ($titleDetailPart as $titleDetailElementKey => $titleDetailElement) { @@ -660,7 +668,8 @@ class Chart $latinName = null; $eastAsian = null; $complexScript = null; - $fontColor = null; + $fontSrgbClr = ''; + $fontSchemeClr = ''; $uSchemeClr = null; if (isset($titleDetailElement->rPr)) { // not used now, not sure it ever was, grandfathering @@ -686,10 +695,9 @@ class Chart // not used now, not sure it ever was, grandfathering /** @var ?string */ - $fontColor = self::getAttribute($titleDetailElement->rPr, 'color', 'string'); - if (isset($titleDetailElement->rPr->solidFill->srgbClr)) { - /** @var ?string */ - $fontColor = self::getAttribute($titleDetailElement->rPr->solidFill->srgbClr, 'val', 'string'); + $fontSrgbClr = self::getAttribute($titleDetailElement->rPr, 'color', 'string'); + if (isset($titleDetailElement->rPr->solidFill)) { + $this->readColor($titleDetailElement->rPr->solidFill, $fontSrgbClr, $fontSchemeClr); } /** @var ?bool */ @@ -742,11 +750,17 @@ class Chart $fontFound = true; } - $fontColor = $fontColor ?? $defaultColor; - if ($fontColor !== null) { - $objText->getFont()->setColor(new Color($fontColor)); + $fontSrgbClr = $fontSrgbClr ?? $defaultSrgbColor; + if (!empty($fontSrgbClr)) { + $objText->getFont()->setColor(new Color($fontSrgbClr)); $fontFound = true; } + // need to think about what to do here + //$fontSchemeClr = $fontSchemeClr ?? $defaultSchemeColor; + //if (!empty($fontSchemeClr)) { + // $objText->getFont()->setColor(new Color($fontSrgbClr)); + // $fontFound = true; + //} $bold = $bold ?? $defaultBold; if ($bold !== null) { @@ -877,4 +891,56 @@ class Chart } } } + + /** + * @param null|Axis $chartObject may be extended to include other types + */ + private function readEffects(SimpleXMLElement $chartDetail, $chartObject): void + { + if (!isset($chartObject, $chartDetail->spPr)) { + return; + } + $sppr = $chartDetail->spPr->children($this->aNamespace); + + if (isset($sppr->effectLst->glow)) { + $axisGlowSize = (float) self::getAttribute($sppr->effectLst->glow, 'rad', 'integer') / Properties::POINTS_WIDTH_MULTIPLIER; + if ($axisGlowSize != 0.0) { + $srgbClr = $schemeClr = ''; + $colorArray = $this->readColor($sppr->effectLst->glow, $srgbClr, $schemeClr); + $chartObject->setGlowProperties($axisGlowSize, $colorArray['value'], $colorArray['alpha'], $colorArray['type']); + } + } + + if (isset($sppr->effectLst->softEdge)) { + $chartObject->setSoftEdges((float) self::getAttribute($sppr->effectLst->softEdge, 'rad', 'string') / Properties::POINTS_WIDTH_MULTIPLIER); + } + } + + private function readColor(SimpleXMLElement $colorXml, ?string &$srgbClr, ?string &$schemeClr): array + { + $result = [ + 'type' => null, + 'value' => null, + 'alpha' => null, + ]; + if (isset($colorXml->srgbClr)) { + $result['type'] = Properties::EXCEL_COLOR_TYPE_ARGB; + $result['value'] = $srgbClr = self::getAttribute($colorXml->srgbClr, 'val', 'string'); + if (isset($colorXml->srgbClr->alpha)) { + $alpha = (int) self::getAttribute($colorXml->srgbClr->alpha, 'val', 'string'); + $alpha = 100 - (int) ($alpha / 1000); + $result['alpha'] = $alpha; + } + } elseif (isset($colorXml->schemeClr)) { + $result['type'] = Properties::EXCEL_COLOR_TYPE_SCHEME; + $result['value'] = $schemeClr = self::getAttribute($colorXml->schemeClr, 'val', 'string'); + if (isset($colorXml->schemeClr->alpha)) { + $alpha = (int) self::getAttribute($colorXml->schemeClr->alpha, 'val', 'string'); + $alpha = 100 - (int) ($alpha / 1000); + $result['alpha'] = $alpha; + } + } + + return $result; + } } diff --git a/src/PhpSpreadsheet/Shared/StringHelper.php b/src/PhpSpreadsheet/Shared/StringHelper.php index 9970a211..7d6a990f 100644 --- a/src/PhpSpreadsheet/Shared/StringHelper.php +++ b/src/PhpSpreadsheet/Shared/StringHelper.php @@ -470,7 +470,7 @@ class StringHelper /** * Get a substring of a UTF-8 encoded string. * - * @param string $textValue UTF-8 encoded string + * @param null|string $textValue UTF-8 encoded string * @param int $offset Start offset * @param int $length Maximum number of characters in substring * @@ -478,7 +478,7 @@ class StringHelper */ public static function substring($textValue, $offset, $length = 0) { - return mb_substr($textValue, $offset, $length, 'UTF-8'); + return mb_substr($textValue ?? '', $offset, $length, 'UTF-8'); } /** diff --git a/src/PhpSpreadsheet/Style/Font.php b/src/PhpSpreadsheet/Style/Font.php index 616b2d4f..7b1ced63 100644 --- a/src/PhpSpreadsheet/Style/Font.php +++ b/src/PhpSpreadsheet/Style/Font.php @@ -19,7 +19,7 @@ class Font extends Supervisor protected $name = 'Calibri'; /** - * The following 6 are used only for chart titles, I think. + * The following 7 are used only for chart titles, I think. * *@var string */ @@ -39,6 +39,9 @@ class Font extends Supervisor /** @var string */ private $uSchemeClr = ''; + + /** @var string */ + private $uSrgbClr = ''; // end of chart title items /** @@ -603,6 +606,30 @@ class Font extends Supervisor return $this; } + public function getUSrgbClr(): string + { + if ($this->isSupervisor) { + return $this->getSharedComponent()->getUSrgbClr(); + } + + return $this->uSrgbClr; + } + + public function setUSrgbClr(string $uSrgbClr): self + { + if (!$this->isSupervisor) { + $this->uSrgbClr = $uSrgbClr; + } else { + // should never be true + // @codeCoverageIgnoreStart + $styleArray = $this->getStyleArray(['uSrgbClr' => $uSrgbClr]); + $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); + // @codeCoverageIgnoreEnd + } + + return $this; + } + /** * Get Underline. * @@ -731,15 +758,18 @@ class Font extends Supervisor $this->underline . ($this->strikethrough ? 't' : 'f') . $this->color->getHashCode() . - '*' . - $this->latin . - '*' . - $this->eastAsian . - '*' . - $this->complexScript . - $this->strikeType . - $this->uSchemeClr . - (string) $this->baseLine . + implode( + '*', + [ + $this->latin, + $this->eastAsian, + $this->complexScript, + $this->strikeType, + $this->uSchemeClr, + $this->uSrgbClr, + (string) $this->baseLine, + ] + ) . __CLASS__ ); } @@ -762,6 +792,7 @@ class Font extends Supervisor $this->exportArray2($exportedArray, 'superscript', $this->getSuperscript()); $this->exportArray2($exportedArray, 'underline', $this->getUnderline()); $this->exportArray2($exportedArray, 'uSchemeClr', $this->getUSchemeClr()); + $this->exportArray2($exportedArray, 'uSrgbClr', $this->getUSrgbClr()); return $exportedArray; } diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php index 08d578e6..0aae3646 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php @@ -497,6 +497,27 @@ class Chart extends WriterPart $objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('axis_labels')); $objWriter->endElement(); + $objWriter->startElement('c:spPr'); + $objWriter->startElement('a:effectLst'); + if ($yAxis->getGlowProperty('size') !== null) { + $objWriter->startElement('a:glow'); + $objWriter->writeAttribute('rad', $yAxis->getGlowProperty('size')); + $objWriter->startElement("a:{$yAxis->getGlowProperty(['color', 'type'])}"); + $objWriter->writeAttribute('val', (string) $yAxis->getGlowProperty(['color', 'value'])); + $objWriter->startElement('a:alpha'); + $objWriter->writeAttribute('val', (string) $yAxis->getGlowProperty(['color', 'alpha'])); + $objWriter->endElement(); + $objWriter->endElement(); + $objWriter->endElement(); + } + if ($yAxis->getSoftEdgesSize() !== null) { + $objWriter->startElement('a:softEdge'); + $objWriter->writeAttribute('rad', $yAxis->getSoftEdgesSize()); + $objWriter->endElement(); //end softEdge + } + $objWriter->endElement(); // effectLst + $objWriter->endElement(); // spPr + if ($id2 !== '0') { $objWriter->startElement('c:crossAx'); $objWriter->writeAttribute('val', $id2); @@ -909,9 +930,9 @@ class Chart extends WriterPart $objWriter->startElement('a:glow'); $objWriter->writeAttribute('rad', $xAxis->getGlowProperty('size')); $objWriter->startElement("a:{$xAxis->getGlowProperty(['color', 'type'])}"); - $objWriter->writeAttribute('val', $xAxis->getGlowProperty(['color', 'value'])); + $objWriter->writeAttribute('val', (string) $xAxis->getGlowProperty(['color', 'value'])); $objWriter->startElement('a:alpha'); - $objWriter->writeAttribute('val', $xAxis->getGlowProperty(['color', 'alpha'])); + $objWriter->writeAttribute('val', (string) $xAxis->getGlowProperty(['color', 'alpha'])); $objWriter->endElement(); $objWriter->endElement(); $objWriter->endElement(); diff --git a/tests/PhpSpreadsheetTests/Chart/AxisGlowTest.php b/tests/PhpSpreadsheetTests/Chart/AxisGlowTest.php new file mode 100644 index 00000000..88afef53 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Chart/AxisGlowTest.php @@ -0,0 +1,268 @@ +setIncludeCharts(true); + } + + public function writeCharts(XlsxWriter $writer): void + { + $writer->setIncludeCharts(true); + } + + public function testGlowY(): void + { + $spreadsheet = new Spreadsheet(); + $worksheet = $spreadsheet->getActiveSheet(); + $worksheet->fromArray( + [ + ['', 2010, 2011, 2012], + ['Q1', 12, 15, 21], + ['Q2', 56, 73, 86], + ['Q3', 52, 61, 69], + ['Q4', 30, 32, 0], + ] + ); + + // Set the Labels for each data series we want to plot + // Datatype + // Cell reference for data + // Format Code + // Number of datapoints in series + // Data values + // Data Marker + $dataSeriesLabels = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$B$1', null, 1), // 2010 + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', null, 1), // 2011 + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$D$1', null, 1), // 2012 + ]; + // Set the X-Axis Labels + // Datatype + // Cell reference for data + // Format Code + // Number of datapoints in series + // Data values + // Data Marker + $xAxisTickValues = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4 + ]; + // Set the Data values for each data series we want to plot + // Datatype + // Cell reference for data + // Format Code + // Number of datapoints in series + // Data values + // Data Marker + $dataSeriesValues = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$B$2:$B$5', null, 4), + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', null, 4), + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$D$2:$D$5', null, 4), + ]; + + // Build the dataseries + $series = new DataSeries( + DataSeries::TYPE_AREACHART, // plotType + DataSeries::GROUPING_PERCENT_STACKED, // plotGrouping + range(0, count($dataSeriesValues) - 1), // plotOrder + $dataSeriesLabels, // plotLabel + $xAxisTickValues, // plotCategory + $dataSeriesValues // plotValues + ); + + // Set the series in the plot area + $plotArea = new PlotArea(null, [$series]); + // Set the chart legend + $legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false); + + $title = new Title('Test %age-Stacked Area Chart'); + $yAxisLabel = new Title('Value ($k)'); + + // Create the chart + $chart = new Chart( + 'chart1', // name + $title, // title + $legend, // legend + $plotArea, // plotArea + true, // plotVisibleOnly + DataSeries::EMPTY_AS_GAP, // displayBlanksAs + null, // xAxisLabel + $yAxisLabel // yAxisLabel + ); + $yAxis = $chart->getChartAxisY(); + $xAxis = $chart->getChartAxisX(); + $yAxis->setGlowProperties(10, 'FFFF00', 30, Properties::EXCEL_COLOR_TYPE_ARGB); + $expectedSize = 127000.0; + $expectedGlowColor = [ + 'type' => 'srgbClr', + 'value' => 'FFFF00', + 'alpha' => '70000', + ]; + $yAxis->setSoftEdges(2.5); + $xAxis->setSoftEdges(5); + $expectedSoftEdgesY = '31750'; + $expectedSoftEdgesX = '63500'; + self::assertEquals($expectedSize, $yAxis->getGlowProperty('size')); + self::assertEquals($expectedGlowColor, $yAxis->getGlowProperty('color')); + self::assertEquals($expectedSoftEdgesY, $yAxis->getSoftEdgesSize()); + self::assertEquals($expectedSoftEdgesX, $xAxis->getSoftEdgesSize()); + + // Set the position where the chart should appear in the worksheet + $chart->setTopLeftPosition('A7'); + $chart->setBottomRightPosition('H20'); + + // Add the chart to the worksheet + $worksheet->addChart($chart); + + /** @var callable */ + $callableReader = [$this, 'readCharts']; + /** @var callable */ + $callableWriter = [$this, 'writeCharts']; + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx', $callableReader, $callableWriter); + $spreadsheet->disconnectWorksheets(); + + $sheet = $reloadedSpreadsheet->getActiveSheet(); + $charts2 = $sheet->getChartCollection(); + self::assertCount(1, $charts2); + $chart2 = $charts2[0]; + self::assertNotNull($chart2); + $yAxis2 = $chart2->getChartAxisY(); + self::assertEquals($expectedSize, $yAxis2->getGlowProperty('size')); + self::assertEquals($expectedGlowColor, $yAxis2->getGlowProperty('color')); + self::assertEquals($expectedSoftEdgesY, $yAxis2->getSoftEdgesSize()); + $xAxis2 = $chart2->getChartAxisX(); + self::assertNull($xAxis2->getGlowProperty('size')); + $reloadedSpreadsheet->disconnectWorksheets(); + } + + public function testGlowX(): void + { + $spreadsheet = new Spreadsheet(); + $worksheet = $spreadsheet->getActiveSheet(); + $worksheet->fromArray( + [ + ['', 2010, 2011, 2012], + ['Q1', 12, 15, 21], + ['Q2', 56, 73, 86], + ['Q3', 52, 61, 69], + ['Q4', 30, 32, 0], + ] + ); + + // Set the Labels for each data series we want to plot + // Datatype + // Cell reference for data + // Format Code + // Number of datapoints in series + // Data values + // Data Marker + $dataSeriesLabels = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$B$1', null, 1), // 2010 + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', null, 1), // 2011 + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$D$1', null, 1), // 2012 + ]; + // Set the X-Axis Labels + // Datatype + // Cell reference for data + // Format Code + // Number of datapoints in series + // Data values + // Data Marker + $xAxisTickValues = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4 + ]; + // Set the Data values for each data series we want to plot + // Datatype + // Cell reference for data + // Format Code + // Number of datapoints in series + // Data values + // Data Marker + $dataSeriesValues = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$B$2:$B$5', null, 4), + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', null, 4), + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$D$2:$D$5', null, 4), + ]; + + // Build the dataseries + $series = new DataSeries( + DataSeries::TYPE_AREACHART, // plotType + DataSeries::GROUPING_PERCENT_STACKED, // plotGrouping + range(0, count($dataSeriesValues) - 1), // plotOrder + $dataSeriesLabels, // plotLabel + $xAxisTickValues, // plotCategory + $dataSeriesValues // plotValues + ); + + // Set the series in the plot area + $plotArea = new PlotArea(null, [$series]); + // Set the chart legend + $legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false); + + $title = new Title('Test %age-Stacked Area Chart'); + $yAxisLabel = new Title('Value ($k)'); + + // Create the chart + $chart = new Chart( + 'chart1', // name + $title, // title + $legend, // legend + $plotArea, // plotArea + true, // plotVisibleOnly + DataSeries::EMPTY_AS_GAP, // displayBlanksAs + null, // xAxisLabel + $yAxisLabel // yAxisLabel + ); + $yAxis = $chart->getChartAxisX(); // deliberate + $yAxis->setGlowProperties(20, 'accent1', 20, Properties::EXCEL_COLOR_TYPE_SCHEME); + $expectedSize = 254000.0; + $expectedGlowColor = [ + 'type' => 'schemeClr', + 'value' => 'accent1', + 'alpha' => '80000', + ]; + self::assertEquals($expectedSize, $yAxis->getGlowProperty('size')); + self::assertEquals($expectedGlowColor, $yAxis->getGlowProperty('color')); + + // Set the position where the chart should appear in the worksheet + $chart->setTopLeftPosition('A7'); + $chart->setBottomRightPosition('H20'); + + // Add the chart to the worksheet + $worksheet->addChart($chart); + + /** @var callable */ + $callableReader = [$this, 'readCharts']; + /** @var callable */ + $callableWriter = [$this, 'writeCharts']; + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx', $callableReader, $callableWriter); + $spreadsheet->disconnectWorksheets(); + + $sheet = $reloadedSpreadsheet->getActiveSheet(); + $charts2 = $sheet->getChartCollection(); + self::assertCount(1, $charts2); + $chart2 = $charts2[0]; + self::assertNotNull($chart2); + $yAxis2 = $chart2->getChartAxisX(); // deliberate + self::assertEquals($expectedSize, $yAxis2->getGlowProperty('size')); + self::assertEquals($expectedGlowColor, $yAxis2->getGlowProperty('color')); + $xAxis2 = $chart2->getChartAxisY(); // deliberate + self::assertNull($xAxis2->getGlowProperty('size')); + $reloadedSpreadsheet->disconnectWorksheets(); + } +}