Skip to content

Better Handling of Chart DisplayBlanksAs #4414

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).

- BIN2DEC, OCT2DEC, and HEX2DEC return numbers rather than strings. [Issue #4383](https://github.com/PHPOffice/PhpSpreadsheet/issues/4383) [PR #4389](https://github.com/PHPOffice/PhpSpreadsheet/pull/4389)
- Fix TREND_BEST_FIT_NO_POLY. [Issue #4400](https://github.com/PHPOffice/PhpSpreadsheet/issues/4400) [PR #4339](https://github.com/PHPOffice/PhpSpreadsheet/pull/4339)
- Better handling of Chart DisplayBlanksAs. [Issue #4411](https://github.com/PHPOffice/PhpSpreadsheet/issues/4411) [PR #4414](https://github.com/PHPOffice/PhpSpreadsheet/pull/4414)

## 2025-03-02 - 4.1.0

Expand Down
144 changes: 144 additions & 0 deletions samples/Chart33b/33_Chart_create_scatter7_blanks.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?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\Spreadsheet;

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

$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$worksheet->fromArray(
[
['', 2010, 2011, 2012],
['Q1', 12, 15, 21],
['Q2', 56, null, 86],
['Q3', 52, 61, 69],
['Q4', 30, 32, 0],
],
strictNullComparison: true
);

// 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
$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_SCATTERCHART, // plotType
null, // plotGrouping (Scatter charts don't have any grouping)
range(0, count($dataSeriesValues) - 1), // plotOrder
$dataSeriesLabels, // plotLabel
$xAxisTickValues, // plotCategory
$dataSeriesValues, // plotValues
null, // plotDirection
false, // smooth line
DataSeries::STYLE_LINEMARKER // plotStyle
);

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

$title1 = new Title('Test Scatter Chart Gap');
$yAxisLabel1 = new Title('Value ($k)');
// Create the chart
$chart1 = new Chart(
'chart1', // name
$title1, // title
$legend, // legend
$plotArea, // plotArea
true, // plotVisibleOnly
DataSeries::EMPTY_AS_GAP, // displayBlanksAs
null, // xAxisLabel
$yAxisLabel1 // yAxisLabel
);

// 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);

$helper->renderChart($chart1, __FILE__);

$title2 = new Title('Test Scatter Chart Zero');
$yAxisLabel2 = new Title('Value ($k)');
// Create the chart
$chart2 = new Chart(
'chart2', // name
$title2, // title
$legend, // legend
$plotArea, // plotArea
true, // plotVisibleOnly
DataSeries::EMPTY_AS_ZERO, // displayBlanksAs
null, // xAxisLabel
$yAxisLabel2 // yAxisLabel
);

// Set the position where the chart should appear in the worksheet
$chart2->setTopLeftPosition('A22');
$chart2->setBottomRightPosition('H35');

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

$helper->renderChart($chart2, __FILE__);

$title3 = new Title('Test Scatter Chart Span');
$yAxisLabel3 = new Title('Value ($k)');

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

// Set the position where the chart should appear in the worksheet
$chart3->setTopLeftPosition('A37');
$chart3->setBottomRightPosition('H50');

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

$helper->renderChart($chart3, __FILE__);

// Save Excel 2007 file
$helper->write($spreadsheet, __FILE__, ['Xlsx'], true);
7 changes: 4 additions & 3 deletions src/PhpSpreadsheet/Chart/Chart.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class Chart
* Create a new Chart.
* majorGridlines and minorGridlines are deprecated, moved to Axis.
*/
public function __construct(string $name, ?Title $title = null, ?Legend $legend = null, ?PlotArea $plotArea = null, bool $plotVisibleOnly = true, string $displayBlanksAs = DataSeries::EMPTY_AS_GAP, ?Title $xAxisLabel = null, ?Title $yAxisLabel = null, ?Axis $xAxis = null, ?Axis $yAxis = null, ?GridLines $majorGridlines = null, ?GridLines $minorGridlines = null)
public function __construct(string $name, ?Title $title = null, ?Legend $legend = null, ?PlotArea $plotArea = null, bool $plotVisibleOnly = true, string $displayBlanksAs = DataSeries::DEFAULT_EMPTY_AS, ?Title $xAxisLabel = null, ?Title $yAxisLabel = null, ?Axis $xAxis = null, ?Axis $yAxis = null, ?GridLines $majorGridlines = null, ?GridLines $minorGridlines = null)
{
$this->name = $name;
$this->title = $title;
Expand All @@ -137,7 +137,7 @@ public function __construct(string $name, ?Title $title = null, ?Legend $legend
$this->yAxisLabel = $yAxisLabel;
$this->plotArea = $plotArea;
$this->plotVisibleOnly = $plotVisibleOnly;
$this->displayBlanksAs = $displayBlanksAs;
$this->setDisplayBlanksAs($displayBlanksAs);
$this->xAxis = $xAxis ?? new Axis();
$this->yAxis = $yAxis ?? new Axis();
if ($majorGridlines !== null) {
Expand Down Expand Up @@ -318,7 +318,8 @@ public function getDisplayBlanksAs(): string
*/
public function setDisplayBlanksAs(string $displayBlanksAs): static
{
$this->displayBlanksAs = $displayBlanksAs;
$displayBlanksAs = strtolower($displayBlanksAs);
$this->displayBlanksAs = in_array($displayBlanksAs, DataSeries::VALID_EMPTY_AS, true) ? $displayBlanksAs : DataSeries::DEFAULT_EMPTY_AS;

return $this;
}
Expand Down
2 changes: 2 additions & 0 deletions src/PhpSpreadsheet/Chart/DataSeries.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class DataSeries
const EMPTY_AS_GAP = 'gap';
const EMPTY_AS_ZERO = 'zero';
const EMPTY_AS_SPAN = 'span';
const DEFAULT_EMPTY_AS = self::EMPTY_AS_GAP;
const VALID_EMPTY_AS = [self::EMPTY_AS_GAP, self::EMPTY_AS_ZERO, self::EMPTY_AS_SPAN];

/**
* Series Plot Type.
Expand Down
8 changes: 3 additions & 5 deletions src/PhpSpreadsheet/Writer/Xlsx/Chart.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,9 @@ public function writeChart(\PhpOffice\PhpSpreadsheet\Chart\Chart $chart, bool $c
$objWriter->writeAttribute('val', (string) (int) $chart->getPlotVisibleOnly());
$objWriter->endElement();

if ($chart->getDisplayBlanksAs() !== '') {
$objWriter->startElement('c:dispBlanksAs');
$objWriter->writeAttribute('val', $chart->getDisplayBlanksAs());
$objWriter->endElement();
}
$objWriter->startElement('c:dispBlanksAs');
$objWriter->writeAttribute('val', $chart->getDisplayBlanksAs());
$objWriter->endElement();

$objWriter->startElement('c:showDLblsOverMax');
$objWriter->writeAttribute('val', '0');
Expand Down
106 changes: 106 additions & 0 deletions tests/PhpSpreadsheetTests/Chart/DisplayBlanksAsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php

declare(strict_types=1);

namespace PhpOffice\PhpSpreadsheetTests\Chart;

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\Spreadsheet;
use PHPUnit\Framework\TestCase;

class DisplayBlanksAsTest extends TestCase
{
public function testDisplayBlanksAs(): void
{
$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],
]
);

$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
];

$xAxisTickValues = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4
];

$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
);

$plotArea = new PlotArea(null, [$series]);
$legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);

$title = new Title('Test %age-Stacked Area Chart');
$yAxisLabel = new Title('Value ($k)');

$chart1 = new Chart(
'chart1', // name
$title, // title
$legend, // legend
$plotArea, // plotArea
true, // plotVisibleOnly
DataSeries::EMPTY_AS_GAP, // displayBlanksAs
null, // xAxisLabel
$yAxisLabel // yAxisLabel
);
self::assertSame(DataSeries::EMPTY_AS_GAP, $chart1->getDisplayBlanksAs());
$chart1->setDisplayBlanksAs(DataSeries::EMPTY_AS_ZERO);
self::assertSame(DataSeries::EMPTY_AS_ZERO, $chart1->getDisplayBlanksAs());
$chart1->setDisplayBlanksAs('0');
self::assertSame(DataSeries::EMPTY_AS_GAP, $chart1->getDisplayBlanksAs(), 'invalid setting converted to default');

$chart2 = new Chart(
'chart2', // name
$title, // title
$legend, // legend
$plotArea, // plotArea
true, // plotVisibleOnly
DataSeries::EMPTY_AS_SPAN, // displayBlanksAs
null, // xAxisLabel
$yAxisLabel // yAxisLabel
);
self::assertSame(DataSeries::EMPTY_AS_SPAN, $chart2->getDisplayBlanksAs());

$chart3 = new Chart(
'chart3', // name
$title, // title
$legend, // legend
$plotArea, // plotArea
true, // plotVisibleOnly
'0', // displayBlanksAs, PHPExcel default
null, // xAxisLabel
$yAxisLabel // yAxisLabel
);
self::assertSame(DataSeries::EMPTY_AS_GAP, $chart3->getDisplayBlanksAs(), 'invalid setting converted to default');

$spreadsheet->disconnectWorksheets();
}
}