Skip to content

Commit f427d02

Browse files
committed
Two Problems with Html Chart Rendering - Minor Break
Several problems are noted in PHPOffice#3783. This PR addresses those problems which make the rendering unsatisfactory (see following paragraphs). It does not address some items where the rendering is IMO satisfactory although it doesn't match Excel. In particular, the use of a different color palette and the rotation of charts are not addressed. I will leave the issue open for now because of those. As for the items which are addressed, in some cases the Html was omitting a chart altogether. This is because it had been extending the column range for charts only when it decided that extending the row range was needed. The code is changed to now extend the column range whenever the chart begins beyond the current column range of the sheet. Also, the rendering always produced a fixed-size image, so saving as Html could result in charts overlaying each other or other parts of the spreadsheet. New properties `renderedWidth` and `renderedHeight` are added to Chart, along with setters and getters. Writer/Html is changed to set these values using the chart's top left and bottom right cells to try to determine the actual size that is needed. Users can also set these properties outside of Writer/Html if they wish. Thanks to @f1mishutka for determining the source of this problem and suggesting an approach to resolving it. Because the size of the rendered image in Html/Pdf is changed, this could be considered a breaking change. To restore the prior behavior, do the following for all charts before saving as Html: ```php $chart->setRenderedWidth(640.0); $chart->setRenderedHeight(480.0); ```
1 parent 656a716 commit f427d02

File tree

5 files changed

+103
-17
lines changed

5 files changed

+103
-17
lines changed
21.3 KB
Binary file not shown.

src/PhpSpreadsheet/Chart/Chart.php

+34
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,16 @@ class Chart
132132

133133
private ChartColor $fillColor;
134134

135+
/**
136+
* Rendered width in pixels.
137+
*/
138+
private ?float $renderedWidth = null;
139+
140+
/**
141+
* Rendered height in pixels.
142+
*/
143+
private ?float $renderedHeight = null;
144+
135145
/**
136146
* Create a new Chart.
137147
* majorGridlines and minorGridlines are deprecated, moved to Axis.
@@ -791,4 +801,28 @@ public function getFillColor(): ChartColor
791801
{
792802
return $this->fillColor;
793803
}
804+
805+
public function setRenderedWidth(?float $width): self
806+
{
807+
$this->renderedWidth = $width;
808+
809+
return $this;
810+
}
811+
812+
public function getRenderedWidth(): ?float
813+
{
814+
return $this->renderedWidth;
815+
}
816+
817+
public function setRenderedHeight(?float $height): self
818+
{
819+
$this->renderedHeight = $height;
820+
821+
return $this;
822+
}
823+
824+
public function getRenderedHeight(): ?float
825+
{
826+
return $this->renderedHeight;
827+
}
794828
}

src/PhpSpreadsheet/Chart/Renderer/JpGraphRendererBase.php

+16-7
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626
*/
2727
abstract class JpGraphRendererBase implements IRenderer
2828
{
29-
private static $width = 640;
29+
private const DEFAULT_WIDTH = 640.0;
3030

31-
private static $height = 480;
31+
private const DEFAULT_HEIGHT = 480.0;
3232

3333
private static $colourSet = [
3434
'mediumpurple1', 'palegreen3', 'gold1', 'cadetblue1',
@@ -70,6 +70,16 @@ public function __construct(Chart $chart)
7070
];
7171
}
7272

73+
private function getGraphWidth(): float
74+
{
75+
return $this->chart->getRenderedWidth() ?? self::DEFAULT_WIDTH;
76+
}
77+
78+
private function getGraphHeight(): float
79+
{
80+
return $this->chart->getRenderedHeight() ?? self::DEFAULT_HEIGHT;
81+
}
82+
7383
/**
7484
* This method should be overriden in descendants to do real JpGraph library initialization.
7585
*/
@@ -221,7 +231,7 @@ private function renderLegend(): void
221231

