Skip to content

Commit

Permalink
Additional Support for Chart DataSeriesValues (#2906)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
oleibman authored Jun 30, 2022
1 parent b5b83ab commit 5d5e550
Show file tree
Hide file tree
Showing 25 changed files with 1,272 additions and 378 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 1 addition & 11 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

-
Expand Down
183 changes: 183 additions & 0 deletions samples/Chart/33_Chart_create_bar_custom_colors.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
<?php

use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Layout;
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;

require __DIR__ . '/../Header.php';

$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', '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);
4 changes: 3 additions & 1 deletion samples/Chart/33_Chart_create_line.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Properties;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
Expand Down Expand Up @@ -35,6 +36,7 @@
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', null, 1), // 2011
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$D$1', null, 1), // 2012
];
$dataSeriesLabels[0]->setFillColor('FF0000');
// Set the X-Axis Labels
// Datatype
// Cell reference for data
Expand All @@ -57,7 +59,7 @@
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', null, 4),
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$D$2:$D$5', null, 4),
];
$dataSeriesValues[2]->setLineWidth(60000);
$dataSeriesValues[2]->setLineWidth(60000 / Properties::POINTS_WIDTH_MULTIPLIER);

// Build the dataseries
$series = new DataSeries(
Expand Down
70 changes: 68 additions & 2 deletions samples/Chart/33_Chart_create_scatter2.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -64,11 +65,76 @@
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', Properties::FORMAT_CODE_NUMBER, 4),
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$D$2:$D$5', Properties::FORMAT_CODE_NUMBER, 4),
];

// series 1
// marker details
$dataSeriesValues[0]
->setPointMarker('diamond')
->setPointSize(5)
->getMarkerFillColor()
->setColorProperties('0070C0', null, ChartColor::EXCEL_COLOR_TYPE_RGB);
$dataSeriesValues[0]
->getMarkerBorderColor()
->setColorProperties('002060', null, ChartColor::EXCEL_COLOR_TYPE_RGB);

// line details - smooth line, connected
$dataSeriesValues[0]
->setScatterLines(true)
->setSmoothLine(true)
->setLineColorProperties('accent1', 40, ChartColor::EXCEL_COLOR_TYPE_SCHEME); // value, alpha, type
$dataSeriesValues[0]->setLineStyleProperties(
2.5, // width in points
Properties::LINE_STYLE_COMPOUND_TRIPLE, // compound
Properties::LINE_STYLE_DASH_SQUARE_DOT, // dash
Properties::LINE_STYLE_CAP_SQUARE, // cap
Properties::LINE_STYLE_JOIN_MITER, // join
Properties::LINE_STYLE_ARROW_TYPE_OPEN, // head type
Properties::LINE_STYLE_ARROW_SIZE_4, // head size preset index
Properties::LINE_STYLE_ARROW_TYPE_ARROW, // end type
Properties::LINE_STYLE_ARROW_SIZE_6 // end size preset index
);

// series 2 - straight line - no special effects, connected, straight line
$dataSeriesValues[1] // square fill
->setPointMarker('square')
->setPointSize(6)
->getMarkerBorderColor()
->setColorProperties('accent6', 3, ChartColor::EXCEL_COLOR_TYPE_SCHEME);
$dataSeriesValues[1] // square border
->getMarkerFillColor()
->setColorProperties('0FFF00', null, ChartColor::EXCEL_COLOR_TYPE_RGB);
$dataSeriesValues[1]
->setScatterLines(true)
->setSmoothLine(false)
->setLineColorProperties('FF0000', 80, ChartColor::EXCEL_COLOR_TYPE_RGB);
$dataSeriesValues[1]->setLineWidth(2.0);

// series 3 - markers, no line
$dataSeriesValues[2] // triangle fill
//->setPointMarker('triangle') // let Excel choose shape
->setPointSize(7)
->getMarkerFillColor()
->setColorProperties('FFFF00', null, ChartColor::EXCEL_COLOR_TYPE_RGB);
$dataSeriesValues[2] // triangle border
->getMarkerBorderColor()
->setColorProperties('accent4', null, ChartColor::EXCEL_COLOR_TYPE_SCHEME);
$dataSeriesValues[2]->setScatterLines(false); // points not connected

// Added so that Xaxis shows dates instead of Excel-equivalent-year1900-numbers
$xAxis = new Axis();
//$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE );
$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE_ISO8601, true);

$yAxis = new Axis();
$yAxis->setLineStyleProperties(
2.5, // width in points
Properties::LINE_STYLE_COMPOUND_SIMPLE,
Properties::LINE_STYLE_DASH_DASH_DOT,
Properties::LINE_STYLE_CAP_FLAT,
Properties::LINE_STYLE_JOIN_BEVEL
);
$yAxis->setLineColorProperties('ffc000', null, ChartColor::EXCEL_COLOR_TYPE_RGB);

// Build the dataseries
$series = new DataSeries(
DataSeries::TYPE_SCATTERCHART, // plotType
Expand All @@ -79,8 +145,7 @@
$dataSeriesValues, // plotValues
null, // plotDirection
false, // smooth line
//DataSeries::STYLE_LINEMARKER // plotStyle
DataSeries::STYLE_MARKER // plotStyle
DataSeries::STYLE_SMOOTHMARKER // plotStyle
);

// Set the series in the plot area
Expand All @@ -103,6 +168,7 @@
$yAxisLabel, // yAxisLabel
// added xAxis for correct date display
$xAxis, // xAxis
$yAxis, // yAxis
);

// Set the position where the chart should appear in the worksheet
Expand Down
Binary file added samples/templates/32readwriteScatterChart8.xlsx
Binary file not shown.
23 changes: 21 additions & 2 deletions src/PhpSpreadsheet/Chart/Axis.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ public function __construct()
'numeric' => null,
];

/** @var string */
private $axisType = '';

/**
* Axis Options.
*
Expand Down Expand Up @@ -62,11 +65,11 @@ public function __construct()
*
* @param mixed $format_code
*/
public function setAxisNumberProperties($format_code, ?bool $numeric = null): void
public function setAxisNumberProperties($format_code, ?bool $numeric = null, int $sourceLinked = 0): void
{
$format = (string) $format_code;
$this->axisNumber['format'] = $format;
$this->axisNumber['source_linked'] = 0;
$this->axisNumber['source_linked'] = $sourceLinked;
if (is_bool($numeric)) {
$this->axisNumber['numeric'] = $numeric;
} elseif (in_array($format, self::NUMERIC_FORMAT, true)) {
Expand Down Expand Up @@ -156,6 +159,22 @@ public function setAxisOrientation($orientation): void
$this->axisOptions['orientation'] = (string) $orientation;
}

public function getAxisType(): string
{
return $this->axisType;
}

public function setAxisType(string $type): self
{
if ($type === 'catAx' || $type === 'valAx') {
$this->axisType = $type;
} else {
$this->axisType = '';
}

return $this;
}

/**
* Set Fill Property.
*
Expand Down
Loading

0 comments on commit 5d5e550

Please sign in to comment.