From b208c521072f32585beb2cee138ebe5f28a54895 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Fri, 11 Mar 2022 21:02:55 +0100 Subject: [PATCH] Fix for setting Active Sheet to the first loaded worksheet when `bookViews` element isn't defined (e.g. when file is created from Google Sheets) --- CHANGELOG.md | 1 + src/PhpSpreadsheet/Reader/Xlsx.php | 81 +--------- .../Reader/Xlsx/WorkbookView.php | 153 ++++++++++++++++++ 3 files changed, 156 insertions(+), 79 deletions(-) create mode 100644 src/PhpSpreadsheet/Reader/Xlsx/WorkbookView.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 030efbf5..6ff894ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Fixed +- Fix for setting Active Sheet to the first loaded worksheet when bookViews element isn't defined [Issue #2666](https://github.com/PHPOffice/PhpSpreadsheet/issues/2666) [PR #2669](https://github.com/PHPOffice/PhpSpreadsheet/pull/2669) - Fixed behaviour of XLSX font style vertical align settings. - Resolved formula translations to handle separators (row and column) for array functions as well as for function argument separators; and cleanly handle nesting levels. diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index 5d6ef59d..c0b0c5ee 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -20,6 +20,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Xlsx\SheetViewOptions; use PhpOffice\PhpSpreadsheet\Reader\Xlsx\SheetViews; use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Styles; use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Theme; +use PhpOffice\PhpSpreadsheet\Reader\Xlsx\WorkbookView; use PhpOffice\PhpSpreadsheet\ReferenceHelper; use PhpOffice\PhpSpreadsheet\RichText\RichText; use PhpOffice\PhpSpreadsheet\Settings; @@ -1564,62 +1565,7 @@ class Xlsx extends BaseReader } } - $workbookView = $xmlWorkbook->children($mainNS)->bookViews->workbookView; - if ((!$this->readDataOnly || !empty($this->loadSheetsOnly)) && !empty($workbookView)) { - $workbookViewAttributes = self::testSimpleXml(self::getAttributes($workbookView)); - // active sheet index - $activeTab = (int) $workbookViewAttributes->activeTab; // refers to old sheet index - - // keep active sheet index if sheet is still loaded, else first sheet is set as the active - if (isset($mapSheetId[$activeTab]) && $mapSheetId[$activeTab] !== null) { - $excel->setActiveSheetIndex($mapSheetId[$activeTab]); - } else { - if ($excel->getSheetCount() == 0) { - $excel->createSheet(); - } - $excel->setActiveSheetIndex(0); - } - - if (isset($workbookViewAttributes->showHorizontalScroll)) { - $showHorizontalScroll = (string) $workbookViewAttributes->showHorizontalScroll; - $excel->setShowHorizontalScroll($this->castXsdBooleanToBool($showHorizontalScroll)); - } - - if (isset($workbookViewAttributes->showVerticalScroll)) { - $showVerticalScroll = (string) $workbookViewAttributes->showVerticalScroll; - $excel->setShowVerticalScroll($this->castXsdBooleanToBool($showVerticalScroll)); - } - - if (isset($workbookViewAttributes->showSheetTabs)) { - $showSheetTabs = (string) $workbookViewAttributes->showSheetTabs; - $excel->setShowSheetTabs($this->castXsdBooleanToBool($showSheetTabs)); - } - - if (isset($workbookViewAttributes->minimized)) { - $minimized = (string) $workbookViewAttributes->minimized; - $excel->setMinimized($this->castXsdBooleanToBool($minimized)); - } - - if (isset($workbookViewAttributes->autoFilterDateGrouping)) { - $autoFilterDateGrouping = (string) $workbookViewAttributes->autoFilterDateGrouping; - $excel->setAutoFilterDateGrouping($this->castXsdBooleanToBool($autoFilterDateGrouping)); - } - - if (isset($workbookViewAttributes->firstSheet)) { - $firstSheet = (string) $workbookViewAttributes->firstSheet; - $excel->setFirstSheetIndex((int) $firstSheet); - } - - if (isset($workbookViewAttributes->visibility)) { - $visibility = (string) $workbookViewAttributes->visibility; - $excel->setVisibility($visibility); - } - - if (isset($workbookViewAttributes->tabRatio)) { - $tabRatio = (string) $workbookViewAttributes->tabRatio; - $excel->setTabRatio((int) $tabRatio); - } - } + (new WorkbookView($excel))->viewSettings($xmlWorkbook, $mainNS, $mapSheetId, $this->readDataOnly); break; } @@ -1978,29 +1924,6 @@ class Xlsx extends BaseReader unset($unparsedPrinterSettings); } - /** - * Convert an 'xsd:boolean' XML value to a PHP boolean value. - * A valid 'xsd:boolean' XML value can be one of the following - * four values: 'true', 'false', '1', '0'. It is case sensitive. - * - * Note that just doing '(bool) $xsdBoolean' is not safe, - * since '(bool) "false"' returns true. - * - * @see https://www.w3.org/TR/xmlschema11-2/#boolean - * - * @param string $xsdBoolean An XML string value of type 'xsd:boolean' - * - * @return bool Boolean value - */ - private function castXsdBooleanToBool($xsdBoolean) - { - if ($xsdBoolean === 'false') { - return false; - } - - return (bool) $xsdBoolean; - } - private function getWorkbookBaseName(): array { $workbookBasename = ''; diff --git a/src/PhpSpreadsheet/Reader/Xlsx/WorkbookView.php b/src/PhpSpreadsheet/Reader/Xlsx/WorkbookView.php new file mode 100644 index 00000000..9d61e3d3 --- /dev/null +++ b/src/PhpSpreadsheet/Reader/Xlsx/WorkbookView.php @@ -0,0 +1,153 @@ +spreadsheet = $spreadsheet; + } + + /** + * @param mixed $mainNS + */ + public function viewSettings(SimpleXMLElement $xmlWorkbook, $mainNS, array $mapSheetId, bool $readDataOnly): void + { + if ($this->spreadsheet->getSheetCount() == 0) { + $this->spreadsheet->createSheet(); + } + // Default active sheet index to the first loaded worksheet from the file + $this->spreadsheet->setActiveSheetIndex(0); + + $workbookView = $xmlWorkbook->children($mainNS)->bookViews->workbookView; + if (($readDataOnly !== true || !empty($this->loadSheetsOnly)) && !empty($workbookView)) { + $workbookViewAttributes = self::testSimpleXml(self::getAttributes($workbookView)); + // active sheet index + $activeTab = (int) $workbookViewAttributes->activeTab; // refers to old sheet index + // keep active sheet index if sheet is still loaded, else first sheet is set as the active worksheet + if (isset($mapSheetId[$activeTab]) && $mapSheetId[$activeTab] !== null) { + $this->spreadsheet->setActiveSheetIndex($mapSheetId[$activeTab]); + } + + $this->horizontalScroll($workbookViewAttributes); + $this->verticalScroll($workbookViewAttributes); + $this->sheetTabs($workbookViewAttributes); + $this->minimized($workbookViewAttributes); + $this->autoFilterDateGrouping($workbookViewAttributes); + $this->firstSheet($workbookViewAttributes); + $this->visibility($workbookViewAttributes); + $this->tabRatio($workbookViewAttributes); + } + } + + /** + * @param mixed $value + */ + public static function testSimpleXml($value): SimpleXMLElement + { + return ($value instanceof SimpleXMLElement) + ? $value + : new SimpleXMLElement(''); + } + + public static function getAttributes(?SimpleXMLElement $value, string $ns = ''): SimpleXMLElement + { + return self::testSimpleXml($value === null ? $value : $value->attributes($ns)); + } + + /** + * Convert an 'xsd:boolean' XML value to a PHP boolean value. + * A valid 'xsd:boolean' XML value can be one of the following + * four values: 'true', 'false', '1', '0'. It is case sensitive. + * + * Note that just doing '(bool) $xsdBoolean' is not safe, + * since '(bool) "false"' returns true. + * + * @see https://www.w3.org/TR/xmlschema11-2/#boolean + * + * @param string $xsdBoolean An XML string value of type 'xsd:boolean' + * + * @return bool Boolean value + */ + private function castXsdBooleanToBool(string $xsdBoolean): bool + { + if ($xsdBoolean === 'false') { + return false; + } + + return (bool) $xsdBoolean; + } + + private function horizontalScroll(SimpleXMLElement $workbookViewAttributes): void + { + if (isset($workbookViewAttributes->showHorizontalScroll)) { + $showHorizontalScroll = (string) $workbookViewAttributes->showHorizontalScroll; + $this->spreadsheet->setShowHorizontalScroll($this->castXsdBooleanToBool($showHorizontalScroll)); + } + } + + private function verticalScroll(SimpleXMLElement $workbookViewAttributes): void + { + if (isset($workbookViewAttributes->showVerticalScroll)) { + $showVerticalScroll = (string) $workbookViewAttributes->showVerticalScroll; + $this->spreadsheet->setShowVerticalScroll($this->castXsdBooleanToBool($showVerticalScroll)); + } + } + + private function sheetTabs(SimpleXMLElement $workbookViewAttributes): void + { + if (isset($workbookViewAttributes->showSheetTabs)) { + $showSheetTabs = (string) $workbookViewAttributes->showSheetTabs; + $this->spreadsheet->setShowSheetTabs($this->castXsdBooleanToBool($showSheetTabs)); + } + } + + private function minimized(SimpleXMLElement $workbookViewAttributes): void + { + if (isset($workbookViewAttributes->minimized)) { + $minimized = (string) $workbookViewAttributes->minimized; + $this->spreadsheet->setMinimized($this->castXsdBooleanToBool($minimized)); + } + } + + private function autoFilterDateGrouping(SimpleXMLElement $workbookViewAttributes): void + { + if (isset($workbookViewAttributes->autoFilterDateGrouping)) { + $autoFilterDateGrouping = (string) $workbookViewAttributes->autoFilterDateGrouping; + $this->spreadsheet->setAutoFilterDateGrouping($this->castXsdBooleanToBool($autoFilterDateGrouping)); + } + } + + private function firstSheet(SimpleXMLElement $workbookViewAttributes): void + { + if (isset($workbookViewAttributes->firstSheet)) { + $firstSheet = (string) $workbookViewAttributes->firstSheet; + $this->spreadsheet->setFirstSheetIndex((int) $firstSheet); + } + } + + private function visibility(SimpleXMLElement $workbookViewAttributes): void + { + if (isset($workbookViewAttributes->visibility)) { + $visibility = (string) $workbookViewAttributes->visibility; + $this->spreadsheet->setVisibility($visibility); + } + } + + private function tabRatio(SimpleXMLElement $workbookViewAttributes): void + { + if (isset($workbookViewAttributes->tabRatio)) { + $tabRatio = (string) $workbookViewAttributes->tabRatio; + $this->spreadsheet->setTabRatio((int) $tabRatio); + } + } +}