diff --git a/samples/Chart33a/33_Chart_create_area_3.php b/samples/Chart33a/33_Chart_create_area_3.php new file mode 100644 index 000000000..5e5a3f802 --- /dev/null +++ b/samples/Chart33a/33_Chart_create_area_3.php @@ -0,0 +1,108 @@ +getTheme()->setThemeColorName(SpreadsheetTheme::COLOR_SCHEME_2013_2022_NAME); +$worksheet = $spreadsheet->getActiveSheet(); +$sheet2 = $spreadsheet->createSheet(); +$sheet2->setTitle('Sheet2'); +$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( + DataSeries::TYPE_AREACHART, // plotType + DataSeries::GROUPING_PERCENT_STACKED, // plotGrouping + range(0, count($dataSeriesValues) - 1), // plotOrder + $dataSeriesLabels, // plotLabel + $xAxisTickValues, // plotCategory + $dataSeriesValues // plotValues +); + +// Set the series in the plot area +$plotArea = new PlotArea(null, [$series]); +$plotArea->setDataTable(new DataTable()); +// No need for Legend if using DataTable +$legend = null; //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('A1'); +$chart->setBottomRightPosition('H18'); + +// Add the chart to the worksheet +$sheet2->addChart($chart); +$spreadsheet->setActiveSheetIndex(1); + +$helper->renderChart($chart, __FILE__); + +// Save Excel 2007 file +$helper->write($spreadsheet, __FILE__, ['Xlsx'], true, null, false); diff --git a/src/PhpSpreadsheet/Chart/DataTable.php b/src/PhpSpreadsheet/Chart/DataTable.php new file mode 100644 index 000000000..71c18fe7c --- /dev/null +++ b/src/PhpSpreadsheet/Chart/DataTable.php @@ -0,0 +1,62 @@ +showHorizontalBorder; + } + + public function getShowVerticalBorder(): bool + { + return $this->showVerticalBorder; + } + + public function getShowOutline(): bool + { + return $this->showOutline; + } + + public function getShowKeys(): bool + { + return $this->showKeys; + } + + public function setShowHorizontalBorder(bool $showHorizontalBorder): self + { + $this->showHorizontalBorder = $showHorizontalBorder; + + return $this; + } + + public function setShowVerticalBorder(bool $showVerticalBorder): self + { + $this->showVerticalBorder = $showVerticalBorder; + + return $this; + } + + public function setShowOutline(bool $showOutline): self + { + $this->showOutline = $showOutline; + + return $this; + } + + public function setShowKeys(bool $showKeys): self + { + $this->showKeys = $showKeys; + + return $this; + } +} diff --git a/src/PhpSpreadsheet/Chart/PlotArea.php b/src/PhpSpreadsheet/Chart/PlotArea.php index e5e390789..05a9ce20e 100644 --- a/src/PhpSpreadsheet/Chart/PlotArea.php +++ b/src/PhpSpreadsheet/Chart/PlotArea.php @@ -38,6 +38,8 @@ class PlotArea */ private array $plotSeries; + private ?DataTable $dataTable = null; + /** * Create a new PlotArea. * @@ -195,6 +197,18 @@ class PlotArea return $this; } + public function setDataTable(DataTable $dataTable): self + { + $this->dataTable = $dataTable; + + return $this; + } + + public function getDataTable(): ?DataTable + { + return $this->dataTable; + } + /** * Implement PHP __clone to create a deep clone, not just a shallow copy. */ diff --git a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php index 4ae3fd4e2..2b6dfb687 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/Chart.php @@ -8,6 +8,7 @@ use PhpOffice\PhpSpreadsheet\Chart\AxisText; use PhpOffice\PhpSpreadsheet\Chart\ChartColor; use PhpOffice\PhpSpreadsheet\Chart\DataSeries; use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues; +use PhpOffice\PhpSpreadsheet\Chart\DataTable; use PhpOffice\PhpSpreadsheet\Chart\GridLines; use PhpOffice\PhpSpreadsheet\Chart\Layout; use PhpOffice\PhpSpreadsheet\Chart\Legend; @@ -137,7 +138,7 @@ class Chart break; case 'plotArea': - $plotAreaLayout = $XaxisLabel = $YaxisLabel = null; + $plotAreaLayout = $XaxisLabel = $YaxisLabel = $dataTable = null; $plotSeries = $plotAttributes = []; $catAxRead = false; $plotNoFill = false; @@ -358,6 +359,10 @@ class Chart } $plotAttributes = $this->readChartAttributes($chartDetail); + break; + case 'dTable': + $dataTable = $this->readDataTable($chartDetail); + break; } } @@ -365,6 +370,11 @@ class Chart $plotAreaLayout = new Layout(); } $plotArea = new PlotArea($plotAreaLayout, $plotSeries); + if ($dataTable !== null) { + $plotArea->setDataTable( + $dataTable + ); + } $this->setChartAttributes($plotAreaLayout, $plotAttributes); if ($plotNoFill) { $plotArea->setNoFill(true); @@ -1295,6 +1305,29 @@ class Chart return $plotAttributes; } + private function readDataTable(SimpleXMLElement $chartDetail): DataTable + { + $dataTable = new DataTable(); + $temp = self::getAttributeBoolean($chartDetail->showHorzBorder, 'val'); + if ($temp !== null) { + $dataTable->setShowHorizontalBorder($temp); + } + $temp = self::getAttributeBoolean($chartDetail->showVertBorder, 'val'); + if ($temp !== null) { + $dataTable->setShowVerticalBorder($temp); + } + $temp = self::getAttributeBoolean($chartDetail->showOutline, 'val'); + if ($temp !== null) { + $dataTable->setShowOutline($temp); + } + $temp = self::getAttributeBoolean($chartDetail->showKeys, 'val'); + if ($temp !== null) { + $dataTable->setShowKeys($temp); + } + + return $dataTable; + } + /** @param array $plotAttributes */ private function setChartAttributes(Layout $plotArea, array $plotAttributes): void { diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php index db8c0321c..1cbc38790 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php @@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Chart as SpreadsheetChart; use PhpOffice\PhpSpreadsheet\Chart\ChartColor; use PhpOffice\PhpSpreadsheet\Chart\DataSeries; use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues; +use PhpOffice\PhpSpreadsheet\Chart\DataTable; use PhpOffice\PhpSpreadsheet\Chart\Layout; use PhpOffice\PhpSpreadsheet\Chart\Legend; use PhpOffice\PhpSpreadsheet\Chart\PlotArea; @@ -444,6 +445,10 @@ class Chart extends WriterPart $this->writeValueAxis($objWriter, $xAxisLabel, $chartType, $id2, $id1, $catIsMultiLevelSeries, $xAxis ?? new Axis()); } else { $this->writeCategoryAxis($objWriter, $xAxisLabel, $id1, $id2, $catIsMultiLevelSeries, $xAxis ?? new Axis()); + $dataTable = $plotArea->getDataTable(); + if ($dataTable !== null) { + $this->writeDataTable($objWriter, $dataTable); + } } $this->writeValueAxis($objWriter, $yAxisLabel, $chartType, $id1, $id2, $valIsMultiLevelSeries, $yAxis ?? new Axis()); @@ -482,6 +487,29 @@ class Chart extends WriterPart $objWriter->endElement(); // c:plotArea } + private function writeDataTable(XMLWriter $objWriter, DataTable $dataTable): void + { + $objWriter->startElement('c:dTable'); + + $objWriter->startElement('c:showHorzBorder'); + $objWriter->writeAttribute('val', $dataTable->getShowHorizontalBorder() ? '1' : '0'); + $objWriter->endElement(); + + $objWriter->startElement('c:showVertBorder'); + $objWriter->writeAttribute('val', $dataTable->getShowVerticalBorder() ? '1' : '0'); + $objWriter->endElement(); + + $objWriter->startElement('c:showOutline'); + $objWriter->writeAttribute('val', $dataTable->getShowOutline() ? '1' : '0'); + $objWriter->endElement(); + + $objWriter->startElement('c:showKeys'); + $objWriter->writeAttribute('val', $dataTable->getShowKeys() ? '1' : '0'); + $objWriter->endElement(); + + $objWriter->endElement(); // c:dTable + } + private function writeDataLabelsBool(XMLWriter $objWriter, string $name, ?bool $value): void { if ($value !== null) { diff --git a/tests/PhpSpreadsheetTests/Chart/DataTableTest.php b/tests/PhpSpreadsheetTests/Chart/DataTableTest.php new file mode 100644 index 000000000..f9448a866 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Chart/DataTableTest.php @@ -0,0 +1,173 @@ +getShowHorizontalBorder()); + self::assertTrue($dataTable->getShowVerticalBorder()); + self::assertTrue($dataTable->getShowOutline()); + self::assertTrue($dataTable->getShowKeys()); + $dataTable->setShowHorizontalBorder(false) + ->setShowVerticalBorder(false) + ->setShowOutline(false) + ->setShowKeys(false); + self::assertFalse($dataTable->getShowHorizontalBorder()); + self::assertFalse($dataTable->getShowVerticalBorder()); + self::assertFalse($dataTable->getShowOutline()); + self::assertFalse($dataTable->getShowKeys()); + } + + private function readCharts(XlsxReader $reader): void + { + $reader->setIncludeCharts(true); + } + + private function writeCharts(XlsxWriter $writer): void + { + $writer->setIncludeCharts(true); + } + + public function testCopyDataTable(): void + { + $spreadsheet = new Spreadsheet(); + // based on 33_Chart_create_area3. + $spreadsheet->getTheme() + ->setThemeColorName( + SpreadsheetTheme::COLOR_SCHEME_2013_2022_NAME + ); + $worksheet = $spreadsheet->getActiveSheet(); + $sheet2 = $spreadsheet->createSheet(); + $sheet2->setTitle('Sheet2'); + $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( + DataSeries::TYPE_AREACHART, // plotType + DataSeries::GROUPING_PERCENT_STACKED, // plotGrouping + range(0, count($dataSeriesValues) - 1), // plotOrder + $dataSeriesLabels, // plotLabel + $xAxisTickValues, // plotCategory + $dataSeriesValues // plotValues + ); + + // Set the series in the plot area + $plotArea = new PlotArea(null, [$series]); + $plotArea->setDataTable(new DataTable()); + // No need for Legend if using DataTable + $legend = null; //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('A1'); + $chart->setBottomRightPosition('H18'); + + // Add the chart to the worksheet + $sheet2->addChart($chart); + $spreadsheet->setActiveSheetIndex(1); + + // Save Excel 2007 file + /** @var callable */ + $callableReader = [$this, 'readCharts']; + /** @var callable */ + $callableWriter = [$this, 'writeCharts']; + $reloadedSpreadsheet = $this->writeAndReload( + $spreadsheet, + 'Xlsx', + $this->readCharts(...), + $this->writeCharts(...) + ); + $spreadsheet->disconnectWorksheets(); + + $sheet = $reloadedSpreadsheet->getActiveSheet(); + $charts2 = $sheet->getChartCollection(); + self::assertCount(1, $charts2); + $chart2 = $charts2[0]; + self::assertNotNull($chart2); + $plotArea = $chart2->getPlotArea(); + self::assertNotNull($plotArea); + $dtab = $plotArea->getDataTable(); + self::assertNotNull($dtab); + self::assertTrue($dtab->getShowHorizontalBorder()); + self::assertTrue($dtab->getShowVerticalBorder()); + self::assertTrue($dtab->getShowOutline()); + self::assertTrue($dtab->getShowKeys()); + + $reloadedSpreadsheet->disconnectWorksheets(); + } +}