222232
private function renderCartesianPlotArea(string $type = 'textlin'): void
223233
{
224-
$this->graph = new Graph(self::$width, self::$height);
234+
$this->graph = new Graph($this->getGraphWidth(), $this->getGraphHeight());
225235
$this->graph->SetScale($type);
226236

227237
$this->renderTitle();
@@ -258,14 +268,14 @@ private function renderCartesianPlotArea(string $type = 'textlin'): void
258268

259269
private function renderPiePlotArea(): void
260270
{
261-
$this->graph = new PieGraph(self::$width, self::$height);
271+
$this->graph = new PieGraph($this->getGraphWidth(), $this->getGraphHeight());
262272

263273
$this->renderTitle();
264274
}
265275

266276
private function renderRadarPlotArea(): void
267277
{
268-
$this->graph = new RadarGraph(self::$width, self::$height);
278+
$this->graph = new RadarGraph($this->getGraphWidth(), $this->getGraphHeight());
269279
$this->graph->SetScale('lin');
270280

271281
$this->renderTitle();
@@ -460,15 +470,14 @@ private function renderPlotScatter(int $groupID, bool $bubble): void
460470
$dataValuesY[$k] = $k;
461471
}
462472
}
463-
//var_dump($dataValuesY, $dataValuesX, $bubbleSize);
464473

465474
$seriesPlot = new ScatterPlot($dataValuesX, $dataValuesY);
466475
if ($scatterStyle == 'lineMarker') {
467476
$seriesPlot->SetLinkPoints();
468477
$seriesPlot->link->SetColor(self::$colourSet[self::$plotColour]);
469478
} elseif ($scatterStyle == 'smoothMarker') {
470479
$spline = new Spline($dataValuesY, $dataValuesX);
471-
[$splineDataY, $splineDataX] = $spline->Get(count($dataValuesX) * self::$width / 20);
480+
[$splineDataY, $splineDataX] = $spline->Get(count($dataValuesX) * $this->getGraphWidth() / 20);
472481
$lplot = new LinePlot($splineDataX, $splineDataY);
473482
$lplot->SetColor(self::$colourSet[self::$plotColour]);
474483

src/PhpSpreadsheet/Shared/Drawing.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public static function pixelsToPoints($pixelValue): float
110110
/**
111111
* Convert points to pixels.
112112
*
113-
* @param int $pointValue Value in points
113+
* @param float|int $pointValue Value in points
114114
*
115115
* @return int Value in pixels
116116
*/

src/PhpSpreadsheet/Writer/Html.php

+52-9
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@
3131

3232
class Html extends BaseWriter
3333
{
34+
private const DEFAULT_CELL_WIDTH_POINTS = 42;
35+
36+
private const DEFAULT_CELL_WIDTH_PIXELS = 56;
37+
3438
/**
3539
* Spreadsheet object.
3640
*/
@@ -580,9 +584,9 @@ private function extendRowsForCharts(Worksheet $worksheet, int $row): array
580584
$chartCol = Coordinate::columnIndexFromString($chartTL[0]);
581585
if ($chartTL[1] > $rowMax) {
582586
$rowMax = $chartTL[1];
583-
if ($chartCol > Coordinate::columnIndexFromString($colMax)) {
584-
$colMax = $chartTL[0];
585-
}
587+
}
588+
if ($chartCol > Coordinate::columnIndexFromString($colMax)) {
589+
$colMax = $chartTL[0];
586590
}
587591
}
588592
}
@@ -601,9 +605,9 @@ private function extendRowsForChartsAndImages(Worksheet $worksheet, int $row): s
601605
$imageCol = Coordinate::columnIndexFromString($imageTL[0]);
602606
if ($imageTL[1] > $rowMax) {
603607
$rowMax = $imageTL[1];
604-
if ($imageCol > Coordinate::columnIndexFromString($colMax)) {
605-
$colMax = $imageTL[0];
606-
}
608+
}
609+
if ($imageCol > Coordinate::columnIndexFromString($colMax)) {
610+
$colMax = $imageTL[0];
607611
}
608612
}
609613

