Skip to content

Commit

Permalink
Xlsx Reader/Writer Composite Charts (#3265)
Browse files Browse the repository at this point in the history
Fix #2333. Thanks to @SSI-johnnypops for discovering the problem and formulating most of the solution. The classes involved in setting up composite charts made it relatively easy to code them; however, there can be problems when reading a spreadsheet with such a chart and then saving a copy of it. In  particular, the ordering conventions when reading the chart may not match the expectations when writing. The original attempt to resolve this worked for read/write but broke one of our samples (33_Chart_create_composite), which probably means that it would break other code which is now working in the wild. It has now been refined so that it (hopefully) makes adjustments to the ordering if and only if they are required. The new code works with the failing spreadsheet reported in the issue, with the unchanged sample code, and with an alternate version of the sample which contains what is probably a better model for people to use when coding such a chart.
  • Loading branch information
oleibman authored Jan 1, 2023
1 parent 43da1c9 commit d293847
Showing 3 changed files with 166 additions and 2 deletions.
163 changes: 163 additions & 0 deletions samples/Chart/33_Chart_create_composite.alternate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<?php

use PhpOffice\PhpSpreadsheet\Chart\Chart;
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\Title;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;

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

$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$worksheet->fromArray(
[
['', 'Rainfall (mm)', 'Temperature (°F)', 'Humidity (%)'],
['Jan', 78, 52, 61],
['Feb', 64, 54, 62],
['Mar', 62, 57, 63],
['Apr', 21, 62, 59],
['May', 11, 75, 60],
['Jun', 1, 75, 57],
['Jul', 1, 79, 56],
['Aug', 1, 79, 59],
['Sep', 10, 75, 60],
['Oct', 40, 68, 63],
['Nov', 69, 62, 64],
['Dec', 89, 57, 66],
]
);

// 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!$B$1', null, 1), // Temperature
];
$dataSeriesLabels2 = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', null, 1), // Rainfall
];
$dataSeriesLabels3 = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$D$1', null, 1), // Humidity
];

// 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$13', null, 12), // Jan to Dec
];

$order = 0;
// 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
$dataSeriesValues1 = [
$order => new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$B$2:$B$13', null, 12),
];

// Build the dataseries
$series1 = new DataSeries(
DataSeries::TYPE_BARCHART, // plotType
DataSeries::GROUPING_CLUSTERED, // plotGrouping
[$order => $order], // plotOrder
$dataSeriesLabels1, // plotLabel
$xAxisTickValues, // plotCategory
$dataSeriesValues1 // plotValues
);
// Set additional dataseries parameters
// Make it a vertical column rather than a horizontal bar graph
$series1->setPlotDirection(DataSeries::DIRECTION_COL);

$order = 1;
// 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
$dataSeriesValues2 = [
$order => new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$13', null, 12),
];

// Build the dataseries
$series2 = new DataSeries(
DataSeries::TYPE_LINECHART, // plotType
DataSeries::GROUPING_STANDARD, // plotGrouping
[$order => $order], // plotOrder
$dataSeriesLabels2, // plotLabel
[], // plotCategory
$dataSeriesValues2 // plotValues
);

$order = 2;
// 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
$dataSeriesValues3 = [
$order => new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$D$2:$D$13', null, 12),
];

// Build the dataseries
$series3 = new DataSeries(
DataSeries::TYPE_AREACHART, // plotType
DataSeries::GROUPING_STANDARD, // plotGrouping
[$order => $order], // plotOrder
$dataSeriesLabels3, // plotLabel
[], // plotCategory
$dataSeriesValues3 // plotValues
);

// Set the series in the plot area
$plotArea = new PlotArea(null, [$series1, $series2, $series3]);
// Set the chart legend
$legend = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);

$title = new Title('Average Weather Chart for Crete');

// Create the chart
$chart = new Chart(
'chart1', // name
$title, // title
$legend, // legend
$plotArea, // plotArea
true, // plotVisibleOnly
DataSeries::EMPTY_AS_GAP, // displayBlanksAs
null, // xAxisLabel
null // yAxisLabel
);

// Set the position where the chart should appear in the worksheet
$chart->setTopLeftPosition('F2');
$chart->setBottomRightPosition('O16');

// Add the chart to the worksheet
$worksheet->addChart($chart);

// 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);
Binary file added samples/templates/32readwriteComboChart1.xlsx
Binary file not shown.
5 changes: 3 additions & 2 deletions src/PhpSpreadsheet/Writer/Xlsx/Chart.php
Original file line number Diff line number Diff line change
@@ -1041,11 +1041,12 @@ private function writePlotGroup(?DataSeries $plotGroup, string $groupType, XMLWr
$objWriter->startElement('c:ser');

$objWriter->startElement('c:idx');
$objWriter->writeAttribute('val', (string) ($this->seriesIndex + $plotSeriesIdx));
$adder = array_key_exists(0, $plotSeriesOrder) ? $this->seriesIndex : 0;
$objWriter->writeAttribute('val', (string) ($adder + $plotSeriesIdx));
$objWriter->endElement();

$objWriter->startElement('c:order');
$objWriter->writeAttribute('val', (string) ($this->seriesIndex + $plotSeriesRef));
$objWriter->writeAttribute('val', (string) ($adder + $plotSeriesRef));
$objWriter->endElement();

$plotLabel = $plotGroup->getPlotLabelByIndex($plotSeriesIdx);

0 comments on commit d293847

Please sign in to comment.