Skip to content

Commit 06fef87

Browse files
committed
Maximum Column Width
PR #455 was submitted by @Aketos in 2018. It added no unit tests, so it was not merged, and it eventually went stale. Without the tests, I'm not sure exactly what the user had in mind. But my investigation indicates the following: - for Xls files, if the column width in the file specifies a value greater than 255, Excel will choose its own width when it opens the file. - for Xlsx files: - Excel does not allow you to set a column width > 255, neither by dragging the column boundary, nor by right-clicking and setting a value, nor by auto-fitting the column width. - Nevertheless, if the XML specifies a column width > 255, Excel will honor that value in the displayed spreadsheet, and even write it out if the file is saved. I have taken a different approach than the original PR, which changed `Worksheet::calculateColumnWidths`. Instead, this PR adds an option to the Xlsx Writer to either restrict column widths to 255 or not. The default is "not" in order to avoid a breaking change. For emulating Excel's behavior, in the unusual situation where it might matter, the user might consider using the non-default option. This PR also restricts column dimension width to 255 when saving an Xls file.
1 parent d88efca commit 06fef87

File tree

6 files changed

+130
-4
lines changed

6 files changed

+130
-4
lines changed

docs/topics/reading-and-writing-to-file.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,15 +181,26 @@ Because of a bug in the Office2003 compatibility pack, there can be some
181181
small issues when opening Xlsx spreadsheets (mostly related to formula
182182
calculation). You can enable Office2003 compatibility with the following
183183
code:
184-
184+
```php
185185
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
186186
$writer->setOffice2003Compatibility(true);
187187
$writer->save("05featuredemo.xlsx");
188+
```
188189

189190
**Office2003 compatibility option should only be used when needed** because
190191
it disables several Office2007 file format options, resulting in a
191192
lower-featured Office2007 spreadsheet.
192193

194+
#### Maximum Column Width
195+
196+
In the Xlsx User Interface, the user cannot set a column width > 255.
197+
Nevertheless, it will honor a higher value if supplied in the Xml.
198+
PhpSpreadsheet will, by default, allow values > 255 to be written.
199+
However, Excel's behavior, restricting the value to 255, can be emulated:
200+
```php
201+
$writer->setRestrictMaxColumnWidth(true);
202+
```
203+
193204
### Form Control Fields
194205

195206
PhpSpreadsheet offers limited support for Forms Controls (buttons,

src/PhpSpreadsheet/Worksheet/ColumnDimension.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
class ColumnDimension extends Dimension
99
{
10+
public const EXCEL_MAX_WIDTH = 255.0;
11+
1012
/**
1113
* Column index.
1214
*/
@@ -89,6 +91,11 @@ public function getWidth(?string $unitOfMeasure = null): float
8991
: (new CssDimension((string) $this->width))->toUnit($unitOfMeasure);
9092
}
9193

94+
public function getWidthForOutput(bool $restrictMax): float
95+
{
96+
return ($restrictMax && $this->width > self::EXCEL_MAX_WIDTH) ? self::EXCEL_MAX_WIDTH : $this->width;
97+
}
98+
9299
/**
93100
* Set Width.
94101
*

src/PhpSpreadsheet/Writer/Xls/Worksheet.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ public function close(): void
270270
if (isset($columnDimensions[$columnLetter])) {
271271
$columnDimension = $columnDimensions[$columnLetter];
272272
if ($columnDimension->getWidth() >= 0) {
273-
$width = $columnDimension->getWidth();
273+
$width = $columnDimension->getWidthForOutput(true);
274274
}
275275
$hidden = $columnDimension->getVisible() ? 0 : 1;
276276
$level = $columnDimension->getOutlineLevel();

src/PhpSpreadsheet/Writer/Xlsx.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ class Xlsx extends BaseWriter
146146
// Default changed from null in PhpSpreadsheet 4.0.0.
147147
private ?bool $forceFullCalc = self::DEFAULT_FORCE_FULL_CALC;
148148

149+
protected bool $restrictMaxColumnWidth = false;
150+
149151
/**
150152
* Create a new Xlsx Writer.
151153
*/
@@ -818,4 +820,27 @@ public function setForceFullCalc(?bool $forceFullCalc): self
818820

819821
return $this;
820822
}
823+
824+
/**
825+
* Excel has a nominal width limint of 255 for a column.
826+
* Surprisingly, Xlsx can read and write larger values,
827+
* and the file will appear as desired,
828+
* but the User Interface does not allow you to set the width beyond 255,
829+
* either directly or though auto-fit width.
830+
* Xls sets its own value when the width is beyond 255.
831+
* This method gets whether PhpSpreadsheet should restrict the
832+
* column widths which it writes to the Excel limit, for formats
833+
* which allow it to exceed 255.
834+
*/
835+
public function setRestrictMaxColumnWidth(bool $restrictMaxColumnWidth): self
836+
{
837+
$this->restrictMaxColumnWidth = $restrictMaxColumnWidth;
838+
839+
return $this;
840+
}
841+
842+
public function getRestrictMaxColumnWidth(): bool
843+
{
844+
return $this->restrictMaxColumnWidth;
845+
}
821846
}

src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ class Worksheet extends WriterPart
3737

3838
private bool $useDynamicArrays = false;
3939

