Skip to content

Commit

Permalink
Don't corrupt file when using chart with fill color
Browse files Browse the repository at this point in the history
When the fill color property of `DataSeries.plotLabel` using a
DataSeriesValues on a line chart is set, the XLSX file written
is corrupted, and MSExcel2016 removes the drawing1.xml if forced open.

This problem was already documented on issue #589 along with a possible
solution. So all credits go to @madrussa. I am only submitting the PR.

Fixes #589
Closes #1930
  • Loading branch information
tiagomalheiro authored and PowerKiKi committed Nov 23, 2021
1 parent b674042 commit 0c93bba
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 1 deletion.
11 changes: 10 additions & 1 deletion src/PhpSpreadsheet/Writer/Xlsx/Chart.php
Original file line number Diff line number Diff line change
Expand Up @@ -1058,7 +1058,7 @@ private function writePlotGroup(?DataSeries $plotGroup, $groupType, XMLWriter $o
$objWriter->startElement('c:ser');

$plotLabel = $plotGroup->getPlotLabelByIndex($plotSeriesIdx);
if ($plotLabel) {
if ($plotLabel && $groupType !== DataSeries::TYPE_LINECHART) {
$fillColor = $plotLabel->getFillColor();
if ($fillColor !== null && !is_array($fillColor)) {
$objWriter->startElement('c:spPr');
Expand Down Expand Up @@ -1116,6 +1116,15 @@ private function writePlotGroup(?DataSeries $plotGroup, $groupType, XMLWriter $o
if ($groupType == DataSeries::TYPE_STOCKCHART) {
$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();
Expand Down
162 changes: 162 additions & 0 deletions tests/PhpSpreadsheetTests/Writer/Xlsx/Issue589Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<?php

namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;

use DOMDocument;
use DOMNode;
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as Writer;
use PHPUnit\Framework\TestCase;
use ZipArchive;

class Issue589Test extends TestCase
{
/**
* Build a testable chart in a spreadsheet and set fill color for series.
*
* @param string|string[] $color HEX color or array with HEX colors
*/
private function buildChartSpreadsheet($color): Spreadsheet
{
// Problem occurs when setting plot line color
// The output chart xml file is missing the a:ln tag
$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$worksheet->fromArray(
[
[2010],
[12],
[56],
]
);

$dataSeriesLabels = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$1', null, 1),
];
$dataSeriesLabels[0]->setFillColor($color);
$dataSeriesValues = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$A$2:$A$3', null, 2),
];

// Build the dataseries
$series = new DataSeries(
DataSeries::TYPE_LINECHART,
DataSeries::GROUPING_STACKED,
range(0, count($dataSeriesValues) - 1),
$dataSeriesLabels,
[],
$dataSeriesValues
);

// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);

// Create the chart
$chart = new Chart(
'chart1',
null,
null,
$plotArea
);

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

return $spreadsheet;
}

public function testLineChartFill(): void
{
$outputFilename = File::temporaryFilename();
$spreadsheet = $this->buildChartSpreadsheet('98B954');
$writer = new Writer($spreadsheet);
$writer->setIncludeCharts(true);
$writer->save($outputFilename);

$zip = new ZipArchive();
$zip->open($outputFilename);
$resultChart1Raw = $zip->getFromName('xl/charts/chart1.xml');
$zip->close();
unlink($outputFilename);

$dom = new DOMDocument();
if ($resultChart1Raw === false) {
self::fail('Unable to open the chart file');
} else {
$loaded = $dom->loadXML($resultChart1Raw);
if (!$loaded) {
self::fail('Unable to load the chart xml');
} else {
$series = $dom->getElementsByTagName('ser');
$firstSeries = $series->item(0);
if ($firstSeries === null) {
self::fail('The chart XML does not contain a \'ser\' tag!');
} else {
$spPrList = $firstSeries->getElementsByTagName('spPr');

// expect to see only one element with name 'c:spPr'
self::assertCount(1, $spPrList);

/** @var DOMNode $node */
$node = $spPrList->item(0); // Get the spPr element
$actualXml = $dom->saveXML($node);
if ($actualXml === false) {
self::fail('Failure saving the spPr element as xml string!');
} else {
self::assertXmlStringEqualsXmlString('<c:spPr><a:ln w="12700"><a:solidFill><a:srgbClr val="98B954"/></a:solidFill></a:ln></c:spPr>', $actualXml);
}
}
}
}
}

public function testLineChartFillIgnoresColorArray(): void
{
$outputFilename = File::temporaryFilename();
$spreadsheet = $this->buildChartSpreadsheet(['98B954']);
$writer = new Writer($spreadsheet);
$writer->setIncludeCharts(true);
$writer->save($outputFilename);

$zip = new ZipArchive();
$zip->open($outputFilename);
$resultChart1Raw = $zip->getFromName('xl/charts/chart1.xml');
$zip->close();
unlink($outputFilename);

$dom = new DOMDocument();
if ($resultChart1Raw === false) {
self::fail('Unable to open the chart file');
} else {
$loaded = $dom->loadXML($resultChart1Raw);
if (!$loaded) {
self::fail('Unable to load the chart xml');
} else {
$series = $dom->getElementsByTagName('ser');
$firstSeries = $series->item(0);
if ($firstSeries === null) {
self::fail('The chart XML does not contain a \'ser\' tag!');
} else {
$spPrList = $firstSeries->getElementsByTagName('spPr');

// expect to see only one element with name 'c:spPr'
self::assertCount(1, $spPrList);

/** @var DOMNode $node */
$node = $spPrList->item(0); // Get the spPr element
$actualXml = $dom->saveXML($node);
if ($actualXml === false) {
self::fail('Failure saving the spPr element as xml string!');
} else {
self::assertXmlStringEqualsXmlString('<c:spPr><a:ln w="12700"></a:ln></c:spPr>', $actualXml);
}
}
}
}
}
}

0 comments on commit 0c93bba

Please sign in to comment.