From 37a8a56299d3098032e3f8e1c2790607e226952e Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Wed, 16 Aug 2023 22:45:55 -0700 Subject: [PATCH] Read Code Page for Xls ListWorksheetInfo/Names for BIFF5 (#3672) * Read Code Page for Xls ListWorksheetInfo/Names for BIFF5 Fix #3671. Xls reader was not processing Code Page as part of functions ListWorksheetInfo/Names, which was causing them to fail for for BIFF5 (and BIFF7); this was not a problem for BIFF8. There were no unit tests for these functions for either BIFF5 or BIFF8. There are now. * Add getVersion and getCodePage Methods These came about because test file for non-standard codepage was supposed to be BIFF5, but turned out to be BIFF8 using UTF-16 with some string data otherwise encoded. Add a BIFF5 equivalent (some hex editing was required), and the means to distinguish one from the other. * Found MACCENTRALEUROPE Text in BIFF8 It was used for 'Last Modified By' property, even though bulk of spreadsheet uses UTF-16LE. Add a test. --- src/PhpSpreadsheet/Reader/Xls.php | 22 ++- .../Reader/Xls/InfoNamesTest.php | 142 ++++++++++++++++++ .../Reader/Xls/XlsTest.php | 35 ----- .../Reader/XLS/maccentraleurope.biff5.xls | Bin 0 -> 18944 bytes 4 files changed, 162 insertions(+), 37 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Reader/Xls/InfoNamesTest.php create mode 100644 tests/data/Reader/XLS/maccentraleurope.biff5.xls diff --git a/src/PhpSpreadsheet/Reader/Xls.php b/src/PhpSpreadsheet/Reader/Xls.php index 816e7698d..760f8a519 100644 --- a/src/PhpSpreadsheet/Reader/Xls.php +++ b/src/PhpSpreadsheet/Reader/Xls.php @@ -213,7 +213,7 @@ class Xls extends BaseReader * * @var int */ - private $version; + private $version = 0; /** * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95) @@ -221,7 +221,7 @@ class Xls extends BaseReader * * @var string */ - private $codepage; + private $codepage = ''; /** * Shared formats. @@ -459,6 +459,11 @@ class Xls extends BaseReader $this->codepage = $codepage; } + public function getCodepage(): string + { + return $this->codepage; + } + /** * Reads names of the worksheets from a file, without parsing the whole file to a PhpSpreadsheet object. * @@ -498,6 +503,10 @@ class Xls extends BaseReader $this->readDefault(); break 2; + case self::XLS_TYPE_CODEPAGE: + $this->readCodepage(); + + break; default: $this->readDefault(); @@ -557,6 +566,10 @@ class Xls extends BaseReader $this->readDefault(); break 2; + case self::XLS_TYPE_CODEPAGE: + $this->readCodepage(); + + break; default: $this->readDefault(); @@ -8088,4 +8101,9 @@ class Xls extends BaseReader $this->phpSheet->getStyle($cellRange)->setConditionalStyles($conditionalStyles); } } + + public function getVersion(): int + { + return $this->version; + } } diff --git a/tests/PhpSpreadsheetTests/Reader/Xls/InfoNamesTest.php b/tests/PhpSpreadsheetTests/Reader/Xls/InfoNamesTest.php new file mode 100644 index 000000000..a493d65be --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xls/InfoNamesTest.php @@ -0,0 +1,142 @@ +listWorksheetNames($filename); + $expected = ['Invoice', 'Terms and conditions']; + self::assertSame($expected, $names); + } + + public function testWorksheetInfoBiff5(): void + { + $filename = 'samples/templates/30templatebiff5.xls'; + $reader = new Xls(); + $info = $reader->listWorksheetInfo($filename); + $expected = [ + [ + 'worksheetName' => 'Invoice', + 'lastColumnLetter' => 'E', + 'lastColumnIndex' => 4, + 'totalRows' => 19, + 'totalColumns' => 5, + ], + [ + 'worksheetName' => 'Terms and conditions', + 'lastColumnLetter' => 'B', + 'lastColumnIndex' => 1, + 'totalRows' => 3, + 'totalColumns' => 2, + ], + ]; + self::assertSame($expected, $info); + self::assertSame(Xls::XLS_BIFF7, $reader->getVersion()); + self::assertSame('CP1252', $reader->getCodepage()); + } + + public function testWorksheetNamesBiff8(): void + { + $filename = 'samples/templates/31docproperties.xls'; + $reader = new Xls(); + $names = $reader->listWorksheetNames($filename); + $expected = ['Worksheet']; + self::assertSame($expected, $names); + } + + public function testWorksheetInfoBiff8(): void + { + $filename = 'samples/templates/31docproperties.xls'; + $reader = new Xls(); + $info = $reader->listWorksheetInfo($filename); + $expected = [ + [ + 'worksheetName' => 'Worksheet', + 'lastColumnLetter' => 'B', + 'lastColumnIndex' => 1, + 'totalRows' => 1, + 'totalColumns' => 2, + ], + ]; + self::assertSame($expected, $info); + self::assertSame(Xls::XLS_BIFF8, $reader->getVersion()); + self::assertSame('UTF-16LE', $reader->getCodepage()); + } + + /** + * Test load Xls file with MACCENTRALEUROPE encoding, which is implemented + * as MAC-CENTRALEUROPE on some systems. Issue #549. + */ + private const MAC_CE = ['MACCENTRALEUROPE', 'MAC-CENTRALEUROPE']; + + private const MAC_FILE5 = 'tests/data/Reader/XLS/maccentraleurope.biff5.xls'; + private const MAC_FILE8 = 'tests/data/Reader/XLS/maccentraleurope.xls'; + + public function testWorksheetNamesBiff5Mac(): void + { + $codePages = CodePage::getEncodings(); + self::assertSame(self::MAC_CE, $codePages[10029]); + $reader = new Xls(); + $names = $reader->listWorksheetNames(self::MAC_FILE5); + $expected = ['Ärkusz1']; + self::assertSame($expected, $names); + } + + public function testWorksheetInfoBiff5Mac(): void + { + $codePages = CodePage::getEncodings(); + // prior test has replaced array with single string + self::assertContains($codePages[10029], self::MAC_CE); + $reader = new Xls(); + $info = $reader->listWorksheetInfo(self::MAC_FILE5); + $expected = [ + [ + 'worksheetName' => 'Ärkusz1', + 'lastColumnLetter' => 'P', + 'lastColumnIndex' => 15, + 'totalRows' => 3, + 'totalColumns' => 16, + ], + ]; + self::assertSame($expected, $info); + self::assertSame(Xls::XLS_BIFF7, $reader->getVersion()); + self::assertContains($reader->getCodepage(), self::MAC_CE); + } + + public function testLoadMacCentralEuropeBiff5(): void + { + $reader = new Xls(); + $spreadsheet = $reader->load(self::MAC_FILE5); + $sheet = $spreadsheet->getActiveSheet(); + self::assertSame('Ärkusz1', $sheet->getTitle()); + self::assertSame('Ładowność', $sheet->getCell('I1')->getValue()); + self::assertSame(Xls::XLS_BIFF7, $reader->getVersion()); + self::assertContains($reader->getCodepage(), self::MAC_CE); + $spreadsheet->disconnectWorksheets(); + } + + public function testLoadMacCentralEuropeBiff8(): void + { + // Document is UTF-16LE as a whole, + // but some strings are stored as MACCENTRALEUROPE + $reader = new Xls(); + $spreadsheet = $reader->load(self::MAC_FILE8); + $sheet = $spreadsheet->getActiveSheet(); + self::assertSame('Arkusz1', $sheet->getTitle()); + self::assertSame('Ładowność', $sheet->getCell('I1')->getValue()); + self::assertSame(Xls::XLS_BIFF8, $reader->getVersion()); + self::assertSame('UTF-16LE', $reader->getCodepage()); + $properties = $spreadsheet->getProperties(); + // the following is stored as MACCENTRALEUROPE, not UTF-16LE + self::assertSame('Użytkownik Microsoft Office', $properties->getLastModifiedBy()); + $spreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/PhpSpreadsheetTests/Reader/Xls/XlsTest.php b/tests/PhpSpreadsheetTests/Reader/Xls/XlsTest.php index 2cd14a87c..03c3afd4c 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xls/XlsTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xls/XlsTest.php @@ -4,7 +4,6 @@ namespace PhpOffice\PhpSpreadsheetTests\Reader\Xls; use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Reader\Xls; -use PhpOffice\PhpSpreadsheet\Shared\CodePage; use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional; class XlsTest extends AbstractFunctional @@ -88,40 +87,6 @@ class XlsTest extends AbstractFunctional $newspreadsheet->disconnectWorksheets(); } - /** - * Test load Xls file with MACCENTRALEUROPE encoding, which is implemented - * as MAC-CENTRALEUROPE on some systems. Issue #549. - */ - public function testLoadMacCentralEurope(): void - { - $codePages = CodePage::getEncodings(); - self::assertIsArray($codePages[10029]); - $filename = 'tests/data/Reader/XLS/maccentraleurope.xls'; - $reader = new Xls(); - // When no fix applied, spreadsheet fails to load on some systems - $spreadsheet = $reader->load($filename); - $sheet = $spreadsheet->getActiveSheet(); - self::assertSame('Ładowność', $sheet->getCell('I1')->getValue()); - $spreadsheet->disconnectWorksheets(); - } - - /** - * First test changes array entry in CodePage. - * This test confirms new that new entry is okay. - */ - public function testLoadMacCentralEurope2(): void - { - $codePages = CodePage::getEncodings(); - self::assertIsString($codePages[10029]); - $filename = 'tests/data/Reader/XLS/maccentraleurope.xls'; - $reader = new Xls(); - // When no fix applied, spreadsheet fails to load on some systems - $spreadsheet = $reader->load($filename); - $sheet = $spreadsheet->getActiveSheet(); - self::assertSame('Ładowność', $sheet->getCell('I1')->getValue()); - $spreadsheet->disconnectWorksheets(); - } - public function testLoadXlsBug1114(): void { $filename = 'tests/data/Reader/XLS/bug1114.xls'; diff --git a/tests/data/Reader/XLS/maccentraleurope.biff5.xls b/tests/data/Reader/XLS/maccentraleurope.biff5.xls new file mode 100644 index 0000000000000000000000000000000000000000..6ab0157febbdbdc0b8b6ac3313f797bd9e874932 GIT binary patch literal 18944 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 VFZ;32np@zD*|XwH)PJ^x|6d&-LKXl3 literal 0 HcmV?d00001