From 9b07be9a0f33429bb25c032860a9837dbfcc4886 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Fri, 14 Feb 2025 23:40:50 -0800 Subject: [PATCH] Add sheetState to ListWorksheetInfo Data Fix #4345. Add a new item to the output array. Although the output is changed, this does not seem like a breaking change to me. --- src/PhpSpreadsheet/Reader/Csv.php | 1 + src/PhpSpreadsheet/Reader/Gnumeric.php | 12 +- src/PhpSpreadsheet/Reader/Html.php | 1 + src/PhpSpreadsheet/Reader/Ods.php | 109 +++++++++--------- src/PhpSpreadsheet/Reader/Slk.php | 1 + .../Reader/Xls/ListFunctions.php | 2 + src/PhpSpreadsheet/Reader/Xlsx.php | 4 +- src/PhpSpreadsheet/Reader/Xml.php | 1 + .../Reader/Gnumeric/GnumericInfoTest.php | 2 + .../Reader/Gnumeric/HiddenWorksheetTest.php | 25 ++++ .../Reader/Ods/HiddenWorksheetTest.php | 27 ++++- .../Reader/Ods/OdsInfoTest.php | 2 + .../Reader/Xls/HiddenWorksheetTest.php | 41 +++++++ .../Reader/Xls/InfoNamesTest.php | 4 + .../Reader/Xlsx/HiddenWorksheetTest.php | 41 +++++++ .../Reader/Xlsx/Issue2516Test.php | 3 +- .../Reader/Xlsx/NamespacePurlTest.php | 3 +- .../Reader/Xlsx/WorksheetInfoNamesTest.php | 2 + .../Reader/Xml/XmlInfoTest.php | 2 + tests/data/Reader/Ods/HiddenSheet.ods | Bin 2821 -> 0 bytes tests/data/Reader/Ods/HiddenSheet2.ods | Bin 0 -> 9052 bytes tests/data/Reader/XLS/visibility.xls | Bin 0 -> 31744 bytes tests/data/Reader/XLSX/visibility.xlsx | Bin 0 -> 11166 bytes 23 files changed, 224 insertions(+), 59 deletions(-) delete mode 100644 tests/data/Reader/Ods/HiddenSheet.ods create mode 100644 tests/data/Reader/Ods/HiddenSheet2.ods create mode 100644 tests/data/Reader/XLS/visibility.xls create mode 100644 tests/data/Reader/XLSX/visibility.xlsx diff --git a/src/PhpSpreadsheet/Reader/Csv.php b/src/PhpSpreadsheet/Reader/Csv.php index 9455993e8..e15675378 100644 --- a/src/PhpSpreadsheet/Reader/Csv.php +++ b/src/PhpSpreadsheet/Reader/Csv.php @@ -252,6 +252,7 @@ class Csv extends BaseReader $worksheetInfo[0]['lastColumnLetter'] = Coordinate::stringFromColumnIndex($worksheetInfo[0]['lastColumnIndex'] + 1); $worksheetInfo[0]['totalColumns'] = $worksheetInfo[0]['lastColumnIndex'] + 1; + $worksheetInfo[0]['sheetState'] = Worksheet::SHEETSTATE_VISIBLE; // Close file fclose($fileHandle); diff --git a/src/PhpSpreadsheet/Reader/Gnumeric.php b/src/PhpSpreadsheet/Reader/Gnumeric.php index ed81efb22..6b71da029 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric.php @@ -33,6 +33,9 @@ class Gnumeric extends BaseReader const NAMESPACE_OOO = 'http://openoffice.org/2004/office'; + const GNM_SHEET_VISIBILITY_VISIBLE = 'GNM_SHEET_VISIBILITY_VISIBLE'; + const GNM_SHEET_VISIBILITY_HIDDEN = 'GNM_SHEET_VISIBILITY_HIDDEN'; + /** * Shared Expressions. */ @@ -144,7 +147,12 @@ class Gnumeric extends BaseReader 'lastColumnIndex' => 0, 'totalRows' => 0, 'totalColumns' => 0, + 'sheetState' => Worksheet::SHEETSTATE_VISIBLE, ]; + $visibility = $xml->getAttribute('Visibility'); + if ((string) $visibility === self::GNM_SHEET_VISIBILITY_HIDDEN) { + $tmpInfo['sheetState'] = Worksheet::SHEETSTATE_HIDDEN; + } while ($xml->read()) { if (self::matchXml($xml, 'Name')) { @@ -271,8 +279,8 @@ class Gnumeric extends BaseReader // name in line with the formula, not the reverse $this->spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false); - $visibility = $sheet->attributes()['Visibility'] ?? 'GNM_SHEET_VISIBILITY_VISIBLE'; - if ((string) $visibility !== 'GNM_SHEET_VISIBILITY_VISIBLE') { + $visibility = $sheet->attributes()['Visibility'] ?? self::GNM_SHEET_VISIBILITY_VISIBLE; + if ((string) $visibility !== self::GNM_SHEET_VISIBILITY_VISIBLE) { $this->spreadsheet->getActiveSheet()->setSheetState(Worksheet::SHEETSTATE_HIDDEN); } diff --git a/src/PhpSpreadsheet/Reader/Html.php b/src/PhpSpreadsheet/Reader/Html.php index 431a08fff..dc0a435a9 100644 --- a/src/PhpSpreadsheet/Reader/Html.php +++ b/src/PhpSpreadsheet/Reader/Html.php @@ -1232,6 +1232,7 @@ class Html extends BaseReader $newEntry['lastColumnIndex'] = Coordinate::columnIndexFromString($sheet->getHighestDataColumn()) - 1; $newEntry['totalRows'] = $sheet->getHighestDataRow(); $newEntry['totalColumns'] = $newEntry['lastColumnIndex'] + 1; + $newEntry['sheetState'] = Worksheet::SHEETSTATE_VISIBLE; $info[] = $newEntry; } $spreadsheet->disconnectWorksheets(); diff --git a/src/PhpSpreadsheet/Reader/Ods.php b/src/PhpSpreadsheet/Reader/Ods.php index 57111a9c2..bcc5d4fd3 100644 --- a/src/PhpSpreadsheet/Reader/Ods.php +++ b/src/PhpSpreadsheet/Reader/Ods.php @@ -157,64 +157,67 @@ class Ods extends BaseReader // Step into the first level of content of the XML $xml->read(); + $tableVisibility = []; + $lastTableStyle = ''; + while ($xml->read()) { - // Quickly jump through to the office:body node - while (self::getXmlName($xml) !== 'office:body') { - if ($xml->isEmptyElement) { + if (self::getXmlName($xml) === 'style:style') { + $styleType = $xml->getAttribute('style:family'); + if ($styleType === 'table') { + $lastTableStyle = $xml->getAttribute('style:name'); + } + } elseif (self::getXmlName($xml) === 'style:table-properties') { + $visibility = $xml->getAttribute('table:display'); + $tableVisibility[$lastTableStyle] = ($visibility === 'false') ? Worksheet::SHEETSTATE_HIDDEN : Worksheet::SHEETSTATE_VISIBLE; + } elseif (self::getXmlName($xml) == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) { + $worksheetNames[] = $xml->getAttribute('table:name'); + + $styleName = $xml->getAttribute('table:style-name') ?? ''; + $visibility = $tableVisibility[$styleName] ?? ''; + $tmpInfo = [ + 'worksheetName' => $xml->getAttribute('table:name'), + 'lastColumnLetter' => 'A', + 'lastColumnIndex' => 0, + 'totalRows' => 0, + 'totalColumns' => 0, + 'sheetState' => $visibility, + ]; + + // Loop through each child node of the table:table element reading + $currCells = 0; + do { $xml->read(); - } else { - $xml->next(); - } - } - // Now read each node until we find our first table:table node - while ($xml->read()) { - if (self::getXmlName($xml) == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) { - $worksheetNames[] = $xml->getAttribute('table:name'); - - $tmpInfo = [ - 'worksheetName' => $xml->getAttribute('table:name'), - 'lastColumnLetter' => 'A', - 'lastColumnIndex' => 0, - 'totalRows' => 0, - 'totalColumns' => 0, - ]; - - // Loop through each child node of the table:table element reading - $currCells = 0; - do { + if (self::getXmlName($xml) == 'table:table-row' && $xml->nodeType == XMLReader::ELEMENT) { + $rowspan = $xml->getAttribute('table:number-rows-repeated'); + $rowspan = empty($rowspan) ? 1 : $rowspan; + $tmpInfo['totalRows'] += $rowspan; + $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells); + $currCells = 0; + // Step into the row $xml->read(); - if (self::getXmlName($xml) == 'table:table-row' && $xml->nodeType == XMLReader::ELEMENT) { - $rowspan = $xml->getAttribute('table:number-rows-repeated'); - $rowspan = empty($rowspan) ? 1 : $rowspan; - $tmpInfo['totalRows'] += $rowspan; - $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells); - $currCells = 0; - // Step into the row - $xml->read(); - do { - $doread = true; - if (self::getXmlName($xml) == 'table:table-cell' && $xml->nodeType == XMLReader::ELEMENT) { - if (!$xml->isEmptyElement) { - ++$currCells; - $xml->next(); - $doread = false; - } - } elseif (self::getXmlName($xml) == 'table:covered-table-cell' && $xml->nodeType == XMLReader::ELEMENT) { - $mergeSize = $xml->getAttribute('table:number-columns-repeated'); - $currCells += (int) $mergeSize; + do { + $doread = true; + if (self::getXmlName($xml) == 'table:table-cell' && $xml->nodeType == XMLReader::ELEMENT) { + if (!$xml->isEmptyElement) { + ++$currCells; + $xml->next(); + $doread = false; } - if ($doread) { - $xml->read(); - } - } while (self::getXmlName($xml) != 'table:table-row'); - } - } while (self::getXmlName($xml) != 'table:table'); + } elseif (self::getXmlName($xml) == 'table:covered-table-cell' && $xml->nodeType == XMLReader::ELEMENT) { + $mergeSize = $xml->getAttribute('table:number-columns-repeated'); + $currCells += (int) $mergeSize; + } + if ($doread) { + $xml->read(); + } + } while (self::getXmlName($xml) != 'table:table-row'); + } + } while (self::getXmlName($xml) != 'table:table'); - $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells); - $tmpInfo['lastColumnIndex'] = $tmpInfo['totalColumns'] - 1; - $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1); - $worksheetInfo[] = $tmpInfo; - } + $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells); + $tmpInfo['lastColumnIndex'] = $tmpInfo['totalColumns'] - 1; + $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1); + $worksheetInfo[] = $tmpInfo; } } diff --git a/src/PhpSpreadsheet/Reader/Slk.php b/src/PhpSpreadsheet/Reader/Slk.php index 7359f926e..da419b45d 100644 --- a/src/PhpSpreadsheet/Reader/Slk.php +++ b/src/PhpSpreadsheet/Reader/Slk.php @@ -131,6 +131,7 @@ class Slk extends BaseReader $worksheetInfo[0]['totalRows'] = $rowIndex; $worksheetInfo[0]['lastColumnLetter'] = Coordinate::stringFromColumnIndex($worksheetInfo[0]['lastColumnIndex'] + 1); $worksheetInfo[0]['totalColumns'] = $worksheetInfo[0]['lastColumnIndex'] + 1; + $worksheetInfo[0]['sheetState'] = Worksheet::SHEETSTATE_VISIBLE; // Close file fclose($fileHandle); diff --git a/src/PhpSpreadsheet/Reader/Xls/ListFunctions.php b/src/PhpSpreadsheet/Reader/Xls/ListFunctions.php index 6477606b0..ab9849a7a 100644 --- a/src/PhpSpreadsheet/Reader/Xls/ListFunctions.php +++ b/src/PhpSpreadsheet/Reader/Xls/ListFunctions.php @@ -5,6 +5,7 @@ namespace PhpOffice\PhpSpreadsheet\Reader\Xls; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Reader\Xls; use PhpOffice\PhpSpreadsheet\Shared\File; +use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; class ListFunctions extends Xls { @@ -104,6 +105,7 @@ class ListFunctions extends Xls $tmpInfo['lastColumnIndex'] = 0; $tmpInfo['totalRows'] = 0; $tmpInfo['totalColumns'] = 0; + $tmpInfo['sheetState'] = $sheet['sheetState']; $xls->pos = $sheet['offset']; diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index e7cd0b711..6b93c9763 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -238,6 +238,8 @@ class Xlsx extends BaseReader 'totalRows' => 0, 'totalColumns' => 0, ]; + $sheetState = (string) (self::getAttributes($eleSheet)['state'] ?? Worksheet::SHEETSTATE_VISIBLE); + $tmpInfo['sheetState'] = $sheetState; $fileWorksheet = (string) $worksheets[self::getArrayItemString(self::getAttributes($eleSheet, $namespace), 'id')]; $fileWorksheetPath = str_starts_with($fileWorksheet, '/') ? substr($fileWorksheet, 1) : "$dir/$fileWorksheet"; @@ -257,7 +259,7 @@ class Xlsx extends BaseReader $currCells = 0; while ($xml->read()) { if ($xml->localName == 'row' && $xml->nodeType == XMLReader::ELEMENT && $xml->namespaceURI === $mainNS) { - $row = $xml->getAttribute('r'); + $row = (int) $xml->getAttribute('r'); $tmpInfo['totalRows'] = $row; $tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells); $currCells = 0; diff --git a/src/PhpSpreadsheet/Reader/Xml.php b/src/PhpSpreadsheet/Reader/Xml.php index 5e9830f43..0a54182d7 100644 --- a/src/PhpSpreadsheet/Reader/Xml.php +++ b/src/PhpSpreadsheet/Reader/Xml.php @@ -229,6 +229,7 @@ class Xml extends BaseReader $tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1); $tmpInfo['totalColumns'] = $tmpInfo['lastColumnIndex'] + 1; + $tmpInfo['sheetState'] = Worksheet::SHEETSTATE_VISIBLE; $worksheetInfo[] = $tmpInfo; ++$worksheetID; diff --git a/tests/PhpSpreadsheetTests/Reader/Gnumeric/GnumericInfoTest.php b/tests/PhpSpreadsheetTests/Reader/Gnumeric/GnumericInfoTest.php index 74b9a8bde..be0174d26 100644 --- a/tests/PhpSpreadsheetTests/Reader/Gnumeric/GnumericInfoTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Gnumeric/GnumericInfoTest.php @@ -32,6 +32,7 @@ class GnumericInfoTest extends TestCase 'lastColumnIndex' => 13, 'totalRows' => 31, 'totalColumns' => 14, + 'sheetState' => 'visible', ], [ 'worksheetName' => 'Report Data', @@ -39,6 +40,7 @@ class GnumericInfoTest extends TestCase 'lastColumnIndex' => 10, 'totalRows' => 65535, 'totalColumns' => 11, + 'sheetState' => 'visible', ], ]; self::assertEquals($expected, $info); diff --git a/tests/PhpSpreadsheetTests/Reader/Gnumeric/HiddenWorksheetTest.php b/tests/PhpSpreadsheetTests/Reader/Gnumeric/HiddenWorksheetTest.php index 021ece1cb..92459adce 100644 --- a/tests/PhpSpreadsheetTests/Reader/Gnumeric/HiddenWorksheetTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Gnumeric/HiddenWorksheetTest.php @@ -49,4 +49,29 @@ class HiddenWorksheetTest extends TestCase ], ]; } + + public function testListWorksheetInfo(): void + { + $filename = 'tests/data/Reader/Gnumeric/HiddenSheet.gnumeric'; + $reader = new Gnumeric(); + $expected = [ + [ + 'worksheetName' => 'Sheet1', + 'lastColumnLetter' => 'A', + 'lastColumnIndex' => 0, + 'totalRows' => 1, + 'totalColumns' => 1, + 'sheetState' => 'visible', + ], + [ + 'worksheetName' => 'Sheet2', + 'lastColumnLetter' => 'A', + 'lastColumnIndex' => 0, + 'totalRows' => 1, + 'totalColumns' => 1, + 'sheetState' => 'hidden', + ], + ]; + self::assertSame($expected, $reader->listWorksheetInfo($filename)); + } } diff --git a/tests/PhpSpreadsheetTests/Reader/Ods/HiddenWorksheetTest.php b/tests/PhpSpreadsheetTests/Reader/Ods/HiddenWorksheetTest.php index 9793a3c09..988947f0e 100644 --- a/tests/PhpSpreadsheetTests/Reader/Ods/HiddenWorksheetTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Ods/HiddenWorksheetTest.php @@ -12,7 +12,7 @@ class HiddenWorksheetTest extends TestCase { public function testPageSetup(): void { - $filename = 'tests/data/Reader/Ods/HiddenSheet.ods'; + $filename = 'tests/data/Reader/Ods/HiddenSheet2.ods'; $reader = new Ods(); $spreadsheet = $reader->load($filename); $assertions = $this->worksheetAssertions(); @@ -49,4 +49,29 @@ class HiddenWorksheetTest extends TestCase ], ]; } + + public function testListWorksheetInfo(): void + { + $filename = 'tests/data/Reader/Ods/HiddenSheet2.ods'; + $reader = new Ods(); + $expected = [ + [ + 'worksheetName' => 'Sheet1', + 'lastColumnLetter' => 'A', + 'lastColumnIndex' => 0, + 'totalRows' => 1, + 'totalColumns' => 1, + 'sheetState' => 'visible', + ], + [ + 'worksheetName' => 'Sheet2', + 'lastColumnLetter' => 'A', + 'lastColumnIndex' => 0, + 'totalRows' => 1, + 'totalColumns' => 1, + 'sheetState' => 'hidden', + ], + ]; + self::assertSame($expected, $reader->listWorksheetInfo($filename)); + } } diff --git a/tests/PhpSpreadsheetTests/Reader/Ods/OdsInfoTest.php b/tests/PhpSpreadsheetTests/Reader/Ods/OdsInfoTest.php index e780d32b6..03e80356c 100644 --- a/tests/PhpSpreadsheetTests/Reader/Ods/OdsInfoTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Ods/OdsInfoTest.php @@ -70,6 +70,7 @@ class OdsInfoTest extends TestCase 'lastColumnIndex' => 2, 'totalRows' => 12, 'totalColumns' => 3, + 'sheetState' => 'visible', ], [ 'worksheetName' => 'Second Sheet', @@ -77,6 +78,7 @@ class OdsInfoTest extends TestCase 'lastColumnIndex' => 0, 'totalRows' => 2, 'totalColumns' => 1, + 'sheetState' => 'visible', ], ], $wsinfo); } diff --git a/tests/PhpSpreadsheetTests/Reader/Xls/HiddenWorksheetTest.php b/tests/PhpSpreadsheetTests/Reader/Xls/HiddenWorksheetTest.php index 904843923..3a2949a51 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xls/HiddenWorksheetTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xls/HiddenWorksheetTest.php @@ -49,4 +49,45 @@ class HiddenWorksheetTest extends TestCase ], ]; } + + public function testListWorksheetInfo(): void + { + $filename = 'tests/data/Reader/XLS/visibility.xls'; + $reader = new Xls(); + $expected = [ + [ + 'worksheetName' => 'Sheet1', + 'lastColumnLetter' => 'A', + 'lastColumnIndex' => 0, + 'totalRows' => 1, + 'totalColumns' => 1, + 'sheetState' => 'visible', + ], + [ + 'worksheetName' => 'Sheet2', + 'lastColumnLetter' => 'A', + 'lastColumnIndex' => 0, + 'totalRows' => 1, + 'totalColumns' => 1, + 'sheetState' => 'hidden', + ], + [ + 'worksheetName' => 'Sheet3', + 'lastColumnLetter' => 'A', + 'lastColumnIndex' => 0, + 'totalRows' => 1, + 'totalColumns' => 1, + 'sheetState' => 'visible', + ], + [ + 'worksheetName' => 'Sheet4', + 'lastColumnLetter' => 'A', + 'lastColumnIndex' => 0, + 'totalRows' => 2, + 'totalColumns' => 1, + 'sheetState' => 'veryHidden', + ], + ]; + self::assertSame($expected, $reader->listWorksheetInfo($filename)); + } } diff --git a/tests/PhpSpreadsheetTests/Reader/Xls/InfoNamesTest.php b/tests/PhpSpreadsheetTests/Reader/Xls/InfoNamesTest.php index 4f9073a7b..6a92a83a6 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xls/InfoNamesTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xls/InfoNamesTest.php @@ -31,6 +31,7 @@ class InfoNamesTest extends TestCase 'lastColumnIndex' => 4, 'totalRows' => 19, 'totalColumns' => 5, + 'sheetState' => 'visible', ], [ 'worksheetName' => 'Terms and conditions', @@ -38,6 +39,7 @@ class InfoNamesTest extends TestCase 'lastColumnIndex' => 1, 'totalRows' => 3, 'totalColumns' => 2, + 'sheetState' => 'visible', ], ]; self::assertSame($expected, $info); @@ -66,6 +68,7 @@ class InfoNamesTest extends TestCase 'lastColumnIndex' => 1, 'totalRows' => 1, 'totalColumns' => 2, + 'sheetState' => 'visible', ], ]; self::assertSame($expected, $info); @@ -106,6 +109,7 @@ class InfoNamesTest extends TestCase 'lastColumnIndex' => 15, 'totalRows' => 3, 'totalColumns' => 16, + 'sheetState' => 'visible', ], ]; self::assertSame($expected, $info); diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/HiddenWorksheetTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/HiddenWorksheetTest.php index b464cb6cf..f6017a120 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xlsx/HiddenWorksheetTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/HiddenWorksheetTest.php @@ -46,4 +46,45 @@ class HiddenWorksheetTest extends TestCase ], ]; } + + public function testListWorksheetInfo(): void + { + $filename = 'tests/data/Reader/XLSX/visibility.xlsx'; + $reader = new Xlsx(); + $expected = [ + [ + 'worksheetName' => 'Sheet1', + 'lastColumnLetter' => 'A', + 'lastColumnIndex' => 0, + 'totalRows' => 1, + 'totalColumns' => 1, + 'sheetState' => 'visible', + ], + [ + 'worksheetName' => 'Sheet2', + 'lastColumnLetter' => 'A', + 'lastColumnIndex' => 0, + 'totalRows' => 1, + 'totalColumns' => 1, + 'sheetState' => 'hidden', + ], + [ + 'worksheetName' => 'Sheet3', + 'lastColumnLetter' => 'A', + 'lastColumnIndex' => 0, + 'totalRows' => 1, + 'totalColumns' => 1, + 'sheetState' => 'visible', + ], + [ + 'worksheetName' => 'Sheet4', + 'lastColumnLetter' => 'B', + 'lastColumnIndex' => 1, + 'totalRows' => 1, + 'totalColumns' => 2, + 'sheetState' => 'veryHidden', + ], + ]; + self::assertSame($expected, $reader->listWorksheetInfo($filename)); + } } diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/Issue2516Test.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/Issue2516Test.php index c4333d6f3..2d5d3901c 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xlsx/Issue2516Test.php +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/Issue2516Test.php @@ -52,8 +52,9 @@ class Issue2516Test extends TestCase 'worksheetName' => 'Sheet1', 'lastColumnLetter' => 'B', 'lastColumnIndex' => 1, - 'totalRows' => '6', + 'totalRows' => 6, 'totalColumns' => 2, + 'sheetState' => 'visible', ], ]; self::assertSame($expected, $infos); diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/NamespacePurlTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/NamespacePurlTest.php index 5e1e6a567..77e136378 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xlsx/NamespacePurlTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/NamespacePurlTest.php @@ -40,8 +40,9 @@ class NamespacePurlTest extends \PHPUnit\Framework\TestCase 'worksheetName' => 'ml_out', 'lastColumnLetter' => 'R', 'lastColumnIndex' => 17, - 'totalRows' => '76', + 'totalRows' => 76, 'totalColumns' => 18, + 'sheetState' => 'visible', ], ]; diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/WorksheetInfoNamesTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/WorksheetInfoNamesTest.php index 4c56339fb..fa6fe9a86 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xlsx/WorksheetInfoNamesTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/WorksheetInfoNamesTest.php @@ -22,6 +22,7 @@ class WorksheetInfoNamesTest extends TestCase 'lastColumnIndex' => 5, 'totalRows' => '6', 'totalColumns' => 6, + 'sheetState' => 'visible', ], ]; @@ -51,6 +52,7 @@ class WorksheetInfoNamesTest extends TestCase 'lastColumnIndex' => 10, 'totalRows' => 2, 'totalColumns' => 11, + 'sheetState' => 'visible', ], ]; diff --git a/tests/PhpSpreadsheetTests/Reader/Xml/XmlInfoTest.php b/tests/PhpSpreadsheetTests/Reader/Xml/XmlInfoTest.php index 623fc8be9..5eb2149bc 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xml/XmlInfoTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xml/XmlInfoTest.php @@ -52,6 +52,7 @@ class XmlInfoTest extends TestCase 'lastColumnIndex' => 9, 'totalRows' => 31, 'totalColumns' => 10, + 'sheetState' => 'visible', ], [ 'worksheetName' => 'Report Data', @@ -59,6 +60,7 @@ class XmlInfoTest extends TestCase 'lastColumnIndex' => 8, 'totalRows' => 15, 'totalColumns' => 9, + 'sheetState' => 'visible', ], ]; self::assertEquals($expected, $info); diff --git a/tests/data/Reader/Ods/HiddenSheet.ods b/tests/data/Reader/Ods/HiddenSheet.ods deleted file mode 100644 index 9b1d01a69cfd9833776b874d4ecb1d669a344d79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2821 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 TcW;+r-+VwfL1&5N_SgRbKKHxh literal 0 HcmV?d00001 diff --git a/tests/data/Reader/XLS/visibility.xls b/tests/data/Reader/XLS/visibility.xls new file mode 100644 index 0000000000000000000000000000000000000000..ffa4053487533f954d0254320462cbefb11cdf4c GIT binary patch literal 31744 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 b4`x@TvoiH(`{EzWJnFEOmPfxzMmX?4I!-w8 literal 0 HcmV?d00001 diff --git a/tests/data/Reader/XLSX/visibility.xlsx b/tests/data/Reader/XLSX/visibility.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..0f66450f6c410da2bec0c69583946ea16ac7a214 GIT binary patch literal 11166 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