Skip to content

Commit 4c350a6

Browse files
authored
Merge pull request #4414 from oleibman/issue4411
Better Handling of Chart DisplayBlanksAs
2 parents 3cb7b36 + 4670aea commit 4c350a6

File tree

6 files changed

+260
-8
lines changed

6 files changed

+260
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
3636

3737
- 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)
3838
- Fix TREND_BEST_FIT_NO_POLY. [Issue #4400](https://github.com/PHPOffice/PhpSpreadsheet/issues/4400) [PR #4339](https://github.com/PHPOffice/PhpSpreadsheet/pull/4339)
39+
- Better handling of Chart DisplayBlanksAs. [Issue #4411](https://github.com/PHPOffice/PhpSpreadsheet/issues/4411) [PR #4414](https://github.com/PHPOffice/PhpSpreadsheet/pull/4414)
3940

4041
## 2025-03-02 - 4.1.0
4142

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
<?php
2+
3+
use PhpOffice\PhpSpreadsheet\Chart\Chart;
4+
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
5+
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
6+
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
7+
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
8+
use PhpOffice\PhpSpreadsheet\Chart\Title;
9+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
10+
11+
require __DIR__ . '/../Header.php';
12+
13+
$spreadsheet = new Spreadsheet();
14+
$worksheet = $spreadsheet->getActiveSheet();
15+
$worksheet->fromArray(
16+
[
17+
['', 2010, 2011, 2012],
18+
['Q1', 12, 15, 21],
19+
['Q2', 56, null, 86],
20+
['Q3', 52, 61, 69],
21+
['Q4', 30, 32, 0],
22+
],
23+
strictNullComparison: true
24+
);
25+
26+
// Set the Labels for each data series we want to plot
27+
// Datatype
28+
// Cell reference for data
29+
// Format Code
30+
// Number of datapoints in series
31+
// Data values
32+
// Data Marker
33+
$dataSeriesLabels = [
34+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$B$1', null, 1), // 2010
35+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', null, 1), // 2011
36+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$D$1', null, 1), // 2012
37+
];
38+
// Set the X-Axis Labels
39+
$xAxisTickValues = [
40+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4
41+
];
42+
// Set the Data values for each data series we want to plot
43+
// Datatype
44+
// Cell reference for data
45+
// Format Code
46+
// Number of datapoints in series
47+
// Data values
48+
// Data Marker
49+
$dataSeriesValues = [
50+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$B$2:$B$5', null, 4),
51+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', null, 4),
52+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$D$2:$D$5', null, 4),
53+
];
54+
55+
// Build the dataseries
56+
$series = new DataSeries(
57+
DataSeries::TYPE_SCATTERCHART, // plotType
58+
null, // plotGrouping (Scatter charts don't have any grouping)
59+
range(0, count($dataSeriesValues) - 1), // plotOrder
60+
$dataSeriesLabels, // plotLabel
61+
$xAxisTickValues, // plotCategory
62+
$dataSeriesValues, // plotValues
63+
null, // plotDirection
64+
false, // smooth line
65+
DataSeries::STYLE_LINEMARKER // plotStyle
66+
);
67+
68+
// Set the series in the plot area
69+
$plotArea = new PlotArea(null, [$series]);
70+
// Set the chart legend
71+
$legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
72+
73+
$title1 = new Title('Test Scatter Chart Gap');
74+
$yAxisLabel1 = new Title('Value ($k)');
75+
// Create the chart
76+
$chart1 = new Chart(
77+
'chart1', // name
78+
$title1, // title
79+
$legend, // legend
80+
$plotArea, // plotArea
81+
true, // plotVisibleOnly
82+
DataSeries::EMPTY_AS_GAP, // displayBlanksAs
83+
null, // xAxisLabel
84+
$yAxisLabel1 // yAxisLabel
85+
);
86+
87+
// Set the position where the chart should appear in the worksheet
88+
$chart1->setTopLeftPosition('A7');
89+
$chart1->setBottomRightPosition('H20');
90+
91+
// Add the chart to the worksheet
92+
$worksheet->addChart($chart1);
93+
94+
$helper->renderChart($chart1, __FILE__);
95+
96+
$title2 = new Title('Test Scatter Chart Zero');
97+
$yAxisLabel2 = new Title('Value ($k)');
98+
// Create the chart
99+
$chart2 = new Chart(
100+
'chart2', // name
101+
$title2, // title
102+
$legend, // legend
103+
$plotArea, // plotArea
104+
true, // plotVisibleOnly
105+
DataSeries::EMPTY_AS_ZERO, // displayBlanksAs
106+
null, // xAxisLabel
107+
$yAxisLabel2 // yAxisLabel
108+
);
109+
110+
// Set the position where the chart should appear in the worksheet
111+
$chart2->setTopLeftPosition('A22');
112+
$chart2->setBottomRightPosition('H35');
113+
114+
// Add the chart to the worksheet
115+
$worksheet->addChart($chart2);
116+
117+
$helper->renderChart($chart2, __FILE__);
118+
119+
$title3 = new Title('Test Scatter Chart Span');
120+
$yAxisLabel3 = new Title('Value ($k)');
121+
122+
// Create the chart
123+
$chart3 = new Chart(
124+
'chart3', // name
125+
$title3, // title
126+
$legend, // legend
127+
$plotArea, // plotArea
128+
true, // plotVisibleOnly
129+
DataSeries::EMPTY_AS_SPAN, // displayBlanksAs
130+
null, // xAxisLabel
131+
$yAxisLabel3 // yAxisLabel
132+
);
133+
134+
// Set the position where the chart should appear in the worksheet
135+
$chart3->setTopLeftPosition('A37');
136+
$chart3->setBottomRightPosition('H50');
137+
138+
// Add the chart to the worksheet
139+
$worksheet->addChart($chart3);
140+
141+
$helper->renderChart($chart3, __FILE__);
142+
143+
// Save Excel 2007 file
144+
$helper->write($spreadsheet, __FILE__, ['Xlsx'], true);

src/PhpSpreadsheet/Chart/Chart.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ class Chart
128128
* Create a new Chart.
129129
* majorGridlines and minorGridlines are deprecated, moved to Axis.
130130
*/
131-
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)
131+
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)
132132
{
133133
$this->name = $name;
134134
$this->title = $title;
@@ -137,7 +137,7 @@ public function __construct(string $name, ?Title $title = null, ?Legend $legend
137137
$this->yAxisLabel = $yAxisLabel;
138138
$this->plotArea = $plotArea;
139139
$this->plotVisibleOnly = $plotVisibleOnly;
140-
$this->displayBlanksAs = $displayBlanksAs;
140+
$this->setDisplayBlanksAs($displayBlanksAs);
141141
$this->xAxis = $xAxis ?? new Axis();
142142
$this->yAxis = $yAxis ?? new Axis();
143143
if ($majorGridlines !== null) {
@@ -318,7 +318,8 @@ public function getDisplayBlanksAs(): string
318318
*/
319319
public function setDisplayBlanksAs(string $displayBlanksAs): static
320320
{
321-
$this->displayBlanksAs = $displayBlanksAs;
321+
$displayBlanksAs = strtolower($displayBlanksAs);
322+
$this->displayBlanksAs = in_array($displayBlanksAs, DataSeries::VALID_EMPTY_AS, true) ? $displayBlanksAs : DataSeries::DEFAULT_EMPTY_AS;
322323

323324
return $this;
324325
}

src/PhpSpreadsheet/Chart/DataSeries.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class DataSeries
4343
const EMPTY_AS_GAP = 'gap';
4444
const EMPTY_AS_ZERO = 'zero';
4545
const EMPTY_AS_SPAN = 'span';
46+
const DEFAULT_EMPTY_AS = self::EMPTY_AS_GAP;
47+
const VALID_EMPTY_AS = [self::EMPTY_AS_GAP, self::EMPTY_AS_ZERO, self::EMPTY_AS_SPAN];
4648

4749
/**
4850
* Series Plot Type.

src/PhpSpreadsheet/Writer/Xlsx/Chart.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,9 @@ public function writeChart(\PhpOffice\PhpSpreadsheet\Chart\Chart $chart, bool $c
9797
$objWriter->writeAttribute('val', (string) (int) $chart->getPlotVisibleOnly());
9898
$objWriter->endElement();
9999

100-
if ($chart->getDisplayBlanksAs() !== '') {
101-
$objWriter->startElement('c:dispBlanksAs');
102-
$objWriter->writeAttribute('val', $chart->getDisplayBlanksAs());
103-
$objWriter->endElement();
104-
}
100+
$objWriter->startElement('c:dispBlanksAs');
101+
$objWriter->writeAttribute('val', $chart->getDisplayBlanksAs());
102+
$objWriter->endElement();
105103

106104
$objWriter->startElement('c:showDLblsOverMax');
107105
$objWriter->writeAttribute('val', '0');
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Chart;
6+
7+
use PhpOffice\PhpSpreadsheet\Chart\Chart;
8+
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
9+
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
10+
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
11+
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
12+
use PhpOffice\PhpSpreadsheet\Chart\Title;
13+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
14+
use PHPUnit\Framework\TestCase;
15+
16+
class DisplayBlanksAsTest extends TestCase
17+
{
18+
public function testDisplayBlanksAs(): void
19+
{
20+
$spreadsheet = new Spreadsheet();
21+
$worksheet = $spreadsheet->getActiveSheet();
22+
$worksheet->fromArray(
23+
[
24+
['', 2010, 2011, 2012],
25+
['Q1', 12, 15, 21],
26+
['Q2', 56, 73, 86],
27+
['Q3', 52, 61, 69],
28+
['Q4', 30, 32, 0],
29+
]
30+
);
31+
32+
$dataSeriesLabels = [
33+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$B$1', null, 1), // 2010
34+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', null, 1), // 2011
35+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$D$1', null, 1), // 2012
36+
];
37+
38+
$xAxisTickValues = [
39+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4
40+
];
41+
42+
$dataSeriesValues = [
43+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$B$2:$B$5', null, 4),
44+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', null, 4),
45+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$D$2:$D$5', null, 4),
46+
];
47+
48+
// Build the dataseries
49+
$series = new DataSeries(
50+
DataSeries::TYPE_AREACHART, // plotType
51+
DataSeries::GROUPING_PERCENT_STACKED, // plotGrouping
52+
range(0, count($dataSeriesValues) - 1), // plotOrder
53+
$dataSeriesLabels, // plotLabel
54+
$xAxisTickValues, // plotCategory
55+
$dataSeriesValues // plotValues
56+
);
57+
58+
$plotArea = new PlotArea(null, [$series]);
59+
$legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
60+
61+
$title = new Title('Test %age-Stacked Area Chart');
62+
$yAxisLabel = new Title('Value ($k)');
63+
64+
$chart1 = new Chart(
65+
'chart1', // name
66+
$title, // title
67+
$legend, // legend
68+
$plotArea, // plotArea
69+
true, // plotVisibleOnly
70+
DataSeries::EMPTY_AS_GAP, // displayBlanksAs
71+
null, // xAxisLabel
72+
$yAxisLabel // yAxisLabel
73+
);
74+
self::assertSame(DataSeries::EMPTY_AS_GAP, $chart1->getDisplayBlanksAs());
75+
$chart1->setDisplayBlanksAs(DataSeries::EMPTY_AS_ZERO);
76+
self::assertSame(DataSeries::EMPTY_AS_ZERO, $chart1->getDisplayBlanksAs());
77+
$chart1->setDisplayBlanksAs('0');
78+
self::assertSame(DataSeries::EMPTY_AS_GAP, $chart1->getDisplayBlanksAs(), 'invalid setting converted to default');
79+
80+
$chart2 = new Chart(
81+
'chart2', // name
82+
$title, // title
83+
$legend, // legend
84+
$plotArea, // plotArea
85+
true, // plotVisibleOnly
86+
DataSeries::EMPTY_AS_SPAN, // displayBlanksAs
87+
null, // xAxisLabel
88+
$yAxisLabel // yAxisLabel
89+
);
90+
self::assertSame(DataSeries::EMPTY_AS_SPAN, $chart2->getDisplayBlanksAs());
91+
92+
$chart3 = new Chart(
93+
'chart3', // name
94+
$title, // title
95+
$legend, // legend
96+
$plotArea, // plotArea
97+
true, // plotVisibleOnly
98+
'0', // displayBlanksAs, PHPExcel default
99+
null, // xAxisLabel
100+
$yAxisLabel // yAxisLabel
101+
);
102+
self::assertSame(DataSeries::EMPTY_AS_GAP, $chart3->getDisplayBlanksAs(), 'invalid setting converted to default');
103+
104+
$spreadsheet->disconnectWorksheets();
105+
}
106+
}

0 commit comments

Comments
 (0)