From 5d5e550342426d51c85f2f10cb8ee6ca0b1bb84b Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Wed, 29 Jun 2022 17:52:09 -0700 Subject: [PATCH] Additional Support for Chart DataSeriesValues (#2906) * Additional Support for Chart DataSeriesValues Fix #2863. DataSeriesValues now extends Properties, allowing it to share code in common with Axis and Gridlines. This causes some minor breakages; in particular line width is now initialized to null instead of Excel's default value, and is specified in points, as the user would expect from Excel, rather than the value stored in Xml. This change: - adds support for 1 or 2 marker colors. - adds support for `smoothLine` to DataSeriesValues. - will determine `catAx` or `valAx` for Axis based on what is read from the Xml when available, rather than guessing based on format. (Another minor break.) - reads `formatCode` and `sourceLinked` for Axis. - correct 2 uses of `$plotSeriesRef` to `$plotSeriesIndex` in Writer/Xlsx/Chart. - pushes coverage over 90% for Chart (88.70% overall). * Update Change Log I had updated previously but forgot to stage the member. * Adopt Some Suggestions Incorporate some changes suggested by @bridgeplayr. * Use ChartColor for DSV Fill And Font Text DataSeriesValues Fill could be a scalar or an array, so I saved it till last. * Some Final Cleanup No code changes. Illustrate even more of the new features in existing sample files. Deprecate *_ARGB in Properties/ChartColors in favor of *_RGB, because it uses only 6 hex digits. The alpha value is stored separately. --- CHANGELOG.md | 2 +- phpstan-baseline.neon | 12 +- .../33_Chart_create_bar_custom_colors.php | 183 +++++++++++++++ samples/Chart/33_Chart_create_line.php | 4 +- samples/Chart/33_Chart_create_scatter2.php | 70 +++++- .../templates/32readwriteScatterChart8.xlsx | Bin 0 -> 12409 bytes src/PhpSpreadsheet/Chart/Axis.php | 23 +- src/PhpSpreadsheet/Chart/ChartColor.php | 52 +++-- src/PhpSpreadsheet/Chart/DataSeries.php | 4 - src/PhpSpreadsheet/Chart/DataSeriesValues.php | 169 ++++++++++---- src/PhpSpreadsheet/Chart/Properties.php | 34 +-- src/PhpSpreadsheet/Reader/Xlsx/Chart.php | 168 +++++++------- src/PhpSpreadsheet/Style/Font.php | 62 +++-- src/PhpSpreadsheet/Writer/Xlsx/Chart.php | 211 ++++++++---------- .../Writer/Xlsx/StringTable.php | 53 +++-- .../Chart/AxisGlowTest.php | 1 + .../Chart/BarChartCustomColorsTest.php | 162 ++++++++++++++ .../Chart/Charts32ColoredAxisLabelTest.php | 10 +- .../Chart/Charts32ScatterTest.php | 147 ++++++++++-- .../Chart/Charts32XmlTest.php | 34 ++- tests/PhpSpreadsheetTests/Chart/ColorTest.php | 32 +++ .../Chart/DataSeriesValues2Test.php | 172 ++++++++++++++ .../Chart/DataSeriesValuesTest.php | 10 +- .../{Writer/Xlsx => Chart}/Issue589Test.php | 6 +- .../Chart/ShadowPresetsTest.php | 29 +++ 25 files changed, 1272 insertions(+), 378 deletions(-) create mode 100644 samples/Chart/33_Chart_create_bar_custom_colors.php create mode 100644 samples/templates/32readwriteScatterChart8.xlsx create mode 100644 tests/PhpSpreadsheetTests/Chart/BarChartCustomColorsTest.php create mode 100644 tests/PhpSpreadsheetTests/Chart/ColorTest.php create mode 100644 tests/PhpSpreadsheetTests/Chart/DataSeriesValues2Test.php rename tests/PhpSpreadsheetTests/{Writer/Xlsx => Chart}/Issue589Test.php (96%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9148425d7f..b7d55bbebf 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) [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) +- 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) - 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 d1f2a03b4f..858502fdc0 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -4055,16 +4055,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Writer/Xlsx.php - - - message: "#^Cannot call method getDataValues\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeriesValues\\|false\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php - - - - message: "#^Cannot call method getFillColor\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Chart\\\\DataSeriesValues\\|false\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Xlsx/Chart.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 @@ -4087,7 +4077,7 @@ parameters: - message: "#^Parameter \\#2 \\$value of method XMLWriter\\:\\:writeAttribute\\(\\) expects string, int given\\.$#" - count: 42 + count: 41 path: src/PhpSpreadsheet/Writer/Xlsx/Chart.php - diff --git a/samples/Chart/33_Chart_create_bar_custom_colors.php b/samples/Chart/33_Chart_create_bar_custom_colors.php new file mode 100644 index 0000000000..75f2306b7d --- /dev/null +++ b/samples/Chart/33_Chart_create_bar_custom_colors.php @@ -0,0 +1,183 @@ +getActiveSheet(); +$worksheet->fromArray( + [ + ['', 2010, 2011, 2012], + ['Q1', 12, 15, 21], + ['Q2', 56, 73, 86], + ['Q3', 52, 61, 69], + ['Q4', 30, 32, 0], + ] +); + +// Custom colors for dataSeries (gray, blue, red, orange) +$colors = [ + 'cccccc', '00abb8', 'b8292f', 'eb8500', +]; + +// 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 +$dataSeriesLabels1 = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', null, 1), // 2011 +]; +// Set the X-Axis Labels +// Datatype +// Cell reference for data +// Format Code +// Number of datapoints in series +// Data values +// Data Marker +$xAxisTickValues1 = [ + 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 +// Custom colors +$dataSeriesValues1 = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', null, 4, [], null, $colors), +]; + +// Build the dataseries +$series1 = new DataSeries( + DataSeries::TYPE_BARCHART, // plotType + null, // plotGrouping (Pie charts don't have any grouping) + range(0, count($dataSeriesValues1) - 1), // plotOrder + $dataSeriesLabels1, // plotLabel + $xAxisTickValues1, // plotCategory + $dataSeriesValues1 // plotValues +); + +// Set up a layout object for the Pie chart +$layout1 = new Layout(); +$layout1->setShowVal(true); +$layout1->setShowPercent(true); + +// Set the series in the plot area +$plotArea1 = new PlotArea($layout1, [$series1]); +// Set the chart legend +$legend1 = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false); + +$title1 = new Title('Test Bar Chart'); + +// Create the chart +$chart1 = new Chart( + 'chart1', // name + $title1, // title + $legend1, // legend + $plotArea1, // plotArea + true, // plotVisibleOnly + DataSeries::EMPTY_AS_GAP, // displayBlanksAs + null, // xAxisLabel + null // yAxisLabel - Pie charts don't have a Y-Axis +); + +// Set the position where the chart should appear in the worksheet +$chart1->setTopLeftPosition('A7'); +$chart1->setBottomRightPosition('H20'); + +// Add the chart to the worksheet +$worksheet->addChart($chart1); + +// 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 +$dataSeriesLabels2 = [ + new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', null, 1), // 2011 +]; +// Set the X-Axis Labels +// Datatype +// Cell reference for data +// Format Code +// Number of datapoints in series +// Data values +// Data Marker +$xAxisTickValues2 = [ + 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 +// Custom colors +$dataSeriesValues2 = [ + $dataSeriesValues2Element = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', null, 4), +]; +$dataSeriesValues2Element->setFillColor($colors); + +// Build the dataseries +$series2 = new DataSeries( + DataSeries::TYPE_DONUTCHART, // plotType + null, // plotGrouping (Donut charts don't have any grouping) + range(0, count($dataSeriesValues2) - 1), // plotOrder + $dataSeriesLabels2, // plotLabel + $xAxisTickValues2, // plotCategory + $dataSeriesValues2 // plotValues +); + +// Set up a layout object for the Pie chart +$layout2 = new Layout(); +$layout2->setShowVal(true); +$layout2->setShowCatName(true); + +// Set the series in the plot area +$plotArea2 = new PlotArea($layout2, [$series2]); + +$title2 = new Title('Test Donut Chart'); + +// Create the chart +$chart2 = new Chart( + 'chart2', // name + $title2, // title + null, // legend + $plotArea2, // plotArea + true, // plotVisibleOnly + DataSeries::EMPTY_AS_GAP, // displayBlanksAs + null, // xAxisLabel + null // yAxisLabel - Like Pie charts, Donut charts don't have a Y-Axis +); + +// Set the position where the chart should appear in the worksheet +$chart2->setTopLeftPosition('I7'); +$chart2->setBottomRightPosition('P20'); + +// Add the chart to the worksheet +$worksheet->addChart($chart2); + +// Save Excel 2007 file +$filename = $helper->getFilename(__FILE__); +$writer = IOFactory::createWriter($spreadsheet, 'Xlsx'); +$writer->setIncludeCharts(true); +$callStartTime = microtime(true); +$writer->save($filename); +$helper->logWrite($writer, $filename, $callStartTime); diff --git a/samples/Chart/33_Chart_create_line.php b/samples/Chart/33_Chart_create_line.php index fee2a2840b..8cb87e306e 100644 --- a/samples/Chart/33_Chart_create_line.php +++ b/samples/Chart/33_Chart_create_line.php @@ -5,6 +5,7 @@ use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues; use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend; use PhpOffice\PhpSpreadsheet\Chart\PlotArea; +use PhpOffice\PhpSpreadsheet\Chart\Properties; use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Spreadsheet; @@ -35,6 +36,7 @@ new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', null, 1), // 2011 new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$D$1', null, 1), // 2012 ]; +$dataSeriesLabels[0]->setFillColor('FF0000'); // Set the X-Axis Labels // Datatype // Cell reference for data @@ -57,7 +59,7 @@ 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), ]; -$dataSeriesValues[2]->setLineWidth(60000); +$dataSeriesValues[2]->setLineWidth(60000 / Properties::POINTS_WIDTH_MULTIPLIER); // Build the dataseries $series = new DataSeries( diff --git a/samples/Chart/33_Chart_create_scatter2.php b/samples/Chart/33_Chart_create_scatter2.php index 1d6e331c69..ef6353bb4f 100644 --- a/samples/Chart/33_Chart_create_scatter2.php +++ b/samples/Chart/33_Chart_create_scatter2.php @@ -2,6 +2,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Axis; use PhpOffice\PhpSpreadsheet\Chart\Chart; +use PhpOffice\PhpSpreadsheet\Chart\ChartColor; use PhpOffice\PhpSpreadsheet\Chart\DataSeries; use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues; use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend; @@ -64,11 +65,76 @@ new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', Properties::FORMAT_CODE_NUMBER, 4), new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$D$2:$D$5', Properties::FORMAT_CODE_NUMBER, 4), ]; + +// series 1 +// marker details +$dataSeriesValues[0] + ->setPointMarker('diamond') + ->setPointSize(5) + ->getMarkerFillColor() + ->setColorProperties('0070C0', null, ChartColor::EXCEL_COLOR_TYPE_RGB); +$dataSeriesValues[0] + ->getMarkerBorderColor() + ->setColorProperties('002060', null, ChartColor::EXCEL_COLOR_TYPE_RGB); + +// line details - smooth line, connected +$dataSeriesValues[0] + ->setScatterLines(true) + ->setSmoothLine(true) + ->setLineColorProperties('accent1', 40, ChartColor::EXCEL_COLOR_TYPE_SCHEME); // value, alpha, type +$dataSeriesValues[0]->setLineStyleProperties( + 2.5, // width in points + Properties::LINE_STYLE_COMPOUND_TRIPLE, // compound + Properties::LINE_STYLE_DASH_SQUARE_DOT, // dash + Properties::LINE_STYLE_CAP_SQUARE, // cap + Properties::LINE_STYLE_JOIN_MITER, // join + Properties::LINE_STYLE_ARROW_TYPE_OPEN, // head type + Properties::LINE_STYLE_ARROW_SIZE_4, // head size preset index + Properties::LINE_STYLE_ARROW_TYPE_ARROW, // end type + Properties::LINE_STYLE_ARROW_SIZE_6 // end size preset index +); + +// series 2 - straight line - no special effects, connected, straight line +$dataSeriesValues[1] // square fill + ->setPointMarker('square') + ->setPointSize(6) + ->getMarkerBorderColor() + ->setColorProperties('accent6', 3, ChartColor::EXCEL_COLOR_TYPE_SCHEME); +$dataSeriesValues[1] // square border + ->getMarkerFillColor() + ->setColorProperties('0FFF00', null, ChartColor::EXCEL_COLOR_TYPE_RGB); +$dataSeriesValues[1] + ->setScatterLines(true) + ->setSmoothLine(false) + ->setLineColorProperties('FF0000', 80, ChartColor::EXCEL_COLOR_TYPE_RGB); +$dataSeriesValues[1]->setLineWidth(2.0); + +// series 3 - markers, no line +$dataSeriesValues[2] // triangle fill + //->setPointMarker('triangle') // let Excel choose shape + ->setPointSize(7) + ->getMarkerFillColor() + ->setColorProperties('FFFF00', null, ChartColor::EXCEL_COLOR_TYPE_RGB); +$dataSeriesValues[2] // triangle border + ->getMarkerBorderColor() + ->setColorProperties('accent4', null, ChartColor::EXCEL_COLOR_TYPE_SCHEME); +$dataSeriesValues[2]->setScatterLines(false); // points not connected + // Added so that Xaxis shows dates instead of Excel-equivalent-year1900-numbers $xAxis = new Axis(); //$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE ); $xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE_ISO8601, true); +$yAxis = new Axis(); +$yAxis->setLineStyleProperties( + 2.5, // width in points + Properties::LINE_STYLE_COMPOUND_SIMPLE, + Properties::LINE_STYLE_DASH_DASH_DOT, + Properties::LINE_STYLE_CAP_FLAT, + Properties::LINE_STYLE_JOIN_BEVEL +); +$yAxis->setLineColorProperties('ffc000', null, ChartColor::EXCEL_COLOR_TYPE_RGB); + // Build the dataseries $series = new DataSeries( DataSeries::TYPE_SCATTERCHART, // plotType @@ -79,8 +145,7 @@ $dataSeriesValues, // plotValues null, // plotDirection false, // smooth line - //DataSeries::STYLE_LINEMARKER // plotStyle - DataSeries::STYLE_MARKER // plotStyle + DataSeries::STYLE_SMOOTHMARKER // plotStyle ); // Set the series in the plot area @@ -103,6 +168,7 @@ $yAxisLabel, // yAxisLabel // added xAxis for correct date display $xAxis, // xAxis + $yAxis, // yAxis ); // Set the position where the chart should appear in the worksheet diff --git a/samples/templates/32readwriteScatterChart8.xlsx b/samples/templates/32readwriteScatterChart8.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..fdd85b0ed3bf9fbac400a46a350da93f74e5d9a4 GIT binary patch literal 12409 zcmeHt1y>yD)-|q;1P$&GBuMZ8!L@-v(BSUwuEE_scyPDi5Znpw?(QD=I+?jQGt7Ly z;Jv+8b^l313=k3u0}KWX4h#&81Z;f9o+$(z4D1FP3=AC%4nkGX(!x&P z!cJ4(*-GD5o!-gZj5rGl;#CG11nB$!d;K3?f#TQ!i*Jmm!q;(kC{c9^Iy+f~G!P;0 z;$Nd(KuU6>hIH0)ckCZ?F@Y;!QV5g>#mv}_q*d_vey=T!W`$xI-pWe96oRO=nwt@( zZ^PC@j9AnbVo|Nkw~$TlsTRqYUlZNO`0N zCa?zVc4nWa@7qTRAT=wl4u_L(y^2C?ljZI8Qs1C*RMXKkYrfj6V_LaaI@vbnmqcQF zTefmu4qb%?y8w5v+zP0ShJH6jNdP+WTBl5J%pe%128?-CKgS!~jxi^3~6r9S>0JKNX>wjY-p zYC2QosdKa6t8s9lIR&pedRL4?(gYf>ſT|-C@Q%5J6zH=;CdiOT&i>jH(7VU)a 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 z zAv*;Pj!a0CA}E{FfbYSRlLzxHgf^Q?q0d)h3wTDPR2%zpSe+jXhC5Du9&FjCxrHFA zW|fN$dnw)~R#g2_s_$Iqvqlto2BU=hR9mbWN#sXo54Naq;Qs1&RaipSPvmct+HzB( zS+mw-Ve0)=KFP`_iqV&pL5u7tO~BW`pv~aa)m4E+ppY3&)82Ctt*n!;tg}mRF+qyW zk>N*zfgBRS;W2sPM(}86l!UHdV!f~P<;T~&B?$+;vPa|wY&I!olVFEX4o?wHqNDV= z`~tQ{b#dZNA@Oir+E-K;PN8`xw~ydxjW#HxBs_4{Eo3gJ(@oyyy3GhtI2kk9#A0Ox z-97tvFn;-^LTv#z$v~t%gL%$}QtDayFL+?`XxV2LOt4$IUz=42#gnSI-_LUB&-#RM zHg0rv&C@Sscw#rxPTYq)ttMiIq@I$l-(Ci-w`Z7nrtm3$Y(*gnFgigW0QKbl#Zf0i z^sd7|j!FgU^`QRMQMIkC{^zHlCHwQ08r}Z0cHn;ud4=qM6qZ{0%AZeBHs`gA@^>)t zMrNw<3Ym{@$fv}vE^^H$?B~)P$ZpYF-$ltQ1$65ZbnE#tblOCP0Gi=*FYg7o508dD zg6Uq|psAfG2a-`*=eByvrMy%L)nn2(Ta_a*$#S63f5XZaiACqCScr;8EWNJbUswra z>EmoZ8pAzu4h(`7(A06BlJ?iJQ?tKl{i4svqv^6lpb9g!*I=H;%J%^Lp(O>NmXBQ| z1iYo#&eCakojh%{n{P|8T`-4h_0>RE-5Z#{A91a^H;olZuorc>$mAHs73mx zIQh>I*mL8*PtW`|1p`}#|7HBgKmNxo&2yaRBNe}q&;bAa5&sylc#iVCulpNi98@3y z-OBSW?{k3XMfcwTj#$3{ewW~%n?A2i{x%K4`NQ;irSdt#^BUl91by6J2>(?=f7L+G zQJ&X)exo$v|3djk1?V}#bA9?7L6-a%!q46Ob1(i?tv&~QF6w>*np6D!#{NGN?>XRe zq4XQjgz^{QGnw>UM*Wga&q4pLcYZ^HCjUX8|4={A&Ho;S{%W2{{TK5;W0DLI8bq$2 QbCxJzKW7y5=zf0se<{9 literal 0 HcmV?d00001 diff --git a/src/PhpSpreadsheet/Chart/Axis.php b/src/PhpSpreadsheet/Chart/Axis.php index 69a25d9263..724388917d 100644 --- a/src/PhpSpreadsheet/Chart/Axis.php +++ b/src/PhpSpreadsheet/Chart/Axis.php @@ -27,6 +27,9 @@ public function __construct() 'numeric' => null, ]; + /** @var string */ + private $axisType = ''; + /** * Axis Options. * @@ -62,11 +65,11 @@ public function __construct() * * @param mixed $format_code */ - public function setAxisNumberProperties($format_code, ?bool $numeric = null): void + public function setAxisNumberProperties($format_code, ?bool $numeric = null, int $sourceLinked = 0): void { $format = (string) $format_code; $this->axisNumber['format'] = $format; - $this->axisNumber['source_linked'] = 0; + $this->axisNumber['source_linked'] = $sourceLinked; if (is_bool($numeric)) { $this->axisNumber['numeric'] = $numeric; } elseif (in_array($format, self::NUMERIC_FORMAT, true)) { @@ -156,6 +159,22 @@ public function setAxisOrientation($orientation): void $this->axisOptions['orientation'] = (string) $orientation; } + public function getAxisType(): string + { + return $this->axisType; + } + + public function setAxisType(string $type): self + { + if ($type === 'catAx' || $type === 'valAx') { + $this->axisType = $type; + } else { + $this->axisType = ''; + } + + return $this; + } + /** * Set Fill Property. * diff --git a/src/PhpSpreadsheet/Chart/ChartColor.php b/src/PhpSpreadsheet/Chart/ChartColor.php index 05c1bb9af0..7f87e39126 100644 --- a/src/PhpSpreadsheet/Chart/ChartColor.php +++ b/src/PhpSpreadsheet/Chart/ChartColor.php @@ -6,6 +6,8 @@ class ChartColor { const EXCEL_COLOR_TYPE_STANDARD = 'prstClr'; const EXCEL_COLOR_TYPE_SCHEME = 'schemeClr'; + const EXCEL_COLOR_TYPE_RGB = 'srgbClr'; + /** @deprecated 1.24 use EXCEL_COLOR_TYPE_RGB instead */ const EXCEL_COLOR_TYPE_ARGB = 'srgbClr'; const EXCEL_COLOR_TYPES = [ self::EXCEL_COLOR_TYPE_ARGB, @@ -22,6 +24,18 @@ class ChartColor /** @var ?int */ private $alpha; + /** + * @param string|string[] $value + */ + public function __construct($value = '', ?int $alpha = null, ?string $type = null) + { + if (is_array($value)) { + $this->setColorPropertiesArray($value); + } else { + $this->setColorProperties($value, $alpha, $type); + } + } + public function getValue(): string { return $this->value; @@ -61,10 +75,21 @@ public function setAlpha(?int $alpha): self /** * @param null|float|int|string $alpha */ - public function setColorProperties(?string $color, $alpha, ?string $type): self + public function setColorProperties(?string $color, $alpha = null, ?string $type = null): self { + if (empty($type) && !empty($color)) { + if (substr($color, 0, 1) === '*') { + $type = 'schemeClr'; + $color = substr($color, 1); + } elseif (substr($color, 0, 1) === '/') { + $type = 'prstClr'; + $color = substr($color, 1); + } elseif (preg_match('/^[0-9A-Fa-f]{6}$/', $color) === 1) { + $type = 'srgbClr'; + } + } if ($color !== null) { - $this->setValue($color); + $this->setValue("$color"); } if ($type !== null) { $this->setType($type); @@ -80,21 +105,16 @@ public function setColorProperties(?string $color, $alpha, ?string $type): self public function setColorPropertiesArray(array $color): self { - if (array_key_exists('value', $color) && is_string($color['value'])) { - $this->setValue($color['value']); - } - if (array_key_exists('type', $color) && is_string($color['type'])) { - $this->setType($color['type']); - } - if (array_key_exists('alpha', $color)) { - if ($color['alpha'] === null) { - $this->setAlpha(null); - } elseif (is_numeric($color['alpha'])) { - $this->setAlpha((int) $color['alpha']); - } - } + return $this->setColorProperties( + $color['value'] ?? '', + $color['alpha'] ?? null, + $color['type'] ?? null + ); + } - return $this; + public function isUsable(): bool + { + return $this->type !== '' && $this->value !== ''; } /** diff --git a/src/PhpSpreadsheet/Chart/DataSeries.php b/src/PhpSpreadsheet/Chart/DataSeries.php index dca1186ec5..d27db33ec0 100644 --- a/src/PhpSpreadsheet/Chart/DataSeries.php +++ b/src/PhpSpreadsheet/Chart/DataSeries.php @@ -257,8 +257,6 @@ public function getPlotLabelByIndex($index) $keys = array_keys($this->plotLabel); if (in_array($index, $keys)) { return $this->plotLabel[$index]; - } elseif (isset($keys[$index])) { - return $this->plotLabel[$keys[$index]]; } return false; @@ -339,8 +337,6 @@ public function getPlotValuesByIndex($index) $keys = array_keys($this->plotValues); if (in_array($index, $keys)) { return $this->plotValues[$index]; - } elseif (isset($keys[$index])) { - return $this->plotValues[$keys[$index]]; } return false; diff --git a/src/PhpSpreadsheet/Chart/DataSeriesValues.php b/src/PhpSpreadsheet/Chart/DataSeriesValues.php index 0a2f5a8583..d6a8dcca58 100644 --- a/src/PhpSpreadsheet/Chart/DataSeriesValues.php +++ b/src/PhpSpreadsheet/Chart/DataSeriesValues.php @@ -7,7 +7,7 @@ use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; -class DataSeriesValues +class DataSeriesValues extends Properties { const DATASERIES_TYPE_STRING = 'String'; const DATASERIES_TYPE_NUMBER = 'Number'; @@ -45,6 +45,12 @@ class DataSeriesValues */ private $pointMarker; + /** @var ChartColor */ + private $markerFillColor; + + /** @var ChartColor */ + private $markerBorderColor; + /** * Series Point Size. * @@ -69,23 +75,10 @@ class DataSeriesValues /** * Fill color (can be array with colors if dataseries have custom colors). * - * @var null|string|string[] + * @var null|ChartColor|ChartColor[] */ private $fillColor; - /** @var string */ - private $schemeClr = ''; - - /** @var string */ - private $prstClr = ''; - - /** - * Line Width. - * - * @var int - */ - private $lineWidth = 12700; - /** @var bool */ private $scatterLines = true; @@ -101,18 +94,23 @@ class DataSeriesValues * @param int $pointCount * @param mixed $dataValues * @param null|mixed $marker - * @param null|string|string[] $fillColor + * @param null|ChartColor|ChartColor[]|string|string[] $fillColor * @param string $pointSize */ public function __construct($dataType = self::DATASERIES_TYPE_NUMBER, $dataSource = null, $formatCode = null, $pointCount = 0, $dataValues = [], $marker = null, $fillColor = null, $pointSize = '3') { + parent::__construct(); + $this->markerFillColor = new ChartColor(); + $this->markerBorderColor = new ChartColor(); $this->setDataType($dataType); $this->dataSource = $dataSource; $this->formatCode = $formatCode; $this->pointCount = $pointCount; $this->dataValues = $dataValues; $this->pointMarker = $marker; - $this->fillColor = $fillColor; + if ($fillColor !== null) { + $this->setFillColor($fillColor); + } if (is_numeric($pointSize)) { $this->pointSize = (int) $pointSize; } @@ -198,6 +196,16 @@ public function setPointMarker($marker) return $this; } + public function getMarkerFillColor(): ChartColor + { + return $this->markerFillColor; + } + + public function getMarkerBorderColor(): ChartColor + { + return $this->markerBorderColor; + } + /** * Get Point Size. */ @@ -252,37 +260,96 @@ public function getPointCount() return $this->pointCount; } + /** + * Get fill color object. + * + * @return null|ChartColor|ChartColor[] + */ + public function getFillColorObject() + { + return $this->fillColor; + } + + private function stringToChartColor(string $fillString): ChartColor + { + $value = $type = ''; + if (substr($fillString, 0, 1) === '*') { + $type = 'schemeClr'; + $value = substr($fillString, 1); + } elseif (substr($fillString, 0, 1) === '/') { + $type = 'prstClr'; + $value = substr($fillString, 1); + } elseif ($fillString !== '') { + $type = 'srgbClr'; + $value = $fillString; + $this->validateColor($value); + } + + return new ChartColor($value, null, $type); + } + + private function chartColorToString(ChartColor $chartColor): string + { + $type = (string) $chartColor->getColorProperty('type'); + $value = (string) $chartColor->getColorProperty('value'); + if ($type === '' || $value === '') { + return ''; + } + if ($type === 'schemeClr') { + return "*$value"; + } + if ($type === 'prstClr') { + return "/$value"; + } + + return $value; + } + /** * Get fill color. * - * @return null|string|string[] HEX color or array with HEX colors + * @return string|string[] HEX color or array with HEX colors */ public function getFillColor() { - return $this->fillColor; + if ($this->fillColor === null) { + return ''; + } + if (is_array($this->fillColor)) { + $array = []; + foreach ($this->fillColor as $chartColor) { + $array[] = self::chartColorToString($chartColor); + } + + return $array; + } + + return self::chartColorToString($this->fillColor); } /** * Set fill color for series. * - * @param string|string[] $color HEX color or array with HEX colors + * @param ChartColor|ChartColor[]|string|string[] $color HEX color or array with HEX colors * * @return DataSeriesValues */ public function setFillColor($color) { if (is_array($color)) { - foreach ($color as $colorValue) { - if (substr($colorValue, 0, 1) !== '*' && substr($colorValue, 0, 1) !== '/') { - $this->validateColor($colorValue); + $this->fillColor = []; + foreach ($color as $fillString) { + if ($fillString instanceof ChartColor) { + $this->fillColor[] = $fillString; + } else { + $this->fillColor[] = self::stringToChartColor($fillString); } } - } else { - if (substr($color, 0, 1) !== '*' && substr($color, 0, 1) !== '/') { - $this->validateColor("$color"); - } + } elseif ($color instanceof ChartColor) { + $this->fillColor = $color; + } elseif (is_string($color)) { + $this->fillColor = self::stringToChartColor($color); } - $this->fillColor = $color; return $this; } @@ -306,24 +373,23 @@ private function validateColor($color) /** * Get line width for series. * - * @return int + * @return null|float|int */ public function getLineWidth() { - return $this->lineWidth; + return $this->lineStyleProperties['width']; } /** * Set line width for the series. * - * @param int $width + * @param null|float|int $width * * @return $this */ public function setLineWidth($width) { - $minWidth = 12700; - $this->lineWidth = max($minWidth, $width); + $this->lineStyleProperties['width'] = $width; return $this; } @@ -466,26 +532,33 @@ public function setBubble3D(bool $bubble3D): self return $this; } - public function getSchemeClr(): string - { - return $this->schemeClr; - } - - public function setSchemeClr(string $schemeClr): self - { - $this->schemeClr = $schemeClr; - - return $this; - } + /** + * Smooth Line. + * + * @var bool + */ + private $smoothLine; - public function getPrstClr(): string + /** + * Get Smooth Line. + * + * @return bool + */ + public function getSmoothLine() { - return $this->prstClr; + return $this->smoothLine; } - public function setPrstClr(string $prstClr): self + /** + * Set Smooth Line. + * + * @param bool $smoothLine + * + * @return $this + */ + public function setSmoothLine($smoothLine) { - $this->prstClr = $prstClr; + $this->smoothLine = $smoothLine; return $this; } diff --git a/src/PhpSpreadsheet/Chart/Properties.php b/src/PhpSpreadsheet/Chart/Properties.php index a64a826fa7..fdc3c12b3f 100644 --- a/src/PhpSpreadsheet/Chart/Properties.php +++ b/src/PhpSpreadsheet/Chart/Properties.php @@ -59,6 +59,8 @@ abstract class Properties const LINE_STYLE_COMPOUND_TRIPLE = 'tri'; const LINE_STYLE_DASH_SOLID = 'solid'; const LINE_STYLE_DASH_ROUND_DOT = 'sysDot'; + const LINE_STYLE_DASH_SQUARE_DOT = 'sysDash'; + /** @deprecated 1.24 use LINE_STYLE_DASH_SQUARE_DOT instead */ const LINE_STYLE_DASH_SQUERE_DOT = 'sysDash'; const LINE_STYPE_DASH_DASH = 'dash'; const LINE_STYLE_DASH_DASH_DOT = 'dashDot'; @@ -68,7 +70,7 @@ abstract class Properties const LINE_STYLE_CAP_SQUARE = 'sq'; const LINE_STYLE_CAP_ROUND = 'rnd'; const LINE_STYLE_CAP_FLAT = 'flat'; - const LINE_STYLE_JOIN_ROUND = 'bevel'; + const LINE_STYLE_JOIN_ROUND = 'round'; const LINE_STYLE_JOIN_MITER = 'miter'; const LINE_STYLE_JOIN_BEVEL = 'bevel'; const LINE_STYLE_ARROW_TYPE_NOARROW = null; @@ -643,30 +645,6 @@ protected function setShadowPropertiesMapValues(array $propertiesMap, &$referenc return $this; } - /** - * Set Shadow Color. - * - * @param string $color - * @param int $alpha - * @param string $colorType - * - * @return $this - */ - protected function setShadowColor($color, $alpha, $colorType) - { - if ($color !== null) { - $this->shadowProperties['color']['value'] = (string) $color; - } - if ($alpha !== null) { - $this->shadowProperties['color']['alpha'] = (int) $alpha; - } - if ($colorType !== null) { - $this->shadowProperties['color']['type'] = (string) $colorType; - } - - return $this; - } - /** * Set Shadow Blur. * @@ -766,6 +744,12 @@ public function getShadowProperty($elements) ], ]; + public function copyLineStyles(self $otherProperties): void + { + $this->lineStyleProperties = $otherProperties->lineStyleProperties; + $this->lineColor = $otherProperties->lineColor; + } + public function getLineColor(): ChartColor { return $this->lineColor; diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php index 2c060d0727..df25dac78a 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php @@ -14,7 +14,6 @@ use PhpOffice\PhpSpreadsheet\Chart\Properties; use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\RichText\RichText; -use PhpOffice\PhpSpreadsheet\Style\Color; use PhpOffice\PhpSpreadsheet\Style\Font; use SimpleXMLElement; @@ -90,6 +89,7 @@ public function readChart(SimpleXMLElement $chartElements, $chartName) case 'plotArea': $plotAreaLayout = $XaxisLabel = $YaxisLabel = null; $plotSeries = $plotAttributes = []; + $catAxRead = false; foreach ($chartDetails as $chartDetailKey => $chartDetail) { switch ($chartDetailKey) { case 'layout': @@ -97,9 +97,11 @@ public function readChart(SimpleXMLElement $chartElements, $chartName) break; case 'catAx': + $catAxRead = true; if (isset($chartDetail->title)) { $XaxisLabel = $this->chartTitle($chartDetail->title->children($this->cNamespace)); } + $xAxis->setAxisType('catAx'); $this->readEffects($chartDetail, $xAxis); if (isset($chartDetail->spPr)) { $sppr = $chartDetail->spPr->children($this->aNamespace); @@ -122,16 +124,22 @@ public function readChart(SimpleXMLElement $chartElements, $chartName) $axPos = null; if (isset($chartDetail->axPos)) { $axPos = self::getAttribute($chartDetail->axPos, 'val', 'string'); - + } + if ($catAxRead) { + $whichAxis = $yAxis; + $yAxis->setAxisType($chartDetailKey); + } elseif (!empty($axPos)) { switch ($axPos) { case 't': case 'b': $whichAxis = $xAxis; + $xAxis->setAxisType($chartDetailKey); break; case 'r': case 'l': $whichAxis = $yAxis; + $yAxis->setAxisType($chartDetailKey); break; } @@ -373,14 +381,14 @@ private function chartDataSeries(SimpleXMLElement $chartDetail, string $plotType case 'ser': $marker = null; $seriesIndex = ''; - $srgbClr = null; - $lineWidth = null; + $fillColor = null; $pointSize = null; $noFill = false; - $schemeClr = ''; - $prstClr = ''; $bubble3D = false; $dPtColors = []; + $markerFillColor = null; + $markerBorderColor = null; + $lineStyle = null; foreach ($seriesDetails as $seriesKey => $seriesDetail) { switch ($seriesKey) { case 'idx': @@ -399,12 +407,16 @@ private function chartDataSeries(SimpleXMLElement $chartDetail, string $plotType case 'spPr': $children = $seriesDetail->children($this->aNamespace); $ln = $children->ln; - $lineWidth = self::getAttribute($ln, 'w', 'string'); - if (is_countable($ln->noFill) && count($ln->noFill) === 1) { - $noFill = true; + if (isset($children->ln)) { + $ln = $children->ln; + if (is_countable($ln->noFill) && count($ln->noFill) === 1) { + $noFill = true; + } + $lineStyle = new GridLines(); + $this->readLineStyle($seriesDetails, $lineStyle); } if (isset($children->solidFill)) { - $this->readColor($children->solidFill, $srgbClr, $schemeClr, $prstClr); + $fillColor = new ChartColor($this->readColor($children->solidFill)); } break; @@ -414,13 +426,7 @@ private function chartDataSeries(SimpleXMLElement $chartDetail, string $plotType $children = $seriesDetail->spPr->children($this->aNamespace); if (isset($children->solidFill)) { $arrayColors = $this->readColor($children->solidFill); - if ($arrayColors['type'] === 'srgbClr') { - $dptColors[$dptIdx] = $arrayColors['value']; - } elseif ($arrayColors['type'] === 'prstClr') { - $dptColors[$dptIdx] = '/' . $arrayColors['value']; - } else { - $dptColors[$dptIdx] = '*' . $arrayColors['value']; - } + $dptColors[$dptIdx] = new ChartColor($arrayColors); } } @@ -429,10 +435,13 @@ private function chartDataSeries(SimpleXMLElement $chartDetail, string $plotType $marker = self::getAttribute($seriesDetail->symbol, 'val', 'string'); $pointSize = self::getAttribute($seriesDetail->size, 'val', 'string'); $pointSize = is_numeric($pointSize) ? ((int) $pointSize) : null; - if (count($seriesDetail->spPr) === 1) { - $ln = $seriesDetail->spPr->children($this->aNamespace); - if (isset($ln->solidFill)) { - $this->readColor($ln->solidFill, $srgbClr, $schemeClr, $prstClr); + if (isset($seriesDetail->spPr)) { + $children = $seriesDetail->spPr->children($this->aNamespace); + if (isset($children->solidFill)) { + $markerFillColor = $this->readColor($children->solidFill); + } + if (isset($children->ln->solidFill)) { + $markerBorderColor = $this->readColor($children->ln->solidFill); } } @@ -446,19 +455,19 @@ private function chartDataSeries(SimpleXMLElement $chartDetail, string $plotType break; case 'val': - $seriesValues[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", "$srgbClr", "$pointSize"); + $seriesValues[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize"); break; case 'xVal': - $seriesCategory[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", "$srgbClr", "$pointSize"); + $seriesCategory[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize"); break; case 'yVal': - $seriesValues[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", "$srgbClr", "$pointSize"); + $seriesValues[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize"); break; case 'bubbleSize': - $seriesBubbles[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", "$srgbClr", "$pointSize"); + $seriesBubbles[$seriesIndex] = $this->chartDataSeriesValueSet($seriesDetail, "$marker", $fillColor, "$pointSize"); break; case 'bubble3D': @@ -478,58 +487,70 @@ private function chartDataSeries(SimpleXMLElement $chartDetail, string $plotType $seriesValues[$seriesIndex]->setScatterLines(false); } } - if (is_numeric($lineWidth)) { + if ($lineStyle !== null) { if (isset($seriesLabel[$seriesIndex])) { - $seriesLabel[$seriesIndex]->setLineWidth((int) $lineWidth); + $seriesLabel[$seriesIndex]->copyLineStyles($lineStyle); } if (isset($seriesCategory[$seriesIndex])) { - $seriesCategory[$seriesIndex]->setLineWidth((int) $lineWidth); + $seriesCategory[$seriesIndex]->copyLineStyles($lineStyle); } if (isset($seriesValues[$seriesIndex])) { - $seriesValues[$seriesIndex]->setLineWidth((int) $lineWidth); + $seriesValues[$seriesIndex]->copyLineStyles($lineStyle); } } - if ($schemeClr) { + if ($bubble3D) { if (isset($seriesLabel[$seriesIndex])) { - $seriesLabel[$seriesIndex]->setSchemeClr($schemeClr); + $seriesLabel[$seriesIndex]->setBubble3D($bubble3D); } if (isset($seriesCategory[$seriesIndex])) { - $seriesCategory[$seriesIndex]->setSchemeClr($schemeClr); + $seriesCategory[$seriesIndex]->setBubble3D($bubble3D); } if (isset($seriesValues[$seriesIndex])) { - $seriesValues[$seriesIndex]->setSchemeClr($schemeClr); + $seriesValues[$seriesIndex]->setBubble3D($bubble3D); } - } elseif ($prstClr) { + } + if (!empty($dptColors)) { if (isset($seriesLabel[$seriesIndex])) { - $seriesLabel[$seriesIndex]->setPrstClr($prstClr); + $seriesLabel[$seriesIndex]->setFillColor($dptColors); } if (isset($seriesCategory[$seriesIndex])) { - $seriesCategory[$seriesIndex]->setPrstClr($prstClr); + $seriesCategory[$seriesIndex]->setFillColor($dptColors); } if (isset($seriesValues[$seriesIndex])) { - $seriesValues[$seriesIndex]->setPrstClr($prstClr); + $seriesValues[$seriesIndex]->setFillColor($dptColors); } } - if ($bubble3D) { + if ($markerFillColor !== null) { if (isset($seriesLabel[$seriesIndex])) { - $seriesLabel[$seriesIndex]->setBubble3D($bubble3D); + $seriesLabel[$seriesIndex]->getMarkerFillColor()->setColorPropertiesArray($markerFillColor); } if (isset($seriesCategory[$seriesIndex])) { - $seriesCategory[$seriesIndex]->setBubble3D($bubble3D); + $seriesCategory[$seriesIndex]->getMarkerFillColor()->setColorPropertiesArray($markerFillColor); } if (isset($seriesValues[$seriesIndex])) { - $seriesValues[$seriesIndex]->setBubble3D($bubble3D); + $seriesValues[$seriesIndex]->getMarkerFillColor()->setColorPropertiesArray($markerFillColor); } } - if (!empty($dptColors)) { + if ($markerBorderColor !== null) { if (isset($seriesLabel[$seriesIndex])) { - $seriesLabel[$seriesIndex]->setFillColor($dptColors); + $seriesLabel[$seriesIndex]->getMarkerBorderColor()->setColorPropertiesArray($markerBorderColor); } if (isset($seriesCategory[$seriesIndex])) { - $seriesCategory[$seriesIndex]->setFillColor($dptColors); + $seriesCategory[$seriesIndex]->getMarkerBorderColor()->setColorPropertiesArray($markerBorderColor); } if (isset($seriesValues[$seriesIndex])) { - $seriesValues[$seriesIndex]->setFillColor($dptColors); + $seriesValues[$seriesIndex]->getMarkerBorderColor()->setColorPropertiesArray($markerBorderColor); + } + } + if ($smoothLine) { + if (isset($seriesLabel[$seriesIndex])) { + $seriesLabel[$seriesIndex]->setSmoothLine(true); + } + if (isset($seriesCategory[$seriesIndex])) { + $seriesCategory[$seriesIndex]->setSmoothLine(true); + } + if (isset($seriesValues[$seriesIndex])) { + $seriesValues[$seriesIndex]->setSmoothLine(true); } } } @@ -544,11 +565,11 @@ private function chartDataSeries(SimpleXMLElement $chartDetail, string $plotType /** * @return mixed */ - private function chartDataSeriesValueSet(SimpleXMLElement $seriesDetail, ?string $marker = null, ?string $srgbClr = null, ?string $pointSize = null) + private function chartDataSeriesValueSet(SimpleXMLElement $seriesDetail, ?string $marker = null, ?ChartColor $fillColor = null, ?string $pointSize = null) { if (isset($seriesDetail->strRef)) { $seriesSource = (string) $seriesDetail->strRef->f; - $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $srgbClr, "$pointSize"); + $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $fillColor, "$pointSize"); if (isset($seriesDetail->strRef->strCache)) { $seriesData = $this->chartDataSeriesValues($seriesDetail->strRef->strCache->children($this->cNamespace), 's'); @@ -560,7 +581,7 @@ private function chartDataSeriesValueSet(SimpleXMLElement $seriesDetail, ?string return $seriesValues; } elseif (isset($seriesDetail->numRef)) { $seriesSource = (string) $seriesDetail->numRef->f; - $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, $seriesSource, null, 0, null, $marker, $srgbClr, "$pointSize"); + $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, $seriesSource, null, 0, null, $marker, $fillColor, "$pointSize"); if (isset($seriesDetail->numRef->numCache)) { $seriesData = $this->chartDataSeriesValues($seriesDetail->numRef->numCache->children($this->cNamespace)); $seriesValues @@ -571,7 +592,7 @@ private function chartDataSeriesValueSet(SimpleXMLElement $seriesDetail, ?string return $seriesValues; } elseif (isset($seriesDetail->multiLvlStrRef)) { $seriesSource = (string) $seriesDetail->multiLvlStrRef->f; - $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $srgbClr, "$pointSize"); + $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $fillColor, "$pointSize"); if (isset($seriesDetail->multiLvlStrRef->multiLvlStrCache)) { $seriesData = $this->chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlStrRef->multiLvlStrCache->children($this->cNamespace), 's'); @@ -583,7 +604,7 @@ private function chartDataSeriesValueSet(SimpleXMLElement $seriesDetail, ?string return $seriesValues; } elseif (isset($seriesDetail->multiLvlNumRef)) { $seriesSource = (string) $seriesDetail->multiLvlNumRef->f; - $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $srgbClr, "$pointSize"); + $seriesValues = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, $seriesSource, null, 0, null, $marker, $fillColor, "$pointSize"); if (isset($seriesDetail->multiLvlNumRef->multiLvlNumCache)) { $seriesData = $this->chartDataSeriesValuesMultiLevel($seriesDetail->multiLvlNumRef->multiLvlNumCache->children($this->cNamespace), 's'); @@ -698,8 +719,7 @@ private function parseRichText(SimpleXMLElement $titleDetailPart): RichText $defaultLatin = null; $defaultEastAsian = null; $defaultComplexScript = null; - $defaultSrgbColor = ''; - $defaultSchemeColor = ''; + $defaultFontColor = null; if (isset($titleDetailPart->pPr->defRPr)) { /** @var ?int */ $defaultFontSize = self::getAttribute($titleDetailPart->pPr->defRPr, 'sz', 'integer'); @@ -729,7 +749,7 @@ private function parseRichText(SimpleXMLElement $titleDetailPart): RichText $defaultComplexScript = self::getAttribute($titleDetailPart->pPr->defRPr->cs, 'typeface', 'string'); } if (isset($titleDetailPart->pPr->defRPr->solidFill)) { - $this->readColor($titleDetailPart->pPr->defRPr->solidFill, $defaultSrgbColor, $defaultSchemeClr); + $defaultFontColor = $this->readColor($titleDetailPart->pPr->defRPr->solidFill); } } foreach ($titleDetailPart as $titleDetailElementKey => $titleDetailElement) { @@ -755,8 +775,7 @@ private function parseRichText(SimpleXMLElement $titleDetailPart): RichText $latinName = null; $eastAsian = null; $complexScript = null; - $fontSrgbClr = ''; - $fontSchemeClr = ''; + $fontColor = null; $underlineColor = null; if (isset($titleDetailElement->rPr)) { // not used now, not sure it ever was, grandfathering @@ -781,10 +800,8 @@ private function parseRichText(SimpleXMLElement $titleDetailPart): RichText $fontSize = self::getAttribute($titleDetailElement->rPr, 'sz', 'integer'); // not used now, not sure it ever was, grandfathering - /** @var ?string */ - $fontSrgbClr = self::getAttribute($titleDetailElement->rPr, 'color', 'string'); if (isset($titleDetailElement->rPr->solidFill)) { - $this->readColor($titleDetailElement->rPr->solidFill, $fontSrgbClr, $fontSchemeClr); + $fontColor = $this->readColor($titleDetailElement->rPr->solidFill); } /** @var ?bool */ @@ -834,19 +851,15 @@ private function parseRichText(SimpleXMLElement $titleDetailPart): RichText if (is_int($fontSize)) { $objText->getFont()->setSize(floor($fontSize / 100)); $fontFound = true; + } else { + $objText->getFont()->setSize(null, true); } - $fontSrgbClr = $fontSrgbClr ?? $defaultSrgbColor; - if (!empty($fontSrgbClr)) { - $objText->getFont()->setColor(new Color($fontSrgbClr)); + $fontColor = $fontColor ?? $defaultFontColor; + if (!empty($fontColor)) { + $objText->getFont()->setChartColor($fontColor); $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) { @@ -1059,7 +1072,7 @@ private function readEffects(SimpleXMLElement $chartDetail, $chartObject): void 'innerShdw', ]; - private function readColor(SimpleXMLElement $colorXml, ?string &$srgbClr = null, ?string &$schemeClr = null, ?string &$prstClr = null): array + private function readColor(SimpleXMLElement $colorXml): array { $result = [ 'type' => null, @@ -1070,13 +1083,6 @@ private function readColor(SimpleXMLElement $colorXml, ?string &$srgbClr = null, if (isset($colorXml->$type)) { $result['type'] = $type; $result['value'] = self::getAttribute($colorXml->$type, 'val', 'string'); - if ($type === Properties::EXCEL_COLOR_TYPE_ARGB) { - $srgbClr = $result['value']; - } elseif ($type === Properties::EXCEL_COLOR_TYPE_SCHEME) { - $schemeClr = $result['value']; - } elseif ($type === Properties::EXCEL_COLOR_TYPE_STANDARD) { - $prstClr = $result['value']; - } if (isset($colorXml->$type->alpha)) { /** @var string */ $alpha = self::getAttribute($colorXml->$type->alpha, 'val', 'string'); @@ -1092,10 +1098,7 @@ private function readColor(SimpleXMLElement $colorXml, ?string &$srgbClr = null, return $result; } - /** - * @param null|GridLines $chartObject may be extended to include other types - */ - private function readLineStyle(SimpleXMLElement $chartDetail, $chartObject): void + private function readLineStyle(SimpleXMLElement $chartDetail, ?Properties $chartObject): void { if (!isset($chartObject, $chartDetail->spPr)) { return; @@ -1164,6 +1167,13 @@ private function setAxisProperties(SimpleXMLElement $chartDetail, ?Axis $whichAx if (!isset($whichAxis)) { return; } + if (isset($chartDetail->numFmt)) { + $whichAxis->setAxisNumberProperties( + (string) self::getAttribute($chartDetail->numFmt, 'formatCode', 'string'), + null, + (int) self::getAttribute($chartDetail->numFmt, 'sourceLinked', 'int') + ); + } if (isset($chartDetail->crossBetween)) { $whichAxis->setCrossBetween((string) self::getAttribute($chartDetail->crossBetween, 'val', 'string')); } diff --git a/src/PhpSpreadsheet/Style/Font.php b/src/PhpSpreadsheet/Style/Font.php index 4dbe7272f1..19d67563eb 100644 --- a/src/PhpSpreadsheet/Style/Font.php +++ b/src/PhpSpreadsheet/Style/Font.php @@ -21,7 +21,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 */ @@ -41,6 +41,9 @@ class Font extends Supervisor /** @var ?ChartColor */ private $underlineColor; + + /** @var ?ChartColor */ + private $chartColor; // end of chart title items /** @@ -371,7 +374,7 @@ public function getSize() * * @return $this */ - public function setSize($sizeInPoints) + public function setSize($sizeInPoints, bool $nullOk = false) { if (is_string($sizeInPoints) || is_int($sizeInPoints)) { $sizeInPoints = (float) $sizeInPoints; // $pValue = 0 if given string is not numeric @@ -380,7 +383,9 @@ public function setSize($sizeInPoints) // Size must be a positive floating point number // ECMA-376-1:2016, part 1, chapter 18.4.11 sz (Font Size), p. 1536 if (!is_float($sizeInPoints) || !($sizeInPoints > 0)) { - $sizeInPoints = 10.0; + if (!$nullOk || $sizeInPoints !== null) { + $sizeInPoints = 10.0; + } } if ($this->isSupervisor) { @@ -593,8 +598,7 @@ public function getUnderlineColor(): ?ChartColor public function setUnderlineColor(array $colorArray): self { if (!$this->isSupervisor) { - $this->underlineColor = new ChartColor(); - $this->underlineColor->setColorPropertiesArray($colorArray); + $this->underlineColor = new ChartColor($colorArray); } else { // should never be true // @codeCoverageIgnoreStart @@ -606,6 +610,30 @@ public function setUnderlineColor(array $colorArray): self return $this; } + public function getChartColor(): ?ChartColor + { + if ($this->isSupervisor) { + return $this->getSharedComponent()->getChartColor(); + } + + return $this->chartColor; + } + + public function setChartColor(array $colorArray): self + { + if (!$this->isSupervisor) { + $this->chartColor = new ChartColor($colorArray); + } else { + // should never be true + // @codeCoverageIgnoreStart + $styleArray = $this->getStyleArray(['chartColor' => $colorArray]); + $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); + // @codeCoverageIgnoreEnd + } + + return $this; + } + /** * Get Underline. * @@ -713,6 +741,18 @@ public function setColor(Color $color) return $this; } + private function hashChartColor(?ChartColor $underlineColor): string + { + if ($this->underlineColor === null) { + return ''; + } + + return + $this->underlineColor->getValue() + . $this->underlineColor->getType() + . (string) $this->underlineColor->getAlpha(); + } + /** * Get hash code. * @@ -723,14 +763,6 @@ public function getHashCode() if ($this->isSupervisor) { return $this->getSharedComponent()->getHashCode(); } - if ($this->underlineColor === null) { - $underlineColor = ''; - } else { - $underlineColor = - $this->underlineColor->getValue() - . $this->underlineColor->getType() - . (string) $this->underlineColor->getAlpha(); - } return md5( $this->name . @@ -749,7 +781,8 @@ public function getHashCode() $this->eastAsian, $this->complexScript, $this->strikeType, - $underlineColor, + $this->hashChartColor($this->chartColor), + $this->hashChartColor($this->underlineColor), (string) $this->baseLine, ] ) . @@ -762,6 +795,7 @@ protected function exportArray1(): array $exportedArray = []; $this->exportArray2($exportedArray, 'baseLine', $this->getBaseLine()); $this->exportArray2($exportedArray, 'bold', $this->getBold()); + $this->exportArray2($exportedArray, 'chartColor', $this->getChartColor()); $this->exportArray2($exportedArray, 'color', $this->getColor()); $this->exportArray2($exportedArray, 'complexScript', $this->getComplexScript()); $this->exportArray2($exportedArray, 'eastAsian', $this->getEastAsian()); diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php index acc6f3af34..d242e60202 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php @@ -419,7 +419,9 @@ private function writeCategoryAxis(XMLWriter $objWriter, ?Title $xAxisLabel, $id { // N.B. writeCategoryAxis may be invoked with the last parameter($yAxis) using $xAxis for ScatterChart, etc // In that case, xAxis is NOT a category. - if ($yAxis->getAxisIsNumericFormat()) { + if ($yAxis->getAxisType() !== '') { + $objWriter->startElement('c:' . $yAxis->getAxisType()); + } elseif ($yAxis->getAxisIsNumericFormat()) { $objWriter->startElement('c:valAx'); } else { $objWriter->startElement('c:catAx'); @@ -469,10 +471,6 @@ private function writeCategoryAxis(XMLWriter $objWriter, ?Title $xAxisLabel, $id $objWriter->endElement(); $objWriter->startElement('a:p'); - $objWriter->startElement('a:pPr'); - $objWriter->startElement('a:defRPr'); - $objWriter->endElement(); - $objWriter->endElement(); $caption = $xAxisLabel->getCaption(); if (is_array($caption)) { @@ -622,7 +620,7 @@ private function writeValueAxis(XMLWriter $objWriter, ?Title $yAxisLabel, $group $objWriter->startElement('c:majorGridlines'); $objWriter->startElement('c:spPr'); - $this->writeGridlinesLn($objWriter, $majorGridlines); + $this->writeLineStyles($objWriter, $majorGridlines); $objWriter->startElement('a:effectLst'); $this->writeGlow($objWriter, $majorGridlines); @@ -637,7 +635,7 @@ private function writeValueAxis(XMLWriter $objWriter, ?Title $yAxisLabel, $group $objWriter->startElement('c:minorGridlines'); $objWriter->startElement('c:spPr'); - $this->writeGridlinesLn($objWriter, $minorGridlines); + $this->writeLineStyles($objWriter, $minorGridlines); $objWriter->startElement('a:effectLst'); $this->writeGlow($objWriter, $minorGridlines); @@ -661,10 +659,6 @@ private function writeValueAxis(XMLWriter $objWriter, ?Title $yAxisLabel, $group $objWriter->endElement(); $objWriter->startElement('a:p'); - $objWriter->startElement('a:pPr'); - $objWriter->startElement('a:defRPr'); - $objWriter->endElement(); - $objWriter->endElement(); $caption = $yAxisLabel->getCaption(); if (is_array($caption)) { @@ -715,7 +709,7 @@ private function writeValueAxis(XMLWriter $objWriter, ?Title $yAxisLabel, $group $this->writeColor($objWriter, $xAxis->getFillColorObject()); - $this->writeGridlinesLn($objWriter, $xAxis); + $this->writeLineStyles($objWriter, $xAxis); $objWriter->startElement('a:effectLst'); $this->writeGlow($objWriter, $xAxis); @@ -849,40 +843,27 @@ private static function getChartType(PlotArea $plotArea): array /** * Method writing plot series values. - * - * @param int $val value for idx (default: 3) - * @param string $fillColor hex color (default: FF9900) */ - private function writePlotSeriesValuesElement(XMLWriter $objWriter, $val = 3, $fillColor = 'FF9900'): void + private function writePlotSeriesValuesElement(XMLWriter $objWriter, int $val, ?ChartColor $fillColor): void { - if ($fillColor === '') { + if ($fillColor === null || !$fillColor->isUsable()) { return; } $objWriter->startElement('c:dPt'); + $objWriter->startElement('c:idx'); $objWriter->writeAttribute('val', $val); - $objWriter->endElement(); + $objWriter->endElement(); // c:idx $objWriter->startElement('c:bubble3D'); $objWriter->writeAttribute('val', 0); - $objWriter->endElement(); + $objWriter->endElement(); // c:bubble3D $objWriter->startElement('c:spPr'); - $objWriter->startElement('a:solidFill'); - if (substr($fillColor, 0, 1) === '*') { - $objWriter->startElement('a:schemeClr'); - $objWriter->writeAttribute('val', substr($fillColor, 1)); - } elseif (substr($fillColor, 0, 1) === '/') { - $objWriter->startElement('a:prstClr'); - $objWriter->writeAttribute('val', substr($fillColor, 1)); - } else { - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $fillColor); - } - $objWriter->endElement(); - $objWriter->endElement(); - $objWriter->endElement(); - $objWriter->endElement(); + $this->writeColor($objWriter, $fillColor); + $objWriter->endElement(); // c:spPr + + $objWriter->endElement(); // c:dPt } /** @@ -934,20 +915,6 @@ private function writePlotGroup(?DataSeries $plotGroup, string $groupType, XMLWr foreach ($plotSeriesOrder as $plotSeriesIdx => $plotSeriesRef) { $objWriter->startElement('c:ser'); - $plotLabel = $plotGroup->getPlotLabelByIndex($plotSeriesIdx); - if ($plotLabel && $groupType !== DataSeries::TYPE_LINECHART) { - $fillColor = $plotLabel->getFillColor(); - if ($fillColor !== null && !is_array($fillColor)) { - $objWriter->startElement('c:spPr'); - $objWriter->startElement('a:solidFill'); - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $fillColor); - $objWriter->endElement(); - $objWriter->endElement(); - $objWriter->endElement(); - } - } - $objWriter->startElement('c:idx'); $objWriter->writeAttribute('val', $this->seriesIndex + $plotSeriesIdx); $objWriter->endElement(); @@ -956,22 +923,35 @@ private function writePlotGroup(?DataSeries $plotGroup, string $groupType, XMLWr $objWriter->writeAttribute('val', $this->seriesIndex + $plotSeriesRef); $objWriter->endElement(); + $plotLabel = $plotGroup->getPlotLabelByIndex($plotSeriesIdx); + $labelFill = null; + if ($plotLabel && $groupType === DataSeries::TYPE_LINECHART) { + $labelFill = $plotLabel->getFillColorObject(); + $labelFill = ($labelFill instanceof ChartColor) ? $labelFill : null; + } + if ($plotLabel && $groupType !== DataSeries::TYPE_LINECHART) { + $fillColor = $plotLabel->getFillColorObject(); + if ($fillColor !== null && !is_array($fillColor) && $fillColor->isUsable()) { + $objWriter->startElement('c:spPr'); + $this->writeColor($objWriter, $fillColor); + $objWriter->endElement(); // c:spPr + } + } + // Values - $plotSeriesValues = $plotGroup->getPlotValuesByIndex($plotSeriesRef); + $plotSeriesValues = $plotGroup->getPlotValuesByIndex($plotSeriesIdx); - if (($groupType == DataSeries::TYPE_PIECHART) || ($groupType == DataSeries::TYPE_PIECHART_3D) || ($groupType == DataSeries::TYPE_DONUTCHART)) { - $fillColorValues = $plotSeriesValues->getFillColor(); + if ($plotSeriesValues !== false && in_array($groupType, self::CUSTOM_COLOR_TYPES, true)) { + $fillColorValues = $plotSeriesValues->getFillColorObject(); if ($fillColorValues !== null && is_array($fillColorValues)) { foreach ($plotSeriesValues->getDataValues() as $dataKey => $dataValue) { - $this->writePlotSeriesValuesElement($objWriter, $dataKey, $fillColorValues[$dataKey] ?? ''); + $this->writePlotSeriesValuesElement($objWriter, $dataKey, $fillColorValues[$dataKey] ?? null); } - } else { - $this->writePlotSeriesValuesElement($objWriter); } } // Labels - $plotSeriesLabel = $plotGroup->getPlotLabelByIndex($plotSeriesRef); + $plotSeriesLabel = $plotGroup->getPlotLabelByIndex($plotSeriesIdx); if ($plotSeriesLabel && ($plotSeriesLabel->getPointCount() > 0)) { $objWriter->startElement('c:tx'); $objWriter->startElement('c:strRef'); @@ -982,77 +962,54 @@ private function writePlotGroup(?DataSeries $plotGroup, string $groupType, XMLWr // Formatting for the points if ( - $groupType == DataSeries::TYPE_LINECHART - || $groupType == DataSeries::TYPE_STOCKCHART - || ($groupType === DataSeries::TYPE_SCATTERCHART && $plotSeriesValues !== false && !$plotSeriesValues->getScatterLines()) - || ($plotSeriesValues !== false && ($plotSeriesValues->getSchemeClr() || $plotSeriesValues->getPrstClr())) + $plotSeriesValues !== false ) { - $plotLineWidth = 12700; - if ($plotSeriesValues) { - $plotLineWidth = $plotSeriesValues->getLineWidth(); - } - $objWriter->startElement('c:spPr'); - $schemeClr = $typeClr = ''; - if ($plotLabel) { - $schemeClr = $plotLabel->getSchemeClr(); - if ($schemeClr) { - $typeClr = 'schemeClr'; - } else { - $schemeClr = $plotLabel->getPrstClr(); - if ($schemeClr) { - $typeClr = 'prstClr'; - } + $fillObject = $labelFill ?? $plotSeriesValues->getFillColorObject(); + $callLineStyles = true; + if ($fillObject instanceof ChartColor && $fillObject->isUsable()) { + if ($groupType === DataSeries::TYPE_LINECHART) { + $objWriter->startElement('a:ln'); + $callLineStyles = false; } - } - if ($schemeClr) { - $objWriter->startElement('a:solidFill'); - $objWriter->startElement("a:$typeClr"); - $objWriter->writeAttribute('val', $schemeClr); - $objWriter->endElement(); - $objWriter->endElement(); - } - $objWriter->startElement('a:ln'); - $objWriter->writeAttribute('w', $plotLineWidth); - if ($groupType == DataSeries::TYPE_STOCKCHART || $groupType === DataSeries::TYPE_SCATTERCHART) { - $objWriter->startElement('a:noFill'); - $objWriter->endElement(); - } elseif ($plotLabel) { - $fillColor = $plotLabel->getFillColor(); - if (is_string($fillColor)) { - $objWriter->startElement('a:solidFill'); - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $fillColor); - $objWriter->endElement(); - $objWriter->endElement(); + $this->writeColor($objWriter, $fillObject); + if (!$callLineStyles) { + $objWriter->endElement(); // a:ln } } - $objWriter->endElement(); - $objWriter->endElement(); + $nofill = $groupType == DataSeries::TYPE_STOCKCHART || ($groupType === DataSeries::TYPE_SCATTERCHART && !$plotSeriesValues->getScatterLines()); + if ($callLineStyles) { + $this->writeLineStyles($objWriter, $plotSeriesValues, $nofill); + } + $objWriter->endElement(); // c:spPr } if ($plotSeriesValues) { $plotSeriesMarker = $plotSeriesValues->getPointMarker(); - if ($plotSeriesMarker) { + $markerFillColor = $plotSeriesValues->getMarkerFillColor(); + $fillUsed = $markerFillColor->IsUsable(); + $markerBorderColor = $plotSeriesValues->getMarkerBorderColor(); + $borderUsed = $markerBorderColor->isUsable(); + if ($plotSeriesMarker || $fillUsed || $borderUsed) { $objWriter->startElement('c:marker'); $objWriter->startElement('c:symbol'); - $objWriter->writeAttribute('val', $plotSeriesMarker); + if ($plotSeriesMarker) { + $objWriter->writeAttribute('val', $plotSeriesMarker); + } $objWriter->endElement(); if ($plotSeriesMarker !== 'none') { $objWriter->startElement('c:size'); $objWriter->writeAttribute('val', (string) $plotSeriesValues->getPointSize()); - $objWriter->endElement(); - $fillColor = $plotSeriesValues->getFillColor(); - if (is_string($fillColor) && $fillColor !== '') { - $objWriter->startElement('c:spPr'); - $objWriter->startElement('a:solidFill'); - $objWriter->startElement('a:srgbClr'); - $objWriter->writeAttribute('val', $fillColor); - $objWriter->endElement(); // srgbClr - $objWriter->endElement(); // solidFill - $objWriter->endElement(); // spPr + $objWriter->endElement(); // c:size + $objWriter->startElement('c:spPr'); + $this->writeColor($objWriter, $markerFillColor); + if ($borderUsed) { + $objWriter->startElement('a:ln'); + $this->writeColor($objWriter, $markerBorderColor); + $objWriter->endElement(); // a:ln } + $objWriter->endElement(); // spPr } $objWriter->endElement(); @@ -1066,7 +1023,7 @@ private function writePlotGroup(?DataSeries $plotGroup, string $groupType, XMLWr } // Category Labels - $plotSeriesCategory = $plotGroup->getPlotCategoryByIndex($plotSeriesRef); + $plotSeriesCategory = $plotGroup->getPlotCategoryByIndex($plotSeriesIdx); if ($plotSeriesCategory && ($plotSeriesCategory->getPointCount() > 0)) { $catIsMultiLevelSeries = $catIsMultiLevelSeries || $plotSeriesCategory->isMultiLevelSeries(); @@ -1112,7 +1069,7 @@ private function writePlotGroup(?DataSeries $plotGroup, string $groupType, XMLWr $objWriter->endElement(); if ($groupType === DataSeries::TYPE_SCATTERCHART && $plotGroup->getPlotStyle() === 'smoothMarker') { $objWriter->startElement('c:smooth'); - $objWriter->writeAttribute('val', '1'); + $objWriter->writeAttribute('val', $plotSeriesValues->getSmoothLine() ? '1' : '0'); $objWriter->endElement(); } } @@ -1268,6 +1225,14 @@ private function writePlotSeriesValues(?DataSeriesValues $plotSeriesValues, XMLW } } + private const CUSTOM_COLOR_TYPES = [ + DataSeries::TYPE_BARCHART, + DataSeries::TYPE_BARCHART_3D, + DataSeries::TYPE_PIECHART, + DataSeries::TYPE_PIECHART_3D, + DataSeries::TYPE_DONUTCHART, + ]; + /** * Write Bubble Chart Details. */ @@ -1384,8 +1349,8 @@ private function writeAlternateContent(XMLWriter $objWriter): void $objWriter->writeAttribute('xmlns:mc', 'http://schemas.openxmlformats.org/markup-compatibility/2006'); $objWriter->startElement('mc:Choice'); - $objWriter->writeAttribute('xmlns:c14', 'http://schemas.microsoft.com/office/drawing/2007/8/2/chart'); $objWriter->writeAttribute('Requires', 'c14'); + $objWriter->writeAttribute('xmlns:c14', 'http://schemas.microsoft.com/office/drawing/2007/8/2/chart'); $objWriter->startElement('c14:style'); $objWriter->writeAttribute('val', '102'); @@ -1509,12 +1474,7 @@ private function writeSoftEdge(XMLWriter $objWriter, $yAxis): void $objWriter->endElement(); //end softEdge } - /** - * Write Line Style for Gridlines. - * - * @param Axis|GridLines $gridlines - */ - private function writeGridlinesLn(XMLWriter $objWriter, $gridlines): void + private function writeLineStyles(XMLWriter $objWriter, Properties $gridlines, bool $noFill = false): void { $objWriter->startElement('a:ln'); $widthTemp = $gridlines->getLineStyleProperty('width'); @@ -1523,7 +1483,12 @@ private function writeGridlinesLn(XMLWriter $objWriter, $gridlines): void } $this->writeNotEmpty($objWriter, 'cap', $gridlines->getLineStyleProperty('cap')); $this->writeNotEmpty($objWriter, 'cmpd', $gridlines->getLineStyleProperty('compound')); - $this->writeColor($objWriter, $gridlines->getLineColor()); + if ($noFill) { + $objWriter->startElement('a:noFill'); + $objWriter->endElement(); + } else { + $this->writeColor($objWriter, $gridlines->getLineColor()); + } $dash = $gridlines->getLineStyleProperty('dash'); if (!empty($dash)) { @@ -1544,16 +1509,16 @@ private function writeGridlinesLn(XMLWriter $objWriter, $gridlines): void if ($gridlines->getLineStyleProperty(['arrow', 'head', 'type'])) { $objWriter->startElement('a:headEnd'); $objWriter->writeAttribute('type', $gridlines->getLineStyleProperty(['arrow', 'head', 'type'])); - $this->writeNotEmpty($objWriter, 'w', $gridlines->getLineStyleArrowParameters('head', 'w')); - $this->writeNotEmpty($objWriter, 'len', $gridlines->getLineStyleArrowParameters('head', 'len')); + $this->writeNotEmpty($objWriter, 'w', $gridlines->getLineStyleArrowWidth('head')); + $this->writeNotEmpty($objWriter, 'len', $gridlines->getLineStyleArrowLength('head')); $objWriter->endElement(); } if ($gridlines->getLineStyleProperty(['arrow', 'end', 'type'])) { $objWriter->startElement('a:tailEnd'); $objWriter->writeAttribute('type', $gridlines->getLineStyleProperty(['arrow', 'end', 'type'])); - $this->writeNotEmpty($objWriter, 'w', $gridlines->getLineStyleArrowParameters('end', 'w')); - $this->writeNotEmpty($objWriter, 'len', $gridlines->getLineStyleArrowParameters('end', 'len')); + $this->writeNotEmpty($objWriter, 'w', $gridlines->getLineStyleArrowWidth('end')); + $this->writeNotEmpty($objWriter, 'len', $gridlines->getLineStyleArrowLength('end')); $objWriter->endElement(); } $objWriter->endElement(); //end ln diff --git a/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php b/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php index 8a376df420..8b293bc19d 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Cell\DataType; +use PhpOffice\PhpSpreadsheet\Chart\ChartColor; use PhpOffice\PhpSpreadsheet\RichText\RichText; use PhpOffice\PhpSpreadsheet\RichText\Run; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; @@ -198,7 +199,7 @@ public function writeRichText(XMLWriter $objWriter, RichText $richText, $prefix * @param RichText|string $richText text string or Rich text * @param string $prefix Optional Namespace prefix */ - public function writeRichTextForCharts(XMLWriter $objWriter, $richText = null, $prefix = null): void + public function writeRichTextForCharts(XMLWriter $objWriter, $richText = null, $prefix = ''): void { if (!$richText instanceof RichText) { $textRun = $richText; @@ -207,7 +208,7 @@ public function writeRichTextForCharts(XMLWriter $objWriter, $richText = null, $ $run->setFont(null); } - if ($prefix !== null) { + if ($prefix !== '') { $prefix .= ':'; } @@ -249,27 +250,10 @@ public function writeRichTextForCharts(XMLWriter $objWriter, $richText = null, $ } // Color - $objWriter->startElement($prefix . 'solidFill'); - $objWriter->startElement($prefix . 'srgbClr'); - $objWriter->writeAttribute('val', $element->getFont()->getColor()->getRGB()); - $objWriter->endElement(); // srgbClr - $objWriter->endElement(); // solidFill + $this->writeChartTextColor($objWriter, $element->getFont()->getChartColor(), $prefix); // Underscore Color - $underlineColor = $element->getFont()->getUnderlineColor(); - if ($underlineColor !== null) { - $type = $underlineColor->getType(); - $value = $underlineColor->getValue(); - if (!empty($type) && !empty($value)) { - $objWriter->startElement($prefix . 'uFill'); - $objWriter->startElement($prefix . 'solidFill'); - $objWriter->startElement($prefix . $type); - $objWriter->writeAttribute('val', $value); - $objWriter->endElement(); // schemeClr - $objWriter->endElement(); // solidFill - $objWriter->endElement(); // uFill - } - } + $this->writeChartTextColor($objWriter, $element->getFont()->getUnderlineColor(), $prefix, 'uFill'); // fontName if ($element->getFont()->getLatin()) { @@ -300,6 +284,33 @@ public function writeRichTextForCharts(XMLWriter $objWriter, $richText = null, $ } } + private function writeChartTextColor(XMLWriter $objWriter, ?ChartColor $underlineColor, string $prefix, ?string $openTag = ''): void + { + if ($underlineColor !== null) { + $type = $underlineColor->getType(); + $value = $underlineColor->getValue(); + if (!empty($type) && !empty($value)) { + if ($openTag !== '') { + $objWriter->startElement($prefix . $openTag); + } + $objWriter->startElement($prefix . 'solidFill'); + $objWriter->startElement($prefix . $type); + $objWriter->writeAttribute('val', $value); + $alpha = $underlineColor->getAlpha(); + if (is_numeric($alpha)) { + $objWriter->startElement('a:alpha'); + $objWriter->writeAttribute('val', ChartColor::alphaToXml((int) $alpha)); + $objWriter->endElement(); + } + $objWriter->endElement(); // srgbClr/schemeClr/prstClr + $objWriter->endElement(); // solidFill + if ($openTag !== '') { + $objWriter->endElement(); // uFill + } + } + } + } + /** * Flip string table (for index searching). * diff --git a/tests/PhpSpreadsheetTests/Chart/AxisGlowTest.php b/tests/PhpSpreadsheetTests/Chart/AxisGlowTest.php index ad7fc77649..0ed4a1b4ca 100644 --- a/tests/PhpSpreadsheetTests/Chart/AxisGlowTest.php +++ b/tests/PhpSpreadsheetTests/Chart/AxisGlowTest.php @@ -119,6 +119,7 @@ public function testGlowY(): void $xAxis->setSoftEdges($softEdgesX); self::assertEquals($yGlowSize, $yAxis->getGlowProperty('size')); self::assertEquals($expectedGlowColor, $yAxis->getGlowProperty('color')); + self::assertSame($expectedGlowColor['value'], $yAxis->getGlowProperty(['color', 'value'])); self::assertEquals($softEdgesY, $yAxis->getSoftEdgesSize()); self::assertEquals($softEdgesX, $xAxis->getSoftEdgesSize()); diff --git a/tests/PhpSpreadsheetTests/Chart/BarChartCustomColorsTest.php b/tests/PhpSpreadsheetTests/Chart/BarChartCustomColorsTest.php new file mode 100644 index 0000000000..824e9600d8 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Chart/BarChartCustomColorsTest.php @@ -0,0 +1,162 @@ +setIncludeCharts(true); + } + + public function writeCharts(XlsxWriter $writer): void + { + $writer->setIncludeCharts(true); + } + + public function testBarchartColor(): 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], + ] + ); + // Custom colors for dataSeries (gray, blue, red, orange) + $colors = [ + 'cccccc', + '*accent1', // use schemeClr, was '00abb8', + '/green', // use prstClr, was 'b8292f', + 'eb8500', + ]; + + // 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 + $dataSeriesLabels1 = [ + new DataSeriesValues( + DataSeriesValues::DATASERIES_TYPE_STRING, + 'Worksheet!$C$1', + null, + 1 + ), // 2011 + ]; + // Set the X-Axis Labels + // Datatype + // Cell reference for data + // Format Code + // Number of datapoints in series + // Data values + // Data Marker + $xAxisTickValues1 = [ + 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 + // Custom Colors + $dataSeriesValues1Element = new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', null, 4); + $dataSeriesValues1Element->setFillColor($colors); + $dataSeriesValues1 = [$dataSeriesValues1Element]; + + // Build the dataseries + $series1 = new DataSeries( + DataSeries::TYPE_PIECHART, // plotType + null, // plotGrouping (Pie charts don't have any grouping) + range(0, count($dataSeriesValues1) - 1), // plotOrder + $dataSeriesLabels1, // plotLabel + $xAxisTickValues1, // plotCategory + $dataSeriesValues1 // plotValues + ); + + // Set up a layout object for the Pie chart + $layout1 = new Layout(); + $layout1->setShowVal(true); + $layout1->setShowPercent(true); + + // Set the series in the plot area + $plotArea1 = new PlotArea($layout1, [$series1]); + // Set the chart legend + $legend1 = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false); + + $title1 = new Title('Test Pie Chart'); + + // Create the chart + $chart1 = new Chart( + 'chart1', // name + $title1, // title + $legend1, // legend + $plotArea1, // plotArea + true, // plotVisibleOnly + DataSeries::EMPTY_AS_GAP, // displayBlanksAs + null, // xAxisLabel + null // no Y-Axis for Pie Chart + ); + + // Set the position where the chart should appear in the worksheet + $chart1->setTopLeftPosition('A7'); + $chart1->setBottomRightPosition('H20'); + + // Add the chart to the worksheet + $worksheet->addChart($chart1); + + /** @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); + $plotArea2 = $chart2->getPlotArea(); + $dataSeries2 = $plotArea2->getPlotGroup(); + self::assertCount(1, $dataSeries2); + $plotValues = $dataSeries2[0]->getPlotValues(); + self::assertCount(1, $plotValues); + $fillColors = $plotValues[0]->getFillColor(); + self::assertSame($colors, $fillColors); + + $writer = new XlsxWriter($reloadedSpreadsheet); + $writer->setIncludeCharts(true); + $writerChart = new XlsxWriter\Chart($writer); + $data = $writerChart->writeChart($chart2); + self::assertSame(1, substr_count($data, '')); + self::assertSame(1, substr_count($data, '')); + self::assertSame(1, substr_count($data, '')); + self::assertSame(1, substr_count($data, '')); + self::assertSame(4, substr_count($data, '')); + + $reloadedSpreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/PhpSpreadsheetTests/Chart/Charts32ColoredAxisLabelTest.php b/tests/PhpSpreadsheetTests/Chart/Charts32ColoredAxisLabelTest.php index 71cda504c4..b8041623ba 100644 --- a/tests/PhpSpreadsheetTests/Chart/Charts32ColoredAxisLabelTest.php +++ b/tests/PhpSpreadsheetTests/Chart/Charts32ColoredAxisLabelTest.php @@ -58,7 +58,10 @@ public function testStock5(): void self::assertInstanceOf(Run::class, $run); $font = $run->getFont(); self::assertInstanceOf(Font::class, $font); - self::assertSame('00B050', $font->getColor()->getRGB()); + $chartColor = $font->getChartColor(); + self::assertNotNull($chartColor); + self::assertSame('00B050', $chartColor->getValue()); + self::assertSame('srgbClr', $chartColor->getType()); $yAxisLabel = $chart->getYAxisLabel(); $captionArray = $yAxisLabel->getCaption(); @@ -73,7 +76,10 @@ public function testStock5(): void self::assertInstanceOf(Run::class, $run); $font = $run->getFont(); self::assertInstanceOf(Font::class, $font); - self::assertSame('FF0000', $font->getColor()->getRGB()); + $chartColor = $font->getChartColor(); + self::assertNotNull($chartColor); + self::assertSame('FF0000', $chartColor->getValue()); + self::assertSame('srgbClr', $chartColor->getType()); $reloadedSpreadsheet->disconnectWorksheets(); } diff --git a/tests/PhpSpreadsheetTests/Chart/Charts32ScatterTest.php b/tests/PhpSpreadsheetTests/Chart/Charts32ScatterTest.php index d79cba6f65..38884eaf5f 100644 --- a/tests/PhpSpreadsheetTests/Chart/Charts32ScatterTest.php +++ b/tests/PhpSpreadsheetTests/Chart/Charts32ScatterTest.php @@ -2,6 +2,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Chart; +use PhpOffice\PhpSpreadsheet\Chart\Properties; use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader; use PhpOffice\PhpSpreadsheet\RichText\RichText; use PhpOffice\PhpSpreadsheet\RichText\Run; @@ -65,7 +66,10 @@ public function testScatter1(): void self::assertFalse($font->getSubscript()); self::assertFalse($font->getStrikethrough()); self::assertSame('none', $font->getUnderline()); - self::assertSame('000000', $font->getColor()->getRGB()); + $chartColor = $font->getChartColor(); + self::assertNotNull($chartColor); + self::assertSame('000000', $chartColor->getValue()); + self::assertSame('srgbClr', $chartColor->getType()); $plotArea = $chart->getPlotArea(); $plotSeries = $plotArea->getPlotGroup(); @@ -75,19 +79,22 @@ public function testScatter1(): void self::assertCount(3, $plotValues); $values = $plotValues[0]; self::assertFalse($values->getScatterLines()); - self::assertSame(28575, $values->getLineWidth()); + self::assertSame(28575 / Properties::POINTS_WIDTH_MULTIPLIER, $values->getLineWidth()); self::assertSame(3, $values->getPointSize()); self::assertSame('', $values->getFillColor()); $values = $plotValues[1]; self::assertFalse($values->getScatterLines()); - self::assertSame(28575, $values->getLineWidth()); + self::assertSame(28575 / Properties::POINTS_WIDTH_MULTIPLIER, $values->getLineWidth()); self::assertSame(3, $values->getPointSize()); self::assertSame('', $values->getFillColor()); $values = $plotValues[2]; self::assertFalse($values->getScatterLines()); - self::assertSame(28575, $values->getLineWidth()); + self::assertSame(28575 / Properties::POINTS_WIDTH_MULTIPLIER, $values->getLineWidth()); self::assertSame(7, $values->getPointSize()); - self::assertSame('FFFF00', $values->getFillColor()); + // Had been testing for Fill Color, but we actually + // meant to test for marker color, which is now distinct. + self::assertSame('FFFF00', $values->getMarkerFillColor()->getValue()); + self::assertSame('srgbClr', $values->getMarkerFillColor()->getType()); $reloadedSpreadsheet->disconnectWorksheets(); } @@ -135,7 +142,10 @@ public function testScatter6(): void self::assertFalse($font->getSubscript()); self::assertFalse($font->getStrikethrough()); self::assertSame('none', $font->getUnderline()); - self::assertSame('000000', $font->getColor()->getRGB()); + $chartColor = $font->getChartColor(); + self::assertNotNull($chartColor); + self::assertSame('000000', $chartColor->getValue()); + self::assertSame('srgbClr', $chartColor->getType()); $run = $elements[1]; self::assertInstanceOf(Run::class, $run); @@ -149,7 +159,10 @@ public function testScatter6(): void self::assertFalse($font->getSubscript()); self::assertFalse($font->getStrikethrough()); self::assertSame('single', $font->getUnderline()); - self::assertSame('00B0F0', $font->getColor()->getRGB()); + $chartColor = $font->getChartColor(); + self::assertNotNull($chartColor); + self::assertSame('00B0F0', $chartColor->getValue()); + self::assertSame('srgbClr', $chartColor->getType()); $run = $elements[2]; self::assertInstanceOf(Run::class, $run); @@ -163,7 +176,10 @@ public function testScatter6(): void self::assertFalse($font->getSubscript()); self::assertFalse($font->getStrikethrough()); self::assertSame('none', $font->getUnderline()); - self::assertSame('000000', $font->getColor()->getRGB()); + $chartColor = $font->getChartColor(); + self::assertNotNull($chartColor); + self::assertSame('000000', $chartColor->getValue()); + self::assertSame('srgbClr', $chartColor->getType()); $plotArea = $chart->getPlotArea(); $plotSeries = $plotArea->getPlotGroup(); @@ -173,19 +189,22 @@ public function testScatter6(): void self::assertCount(3, $plotValues); $values = $plotValues[0]; self::assertFalse($values->getScatterLines()); - self::assertSame(28575, $values->getLineWidth()); + self::assertSame(28575 / Properties::POINTS_WIDTH_MULTIPLIER, $values->getLineWidth()); self::assertSame(3, $values->getPointSize()); self::assertSame('', $values->getFillColor()); $values = $plotValues[1]; self::assertFalse($values->getScatterLines()); - self::assertSame(28575, $values->getLineWidth()); + self::assertSame(28575 / Properties::POINTS_WIDTH_MULTIPLIER, $values->getLineWidth()); self::assertSame(3, $values->getPointSize()); self::assertSame('', $values->getFillColor()); $values = $plotValues[2]; self::assertFalse($values->getScatterLines()); - self::assertSame(28575, $values->getLineWidth()); + self::assertSame(28575 / Properties::POINTS_WIDTH_MULTIPLIER, $values->getLineWidth()); self::assertSame(7, $values->getPointSize()); - self::assertSame('FFFF00', $values->getFillColor()); + // Had been testing for Fill Color, but we actually + // meant to test for marker color, which is now distinct. + self::assertSame('FFFF00', $values->getMarkerFillColor()->getValue()); + self::assertSame('srgbClr', $values->getMarkerFillColor()->getType()); $reloadedSpreadsheet->disconnectWorksheets(); } @@ -232,7 +251,10 @@ public function testScatter3(): void self::assertFalse($font->getSubscript()); self::assertFalse($font->getStrikethrough()); self::assertSame('none', $font->getUnderline()); - self::assertSame('000000', $font->getColor()->getRGB()); + $chartColor = $font->getChartColor(); + self::assertNotNull($chartColor); + self::assertSame('000000', $chartColor->getValue()); + self::assertSame('srgbClr', $chartColor->getType()); $plotArea = $chart->getPlotArea(); $plotSeries = $plotArea->getPlotGroup(); @@ -242,17 +264,19 @@ public function testScatter3(): void self::assertCount(3, $plotValues); $values = $plotValues[0]; self::assertTrue($values->getScatterLines()); - self::assertSame(12700, $values->getLineWidth()); + // the default value of 1 point is no longer written out + // when not explicitly specified. + self::assertNull($values->getLineWidth()); self::assertSame(3, $values->getPointSize()); self::assertSame('', $values->getFillColor()); $values = $plotValues[1]; self::assertTrue($values->getScatterLines()); - self::assertSame(12700, $values->getLineWidth()); + self::assertNull($values->getLineWidth()); self::assertSame(3, $values->getPointSize()); self::assertSame('', $values->getFillColor()); $values = $plotValues[2]; self::assertTrue($values->getScatterLines()); - self::assertSame(12700, $values->getLineWidth()); + self::assertNull($values->getLineWidth()); self::assertSame(3, $values->getPointSize()); self::assertSame('', $values->getFillColor()); @@ -303,7 +327,10 @@ public function testScatter7(): void self::assertFalse($font->getSubscript()); self::assertFalse($font->getStrikethrough()); self::assertSame('none', $font->getUnderline()); - self::assertSame('000000', $font->getColor()->getRGB()); + $chartColor = $font->getChartColor(); + self::assertNotNull($chartColor); + self::assertSame('000000', $chartColor->getValue()); + self::assertSame('srgbClr', $chartColor->getType()); } $plotArea = $chart->getPlotArea(); @@ -314,19 +341,97 @@ public function testScatter7(): void self::assertCount(3, $plotValues); $values = $plotValues[0]; self::assertFalse($values->getScatterLines()); - self::assertSame(28575, $values->getLineWidth()); + self::assertSame(28575 / Properties::POINTS_WIDTH_MULTIPLIER, $values->getLineWidth()); self::assertSame(3, $values->getPointSize()); self::assertSame('', $values->getFillColor()); $values = $plotValues[1]; self::assertFalse($values->getScatterLines()); - self::assertSame(28575, $values->getLineWidth()); + self::assertSame(28575 / Properties::POINTS_WIDTH_MULTIPLIER, $values->getLineWidth()); self::assertSame(3, $values->getPointSize()); self::assertSame('', $values->getFillColor()); $values = $plotValues[2]; self::assertFalse($values->getScatterLines()); - self::assertSame(28575, $values->getLineWidth()); + self::assertSame(28575 / Properties::POINTS_WIDTH_MULTIPLIER, $values->getLineWidth()); self::assertSame(7, $values->getPointSize()); - self::assertSame('FFFF00', $values->getFillColor()); + // Had been testing for Fill Color, but we actually + // meant to test for marker color, which is now distinct. + self::assertSame('FFFF00', $values->getMarkerFillColor()->getValue()); + self::assertSame('srgbClr', $values->getMarkerFillColor()->getType()); + + $reloadedSpreadsheet->disconnectWorksheets(); + } + + public function testScatter8(): void + { + $file = self::DIRECTORY . '32readwriteScatterChart8.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(); + self::assertSame('Worksheet', $sheet->getTitle()); + $charts = $sheet->getChartCollection(); + self::assertCount(1, $charts); + $chart = $charts[0]; + self::assertNotNull($chart); + + $plotArea = $chart->getPlotArea(); + $plotSeries = $plotArea->getPlotGroup(); + self::assertCount(1, $plotSeries); + $dataSeries = $plotSeries[0]; + $plotValues = $dataSeries->getPlotValues(); + self::assertCount(3, $plotValues); + $values = $plotValues[0]; + self::assertSame(31750 / Properties::POINTS_WIDTH_MULTIPLIER, $values->getLineWidth()); + + self::assertSame('sq', $values->getLineStyleProperty('cap')); + self::assertSame('tri', $values->getLineStyleProperty('compound')); + self::assertSame('sysDash', $values->getLineStyleProperty('dash')); + self::assertSame('miter', $values->getLineStyleProperty('join')); + self::assertSame('arrow', $values->getLineStyleProperty(['arrow', 'head', 'type'])); + self::assertSame('med', $values->getLineStyleProperty(['arrow', 'head', 'w'])); + self::assertSame('sm', $values->getLineStyleProperty(['arrow', 'head', 'len'])); + self::assertSame('triangle', $values->getLineStyleProperty(['arrow', 'end', 'type'])); + self::assertSame('med', $values->getLineStyleProperty(['arrow', 'end', 'w'])); + self::assertSame('lg', $values->getLineStyleProperty(['arrow', 'end', 'len'])); + self::assertSame('accent1', $values->getLineColorProperty('value')); + self::assertSame('schemeClr', $values->getLineColorProperty('type')); + self::assertSame(40, $values->getLineColorProperty('alpha')); + self::assertSame('', $values->getFillColor()); + + self::assertSame(7, $values->getPointSize()); + self::assertSame('diamond', $values->getPointMarker()); + self::assertSame('0070C0', $values->getMarkerFillColor()->getValue()); + self::assertSame('srgbClr', $values->getMarkerFillColor()->getType()); + self::assertSame('002060', $values->getMarkerBorderColor()->getValue()); + self::assertSame('srgbClr', $values->getMarkerBorderColor()->getType()); + + $values = $plotValues[1]; + self::assertSame(7, $values->getPointSize()); + self::assertSame('square', $values->getPointMarker()); + self::assertSame('accent6', $values->getMarkerFillColor()->getValue()); + self::assertSame('schemeClr', $values->getMarkerFillColor()->getType()); + self::assertSame(3, $values->getMarkerFillColor()->getAlpha()); + self::assertSame('0FF000', $values->getMarkerBorderColor()->getValue()); + self::assertSame('srgbClr', $values->getMarkerBorderColor()->getType()); + self::assertNull($values->getMarkerBorderColor()->getAlpha()); + + $values = $plotValues[2]; + self::assertSame(7, $values->getPointSize()); + self::assertSame('triangle', $values->getPointMarker()); + self::assertSame('FFFF00', $values->getMarkerFillColor()->getValue()); + self::assertSame('srgbClr', $values->getMarkerFillColor()->getType()); + self::assertNull($values->getMarkerFillColor()->getAlpha()); + self::assertSame('accent4', $values->getMarkerBorderColor()->getValue()); + self::assertSame('schemeClr', $values->getMarkerBorderColor()->getType()); $reloadedSpreadsheet->disconnectWorksheets(); } diff --git a/tests/PhpSpreadsheetTests/Chart/Charts32XmlTest.php b/tests/PhpSpreadsheetTests/Chart/Charts32XmlTest.php index 3123278f00..6a4673fd76 100644 --- a/tests/PhpSpreadsheetTests/Chart/Charts32XmlTest.php +++ b/tests/PhpSpreadsheetTests/Chart/Charts32XmlTest.php @@ -84,12 +84,16 @@ public function testCatAxValAx(?bool $numeric): void $chart = $charts[0]; self::assertNotNull($chart); $xAxis = $chart->getChartAxisX(); + $yAxis = $chart->getChartAxisY(); self::assertSame(Properties::FORMAT_CODE_GENERAL, $xAxis->getAxisNumberFormat()); if (is_bool($numeric)) { $xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_GENERAL, true); } - $yAxis = $chart->getChartAxisY(); + self::assertSame('valAx', $yAxis->getAxisType()); + self::assertSame('valAx', $xAxis->getAxisType()); self::assertSame(Properties::FORMAT_CODE_GENERAL, $yAxis->getAxisNumberFormat()); + $xAxis->setAxisType(''); + $yAxis->setAxisType(''); if (is_bool($numeric)) { $xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_GENERAL, $numeric); $yAxis->setAxisNumberProperties(Properties::FORMAT_CODE_GENERAL, $numeric); @@ -119,6 +123,34 @@ public function providerCatAxValAx(): array ]; } + public function testCatAxValAxFromRead(): void + { + $file = self::DIRECTORY . '32readwriteScatterChart1.xlsx'; + $reader = new XlsxReader(); + $reader->setIncludeCharts(true); + $spreadsheet = $reader->load($file); + $sheet = $spreadsheet->getActiveSheet(); + $charts = $sheet->getChartCollection(); + self::assertCount(1, $charts); + $chart = $charts[0]; + self::assertNotNull($chart); + $xAxis = $chart->getChartAxisX(); + $yAxis = $chart->getChartAxisY(); + self::assertSame(Properties::FORMAT_CODE_GENERAL, $xAxis->getAxisNumberFormat()); + self::assertSame('valAx', $yAxis->getAxisType()); + self::assertSame('valAx', $xAxis->getAxisType()); + self::assertSame(Properties::FORMAT_CODE_GENERAL, $yAxis->getAxisNumberFormat()); + + $writer = new XlsxWriter($spreadsheet); + $writer->setIncludeCharts(true); + $writerChart = new XlsxWriter\Chart($writer); + $data = $writerChart->writeChart($chart); + $spreadsheet->disconnectWorksheets(); + + self::assertSame(0, substr_count($data, '')); + self::assertSame(2, substr_count($data, '')); + } + public function testAreaPrstClr(): void { $file = self::DIRECTORY . '32readwriteAreaChart4.xlsx'; diff --git a/tests/PhpSpreadsheetTests/Chart/ColorTest.php b/tests/PhpSpreadsheetTests/Chart/ColorTest.php new file mode 100644 index 0000000000..e8326e5237 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Chart/ColorTest.php @@ -0,0 +1,32 @@ +getType()); + self::assertSame('800000', $color->getValue()); + $color->setColorProperties('*accent1'); + self::assertSame('schemeClr', $color->getType()); + self::assertSame('accent1', $color->getValue()); + $color->setColorProperties('/red'); + self::assertSame('prstClr', $color->getType()); + self::assertSame('red', $color->getValue()); + } + + public function testDataSeriesValues(): void + { + $dsv = new DataSeriesValues(); + $dsv->setFillColor([new ChartColor(), new ChartColor()]); + self::assertSame(['', ''], $dsv->getFillColor()); + $dsv->setFillColor('cccccc'); + self::assertSame('cccccc', $dsv->getFillColor()); + } +} diff --git a/tests/PhpSpreadsheetTests/Chart/DataSeriesValues2Test.php b/tests/PhpSpreadsheetTests/Chart/DataSeriesValues2Test.php new file mode 100644 index 0000000000..a27397f9c4 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Chart/DataSeriesValues2Test.php @@ -0,0 +1,172 @@ +setIncludeCharts(true); + } + + public function writeCharts(XlsxWriter $writer): void + { + $writer->setIncludeCharts(true); + } + + public function testDataSeriesValues(): 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( + null, // plotType + null, // plotGrouping + range(0, count($dataSeriesValues) - 1), // plotOrder + $dataSeriesLabels, // plotLabel + $xAxisTickValues, // plotCategory + $dataSeriesValues // plotValues + ); + self::assertEmpty($series->getPlotType()); + self::assertEmpty($series->getPlotGrouping()); + self::assertFalse($series->getSmoothLine()); + $series->setPlotType(DataSeries::TYPE_AREACHART); + $series->setPlotGrouping(DataSeries::GROUPING_PERCENT_STACKED); + $series->setSmoothLine(true); + self::assertSame(DataSeries::TYPE_AREACHART, $series->getPlotType()); + self::assertSame(DataSeries::GROUPING_PERCENT_STACKED, $series->getPlotGrouping()); + self::assertTrue($series->getSmoothLine()); + + // 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 + ); + + // 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); + + self::assertSame(1, $chart->getPlotArea()->getPlotGroupCount()); + $plotValues = $chart->getPlotArea()->getPlotGroup()[0]->getPlotValues(); + self::assertCount(3, $plotValues); + self::assertSame([], $plotValues[1]->getDataValues()); + self::assertNull($plotValues[1]->getDataValue()); + + /** @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); + $plotValues2 = $chart2->getPlotArea()->getPlotGroup()[0]->getPlotValues(); + self::assertCount(3, $plotValues2); + self::assertSame([15.0, 73.0, 61.0, 32.0], $plotValues2[1]->getDataValues()); + self::assertSame([15.0, 73.0, 61.0, 32.0], $plotValues2[1]->getDataValue()); + $labels2 = $chart->getPlotArea()->getPlotGroup()[0]->getPlotLabels(); + self::assertCount(3, $labels2); + self::assertSame(2010, $labels2[0]->getDataValue()); + $dataSeries = $chart->getPlotArea()->getPlotGroup()[0]; + self::assertFalse($dataSeries->getPlotValuesByIndex(99)); + self::assertNotFalse($dataSeries->getPlotValuesByIndex(0)); + self::assertSame([12, 56, 52, 30], $dataSeries->getPlotValuesByIndex(0)->getDataValues()); + self::assertSame(DataSeries::TYPE_AREACHART, $dataSeries->getPlotType()); + self::assertSame(DataSeries::GROUPING_PERCENT_STACKED, $dataSeries->getPlotGrouping()); + self::assertTrue($dataSeries->getSmoothLine()); + + $reloadedSpreadsheet->disconnectWorksheets(); + } + + public function testSomeProperties(): void + { + $dataSeriesValues = new DataSeriesValues(); + self::assertNull($dataSeriesValues->getDataSource()); + self::assertEmpty($dataSeriesValues->getPointMarker()); + self::assertSame(3, $dataSeriesValues->getPointSize()); + $dataSeriesValues->setDataSource('Worksheet!$B$1') + ->setPointMarker('square') + ->setPointSize(6); + self::assertSame('Worksheet!$B$1', $dataSeriesValues->getDataSource()); + self::assertSame('square', $dataSeriesValues->getPointMarker()); + self::assertSame(6, $dataSeriesValues->getPointSize()); + } +} diff --git a/tests/PhpSpreadsheetTests/Chart/DataSeriesValuesTest.php b/tests/PhpSpreadsheetTests/Chart/DataSeriesValuesTest.php index c34ca697ff..47c2fb8956 100644 --- a/tests/PhpSpreadsheetTests/Chart/DataSeriesValuesTest.php +++ b/tests/PhpSpreadsheetTests/Chart/DataSeriesValuesTest.php @@ -3,6 +3,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Chart; use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues; +use PhpOffice\PhpSpreadsheet\Chart\Properties; use PhpOffice\PhpSpreadsheet\Exception; use PHPUnit\Framework\TestCase; @@ -51,13 +52,14 @@ public function testGetDataType(): void public function testGetLineWidth(): void { $testInstance = new DataSeriesValues(); - self::assertEquals(12700, $testInstance->getLineWidth(), 'should have default'); + // default has changed to null from 1 point (12700) + self::assertNull($testInstance->getLineWidth(), 'should have default'); - $testInstance->setLineWidth(40000); - self::assertEquals(40000, $testInstance->getLineWidth()); + $testInstance->setLineWidth(40000 / Properties::POINTS_WIDTH_MULTIPLIER); + self::assertEquals(40000 / Properties::POINTS_WIDTH_MULTIPLIER, $testInstance->getLineWidth()); $testInstance->setLineWidth(1); - self::assertEquals(12700, $testInstance->getLineWidth(), 'should enforce minimum width'); + self::assertEquals(12700 / Properties::POINTS_WIDTH_MULTIPLIER, $testInstance->getLineWidth(), 'should enforce minimum width'); } public function testFillColorCorrectInput(): void diff --git a/tests/PhpSpreadsheetTests/Writer/Xlsx/Issue589Test.php b/tests/PhpSpreadsheetTests/Chart/Issue589Test.php similarity index 96% rename from tests/PhpSpreadsheetTests/Writer/Xlsx/Issue589Test.php rename to tests/PhpSpreadsheetTests/Chart/Issue589Test.php index 82478fb93d..747cba7485 100644 --- a/tests/PhpSpreadsheetTests/Writer/Xlsx/Issue589Test.php +++ b/tests/PhpSpreadsheetTests/Chart/Issue589Test.php @@ -1,6 +1,6 @@ ', $actualXml); + self::assertXmlStringEqualsXmlString('', $actualXml); } } } @@ -153,7 +153,7 @@ public function testLineChartFillIgnoresColorArray(): void if ($actualXml === false) { self::fail('Failure saving the spPr element as xml string!'); } else { - self::assertXmlStringEqualsXmlString('', $actualXml); + self::assertXmlStringEqualsXmlString('', $actualXml); } } } diff --git a/tests/PhpSpreadsheetTests/Chart/ShadowPresetsTest.php b/tests/PhpSpreadsheetTests/Chart/ShadowPresetsTest.php index 58c024c1c7..e96d6c14d2 100644 --- a/tests/PhpSpreadsheetTests/Chart/ShadowPresetsTest.php +++ b/tests/PhpSpreadsheetTests/Chart/ShadowPresetsTest.php @@ -123,6 +123,35 @@ public function testGridlinesShadowPresetsWithChanges(): void } } + public function testPreset0(): void + { + $axis = new Axis(); + $axis->setShadowProperties(0); + $expectedShadow = [ + 'presets' => Properties::SHADOW_PRESETS_NOSHADOW, + 'effect' => null, + 'color' => [ + 'type' => Properties::EXCEL_COLOR_TYPE_STANDARD, + 'value' => 'black', + 'alpha' => 40, + ], + 'size' => [ + 'sx' => null, + 'sy' => null, + 'kx' => null, + 'ky' => null, + ], + 'blur' => null, + 'direction' => null, + 'distance' => null, + 'algn' => null, + 'rotWithShape' => null, + ]; + foreach ($expectedShadow as $key => $value) { + self::assertEquals($value, $axis->getShadowProperty($key), $key); + } + } + public function testOutOfRangePresets(): void { $axis = new Axis();