Default Style Alignment

Fix #3918, sort of. Xlsx cells use the default style when they omit the `s` tag, or when `s="0"` is specified. LibreOffice does not honor Alignment in the default Style unless the cell explicitly uses the second form, even though it honors other default Styles (e.g. bold font) when `s` is omitted. Gnumeric seems to have the same problem. A bug report has been filed with LibreOffice.

In the meantime, this PR adds to Xlsx Writer an optional boolean property `explicitStyle0` with setter and getter. Default is false, which will continue the current behavior by adding an `s` tag only when the cell uses a non-default style (this is how Excel itself behaves). When set to true, Xlsx Writer will explicity write `s="0"` for all cells using default style. This will allow users to create an Xlsx spreadsheet with default alignment of cells that will show up correctly when the spreadsheet is viewed with LibreOffice. Technically speaking, it is *probably* safe to always use `true`, except that the spreadsheet size will be a bit larger. However, my hope is that this is a temporary measure which can go away when the vendors have had a chance to fix their problems, hence the `false` default.
This commit is contained in:
oleibman
2024-02-29 11:11:00 -08:00
parent d620497511
commit d46b7b3e9d
3 changed files with 133 additions and 1 deletions
+19
View File
@@ -134,6 +134,8 @@ class Xlsx extends BaseWriter
private Worksheet $writerPartWorksheet;
private bool $explicitStyle0 = false;
/**
* Create a new Xlsx Writer.
*/
@@ -699,4 +701,21 @@ class Xlsx extends BaseWriter
return $data;
}
public function getExplicitStyle0(): bool
{
return $this->explicitStyle0;
}
/**
* This may be useful if non-default Alignment is part of default style
* and you think you might want to open the spreadsheet
* with LibreOffice or Gnumeric.
*/
public function setExplicitStyle0(bool $explicitStyle0): self
{
$this->explicitStyle0 = $explicitStyle0;
return $this;
}
}
+8 -1
View File
@@ -28,6 +28,8 @@ class Worksheet extends WriterPart
private string $evalError = '';
private bool $explicitStyle0;
/**
* Write worksheet to XML format.
*
@@ -38,6 +40,7 @@ class Worksheet extends WriterPart
*/
public function writeWorksheet(PhpspreadsheetWorksheet $worksheet, array $stringTable = [], bool $includeCharts = false): string
{
$this->explicitStyle0 = $this->getParentWriter()->getExplicitStyle0();
$this->numberStoredAsText = '';
$this->formula = '';
$this->twoDigitTextYear = '';
@@ -1441,7 +1444,11 @@ class Worksheet extends WriterPart
$objWriter->writeAttribute('r', $cellAddress);
// Sheet styles
self::writeAttributeIf($objWriter, (bool) $xfi, 's', "$xfi");
if ($xfi) {
$objWriter->writeAttribute('s', "$xfi");
} elseif ($this->explicitStyle0) {
$objWriter->writeAttribute('s', '0');
}
// If cell value is supplied, write cell value
if ($writeValue) {
@@ -0,0 +1,106 @@
<?php
declare(strict_types=1);
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
use PHPUnit\Framework\TestCase;
class ExplicitStyle0Test extends TestCase
{
private string $outputFile = '';
protected function tearDown(): void
{
if ($this->outputFile !== '') {
unlink($this->outputFile);
$this->outputFile = '';
}
}
public function testWithoutExplicitStyle0(): void
{
$spreadsheet = new Spreadsheet();
$defaultStyle = $spreadsheet->getDefaultStyle();
$defaultStyle->getFont()->setBold(true);
$defaultStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
$sheet = $spreadsheet->getActiveSheet();
$sheet->getCell('A1')->setValue('bold');
$sheet->getCell('A2')->setValue('italic');
$sheet->getStyle('A2')->getFont()->setItalic(true);
$writer = new XlsxWriter($spreadsheet);
$this->outputFile = File::temporaryFilename();
$writer->save($this->outputFile);
$spreadsheet->disconnectWorksheets();
$reader = new XlsxReader();
$spreadsheet2 = $reader->load($this->outputFile);
$sheet2 = $spreadsheet2->getActiveSheet();
$styleA1 = $sheet2->getStyle('A1');
self::assertTrue($styleA1->getFont()->getBold());
self::assertFalse($styleA1->getFont()->getItalic());
self::assertSame(Alignment::HORIZONTAL_CENTER, $styleA1->getAlignment()->getHorizontal());
$styleA2 = $sheet2->getStyle('A2');
self::assertTrue($styleA1->getFont()->getBold());
self::assertTrue($styleA1->getFont()->getItalic());
self::assertSame(Alignment::HORIZONTAL_CENTER, $styleA1->getAlignment()->getHorizontal());
$spreadsheet2->disconnectWorksheets();
$file = 'zip://';
$file .= $this->outputFile;
$file .= '#xl/worksheets/sheet1.xml';
$data = file_get_contents($file);
if ($data === false) {
self::fail('Unable to read file');
} else {
self::assertStringContainsString('<c r="A1" t="s"><v>0</v></c>', $data, 'no s attribute in c tag');
self::assertStringContainsString('<c r="A2" s="1" t="s"><v>1</v></c>', $data);
}
}
public function testWithExplicitStyle0(): void
{
$spreadsheet = new Spreadsheet();
$defaultStyle = $spreadsheet->getDefaultStyle();
$defaultStyle->getFont()->setBold(true);
$defaultStyle->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
$sheet = $spreadsheet->getActiveSheet();
$sheet->getCell('A1')->setValue('bold');
$sheet->getCell('A2')->setValue('italic');
$sheet->getStyle('A2')->getFont()->setItalic(true);
$writer = new XlsxWriter($spreadsheet);
$writer->setExplicitStyle0(true);
$this->outputFile = File::temporaryFilename();
$writer->save($this->outputFile);
$spreadsheet->disconnectWorksheets();
$reader = new XlsxReader();
$spreadsheet2 = $reader->load($this->outputFile);
$sheet2 = $spreadsheet2->getActiveSheet();
$styleA1 = $sheet2->getStyle('A1');
self::assertTrue($styleA1->getFont()->getBold());
self::assertFalse($styleA1->getFont()->getItalic());
self::assertSame(Alignment::HORIZONTAL_CENTER, $styleA1->getAlignment()->getHorizontal());
$styleA2 = $sheet2->getStyle('A2');
self::assertTrue($styleA1->getFont()->getBold());
self::assertTrue($styleA1->getFont()->getItalic());
self::assertSame(Alignment::HORIZONTAL_CENTER, $styleA1->getAlignment()->getHorizontal());
$spreadsheet2->disconnectWorksheets();
$file = 'zip://';
$file .= $this->outputFile;
$file .= '#xl/worksheets/sheet1.xml';
$data = file_get_contents($file);
if ($data === false) {
self::fail('Unable to read file');
} else {
self::assertStringContainsString('<c r="A1" s="0" t="s"><v>0</v></c>', $data, 'has s attribute in c tag');
self::assertStringContainsString('<c r="A2" s="1" t="s"><v>1</v></c>', $data);
}
}
}