Skip to content

Commit

Permalink
Stock Chart Improvements - Minor Break
Browse files Browse the repository at this point in the history
Stock charts currently ignore upDownBars tag and its subsidiary gapWidth, upBars, and downBars tags when reading, and hard-codes those tags on write. As a result, the sample reproductions of stock charts in the 32* series aren't faithful to the originals. This PR fixes samples 1, 2, and 5. Samples 3 and 4 are reproduced better, but they require currently unsupported secondary axes (issue PHPOffice#560, issue PHPOffice#1072, and PR PHPOffice#1073 were closed as stale; see also PHPOffice/PHPExcel#1037). I will start to look at those, but it could take a while, and I don't think there's a reason to delay this in the meantime.

Charts which depended on the hard-coded values written by the Xlsx Chart writer will be slightly different as a result of this change. To restore the hard-coded behavior:
```php
$plotArea->setGapWidth(300);
$plotArea->setUseUpBars(true);
$plotArea->setUseDownBars(true);
```
The new behavior is demonstrated in 33_Chart_create_stock. The old behavior is demonstrated (with the code above) in new 33_Chart_create_stock2.
  • Loading branch information
oleibman committed Apr 9, 2023
1 parent 0e6866d commit 1f4734d
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 13 deletions.
127 changes: 127 additions & 0 deletions samples/Chart/33_Chart_create_stock2.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
<?php

use PhpOffice\PhpSpreadsheet\Chart\AxisText;
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\ChartColor;
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\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;

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

$spreadsheet = new Spreadsheet();
$worksheet = $spreadsheet->getActiveSheet();
$worksheet->fromArray(
[
['Counts', 'Max', 'Min', 'Min Threshold', 'Max Threshold'],
[10, 10, 5, 0, 50],
[30, 20, 10, 0, 50],
[20, 30, 15, 0, 50],
[40, 10, 0, 0, 50],
[100, 40, 5, 0, 50],
],
null,
'A1',
true
);
$worksheet->getStyle('B2:E6')->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_NUMBER_00);

// 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), //Max / Open
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', null, 1), //Min / Close
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$D$1', null, 1), //Min Threshold / Min
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$E$1', null, 1), //Max Threshold / Max
];
// 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$6', null, 5), // Counts
];
// 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$6', null, 5),
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$6', null, 5),
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$D$2:$D$6', null, 5),
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$E$2:$E$6', null, 5),
];

// Build the dataseries
$series = new DataSeries(
DataSeries::TYPE_STOCKCHART, // plotType
null, // plotGrouping - if we set this to not null, then xlsx throws error
range(0, count($dataSeriesValues) - 1), // plotOrder
$dataSeriesLabels, // plotLabel
$xAxisTickValues, // plotCategory
$dataSeriesValues // plotValues
);

// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
// Set the chart legend
$legend = new ChartLegend(ChartLegend::POSITION_RIGHT, null, false);
$legend->getBorderLines()->setLineColorProperties('ffc000', null, ChartColor::EXCEL_COLOR_TYPE_RGB);
$legend->getFillColor()->setColorProperties('cccccc');
$legendText = new AxisText();
$legendText->getFillColorObject()->setValue('008080')->setType(ChartColor::EXCEL_COLOR_TYPE_RGB);
$legendText->setShadowProperties(1);
$legend->setLegendText($legendText);

$title = new Title('Test Stock Chart');
$xAxisLabel = new Title('Counts');
$yAxisLabel = new Title('Values');

// 3 stmts below are only difference from 33_chart_create_stock.php
$plotArea->setGapWidth(300);
$plotArea->setUseUpBars(true);
$plotArea->setUseDownBars(true);

// Create the chart
$chart = new Chart(
'stock-chart', // name
$title, // title
$legend, // legend
$plotArea, // plotArea
true, // plotVisibleOnly
DataSeries::EMPTY_AS_GAP, // displayBlanksAs
$xAxisLabel, // xAxisLabel
$yAxisLabel // yAxisLabel
);

// Set the position where the chart should appear in the worksheet
$chart->setTopLeftPosition('A7');
$chart->setBottomRightPosition('H20');

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