40+
private bool $restrictMaxColumnWidth = false;
41+
4042
/**
4143
* Write worksheet to XML format.
4244
*
@@ -62,6 +64,7 @@ public function writeWorksheet(PhpspreadsheetWorksheet $worksheet, array $string
6264
} else {
6365
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
6466
}
67+
$this->restrictMaxColumnWidth = $this->getParentWriter()->getRestrictMaxColumnWidth();
6568

6669
// XML header
6770
$objWriter->startDocument('1.0', 'UTF-8', 'yes');
@@ -435,7 +438,7 @@ private function writeSheetFormatPr(XMLWriter $objWriter, PhpspreadsheetWorkshee
435438

436439
// Default column width
437440
if ($worksheet->getDefaultColumnDimension()->getWidth() >= 0) {
438-
$objWriter->writeAttribute('defaultColWidth', StringHelper::formatNumber($worksheet->getDefaultColumnDimension()->getWidth()));
441+
$objWriter->writeAttribute('defaultColWidth', StringHelper::formatNumber($worksheet->getDefaultColumnDimension()->getWidthForOutput($this->restrictMaxColumnWidth)));
439442
}
440443

441444
// Outline level - row
@@ -482,7 +485,7 @@ private function writeCols(XMLWriter $objWriter, PhpspreadsheetWorksheet $worksh
482485
$objWriter->writeAttribute('width', '9.10');
483486
} else {
484487
// Width set
485-
$objWriter->writeAttribute('width', StringHelper::formatNumber($colDimension->getWidth()));
488+
$objWriter->writeAttribute('width', StringHelper::formatNumber($colDimension->getWidthForOutput($this->restrictMaxColumnWidth)));
486489
}
487490

488491
// Column visibility
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
6+
7+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
8+
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
9+
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
10+
11+
class ColumnDimension3Test extends AbstractFunctional
12+
{
13+
public function testXlsxDefault(): void
14+
{
15+
$spreadsheet = new Spreadsheet();
16+
$sheet = $spreadsheet->getActiveSheet();
17+
$sheet->getCell('B1')->setValue('hello');
18+
$sheet->getCell('C1')->setValue(2);
19+
$sheet->getCell('A1')->setValue('=REPT("ABCDEFGHIJKLMNOPQRS*",13)');
20+
$sheet->getStyle('A1')->getFont()
21+
->setSize(11)
22+
->setName('Courier New');
23+
$sheet->getColumnDimension('A')->setAutoSize(true);
24+
$sheet->getColumnDimension('B')->setAutoSize(true);
25+
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx');
26+
$spreadsheet->disconnectWorksheets();
27+
$sheet = $reloadedSpreadsheet->getActiveSheet();
28+
self::assertGreaterThan(255.0, $sheet->getColumnDimension('A')->getWidth());
29+
self::assertLessThan(255.0, $sheet->getColumnDimension('B')->getWidth());
30+
self::assertSame(-1.0, $sheet->getColumnDimension('C')->getWidth());
31+
$reloadedSpreadsheet->disconnectWorksheets();
32+
}
33+
34+
public function testXlsxRestrict(): void
35+
{
36+
$spreadsheet = new Spreadsheet();
37+
$sheet = $spreadsheet->getActiveSheet();
38+
$sheet->getCell('B1')->setValue('hello');
39+
$sheet->getCell('C1')->setValue(2);
40+
$sheet->getCell('A1')->setValue('=REPT("ABCDEFGHIJKLMNOPQRS*",13)');
41+
$sheet->getStyle('A1')->getFont()
42+
->setSize(11)
43+
->setName('Courier New');
44+
$sheet->getColumnDimension('A')->setAutoSize(true);
45+
$sheet->getColumnDimension('B')->setAutoSize(true);
46+
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx', null, [self::class, 'restrictWidth']);
47+
$spreadsheet->disconnectWorksheets();
48+
$sheet = $reloadedSpreadsheet->getActiveSheet();
49+
self::assertSame(255.0, $sheet->getColumnDimension('A')->getWidth());
50+
self::assertLessThan(255.0, $sheet->getColumnDimension('B')->getWidth());
51+
self::assertSame(-1.0, $sheet->getColumnDimension('C')->getWidth());
52+
$reloadedSpreadsheet->disconnectWorksheets();
53+
}
54+
55+
public function testXls(): void
56+
{
57+
$spreadsheet = new Spreadsheet();
58+
$sheet = $spreadsheet->getActiveSheet();
59+
$sheet->getCell('B1')->setValue('hello');
60+
$sheet->getCell('C1')->setValue(2);
61+
$sheet->getCell('A1')->setValue('=REPT("ABCDEFGHIJKLMNOPQRS*",13)');
62+
$sheet->getStyle('A1')->getFont()
63+
->setSize(11)
64+
->setName('Courier New');
65+
$sheet->getColumnDimension('A')->setAutoSize(true);
66+
$sheet->getColumnDimension('B')->setAutoSize(true);
67+
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xls');
68+
$spreadsheet->disconnectWorksheets();
69+
$sheet = $reloadedSpreadsheet->getActiveSheet();
70+
self::assertSame(255.0, $sheet->getColumnDimension('A')->getWidth());
71+
self::assertLessThan(255.0, $sheet->getColumnDimension('B')->getWidth());
72+
self::assertLessThan(255.0, $sheet->getColumnDimension('C')->getWidth());
73+
$reloadedSpreadsheet->disconnectWorksheets();
74+
}
75+
76+
public static function restrictWidth(XlsxWriter $writer): void
77+
{
78+
$writer->setRestrictMaxColumnWidth(true);
79+
}
80+
}

0 commit comments

Comments
 (0)