From 117f89c751b5d770ecf6459094917521a8477e1b Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:31:26 -0800 Subject: [PATCH 1/4] Clone Worksheet With Table Fix #3820. When cloning a worksheet, special handling is already included for `cellCollection` and `drawingCollection`. It needs to be added for `tableCollection` as well. In theory, `chartCollection` also needs it. However, there are no clone methods for any of the Chart objects, so that will be a substantially larger effort. There is no need to delay this fix waiting for that. --- src/PhpSpreadsheet/Worksheet/Worksheet.php | 8 ++++ .../Worksheet/Table/Issue3820Test.php | 41 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 tests/PhpSpreadsheetTests/Worksheet/Table/Issue3820Test.php diff --git a/src/PhpSpreadsheet/Worksheet/Worksheet.php b/src/PhpSpreadsheet/Worksheet/Worksheet.php index 35d3ff866d..d572f6ccc8 100644 --- a/src/PhpSpreadsheet/Worksheet/Worksheet.php +++ b/src/PhpSpreadsheet/Worksheet/Worksheet.php @@ -3744,6 +3744,14 @@ public function __clone() $newDrawing->setWorksheet($this); } } + } elseif ($key == 'tableCollection') { + $currentCollection = $this->tableCollection; + $this->tableCollection = new ArrayObject(); + foreach ($currentCollection as $item) { + $newTable = clone $item; + $newTable->setName($item->getName() . 'clone'); + $this->addTable($newTable); + } } elseif (($key == 'autoFilter') && ($this->autoFilter instanceof AutoFilter)) { $newAutoFilter = clone $this->autoFilter; $this->autoFilter = $newAutoFilter; diff --git a/tests/PhpSpreadsheetTests/Worksheet/Table/Issue3820Test.php b/tests/PhpSpreadsheetTests/Worksheet/Table/Issue3820Test.php new file mode 100644 index 0000000000..c9c8c917cc --- /dev/null +++ b/tests/PhpSpreadsheetTests/Worksheet/Table/Issue3820Test.php @@ -0,0 +1,41 @@ +getSpreadsheet(); + $sheet = $this->getSheet(); + $sheet->setTitle('Original'); + $sheet->fromArray( + [ + ['MyCol', 'Colonne2', 'Colonne3'], + [10, 20], + [2], + [3], + [4], + ], + null, + 'B1', + true + ); + $table = new Table('B1:D5', 'Tableau1'); + $sheet->addTable($table); + $clonedSheet = clone $sheet; + $clonedSheet->setTitle('Cloned'); + $spreadsheet->addsheet($clonedSheet); + $originalTable = $spreadsheet->getTableByName('Tableau1'); + $newTable = $spreadsheet->getTableByName('Tableau1clone'); + self::assertNotNull($newTable); + self::assertSame($table, $originalTable); + self::assertSame('Cloned', $newTable->getWorksheet()?->getTitle()); + self::assertSame('B1:D5', $newTable->getRange()); + } +} From a5ce6b55294bc19982fee420f6424808bdb63bc4 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Tue, 12 Dec 2023 09:49:23 -0800 Subject: [PATCH 2/4] Add Cloning of Charts I had originally said I wouldn't add it to this PR, but I have changed my mind. --- src/PhpSpreadsheet/Chart/Axis.php | 12 ++ src/PhpSpreadsheet/Chart/AxisText.php | 9 + src/PhpSpreadsheet/Chart/Chart.php | 17 ++ src/PhpSpreadsheet/Chart/DataSeries.php | 27 +++ src/PhpSpreadsheet/Chart/DataSeriesValues.php | 25 +++ src/PhpSpreadsheet/Chart/Layout.php | 11 ++ src/PhpSpreadsheet/Chart/Legend.php | 11 ++ src/PhpSpreadsheet/Chart/PlotArea.php | 13 ++ src/PhpSpreadsheet/Chart/Properties.php | 10 ++ src/PhpSpreadsheet/Chart/Title.php | 18 ++ src/PhpSpreadsheet/Style/Font.php | 10 ++ src/PhpSpreadsheet/Worksheet/Worksheet.php | 13 +- .../Chart/ChartCloneTest.php | 164 ++++++++++++++++++ 13 files changed, 336 insertions(+), 4 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Chart/ChartCloneTest.php diff --git a/src/PhpSpreadsheet/Chart/Axis.php b/src/PhpSpreadsheet/Chart/Axis.php index 3f9249cedd..5b20e91dd4 100644 --- a/src/PhpSpreadsheet/Chart/Axis.php +++ b/src/PhpSpreadsheet/Chart/Axis.php @@ -316,4 +316,16 @@ public function getNoFill(): bool { return $this->noFill; } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + parent::__clone(); + $this->majorGridlines = ($this->majorGridlines === null) ? null : clone $this->majorGridlines; + $this->majorGridlines = ($this->minorGridlines === null) ? null : clone $this->minorGridlines; + $this->axisText = ($this->axisText === null) ? null : clone $this->axisText; + $this->fillColor = clone $this->fillColor; + } } diff --git a/src/PhpSpreadsheet/Chart/AxisText.php b/src/PhpSpreadsheet/Chart/AxisText.php index e06a2c88a8..09d6b2a5ee 100644 --- a/src/PhpSpreadsheet/Chart/AxisText.php +++ b/src/PhpSpreadsheet/Chart/AxisText.php @@ -51,4 +51,13 @@ public function setFont(Font $font): self return $this; } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + parent::__clone(); + $this->font = clone $this->font; + } } diff --git a/src/PhpSpreadsheet/Chart/Chart.php b/src/PhpSpreadsheet/Chart/Chart.php index 2b8de5d517..70f6b4a5f2 100644 --- a/src/PhpSpreadsheet/Chart/Chart.php +++ b/src/PhpSpreadsheet/Chart/Chart.php @@ -835,4 +835,21 @@ public function getRenderedHeight(): ?float { return $this->renderedHeight; } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + $this->worksheet = null; + $this->title = ($this->title === null) ? null : clone $this->title; + $this->legend = ($this->legend === null) ? null : clone $this->legend; + $this->xAxisLabel = ($this->xAxisLabel === null) ? null : clone $this->xAxisLabel; + $this->yAxisLabel = ($this->yAxisLabel === null) ? null : clone $this->yAxisLabel; + $this->plotArea = ($this->plotArea === null) ? null : clone $this->plotArea; + $this->xAxis = clone $this->xAxis; + $this->yAxis = clone $this->yAxis; + $this->borderLines = clone $this->borderLines; + $this->fillColor = clone $this->fillColor; + } } diff --git a/src/PhpSpreadsheet/Chart/DataSeries.php b/src/PhpSpreadsheet/Chart/DataSeries.php index 6475793457..b8656ad5dd 100644 --- a/src/PhpSpreadsheet/Chart/DataSeries.php +++ b/src/PhpSpreadsheet/Chart/DataSeries.php @@ -408,4 +408,31 @@ public function refresh(Worksheet $worksheet): void } } } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + $plotLabels = $this->plotLabel; + $this->plotLabel = []; + foreach ($plotLabels as $plotLabel) { + $this->plotLabel[] = $plotLabel; + } + $plotCategories = $this->plotCategory; + $this->plotCategory = []; + foreach ($plotCategories as $plotCategory) { + $this->plotCategory[] = clone $plotCategory; + } + $plotValues = $this->plotValues; + $this->plotValues = []; + foreach ($plotValues as $plotValue) { + $this->plotValues[] = clone $plotValue; + } + $plotBubbleSizes = $this->plotBubbleSizes; + $this->plotBubbleSizes = []; + foreach ($plotBubbleSizes as $plotBubbleSize) { + $this->plotBubbleSizes[] = clone $plotBubbleSize; + } + } } diff --git a/src/PhpSpreadsheet/Chart/DataSeriesValues.php b/src/PhpSpreadsheet/Chart/DataSeriesValues.php index 72bbf81036..b31714b72c 100644 --- a/src/PhpSpreadsheet/Chart/DataSeriesValues.php +++ b/src/PhpSpreadsheet/Chart/DataSeriesValues.php @@ -587,4 +587,29 @@ public function getTrendLines(): array { return $this->trendLines; } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + parent::__clone(); + $this->markerFillColor = clone $this->markerFillColor; + $this->markerBorderColor = clone $this->markerBorderColor; + if (is_array($this->fillColor)) { + $fillColor = $this->fillColor; + $this->fillColor = []; + foreach ($fillColor as $color) { + $this->fillColor[] = clone $color; + } + } elseif ($this->fillColor instanceof ChartColor) { + $this->fillColor = clone $this->fillColor; + } + $this->labelLayout = ($this->labelLayout === null) ? null : clone $this->labelLayout; + $trendLines = $this->trendLines; + $this->trendLines = []; + foreach ($trendLines as $trendLine) { + $this->trendLines[] = clone $trendLine; + } + } } diff --git a/src/PhpSpreadsheet/Chart/Layout.php b/src/PhpSpreadsheet/Chart/Layout.php index f64f53319b..97bbc8fd8c 100644 --- a/src/PhpSpreadsheet/Chart/Layout.php +++ b/src/PhpSpreadsheet/Chart/Layout.php @@ -528,4 +528,15 @@ public function setNumFmtLinked(bool $numFmtLinked): self return $this; } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + $this->labelFillColor = ($this->labelFillColor === null) ? null : clone $this->labelFillColor; + $this->labelBorderColor = ($this->labelBorderColor === null) ? null : clone $this->labelBorderColor; + $this->labelFont = ($this->labelFont === null) ? null : clone $this->labelFont; + $this->labelEffects = ($this->labelEffects === null) ? null : clone $this->labelEffects; + } } diff --git a/src/PhpSpreadsheet/Chart/Legend.php b/src/PhpSpreadsheet/Chart/Legend.php index 7c5fa1833f..44c098d95f 100644 --- a/src/PhpSpreadsheet/Chart/Legend.php +++ b/src/PhpSpreadsheet/Chart/Legend.php @@ -175,4 +175,15 @@ public function setBorderLines(GridLines $borderLines): self return $this; } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + $this->layout = ($this->layout === null) ? null : clone $this->layout; + $this->legendText = ($this->legendText === null) ? null : clone $this->legendText; + $this->borderLines = clone $this->borderLines; + $this->fillColor = clone $this->fillColor; + } } diff --git a/src/PhpSpreadsheet/Chart/PlotArea.php b/src/PhpSpreadsheet/Chart/PlotArea.php index aa72fda165..5064d5a0d4 100644 --- a/src/PhpSpreadsheet/Chart/PlotArea.php +++ b/src/PhpSpreadsheet/Chart/PlotArea.php @@ -201,4 +201,17 @@ public function setUseDownBars(bool $useDownBars): self return $this; } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + $this->layout = ($this->layout === null) ? null : clone $this->layout; + $plotSeries = $this->plotSeries; + $this->plotSeries = []; + foreach ($plotSeries as $series) { + $this->plotSeries[] = clone $series; + } + } } diff --git a/src/PhpSpreadsheet/Chart/Properties.php b/src/PhpSpreadsheet/Chart/Properties.php index 740702d12d..8074df6788 100644 --- a/src/PhpSpreadsheet/Chart/Properties.php +++ b/src/PhpSpreadsheet/Chart/Properties.php @@ -957,4 +957,14 @@ public function getLineStyleArrowLength($arrow) { return $this->getLineStyleProperty(['arrow', $arrow, 'len']); } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + $this->lineColor = clone $this->lineColor; + $this->glowColor = clone $this->glowColor; + $this->shadowColor = clone $this->shadowColor; + } } diff --git a/src/PhpSpreadsheet/Chart/Title.php b/src/PhpSpreadsheet/Chart/Title.php index 378987446e..e9c42a4c48 100644 --- a/src/PhpSpreadsheet/Chart/Title.php +++ b/src/PhpSpreadsheet/Chart/Title.php @@ -163,4 +163,22 @@ public function setFont(?Font $font): self return $this; } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + $this->layout = ($this->layout === null) ? null : clone $this->layout; + $this->font = ($this->font === null) ? null : clone $this->font; + if (is_array($this->caption)) { + $captions = $this->caption; + $this->caption = []; + foreach ($captions as $caption) { + $this->caption[] = is_object($caption) ? (clone $caption) : $caption; + } + } else { + $this->caption = is_object($this->caption) ? (clone $this->caption) : $this->caption; + } + } } diff --git a/src/PhpSpreadsheet/Style/Font.php b/src/PhpSpreadsheet/Style/Font.php index ea26b8ce64..78b3f0dc1d 100644 --- a/src/PhpSpreadsheet/Style/Font.php +++ b/src/PhpSpreadsheet/Style/Font.php @@ -878,4 +878,14 @@ public function getCap(): ?string { return $this->cap; } + + /** + * Implement PHP __clone to create a deep clone, not just a shallow copy. + */ + public function __clone() + { + $this->color = clone $this->color; + $this->chartColor = ($this->chartColor === null) ? null : clone $this->chartColor; + $this->underlineColor = ($this->underlineColor === null) ? null : clone $this->underlineColor; + } } diff --git a/src/PhpSpreadsheet/Worksheet/Worksheet.php b/src/PhpSpreadsheet/Worksheet/Worksheet.php index d572f6ccc8..54b9a70e68 100644 --- a/src/PhpSpreadsheet/Worksheet/Worksheet.php +++ b/src/PhpSpreadsheet/Worksheet/Worksheet.php @@ -3739,10 +3739,8 @@ public function __clone() $currentCollection = $this->drawingCollection; $this->drawingCollection = new ArrayObject(); foreach ($currentCollection as $item) { - if (is_object($item)) { - $newDrawing = clone $item; - $newDrawing->setWorksheet($this); - } + $newDrawing = clone $item; + $newDrawing->setWorksheet($this); } } elseif ($key == 'tableCollection') { $currentCollection = $this->tableCollection; @@ -3752,6 +3750,13 @@ public function __clone() $newTable->setName($item->getName() . 'clone'); $this->addTable($newTable); } + } elseif ($key == 'chartCollection') { + $currentCollection = $this->chartCollection; + $this->chartCollection = new ArrayObject(); + foreach ($currentCollection as $item) { + $newChart = clone $item; + $this->addChart($newChart); + } } elseif (($key == 'autoFilter') && ($this->autoFilter instanceof AutoFilter)) { $newAutoFilter = clone $this->autoFilter; $this->autoFilter = $newAutoFilter; diff --git a/tests/PhpSpreadsheetTests/Chart/ChartCloneTest.php b/tests/PhpSpreadsheetTests/Chart/ChartCloneTest.php new file mode 100644 index 0000000000..55c35e6d97 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Chart/ChartCloneTest.php @@ -0,0 +1,164 @@ +setIncludeCharts(true); + $spreadsheet = $reader->load($file); + $oldSheet = $spreadsheet->getActiveSheet(); + + $sheet = clone $oldSheet; + $sheet->setTitle('test2'); + $spreadsheet->addsheet($sheet); + + $oldCharts = $oldSheet->getChartCollection(); + self::assertCount(1, $oldCharts); + $oldChart = $oldCharts[0]; + self::assertNotNull($oldChart); + + $charts = $sheet->getChartCollection(); + self::assertCount(1, $charts); + $chart = $charts[0]; + self::assertNotNull($chart); + self::assertNotSame($oldChart, $chart); + + self::assertSame('ffffff', $chart->getFillColor()->getValue()); + self::assertSame('srgbClr', $chart->getFillColor()->getType()); + self::assertSame('d9d9d9', $chart->getBorderLines()->getLineColorProperty('value')); + self::assertSame('srgbClr', $chart->getBorderLines()->getLineColorProperty('type')); + self::assertEqualsWithDelta(9360 / Properties::POINTS_WIDTH_MULTIPLIER, $chart->getBorderLines()->getLineStyleProperty('width'), 1.0E-8); + self::assertTrue($chart->getChartAxisY()->getNoFill()); + self::assertFalse($chart->getChartAxisX()->getNoFill()); + + $spreadsheet->disconnectWorksheets(); + } + + public function testCloneSheetWithLegendAndTitle(): void + { + $file = self::DIRECTORY . '32readwriteChartWithImages1.xlsx'; + $reader = new XlsxReader(); + $reader->setIncludeCharts(true); + $spreadsheet = $reader->load($file); + $oldSheet = $spreadsheet->getActiveSheet(); + + $sheet = clone $oldSheet; + $sheet->setTitle('test2'); + $spreadsheet->addsheet($sheet); + + $oldCharts = $oldSheet->getChartCollection(); + self::assertCount(1, $oldCharts); + $oldChart = $oldCharts[0]; + self::assertNotNull($oldChart); + + $charts = $sheet->getChartCollection(); + self::assertCount(1, $charts); + $chart = $charts[0]; + self::assertNotNull($chart); + self::assertNotSame($oldChart, $chart); + + self::assertNotNull($chart->getLegend()); + self::assertNotSame($chart->getLegend(), $oldChart->getLegend()); + self::assertNotNull($chart->getTitle()); + self::assertNotSame($chart->getTitle(), $oldChart->getTitle()); + + $spreadsheet->disconnectWorksheets(); + } + + public function testCloneSheetWithBubbleSizes(): void + { + $file = self::DIRECTORY . '32readwriteBubbleChart2.xlsx'; + $reader = new XlsxReader(); + $reader->setIncludeCharts(true); + $spreadsheet = $reader->load($file); + $oldSheet = $spreadsheet->getActiveSheet(); + + $sheet = clone $oldSheet; + $sheet->setTitle('test2'); + $spreadsheet->addsheet($sheet); + + $oldCharts = $oldSheet->getChartCollection(); + self::assertCount(1, $oldCharts); + $oldChart = $oldCharts[0]; + self::assertNotNull($oldChart); + + $charts = $sheet->getChartCollection(); + self::assertCount(1, $charts); + $chart = $charts[0]; + self::assertNotNull($chart); + self::assertNotSame($oldChart, $chart); + + $oldGroup = $oldChart->getPlotArea()?->getPlotGroup(); + self::assertNotNull($oldGroup); + self::assertCount(1, $oldGroup); + $oldSizes = $oldGroup[0]->getPlotBubbleSizes(); + self::assertCount(2, $oldSizes); + + $plotGroup = $chart->getPlotArea()?->getPlotGroup(); + self::assertNotNull($plotGroup); + self::assertCount(1, $plotGroup); + $bubbleSizes = $plotGroup[0]->getPlotBubbleSizes(); + self::assertCount(2, $bubbleSizes); + self::assertNotSame($bubbleSizes, $oldSizes); + + $spreadsheet->disconnectWorksheets(); + } + + public function testCloneSheetWithTrendLines(): void + { + $file = self::DIRECTORY . '32readwriteScatterChartTrendlines1.xlsx'; + $reader = new XlsxReader(); + $reader->setIncludeCharts(true); + $spreadsheet = $reader->load($file); + $oldSheet = $spreadsheet->getActiveSheet(); + + $sheet = clone $oldSheet; + $sheet->setTitle('test2'); + $spreadsheet->addsheet($sheet); + + $oldCharts = $oldSheet->getChartCollection(); + self::assertCount(2, $oldCharts); + $oldChart = $oldCharts[1]; + self::assertNotNull($oldChart); + + $charts = $sheet->getChartCollection(); + self::assertCount(2, $charts); + $chart = $charts[1]; + self::assertNotNull($chart); + self::assertNotSame($oldChart, $chart); + + $oldGroup = $chart->getPlotArea()?->getPlotGroup(); + self::assertNotNull($oldGroup); + self::assertCount(1, $oldGroup); + $oldLabels = $oldGroup[0]->getPlotLabels(); + self::assertCount(1, $oldLabels); + self::assertCount(3, $oldLabels[0]->getTrendLines()); + + $plotGroup = $chart->getPlotArea()?->getPlotGroup(); + self::assertNotNull($plotGroup); + self::assertCount(1, $plotGroup); + $plotLabels = $plotGroup[0]->getPlotLabels(); + self::assertCount(1, $plotLabels); + self::assertCount(3, $plotLabels[0]->getTrendLines()); + + $spreadsheet->disconnectWorksheets(); + } +} From af5b6316c9b17163b33f751e90d4fc5fc1a1beb2 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Tue, 12 Dec 2023 13:28:26 -0800 Subject: [PATCH 3/4] Cover A Bit More Code --- .../Chart/ChartCloneTest.php | 135 +++++++++++++++++- 1 file changed, 129 insertions(+), 6 deletions(-) diff --git a/tests/PhpSpreadsheetTests/Chart/ChartCloneTest.php b/tests/PhpSpreadsheetTests/Chart/ChartCloneTest.php index 55c35e6d97..63b076c3ef 100644 --- a/tests/PhpSpreadsheetTests/Chart/ChartCloneTest.php +++ b/tests/PhpSpreadsheetTests/Chart/ChartCloneTest.php @@ -4,16 +4,18 @@ namespace PhpOffice\PhpSpreadsheetTests\Chart; +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\Properties; +use PhpOffice\PhpSpreadsheet\Chart\Title; use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader; +use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional; -/** - * Confirm cloning worksheet works as expected. - * This class tests everything except: - * DataSeriesValues - no coverage for fillColor as array. - * Title - no coverage for caption when not array. - */ class ChartCloneTest extends AbstractFunctional { private const DIRECTORY = 'samples' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR; @@ -161,4 +163,125 @@ public function testCloneSheetWithTrendLines(): void $spreadsheet->disconnectWorksheets(); } + + public function testCloneFillColorArray(): void + { + // code borrowed from BarChartCustomColorsTest + $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); + + $sheet = clone $worksheet; + $sheet->setTitle('test2'); + $spreadsheet->addsheet($sheet); + + $charts2 = $sheet->getChartCollection(); + self::assertCount(1, $charts2); + $chart2 = $charts2[0]; + self::assertNotNull($chart2); + self::assertSame('Test Pie Chart', $chart2->getTitle()?->getCaption()); + $plotArea2 = $chart2->getPlotArea(); + self::assertNotNull($plotArea2); + $dataSeries2 = $plotArea2->getPlotGroup(); + self::assertCount(1, $dataSeries2); + $plotValues = $dataSeries2[0]->getPlotValues(); + self::assertCount(1, $plotValues); + $fillColors = $plotValues[0]->getFillColor(); + self::assertSame($colors, $fillColors); + + $spreadsheet->disconnectWorksheets(); + } } From 83daee854b0d16ced43c013dd8e0c11032d72523 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Wed, 13 Dec 2023 08:14:09 -0800 Subject: [PATCH 4/4] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76f9746d41..14e9ec44a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,6 +79,8 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Case Insensitive Comparison for Sheet Names [PR #3791](https://github.com/PHPOffice/PhpSpreadsheet/pull/3791) - Performance improvement for Xlsx Reader. [Issue #3683](https://github.com/PHPOffice/PhpSpreadsheet/issues/3683) [PR #3810](https://github.com/PHPOffice/PhpSpreadsheet/pull/3810) - Prevent loop in Shared/File. [Issue #3807](https://github.com/PHPOffice/PhpSpreadsheet/issues/3807) [PR #3809](https://github.com/PHPOffice/PhpSpreadsheet/pull/3809) +- Consistent handling of decimal/thousands separators between StringHelper and Php setlocale. [Issue #3811](https://github.com/PHPOffice/PhpSpreadsheet/issues/3811) [PR #3815](https://github.com/PHPOffice/PhpSpreadsheet/pull/3815) +- Clone worksheet with tables or charts. [Issue #3820](https://github.com/PHPOffice/PhpSpreadsheet/issues/3820) [PR #3821](https://github.com/PHPOffice/PhpSpreadsheet/pull/3821) ## 1.29.0 - 2023-06-15