// Save Excel 2007 file
$filename = $helper->getFilename(__FILE__);
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->setIncludeCharts(true);
$callStartTime = microtime(true);
$writer->save($filename);
$helper->logWrite($writer, $filename, $callStartTime);
Binary file modified samples/templates/32readwriteStockChart2.xlsx
Binary file not shown.
45 changes: 45 additions & 0 deletions src/PhpSpreadsheet/Chart/PlotArea.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,49 @@ public function getGradientFillStops()
{
return $this->gradientFillStops;
}

/** @var ?int */
private $gapWidth;

/** @var bool */
private $useUpBars = false;

/** @var bool */
private $useDownBars = false;

public function getGapWidth(): ?int
{
return $this->gapWidth;
}

public function setGapWidth(?int $gapWidth): self
{
$this->gapWidth = $gapWidth;

return $this;
}

public function getUseUpBars(): bool
{
return $this->useUpBars;
}

public function setUseUpBars(bool $useUpBars): self
{
$this->useUpBars = $useUpBars;

return $this;
}

public function getUseDownBars(): bool
{
return $this->useDownBars;
}

public function setUseDownBars(bool $useDownBars): self
{
$this->useDownBars = $useDownBars;

return $this;
}
}
21 changes: 21 additions & 0 deletions src/PhpSpreadsheet/Reader/Xlsx/Chart.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ public function readChart(SimpleXMLElement $chartElements, $chartName)
$gradientArray = [];
$gradientLin = null;
$roundedCorners = false;
$gapWidth = null;
$useUpBars = null;
$useDownBars = null;
foreach ($chartElementsC as $chartElementKey => $chartElement) {
switch ($chartElementKey) {
case 'spPr':
Expand Down Expand Up @@ -332,6 +335,15 @@ public function readChart(SimpleXMLElement $chartElements, $chartName)
break;
case 'stockChart':
$plotSeries[] = $this->chartDataSeries($chartDetail, $chartDetailKey);
if (isset($chartDetail->upDownBars->gapWidth)) {
$gapWidth = self::getAttribute($chartDetail->upDownBars->gapWidth, 'val', 'integer');
}
if (isset($chartDetail->upDownBars->upBars)) {
$useUpBars = true;
}
if (isset($chartDetail->upDownBars->downBars)) {
$useDownBars = true;
}
$plotAttributes = $this->readChartAttributes($chartDetail);

break;
Expand All @@ -348,6 +360,15 @@ public function readChart(SimpleXMLElement $chartElements, $chartName)
if (!empty($gradientArray)) {
$plotArea->setGradientFillProperties($gradientArray, $gradientLin);
}
if (is_int($gapWidth)) {
$plotArea->setGapWidth($gapWidth);
}
if ($useUpBars === true) {
$plotArea->setUseUpBars(true);
}
if ($useDownBars === true) {
$plotArea->setUseDownBars(true);
}

break;
case 'plotVisOnly':
Expand Down
33 changes: 20 additions & 13 deletions src/PhpSpreadsheet/Writer/Xlsx/Chart.php
Original file line number Diff line number Diff line change
Expand Up @@ -331,19 +331,26 @@ private function writePlotArea(XMLWriter $objWriter, ?PlotArea $plotArea, ?Title
$objWriter->startElement('c:hiLowLines');
$objWriter->endElement();

$objWriter->startElement('c:upDownBars');

$objWriter->startElement('c:gapWidth');
$objWriter->writeAttribute('val', '300');
$objWriter->endElement();

$objWriter->startElement('c:upBars');
$objWriter->endElement();

$objWriter->startElement('c:downBars');
$objWriter->endElement();

$objWriter->endElement();
$gapWidth = $plotArea->getGapWidth();
$upBars = $plotArea->getUseUpBars();
$downBars = $plotArea->getUseDownBars();
if ($gapWidth !== null || $upBars || $downBars) {
$objWriter->startElement('c:upDownBars');
if ($gapWidth !== null) {
$objWriter->startElement('c:gapWidth');
$objWriter->writeAttribute('val', "$gapWidth");
$objWriter->endElement();
}
if ($upBars) {
$objWriter->startElement('c:upBars');
$objWriter->endElement();
}
if ($downBars) {
$objWriter->startElement('c:downBars');
$objWriter->endElement();
}
$objWriter->endElement(); // c:upDownBars
}
}

// Generate 3 unique numbers to use for axId values
Expand Down

0 comments on commit 1f4734d

Please sign in to comment.