From 9694446b262d0a57fd5802fa6fe78ed31a355b4a Mon Sep 17 00:00:00 2001 From: nitta Date: Thu, 11 Jul 2019 19:09:43 +0900 Subject: [PATCH 1/4] Add chart's secondary y axis. --- src/PhpSpreadsheet/Chart/DataSeries.php | 38 +++- src/PhpSpreadsheet/Writer/Xlsx/Chart.php | 251 ++++++++++++++--------- 2 files changed, 187 insertions(+), 102 deletions(-) diff --git a/src/PhpSpreadsheet/Chart/DataSeries.php b/src/PhpSpreadsheet/Chart/DataSeries.php index 8056bbeea2..e90b0044ff 100644 --- a/src/PhpSpreadsheet/Chart/DataSeries.php +++ b/src/PhpSpreadsheet/Chart/DataSeries.php @@ -40,6 +40,9 @@ class DataSeries const STYLE_MARKER = 'marker'; const STYLE_FILLED = 'filled'; + const VALUE_AXIS_POSITION_LEFT = 'l'; + const VALUE_AXIS_POSITION_RIGHT = 'r'; + /** * Series Plot Type. * @@ -103,6 +106,11 @@ class DataSeries */ private $plotValues = []; + /** + * @var string + */ + private $valueAxisPosition; + /** * Create a new DataSeries. * @@ -116,7 +124,7 @@ class DataSeries * @param bool $smoothLine * @param null|string $plotStyle */ - public function __construct($plotType = null, $plotGrouping = null, array $plotOrder = [], array $plotLabel = [], array $plotCategory = [], array $plotValues = [], $plotDirection = null, $smoothLine = false, $plotStyle = null) + public function __construct($plotType = null, $plotGrouping = null, array $plotOrder = [], array $plotLabel = [], array $plotCategory = [], array $plotValues = [], $plotDirection = null, $smoothLine = false, $plotStyle = null, $valueAxisPosition = null) { $this->plotType = $plotType; $this->plotGrouping = $plotGrouping; @@ -140,6 +148,10 @@ public function __construct($plotType = null, $plotGrouping = null, array $plotO $plotDirection = self::DIRECTION_COL; } $this->plotDirection = $plotDirection; + + if ($valueAxisPosition === null) { + $this->valueAxisPosition = self::VALUE_AXIS_POSITION_LEFT; + } } /** @@ -355,6 +367,16 @@ public function getSmoothLine() return $this->smoothLine; } + /** + * Get Value Axis Position. + * + * @return string + */ + public function getValueAxisPosition() + { + return $this->valueAxisPosition; + } + /** * Set Smooth Line. * @@ -369,6 +391,20 @@ public function setSmoothLine($smoothLine) return $this; } + /** + * Set Value Axis Position. + * + * @param string $valueAxisPosition + * + * @return string + */ + public function setValueAxisPosition($valueAxisPosition) + { + $this->valueAxisPosition = $valueAxisPosition; + + return $this; + } + public function refresh(Worksheet $worksheet) { foreach ($this->plotValues as $plotValues) { diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php index 625fd16de2..a850b764b2 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php @@ -232,6 +232,7 @@ private function writePlotArea(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\W $chartTypes = self::getChartType($plotArea); $catIsMultiLevelSeries = $valIsMultiLevelSeries = false; $plotGroupingType = ''; + $useSecondaryYAxis = false; foreach ($chartTypes as $chartType) { $objWriter->startElement('c:' . $chartType); @@ -303,13 +304,27 @@ private function writePlotArea(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\W $id1 = '75091328'; $id2 = '75089408'; + // For secondary y axis id. + $secondaryId2 = '205755944'; + if (($chartType !== DataSeries::TYPE_PIECHART) && ($chartType !== DataSeries::TYPE_PIECHART_3D) && ($chartType !== DataSeries::TYPE_DONUTCHART)) { $objWriter->startElement('c:axId'); $objWriter->writeAttribute('val', $id1); $objWriter->endElement(); - $objWriter->startElement('c:axId'); - $objWriter->writeAttribute('val', $id2); - $objWriter->endElement(); + + $plotGroup = $this->getPlotGroupByChartType($plotArea, $chartType); + if ($plotGroup && $plotGroup->getValueAxisPosition() === DataSeries::VALUE_AXIS_POSITION_RIGHT) { + $useSecondaryYAxis = true; + $objWriter->startElement('c:axId'); + $objWriter->writeAttribute('val', $secondaryId2); + $objWriter->endElement(); + + } else { + $objWriter->startElement('c:axId'); + $objWriter->writeAttribute('val', $id2); + $objWriter->endElement(); + } + } else { $objWriter->startElement('c:firstSliceAng'); $objWriter->writeAttribute('val', 0); @@ -332,12 +347,36 @@ private function writePlotArea(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\W $this->writeCategoryAxis($objWriter, $xAxisLabel, $id1, $id2, $catIsMultiLevelSeries, $yAxis); } - $this->writeValueAxis($objWriter, $yAxisLabel, $chartType, $id1, $id2, $valIsMultiLevelSeries, $xAxis, $majorGridlines, $minorGridlines); + $this->writeValueAxis($objWriter, $yAxisLabel, $chartType, $id1, $id2, $valIsMultiLevelSeries, $xAxis, $majorGridlines, $minorGridlines, DataSeries::VALUE_AXIS_POSITION_LEFT); + } + + if ($useSecondaryYAxis) { + $this->writeValueAxis($objWriter, $yAxisLabel, $chartType, $id1, $secondaryId2, $valIsMultiLevelSeries, $xAxis, $majorGridlines, $minorGridlines,DataSeries::VALUE_AXIS_POSITION_RIGHT); } $objWriter->endElement(); } + /** + * @param $plotArea + * @param $chartType + * + * @return DataSeries|null + */ + private function getPlotGroupByChartType($plotArea, $chartType) + { + + $groupCount = $plotArea->getPlotGroupCount(); + for ($i = 0; $i < $groupCount; ++$i) { + $plotGroup = $plotArea->getPlotGroupByIndex($i); + $groupType = $plotGroup->getPlotType(); + if ($groupType == $chartType) { + return $plotGroup; + } + } + return null; + } + /** * Write Data Labels. * @@ -521,7 +560,7 @@ private function writeCategoryAxis($objWriter, $xAxisLabel, $id1, $id2, $isMulti * * @throws WriterException */ - private function writeValueAxis($objWriter, $yAxisLabel, $groupType, $id1, $id2, $isMultiLevelSeries, Axis $xAxis, GridLines $majorGridlines, GridLines $minorGridlines) + private function writeValueAxis($objWriter, $yAxisLabel, $groupType, $id1, $id2, $isMultiLevelSeries, Axis $xAxis, GridLines $majorGridlines, GridLines $minorGridlines, $axisPosition) { $objWriter->startElement('c:valAx'); @@ -556,115 +595,117 @@ private function writeValueAxis($objWriter, $yAxisLabel, $groupType, $id1, $id2, $objWriter->endElement(); $objWriter->startElement('c:axPos'); - $objWriter->writeAttribute('val', 'l'); + $objWriter->writeAttribute('val', $axisPosition); $objWriter->endElement(); - $objWriter->startElement('c:majorGridlines'); - $objWriter->startElement('c:spPr'); - - if ($majorGridlines->getLineColorProperty('value') !== null) { - $objWriter->startElement('a:ln'); - $objWriter->writeAttribute('w', $majorGridlines->getLineStyleProperty('width')); - $objWriter->startElement('a:solidFill'); - $objWriter->startElement("a:{$majorGridlines->getLineColorProperty('type')}"); - $objWriter->writeAttribute('val', $majorGridlines->getLineColorProperty('value')); - $objWriter->startElement('a:alpha'); - $objWriter->writeAttribute('val', $majorGridlines->getLineColorProperty('alpha')); - $objWriter->endElement(); //end alpha - $objWriter->endElement(); //end srgbClr - $objWriter->endElement(); //end solidFill + if ($axisPosition === DataSeries::VALUE_AXIS_POSITION_LEFT) { + $objWriter->startElement('c:majorGridlines'); + $objWriter->startElement('c:spPr'); - $objWriter->startElement('a:prstDash'); - $objWriter->writeAttribute('val', $majorGridlines->getLineStyleProperty('dash')); - $objWriter->endElement(); + if ($majorGridlines->getLineColorProperty('value') !== null) { + $objWriter->startElement('a:ln'); + $objWriter->writeAttribute('w', $majorGridlines->getLineStyleProperty('width')); + $objWriter->startElement('a:solidFill'); + $objWriter->startElement("a:{$majorGridlines->getLineColorProperty('type')}"); + $objWriter->writeAttribute('val', $majorGridlines->getLineColorProperty('value')); + $objWriter->startElement('a:alpha'); + $objWriter->writeAttribute('val', $majorGridlines->getLineColorProperty('alpha')); + $objWriter->endElement(); //end alpha + $objWriter->endElement(); //end srgbClr + $objWriter->endElement(); //end solidFill - if ($majorGridlines->getLineStyleProperty('join') == 'miter') { - $objWriter->startElement('a:miter'); - $objWriter->writeAttribute('lim', '800000'); - $objWriter->endElement(); - } else { - $objWriter->startElement('a:bevel'); + $objWriter->startElement('a:prstDash'); + $objWriter->writeAttribute('val', $majorGridlines->getLineStyleProperty('dash')); $objWriter->endElement(); - } - if ($majorGridlines->getLineStyleProperty(['arrow', 'head', 'type']) !== null) { - $objWriter->startElement('a:headEnd'); - $objWriter->writeAttribute('type', $majorGridlines->getLineStyleProperty(['arrow', 'head', 'type'])); - $objWriter->writeAttribute('w', $majorGridlines->getLineStyleArrowParameters('head', 'w')); - $objWriter->writeAttribute('len', $majorGridlines->getLineStyleArrowParameters('head', 'len')); - $objWriter->endElement(); + if ($majorGridlines->getLineStyleProperty('join') == 'miter') { + $objWriter->startElement('a:miter'); + $objWriter->writeAttribute('lim', '800000'); + $objWriter->endElement(); + } else { + $objWriter->startElement('a:bevel'); + $objWriter->endElement(); + } + + if ($majorGridlines->getLineStyleProperty(['arrow', 'head', 'type']) !== null) { + $objWriter->startElement('a:headEnd'); + $objWriter->writeAttribute('type', $majorGridlines->getLineStyleProperty(['arrow', 'head', 'type'])); + $objWriter->writeAttribute('w', $majorGridlines->getLineStyleArrowParameters('head', 'w')); + $objWriter->writeAttribute('len', $majorGridlines->getLineStyleArrowParameters('head', 'len')); + $objWriter->endElement(); + } + + if ($majorGridlines->getLineStyleProperty(['arrow', 'end', 'type']) !== null) { + $objWriter->startElement('a:tailEnd'); + $objWriter->writeAttribute('type', $majorGridlines->getLineStyleProperty(['arrow', 'end', 'type'])); + $objWriter->writeAttribute('w', $majorGridlines->getLineStyleArrowParameters('end', 'w')); + $objWriter->writeAttribute('len', $majorGridlines->getLineStyleArrowParameters('end', 'len')); + $objWriter->endElement(); + } + $objWriter->endElement(); //end ln } + $objWriter->startElement('a:effectLst'); - if ($majorGridlines->getLineStyleProperty(['arrow', 'end', 'type']) !== null) { - $objWriter->startElement('a:tailEnd'); - $objWriter->writeAttribute('type', $majorGridlines->getLineStyleProperty(['arrow', 'end', 'type'])); - $objWriter->writeAttribute('w', $majorGridlines->getLineStyleArrowParameters('end', 'w')); - $objWriter->writeAttribute('len', $majorGridlines->getLineStyleArrowParameters('end', 'len')); - $objWriter->endElement(); + if ($majorGridlines->getGlowSize() !== null) { + $objWriter->startElement('a:glow'); + $objWriter->writeAttribute('rad', $majorGridlines->getGlowSize()); + $objWriter->startElement("a:{$majorGridlines->getGlowColor('type')}"); + $objWriter->writeAttribute('val', $majorGridlines->getGlowColor('value')); + $objWriter->startElement('a:alpha'); + $objWriter->writeAttribute('val', $majorGridlines->getGlowColor('alpha')); + $objWriter->endElement(); //end alpha + $objWriter->endElement(); //end schemeClr + $objWriter->endElement(); //end glow } - $objWriter->endElement(); //end ln - } - $objWriter->startElement('a:effectLst'); - if ($majorGridlines->getGlowSize() !== null) { - $objWriter->startElement('a:glow'); - $objWriter->writeAttribute('rad', $majorGridlines->getGlowSize()); - $objWriter->startElement("a:{$majorGridlines->getGlowColor('type')}"); - $objWriter->writeAttribute('val', $majorGridlines->getGlowColor('value')); - $objWriter->startElement('a:alpha'); - $objWriter->writeAttribute('val', $majorGridlines->getGlowColor('alpha')); - $objWriter->endElement(); //end alpha - $objWriter->endElement(); //end schemeClr - $objWriter->endElement(); //end glow - } + if ($majorGridlines->getShadowProperty('presets') !== null) { + $objWriter->startElement("a:{$majorGridlines->getShadowProperty('effect')}"); + if ($majorGridlines->getShadowProperty('blur') !== null) { + $objWriter->writeAttribute('blurRad', $majorGridlines->getShadowProperty('blur')); + } + if ($majorGridlines->getShadowProperty('distance') !== null) { + $objWriter->writeAttribute('dist', $majorGridlines->getShadowProperty('distance')); + } + if ($majorGridlines->getShadowProperty('direction') !== null) { + $objWriter->writeAttribute('dir', $majorGridlines->getShadowProperty('direction')); + } + if ($majorGridlines->getShadowProperty('algn') !== null) { + $objWriter->writeAttribute('algn', $majorGridlines->getShadowProperty('algn')); + } + if ($majorGridlines->getShadowProperty(['size', 'sx']) !== null) { + $objWriter->writeAttribute('sx', $majorGridlines->getShadowProperty(['size', 'sx'])); + } + if ($majorGridlines->getShadowProperty(['size', 'sy']) !== null) { + $objWriter->writeAttribute('sy', $majorGridlines->getShadowProperty(['size', 'sy'])); + } + if ($majorGridlines->getShadowProperty(['size', 'kx']) !== null) { + $objWriter->writeAttribute('kx', $majorGridlines->getShadowProperty(['size', 'kx'])); + } + if ($majorGridlines->getShadowProperty('rotWithShape') !== null) { + $objWriter->writeAttribute('rotWithShape', $majorGridlines->getShadowProperty('rotWithShape')); + } + $objWriter->startElement("a:{$majorGridlines->getShadowProperty(['color', 'type'])}"); + $objWriter->writeAttribute('val', $majorGridlines->getShadowProperty(['color', 'value'])); - if ($majorGridlines->getShadowProperty('presets') !== null) { - $objWriter->startElement("a:{$majorGridlines->getShadowProperty('effect')}"); - if ($majorGridlines->getShadowProperty('blur') !== null) { - $objWriter->writeAttribute('blurRad', $majorGridlines->getShadowProperty('blur')); - } - if ($majorGridlines->getShadowProperty('distance') !== null) { - $objWriter->writeAttribute('dist', $majorGridlines->getShadowProperty('distance')); - } - if ($majorGridlines->getShadowProperty('direction') !== null) { - $objWriter->writeAttribute('dir', $majorGridlines->getShadowProperty('direction')); - } - if ($majorGridlines->getShadowProperty('algn') !== null) { - $objWriter->writeAttribute('algn', $majorGridlines->getShadowProperty('algn')); - } - if ($majorGridlines->getShadowProperty(['size', 'sx']) !== null) { - $objWriter->writeAttribute('sx', $majorGridlines->getShadowProperty(['size', 'sx'])); - } - if ($majorGridlines->getShadowProperty(['size', 'sy']) !== null) { - $objWriter->writeAttribute('sy', $majorGridlines->getShadowProperty(['size', 'sy'])); - } - if ($majorGridlines->getShadowProperty(['size', 'kx']) !== null) { - $objWriter->writeAttribute('kx', $majorGridlines->getShadowProperty(['size', 'kx'])); - } - if ($majorGridlines->getShadowProperty('rotWithShape') !== null) { - $objWriter->writeAttribute('rotWithShape', $majorGridlines->getShadowProperty('rotWithShape')); - } - $objWriter->startElement("a:{$majorGridlines->getShadowProperty(['color', 'type'])}"); - $objWriter->writeAttribute('val', $majorGridlines->getShadowProperty(['color', 'value'])); + $objWriter->startElement('a:alpha'); + $objWriter->writeAttribute('val', $majorGridlines->getShadowProperty(['color', 'alpha'])); + $objWriter->endElement(); //end alpha - $objWriter->startElement('a:alpha'); - $objWriter->writeAttribute('val', $majorGridlines->getShadowProperty(['color', 'alpha'])); - $objWriter->endElement(); //end alpha + $objWriter->endElement(); //end color:type + $objWriter->endElement(); //end shadow + } - $objWriter->endElement(); //end color:type - $objWriter->endElement(); //end shadow - } + if ($majorGridlines->getSoftEdgesSize() !== null) { + $objWriter->startElement('a:softEdge'); + $objWriter->writeAttribute('rad', $majorGridlines->getSoftEdgesSize()); + $objWriter->endElement(); //end softEdge + } - if ($majorGridlines->getSoftEdgesSize() !== null) { - $objWriter->startElement('a:softEdge'); - $objWriter->writeAttribute('rad', $majorGridlines->getSoftEdgesSize()); - $objWriter->endElement(); //end softEdge + $objWriter->endElement(); //end effectLst + $objWriter->endElement(); //end spPr + $objWriter->endElement(); //end majorGridLines } - $objWriter->endElement(); //end effectLst - $objWriter->endElement(); //end spPr - $objWriter->endElement(); //end majorGridLines - if ($minorGridlines->getObjectState()) { $objWriter->startElement('c:minorGridlines'); $objWriter->startElement('c:spPr'); @@ -953,7 +994,7 @@ private function writeValueAxis($objWriter, $yAxisLabel, $groupType, $id1, $id2, if ($id1 > 0) { $objWriter->startElement('c:crossAx'); - $objWriter->writeAttribute('val', $id2); + $objWriter->writeAttribute('val', $id1); $objWriter->endElement(); if ($xAxis->getAxisOptionsProperty('horizontal_crosses_value') !== null) { @@ -962,12 +1003,20 @@ private function writeValueAxis($objWriter, $yAxisLabel, $groupType, $id1, $id2, $objWriter->endElement(); } else { $objWriter->startElement('c:crosses'); - $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('horizontal_crosses')); + if ($axisPosition === DataSeries::VALUE_AXIS_POSITION_RIGHT) { + $objWriter->writeAttribute('val', 'max'); + } else { + $objWriter->writeAttribute('val', $xAxis->getAxisOptionsProperty('horizontal_crosses')); + } $objWriter->endElement(); } $objWriter->startElement('c:crossBetween'); - $objWriter->writeAttribute('val', 'midCat'); + if ($axisPosition === DataSeries::VALUE_AXIS_POSITION_RIGHT) { + $objWriter->writeAttribute('val', 'between'); + } else { + $objWriter->writeAttribute('val', 'midCat'); + } $objWriter->endElement(); if ($xAxis->getAxisOptionsProperty('major_unit') !== null) { From 83bad48396b178a993973df3fd0335f98fbdce44 Mon Sep 17 00:00:00 2001 From: nitta Date: Thu, 11 Jul 2019 19:21:28 +0900 Subject: [PATCH 2/4] Update Changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f15a894f99..a4208508df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org). ## [Unreleased] +### Added + +- Added chart's secondary y axis - [Issue #1072](https://github.com/PHPOffice/PhpSpreadsheet/issues/1072) + ### Fixed - COUPNUM should not return zero when settlement is in the last period - [Issue #1020](https://github.com/PHPOffice/PhpSpreadsheet/issues/1020) and [PR #1021](https://github.com/PHPOffice/PhpSpreadsheet/pull/1021) From fda9f6b3f26a30ccba9c27e43c66ed3c8299a0ee Mon Sep 17 00:00:00 2001 From: nitta Date: Thu, 11 Jul 2019 19:21:28 +0900 Subject: [PATCH 3/4] Update Changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f15a894f99..a4208508df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org). ## [Unreleased] +### Added + +- Added chart's secondary y axis - [Issue #1072](https://github.com/PHPOffice/PhpSpreadsheet/issues/1072) + ### Fixed - COUPNUM should not return zero when settlement is in the last period - [Issue #1020](https://github.com/PHPOffice/PhpSpreadsheet/issues/1020) and [PR #1021](https://github.com/PHPOffice/PhpSpreadsheet/pull/1021) From c73be30e6f36d7a69e2387e8941fa18944ec23fc Mon Sep 17 00:00:00 2001 From: nitta Date: Thu, 11 Jul 2019 19:29:28 +0900 Subject: [PATCH 4/4] Refactor a bit. --- src/PhpSpreadsheet/Writer/Xlsx/Chart.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php index a850b764b2..796ffd1ffe 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php @@ -358,14 +358,15 @@ private function writePlotArea(XMLWriter $objWriter, \PhpOffice\PhpSpreadsheet\W } /** - * @param $plotArea - * @param $chartType + * Get a DataSeries corresponds to specific chart type. + * + * @param PlotArea $plotArea + * @param string $chartType * * @return DataSeries|null */ - private function getPlotGroupByChartType($plotArea, $chartType) + private function getPlotGroupByChartType(PlotArea $plotArea, $chartType) { - $groupCount = $plotArea->getPlotGroupCount(); for ($i = 0; $i < $groupCount; ++$i) { $plotGroup = $plotArea->getPlotGroupByIndex($i);