@@ -745,7 +749,15 @@ private function writeChartInCell(Worksheet $worksheet, string $coordinates): st
745749
$chartCoordinates = $chart->getTopLeftPosition();
746750
if ($chartCoordinates['cell'] == $coordinates) {
747751
$chartFileName = File::sysGetTempDir() . '/' . uniqid('', true) . '.png';
748-
if (!$chart->render($chartFileName)) {
752+
$renderedWidth = $chart->getRenderedWidth();
753+
$renderedHeight = $chart->getRenderedHeight();
754+
if ($renderedWidth === null || $renderedHeight === null) {
755+
$this->adjustRendererPositions($chart, $worksheet);
756+
}
757+
$renderSuccessful = $chart->render($chartFileName);
758+
$chart->setRenderedWidth($renderedWidth);
759+
$chart->setRenderedHeight($renderedHeight);
760+
if (!$renderSuccessful) {
749761
return '';
750762
}
751763

@@ -770,6 +782,37 @@ private function writeChartInCell(Worksheet $worksheet, string $coordinates): st
770782
return $html;
771783
}
772784

785+
private function adjustRendererPositions(Chart $chart, Worksheet $sheet): void
786+
{
787+
$topLeft = $chart->getTopLeftPosition();
788+
$bottomRight = $chart->getBottomRightPosition();
789+
$tlCell = $topLeft['cell'];
790+
$brCell = $bottomRight['cell'];
791+
if ($tlCell !== '' && $brCell !== '') {
792+
$tlCoordinate = Coordinate::indexesFromString($tlCell);
793+
$brCoordinate = Coordinate::indexesFromString($brCell);
794+
$totalHeight = 0.0;
795+
$totalWidth = 0.0;
796+
$defaultRowHeight = $sheet->getDefaultRowDimension()->getRowHeight();
797+
$defaultRowHeight = SharedDrawing::pointsToPixels(($defaultRowHeight >= 0) ? $defaultRowHeight : SharedFont::getDefaultRowHeightByFont($this->defaultFont));
798+
if ($tlCoordinate[1] <= $brCoordinate[1] && $tlCoordinate[0] <= $brCoordinate[0]) {
799+
for ($row = $tlCoordinate[1]; $row <= $brCoordinate[1]; ++$row) {
800+
$height = $sheet->getRowDimension($row)->getRowHeight('pt');
801+
$totalHeight += ($height >= 0) ? $height : $defaultRowHeight;
802+
}
803+
$rightEdge = $brCoordinate[2];
804+
++$rightEdge;
805+
for ($column = $tlCoordinate[2]; $column !== $rightEdge; ++$column) {
806+
$width = $sheet->getColumnDimension($column)->getWidth();
807+
$width = ($width < 0) ? self::DEFAULT_CELL_WIDTH_PIXELS : SharedDrawing::cellDimensionToPixels($sheet->getColumnDimension($column)->getWidth(), $this->defaultFont);
808+
$totalWidth += $width;
809+
}
810+
$chart->setRenderedWidth($totalWidth);
811+
$chart->setRenderedHeight($totalHeight);
812+
}
813+
}
814+
}
815+
773816
/**
774817
* Generate CSS styles.
775818
*
@@ -844,8 +887,8 @@ private function buildCssPerSheet(Worksheet $sheet, array &$css): void
844887
$highestColumnIndex = Coordinate::columnIndexFromString($sheet->getHighestColumn()) - 1;
845888
$column = -1;
846889
while ($column++ < $highestColumnIndex) {
847-
$this->columnWidths[$sheetIndex][$column] = 42; // approximation
848-
$css['table.sheet' . $sheetIndex . ' col.col' . $column]['width'] = '42pt';
890+
$this->columnWidths[$sheetIndex][$column] = self::DEFAULT_CELL_WIDTH_POINTS; // approximation
891+
$css['table.sheet' . $sheetIndex . ' col.col' . $column]['width'] = self::DEFAULT_CELL_WIDTH_POINTS . 'pt';
849892
}
850893

851894
// col elements, loop through columnDimensions and set width

0 commit comments

Comments
 (0)