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 | 171 ++++++++++---- src/PhpSpreadsheet/Chart/Properties.php | 34 +-- src/PhpSpreadsheet/Reader/Xlsx/Chart.php | 178 ++++++++------- src/PhpSpreadsheet/Style/Font.php | 62 +++-- src/PhpSpreadsheet/Writer/Xlsx/Chart.php | 213 ++++++++---------- .../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, 1279 insertions(+), 385 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 9148425d..b7d55bbe 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 d1f2a03b..858502fd 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 00000000..75f2306b --- /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 fee2a284..8cb87e30 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\DataSeries; 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 @@ $dataSeriesLabels = [ 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 @@ $dataSeriesValues = [ 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 1d6e331c..ef6353bb 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 @@ $dataSeriesValues = [ 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 @@ $series = new DataSeries( $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 @@ $chart = new Chart( $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 z`uG;z8xE+@BNg>wp}yy#4&e05w}PrhS#l!+sJ(3!OsNf8qNH{8MG?3x?()9x+T$xM zQ9MEJ_w)n_Ci6EA*C;TOU4X7gf+&gz;;^QTzL_lp{m=LRargf)VgGXL#nDplI~W1} z$6^lwz1NeAk*ESdM}DzJ5_u0V@dcEcuxxU?#a1eORC!!qNKwybkB9#GMeeY@UXrUd zmf{dJ3@)--#}fa9J8OGb8cLgZ5$odB&KC}omy=gXq7u%p9GW9&ioWJ&0{fQ9M8;2r zDo{u0m2lzFbMgJLc#^zTx}`oY>D-ipPYTE#miU*~v1jbXjHGx@$A8&H;tk}KJQz#D z?6uV~oGo?hH6yva#Z{0q;xsPT`E2u+)LB!{tnFMVr4{{$+iU66-VdZKXg6<0M0+SQ zEakJ)3gRVZ|ADY;W92$^gC=W~g?kA`^Nx z)2fLrFjfcnW)T>I3r~d7(&^KFWC<}otH2qNo8Kv+4qiPQ^)4h`nUaKx+GyC;3MX1u zqIOlK1Cn;vJvz>@?JQiY^Ujh5B6o5s7^Ux?JlS={VEuF`*zh2iR$XY|ENV-o0FWK3 zXcBML6%D4@@xg4?x}#leN?-XLP+V&|PtVp9VB--Dr-d2wKHAc#$=4Br%!x=jphe8N zW*>q>@6*RP=fjS%I#jmtcJB$3R+xBjdEDu3o6 zvvmu`iqLi=3{I^i=V@OQv4o2U|H29$Kg(;OLiSGTlO${o_d6cGCPHn|Bxa@!D85dU4m2^PQmM$c*3c_W{4~ zn4Vi5kIdr^v(tu9di$1?fbquW*4ycWL}KEdg#n|hC+T%fkWc^9>%HH_CY*z=G=MxG z7Yq&@V%Cm<^gv-B1+K4} zdY+Pm+2djvkETIgc?!q?Y3c8LJk;ZG&5FDRiGJBmn;(n@<$`W@%mr;ZbUp+Lr*l|B z=pzb^j&`uOUv!9woNS9;|1P?h?lT6**3ByloPg{vq(T?7w%VZ)i&4oaMfvJHKgqNUi{Q01?HPY9&vt{^oV*2^SOpTcj2R$+Uk7+Ln=$2sx zrKMzg3r#TWj|&~SWlVVT{V>sz;H1z~oR&>`oO4_a_bsZ;HC)G34MurI*0|O;FzXDbwmWw+RnJW0 zQ#fhcy5qU1@&;ic>NvnQp+peY?fI~B575s&y^Agh1fzs3sOiB|vTkE&AZ5wB*BA-Vo&4?FE zRUA2WtCd#~rAl#~1mWs0VD0&N4Q~xZW?d%lw-Y{Pd!om*4G?AULGgu!MjMTc&ACgu zbr)a+wY(1$qW9r$!5pecaxpJ@_*bCgJwOJzp;qWvRMsXz{A?!*RQeV{cCrz_7=UlBAa`5x!6#0V}#Gz$6p#6pWeq4+Qm@eBjotEx}W zm0(I$qD<*?mDwg;iUZpXz+dtPKtMN+5CGe-@;%84ade9xuGrW~(-L0DnEcH_Mxb{& z7(WV>%T4fScrG6KE*B?shW?33ouXqc@Gd4)EcwSEzW1#Q%Vjf3<>MXI!G)s5gl~60 z%6yAiLN)=}bz$%wcvF;0CJzUp|M(eRz~OA83=$v*YRsBInZUmg_+BU1 z+!myA^6-A9^uGvfXQXeg&+zO07k3XnzPFrb!)QgBaU{00)TOG$gS8KysYof2!Xvi` zAT$n&SC-4-8xwcOn_Kq+?`Y6Y0B<~GG@~7bXtYbGR1GMByu-W_>#uz|E;R--)4DaP zG7)k7qVii83FUo`G*J@y$=poBmJQ+S^KnXC7K3SnB+b%e3HaB8ta_ZF8`ii_`-&iOEN=nPzC%X=tef3 zTLxT5na1fm0?ihK@@!Ls4|}kbTWHNUXML6#+O@N54yytOi4!#ytbT3dzG zAX;|R;}PprmPOgd6W;@K=jauA`e!plG9O>|2M$+{~3|6wC%uSv4X=_xc9G4SX$? zU)fD$3~zW5HCDG>3+4`#ZuQR8a9(T~pezW%2o85+vD6tJ=rw=0N6sg!p%V|@zA9nOPSRqjy>FlhZ~9I>4Mmy2`=Qm7RNF@m8C{b+0b6A}}X0*pk1 zT!CD2bEz3Kg8jYv?B~zAs#bxw(T3YX6&n8c*zr5&+{0IJdR=QTE=vVki$>c$TLc&e zHbcq=&OZPYT$W5zhPl+|oKUEBVNJgEN*TLY!o$i;_zE*)B=%Xc1#ZGW(9OBQVk4Se z5w}l@%2U9?4`*7IB{RK=hu&4rN(Clj4^`qA^}){>2<478q!?F~ZlkJlk}EcHR}PqN zI@^kLOQu&O8Rvygv3OU#QEfbP7%WsQ%r&r8D8l_NDQhR6QUG|TCXa+=IQ3~5hMyNo z(V}gpOh3RGmKn{sasH#*9@fyEfI2xke$+Qnm1AKdg|L_}6>pHpA|fshoPr}H7Lt<> zRIRu|V)_qEfHP9f*K8We$b+^FyG=Y%;dHcAR0E^3v_?}(```%HO9=jqG#w)>uw}qS zNgb>Xce(seNcVCvv!C`CQlvbU6<4o1=G^(U^N=?L3eD-z@o={kUO><>`!+{;)*u@+ zzk)W)Y4yf1-A4s@reJ7^PS@yhkZ00xHCF2!@c=Sj<+>FIa)@P-v1sX5M6~~SE6<-E zDDm}_5tE;{?J~8t)_D_an{>B7K?lZgnrQ%CEmk!xYh1F{DyD}SXD}viDiF58S)aW> zO%pTb3_C}HGxW)@aOWJVlz58t&>Phv09xd&rYbl#&8m#w*(ZM@&4kbiR6>-!lE*)2n&QzD`EBfgqEp03%h3Z#VdUxgYu)lzNyne z=_S#2SBJ#AGeePq$h0UFmxTcw={?ks2*V)hEN(o=aS^2qZkwanIcllWH9mzli@>!VdG&2pI4>?LgLyTxZsj-gs!w z(?ZF*_puR*VT-8}!j5vx3gby8$C@h#A66P<7>*3j6x;%S7DhE4|>oE2Fo)FMARh^fx0ux>*t7HzTDZbxVrh*RO!9;eCtM z=H=!)Q{7H3N%8#GZ2V0InaD0EClLZgW~4u2l&zh!nZE5Gx%lGGT-+0si?^R~v5rPk z;EcW&kD{@6H>NtA1dma^gnjdcEaj^87ywVQSM5N6qLn_aH>EH?V|H#OND~t z>j{$%%`~*qJd~$SX^0B!sVPtmEu-J`nNe6!XyS2rC5oC1}Tp?M8)&ak88 z*qwlqWV?7lSPoXL0dTLMQDx#N8iV}e2X#~|?E)E{-Xkum_>iugbA3E!javtF-90iQ zSrUUqtpr}fixm?*Nu%&NEfax>hau$Ba3MUF77 zOPt}1Ip-Qw!?5u2jx`W&qx|6XF06wn`*MbC1}HYw+L2X@e==eL8#C?1swP@p*!fr* zg=bJ%x~uN&HNKtUe%Xw%Kaj6ZLwqCaKXS&UhftpdJV~<^=Q}3M5>D|)9GE1sK}-~O z)fsT)S7w)cO9)HbCf_bG2Y1hiPkB@Q_zhvH_jVK+j8>}3CT+Fp-F{@LctaL`L8LUS z-A7YP(;cRptnL%2rzQ=L#Imxg(UjYPB=&l}DE5eWQ-O8_0dPAhL88u#2ngTd|%)ymb(wKK;huc-I2euBq})EB$0p)Uzh3Bvqe zKTF3?%VLGOMJ#c=bVq~w5okXwia0=lu0|lv{ee9m!nOPd5S2bQ9Ny;cM{euCa^ew- zZ8IOlg-y_b@h2yYv~BeDI~11`Wd z$H!cdRIqbcg81EU;F^b!d;Hy6{!t<)d8G^{#j70pOYc&w;k^{ zdclC(t&$l(^#!)9%??d=7tW6_nx#*+=labHb_}53{m+y#f8Ys{7{vBVsQ;#vdN$gQ zKW~cR&p-Z1DdmSO<{1GkC^KAe4P1`}F%%*PI=81X89Oe@M0}B(Gl>+?(aAZl?vAqc zoUIe^8}5kZFm9vPL`|Gs*9hJ+=BiTL;J+sgP0Xk?>7ws>-`cMg9>pdkpA+M=dE;m7 zrt0>il3Rg>YcEUyjMxrl9-qDYpo(##T03 zQmA}`_Mq7v`oU>0)hLHCG`*U^)~o5_{T9SIvB@G63TFn{S|CTb)d!udd3JP?0rbZZD60;^2PyIK z!B+kpnG#zwO2YUap~yt;+B+SqvYE9IciDQuQD8mzWq4VD;Px231HGe~Q#2Pv^Ki|9 zHy@?y^l@W}p^d90d?(q|q4aH-mnoO~?QNJ{tCmjbvfC=XVH%5I9M)%{TbumkYl??H zNf7ooSWx5LK{rrH3-*)&FIdks!Jg{Xv(C}FWKCEtIq2du2Rv>XRWCZ{fvz5e^!);r z)|kf>KT2N5?49giwg&&V-(Mj8fU#xZwi|RLnENrB?Dh{(a zn7LO4C}lD6eC{fRHdD>;F7R9e+cwlJUi(?E{ij!roZkcVK=H!_l;`}(7hMovKt+mQ zr#};&0UJ=Fv*`Hcsm-Rz2)j}&*rg)=Ls0OWG|Dh4hq0t1Vzg)(UnnHL+DXaBW^9xX zIdrYjUt@%;BgMGs>4%#+z`~_97W5|Szp0_dN2oo`*b<~t(NWx*Y_#yE*TIyMk@w39 zeGC^p!a}_6y6GX3j(+u#jD7?mRtq+CgH%G~NTr|Dq)xc98wahYxQ%n-HO@%nC#4pX zJbZQE)6iPTk^P#Q7t^?W>Dt7C8^j-3v0%93hP2LQ?&t=}tj83_ ziR1WW)8C3t#9}Rg3+`JU#LDHouY=Y)Ld<1{SnumD1A!Lo{Mp(f>l#E`p6)+uqan zr;g_^*Hh(BO0^1=+%lgP*(1m3MnCj{S!yyhlk|*YqMWuQc4uVgbn&}kGl-45-A&Q) z)(#dI8cXSJ3(aF3cUeoR2Matt`EaQm{-pCDP|Ot1OCS~D#%iq&E~NuSo`s9~#y^+D z93trj;%=J}4O?N?Bu58oxX^X$*a;?rY@Ads!Fg6s2_L3k5YkZEe$@Qwy?UUNPjD11 z{VECVSQMgzeI#@?c^bNeLMLnMYU12%Cb_rp?!H4HmZ_fGq4P<_N14k}Z;O|-Un7~x zr?4(cWEcwZg5PA>_(hv-AjdM9BMF;^jwM9CquCUpUrSYQG=ag9Ru9l2wu!=lM0?R- z3UBKT$cyX=>$cNR%c%#K>WCH!=8)9uVpC>cZ@$sj>Y#sZ?(J6%kFEKd zrkuO%xC@icUVie#%N%mYV5DuIU-4`1+xkxqVB}l@FSO$=MSm6N!8@ix%b~stb+YBc@K(88=(~H&CirK!wca%w5$xX zGfXCSQI0An3UK}0%Vy!amxhL$V)rwJj9qzECw5v6=XF5=SaA!QMI9ZY`cNffa zAs-23d@8_OSo|VXuzpxq51WIA-w}fJ?i>4A4OqW8sgn&Ger;HUbsqD{8i$#^VRJ&s zC|-BT?tt{D%PfjPd-?LN!`hA9VfpDswuth)zUy~Nssy};#?8lwWcrmN*0jOA62{LC ztMCtr_c0m=JylwBX>Mct8u!K69-N3>aSp4;OpO!|xK;Dfu1P$PItI&#SLjJRJN|rW z*D(WbrMC*Kg|Zx}vW^%QUyah+u4lLDmoO6WI!|Ma6s6BO%j|V)mXtc zEo#hOU3Q8v5yNYxj_gfFJJEKTOTv*~`Ld5!)}X8ai&DYE|F->1ZgA56F+OeA zpd#a@cn#MU9XhDY4rwtq&k_~Q--GI9;6y>z(T6%DXZMx}CCKY(VJLHwBp z|0UH*j^gXEJefLY+n7Q z(VAMQ!gRqncLAri(Q2>4dP6hx-$?bZj4Z~Xy#+Um!{a>I+aA2hx=>)M_<%zIJBe5Eki(YzJX7?z?u-7z^_mpd}&D`GZtR<9X2oQ>q(n1|Mm%d;iFy(5%hA zqatewFJMl+ALN$%p4xRSe~7K4gME&Gd1V3^V-_*(ACqL7BjaxG#l#!r0X;@XY*Xr({WhZ6x!Ni6QRw zOEWnR);WTD==Er_4>p6J-+Of)F;t#<{%cg`WU7gp2VEHjX)5x&_j|L$SjWq3X$IZV{W%^lL)xH^qAG~sS9H1M0~UAu>F+WR zk4!&LP+^4!(N#Bq`)$YtvM4+8yelK(tKdMekobf(+*`h{=s1s^nmR#3sB54=-H9a< z^hTolJcdVeUlxs0b1_R*RK)#Fo85%K&GJjBObY4_R8<}J2Cs4<}V%;;z)X3lVy z-|sz4tfr)F9j?IQ7IAV{-o}&Jw0(AF6>~(A*~5#>K`#zj9K#Bf*VyUX$m!eJ{mKQNeP*K9F}Bdr$^bA{GFB~u 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 69a25d92..72438891 100644 --- a/src/PhpSpreadsheet/Chart/Axis.php +++ b/src/PhpSpreadsheet/Chart/Axis.php @@ -27,6 +27,9 @@ class Axis extends Properties 'numeric' => null, ]; + /** @var string */ + private $axisType = ''; + /** * Axis Options. * @@ -62,11 +65,11 @@ class Axis extends Properties * * @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 @@ class Axis extends Properties $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 05c1bb9a..7f87e391 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 @@ class ChartColor /** * @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 @@ class ChartColor 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 dca1186e..d27db33e 100644 --- a/src/PhpSpreadsheet/Chart/DataSeries.php +++ b/src/PhpSpreadsheet/Chart/DataSeries.php @@ -257,8 +257,6 @@ class DataSeries $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 @@ class DataSeries $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 0a2f5a85..d6a8dcca 100644 --- a/src/PhpSpreadsheet/Chart/DataSeriesValues.php +++ b/src/PhpSpreadsheet/Chart/DataSeriesValues.php @@ -7,7 +7,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Functions; 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 @@ class DataSeriesValues return $this; } + public function getMarkerFillColor(): ChartColor + { + return $this->markerFillColor; + } + + public function getMarkerBorderColor(): ChartColor + { + return $this->markerBorderColor; + } + /** * Get Point Size. */ @@ -252,37 +260,96 @@ class DataSeriesValues 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 @@ class DataSeriesValues /** * 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 @@ class DataSeriesValues return $this; } - public function getSchemeClr(): string + /** + * Smooth Line. + * + * @var bool + */ + private $smoothLine; + + /** + * Get Smooth Line. + * + * @return bool + */ + public function getSmoothLine() { - return $this->schemeClr; + return $this->smoothLine; } - public function setSchemeClr(string $schemeClr): self + /** + * Set Smooth Line. + * + * @param bool $smoothLine + * + * @return $this + */ + public function setSmoothLine($smoothLine) { - $this->schemeClr = $schemeClr; - - return $this; - } - - public function getPrstClr(): string - { - return $this->prstClr; - } - - public function setPrstClr(string $prstClr): self - { - $this->prstClr = $prstClr; + $this->smoothLine = $smoothLine; return $this; } diff --git a/src/PhpSpreadsheet/Chart/Properties.php b/src/PhpSpreadsheet/Chart/Properties.php index a64a826f..fdc3c12b 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 @@ abstract class Properties 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 @@ abstract class Properties ], ]; + 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 2c060d07..df25dac7 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php @@ -14,7 +14,6 @@ use PhpOffice\PhpSpreadsheet\Chart\PlotArea; use PhpOffice\PhpSpreadsheet\Chart\Properties; use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\RichText\RichText; -use PhpOffice\PhpSpreadsheet\Style\Color; use PhpOffice\PhpSpreadsheet\Style\Font; use SimpleXMLElement; @@ -90,6 +89,7 @@ class Chart case 'plotArea': $plotAreaLayout = $XaxisLabel = $YaxisLabel = null; $plotSeries = $plotAttributes = []; + $catAxRead = false; foreach ($chartDetails as $chartDetailKey => $chartDetail) { switch ($chartDetailKey) { case 'layout': @@ -97,9 +97,11 @@ class Chart 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 @@ class Chart $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 @@ class Chart 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 @@ class Chart 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 @@ class Chart $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 @@ class Chart $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 @@ class Chart 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,36 +487,15 @@ class Chart $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); - } - } - if ($schemeClr) { - if (isset($seriesLabel[$seriesIndex])) { - $seriesLabel[$seriesIndex]->setSchemeClr($schemeClr); - } - if (isset($seriesCategory[$seriesIndex])) { - $seriesCategory[$seriesIndex]->setSchemeClr($schemeClr); - } - if (isset($seriesValues[$seriesIndex])) { - $seriesValues[$seriesIndex]->setSchemeClr($schemeClr); - } - } elseif ($prstClr) { - if (isset($seriesLabel[$seriesIndex])) { - $seriesLabel[$seriesIndex]->setPrstClr($prstClr); - } - if (isset($seriesCategory[$seriesIndex])) { - $seriesCategory[$seriesIndex]->setPrstClr($prstClr); - } - if (isset($seriesValues[$seriesIndex])) { - $seriesValues[$seriesIndex]->setPrstClr($prstClr); + $seriesValues[$seriesIndex]->copyLineStyles($lineStyle); } } if ($bubble3D) { @@ -532,6 +520,39 @@ class Chart $seriesValues[$seriesIndex]->setFillColor($dptColors); } } + if ($markerFillColor !== null) { + if (isset($seriesLabel[$seriesIndex])) { + $seriesLabel[$seriesIndex]->getMarkerFillColor()->setColorPropertiesArray($markerFillColor); + } + if (isset($seriesCategory[$seriesIndex])) { + $seriesCategory[$seriesIndex]->getMarkerFillColor()->setColorPropertiesArray($markerFillColor); + } + if (isset($seriesValues[$seriesIndex])) { + $seriesValues[$seriesIndex]->getMarkerFillColor()->setColorPropertiesArray($markerFillColor); + } + } + if ($markerBorderColor !== null) { + if (isset($seriesLabel[$seriesIndex])) { + $seriesLabel[$seriesIndex]->getMarkerBorderColor()->setColorPropertiesArray($markerBorderColor); + } + if (isset($seriesCategory[$seriesIndex])) { + $seriesCategory[$seriesIndex]->getMarkerBorderColor()->setColorPropertiesArray($markerBorderColor); + } + if (isset($seriesValues[$seriesIndex])) { + $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); + } + } } } /** @phpstan-ignore-next-line */ @@ -544,11 +565,11 @@ class Chart /** * @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 @@ class Chart 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 @@ class Chart 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 @@ class Chart 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 @@ class Chart $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 @@ class Chart $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 @@ class Chart $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 @@ class Chart $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 @@ class Chart 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 @@ class Chart '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 @@ class Chart 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 @@ class Chart 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 @@ class Chart 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 4dbe7272..19d67563 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 @@ class Font extends Supervisor * * @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 @@ class Font extends Supervisor // 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 @@ class Font extends Supervisor 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 @@ class Font extends Supervisor 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 @@ class Font extends Supervisor 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 @@ class Font extends Supervisor 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 @@ class Font extends Supervisor $this->eastAsian, $this->complexScript, $this->strikeType, - $underlineColor, + $this->hashChartColor($this->chartColor), + $this->hashChartColor($this->underlineColor), (string) $this->baseLine, ] ) . @@ -762,6 +795,7 @@ class Font extends Supervisor $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 acc6f3af..d242e602 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php @@ -419,7 +419,9 @@ class Chart extends WriterPart { // 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 @@ class Chart extends WriterPart $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 @@ class Chart extends WriterPart $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 @@ class Chart extends WriterPart $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 @@ class Chart extends WriterPart $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 @@ class Chart extends WriterPart $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 @@ class Chart extends WriterPart /** * 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 @@ class Chart extends WriterPart 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 @@ class Chart extends WriterPart $objWriter->writeAttribute('val', $this->seriesIndex + $plotSeriesRef); $objWriter->endElement(); - // Values - $plotSeriesValues = $plotGroup->getPlotValuesByIndex($plotSeriesRef); + $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 + } + } - if (($groupType == DataSeries::TYPE_PIECHART) || ($groupType == DataSeries::TYPE_PIECHART_3D) || ($groupType == DataSeries::TYPE_DONUTCHART)) { - $fillColorValues = $plotSeriesValues->getFillColor(); + // Values + $plotSeriesValues = $plotGroup->getPlotValuesByIndex($plotSeriesIdx); + + 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 @@ class Chart extends WriterPart // 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; + } + $this->writeColor($objWriter, $fillObject); + if (!$callLineStyles) { + $objWriter->endElement(); // a:ln } } - if ($schemeClr) { - $objWriter->startElement('a:solidFill'); - $objWriter->startElement("a:$typeClr"); - $objWriter->writeAttribute('val', $schemeClr); - $objWriter->endElement(); - $objWriter->endElement(); + $nofill = $groupType == DataSeries::TYPE_STOCKCHART || ($groupType === DataSeries::TYPE_SCATTERCHART && !$plotSeriesValues->getScatterLines()); + if ($callLineStyles) { + $this->writeLineStyles($objWriter, $plotSeriesValues, $nofill); } - $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(); - } - } - $objWriter->endElement(); - $objWriter->endElement(); + $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 @@ class Chart extends WriterPart } // Category Labels - $plotSeriesCategory = $plotGroup->getPlotCategoryByIndex($plotSeriesRef); + $plotSeriesCategory = $plotGroup->getPlotCategoryByIndex($plotSeriesIdx); if ($plotSeriesCategory && ($plotSeriesCategory->getPointCount() > 0)) { $catIsMultiLevelSeries = $catIsMultiLevelSeries || $plotSeriesCategory->isMultiLevelSeries(); @@ -1112,7 +1069,7 @@ class Chart extends WriterPart $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 @@ class Chart extends WriterPart } } + 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 @@ class Chart extends WriterPart $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 @@ class Chart extends WriterPart $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 @@ class Chart extends WriterPart } $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 @@ class Chart extends WriterPart 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 8a376df4..8b293bc1 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/StringTable.php @@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx; 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 @@ class StringTable extends WriterPart * @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 @@ class StringTable extends WriterPart $run->setFont(null); } - if ($prefix !== null) { + if ($prefix !== '') { $prefix .= ':'; } @@ -249,27 +250,10 @@ class StringTable extends WriterPart } // 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 @@ class StringTable extends WriterPart } } + 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 ad7fc776..0ed4a1b4 100644 --- a/tests/PhpSpreadsheetTests/Chart/AxisGlowTest.php +++ b/tests/PhpSpreadsheetTests/Chart/AxisGlowTest.php @@ -119,6 +119,7 @@ class AxisGlowTest extends AbstractFunctional $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 00000000..824e9600 --- /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 71cda504..b8041623 100644 --- a/tests/PhpSpreadsheetTests/Chart/Charts32ColoredAxisLabelTest.php +++ b/tests/PhpSpreadsheetTests/Chart/Charts32ColoredAxisLabelTest.php @@ -58,7 +58,10 @@ class Charts32ColoredAxisLabelTest extends AbstractFunctional 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 @@ class Charts32ColoredAxisLabelTest extends AbstractFunctional 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 d79cba6f..38884eaf 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 @@ class Charts32ScatterTest extends AbstractFunctional 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 @@ class Charts32ScatterTest extends AbstractFunctional 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 @@ class Charts32ScatterTest extends AbstractFunctional 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 @@ class Charts32ScatterTest extends AbstractFunctional 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 @@ class Charts32ScatterTest extends AbstractFunctional 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 @@ class Charts32ScatterTest extends AbstractFunctional 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 @@ class Charts32ScatterTest extends AbstractFunctional 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 @@ class Charts32ScatterTest extends AbstractFunctional 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 @@ class Charts32ScatterTest extends AbstractFunctional 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 @@ class Charts32ScatterTest extends AbstractFunctional 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 3123278f..6a4673fd 100644 --- a/tests/PhpSpreadsheetTests/Chart/Charts32XmlTest.php +++ b/tests/PhpSpreadsheetTests/Chart/Charts32XmlTest.php @@ -84,12 +84,16 @@ class Charts32XmlTest extends TestCase $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 @@ class Charts32XmlTest extends TestCase ]; } + 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 00000000..e8326e52 --- /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 00000000..a27397f9 --- /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 c34ca697..47c2fb89 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 @@ class DataSeriesValuesTest extends TestCase 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 82478fb9..747cba74 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 @@ class Issue589Test extends TestCase 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 58c024c1..e96d6c14 100644 --- a/tests/PhpSpreadsheetTests/Chart/ShadowPresetsTest.php +++ b/tests/PhpSpreadsheetTests/Chart/ShadowPresetsTest.php @@ -123,6 +123,35 @@ class ShadowPresetsTest extends TestCase } } + 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();