diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index 2241dc945e..369497683e 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -1973,14 +1973,10 @@ public function load($pFilename) } if ((!$this->readDataOnly) || (!empty($this->loadSheetsOnly))) { - if ($xmlWorkbook->bookViews->workbookView) { - foreach ($xmlWorkbook->bookViews->workbookView->attributes() as $attr => $value) { - $excel->setWorkbookViewAttribute((string) $attr, (string) $value); - } - } + $workbookView = $xmlWorkbook->bookViews->workbookView; // active sheet index - $activeTab = (int) ($xmlWorkbook->bookViews->workbookView['activeTab']); // refers to old sheet index + $activeTab = (int) ($workbookView['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) { @@ -1991,6 +1987,46 @@ public function load($pFilename) } $excel->setActiveSheetIndex(0); } + + if (isset($workbookView['showHorizontalScroll'])) { + $showHorizontalScroll = (string) $workbookView['showHorizontalScroll']; + $excel->setShowHorizontalScroll($this->castXsdBooleanToBool($showHorizontalScroll)); + } + + if (isset($workbookView['showVerticalScroll'])) { + $showVerticalScroll = (string) $workbookView['showVerticalScroll']; + $excel->setShowVerticalScroll($this->castXsdBooleanToBool($showVerticalScroll)); + } + + if (isset($workbookView['showSheetTabs'])) { + $showSheetTabs = (string) $workbookView['showSheetTabs']; + $excel->setShowSheetTabs($this->castXsdBooleanToBool($showSheetTabs)); + } + + if (isset($workbookView['minimized'])) { + $minimized = (string) $workbookView['minimized']; + $excel->setMinimized($this->castXsdBooleanToBool($minimized)); + } + + if (isset($workbookView['autoFilterDateGrouping'])) { + $autoFilterDateGrouping = (string) $workbookView['autoFilterDateGrouping']; + $excel->setAutoFilterDateGrouping($this->castXsdBooleanToBool($autoFilterDateGrouping)); + } + + if (isset($workbookView['firstSheet'])) { + $firstSheet = (string) $workbookView['firstSheet']; + $excel->setFirstSheetIndex((int) $firstSheet); + } + + if (isset($workbookView['visibility'])) { + $visibility = (string) $workbookView['visibility']; + $excel->setVisibility($visibility); + } + + if (isset($workbookView['tabRatio'])) { + $tabRatio = (string) $workbookView['tabRatio']; + $excel->setTabRatio((int) $tabRatio); + } } break; @@ -2481,4 +2517,27 @@ private function readPrinterSettings(Spreadsheet $excel, ZipArchive $zip, $dir, } 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; + } } diff --git a/src/PhpSpreadsheet/Spreadsheet.php b/src/PhpSpreadsheet/Spreadsheet.php index d67eb9539e..da3b0917d1 100644 --- a/src/PhpSpreadsheet/Spreadsheet.php +++ b/src/PhpSpreadsheet/Spreadsheet.php @@ -9,6 +9,17 @@ class Spreadsheet { + // Allowable values for workbook window visilbity + const VISIBILITY_VISIBLE = 'visible'; + const VISIBILITY_HIDDEN = 'hidden'; + const VISIBILITY_VERY_HIDDEN = 'veryHidden'; + + private static $workbookViewVisibilityValues = [ + self::VISIBILITY_VISIBLE, + self::VISIBILITY_HIDDEN, + self::VISIBILITY_VERY_HIDDEN, + ]; + /** * Unique ID. * @@ -124,11 +135,65 @@ class Spreadsheet private $unparsedLoadedData = []; /** - * workbookViewAttributes : null if workbook isn't Excel 2007 or doesn't have any workbook bookviews. + * Controls visibility of the horizonal scroll bar in the application. * - * @var null|array + * @var bool + */ + private $showHorizontalScroll = true; + + /** + * Controls visibility of the horizonal scroll bar in the application. + * + * @var bool + */ + private $showVerticalScroll = true; + + /** + * Controls visibility of the sheet tabs in the application. + * + * @var bool + */ + private $showSheetTabs = true; + + /** + * Specifies a boolean value that indicates whether the workbook window + * is minimized. + * + * @var bool + */ + private $minimized = false; + + /** + * Specifies a boolean value that indicates whether to group dates + * when presenting the user with filtering optiomd in the user + * interface. + * + * @var bool + */ + private $autoFilterDateGrouping = true; + + /** + * Specifies the index to the first sheet in the book view. + * + * @var int + */ + private $firstSheetIndex = 0; + + /** + * Specifies the visible status of the workbook. + * + * @var string */ - private $workbookViewAttributes; + private $visibility = self::VISIBILITY_VISIBLE; + + /** + * Specifies the ratio between the workbook tabs bar and the horizontal + * scroll bar. TabRatio is assumed to be out of 1000 of the horizontal + * window width. + * + * @var int + */ + private $tabRatio = 600; /** * The workbook has macros ? @@ -1225,34 +1290,201 @@ public function getID() } /** - * Get a workbook bookview attribute. + * Get the visibility of the horizonal scroll bar in the application. * - * @param string $attr Name of attribute to read - * @param null|string $default Value to return if attribute doesn't exist + * @return bool True if horizonal scroll bar is visible + */ + public function getShowHorizontalScroll() + { + return $this->showHorizontalScroll; + } + + /** + * Set the visibility of the horizonal scroll bar in the application. + * + * @param bool $showHorizontalScroll True if horizonal scroll bar is visible + */ + public function setShowHorizontalScroll($showHorizontalScroll) + { + $this->showHorizontalScroll = (bool) $showHorizontalScroll; + } + + /** + * Get the visibility of the vertical scroll bar in the application. * - * @return string Value of attribute + * @return bool True if vertical scroll bar is visible */ - public function getWorkbookViewAttribute($attr, $default = null) + public function getShowVerticalScroll() { - if ($this->workbookViewAttributes && isset($this->workbookViewAttributes[$attr])) { - return $this->workbookViewAttributes[$attr]; + return $this->showVerticalScroll; + } + + /** + * Set the visibility of the vertical scroll bar in the application. + * + * @param bool $showVerticalScroll True if vertical scroll bar is visible + */ + public function setShowVerticalScroll($showVerticalScroll) + { + $this->showVerticalScroll = (bool) $showVerticalScroll; + } + + /** + * Get the visibility of the sheet tabs in the application. + * + * @return bool True if the sheet tabs are visible + */ + public function getShowSheetTabs() + { + return $this->showSheetTabs; + } + + /** + * Set the visibility of the sheet tabs in the application. + * + * @param bool $showSheetTabs True if sheet tabs are visible + */ + public function setShowSheetTabs($showSheetTabs) + { + $this->showSheetTabs = (bool) $showSheetTabs; + } + + /** + * Return whether the workbook window is minimized. + * + * @return bool true if workbook window is minimized + */ + public function getMinimized() + { + return $this->minimized; + } + + /** + * Set whether the workbook window is minimized. + * + * @param bool $minimized true if workbook window is minimized + */ + public function setMinimized($minimized) + { + $this->minimized = (bool) $minimized; + } + + /** + * Return whether to group dates when presenting the user with + * filtering optiomd in the user interface. + * + * @return bool true if workbook window is minimized + */ + public function getAutoFilterDateGrouping() + { + return $this->autoFilterDateGrouping; + } + + /** + * Set whether to group dates when presenting the user with + * filtering optiomd in the user interface. + * + * @param bool $autoFilterDateGrouping true if workbook window is minimized + */ + public function setAutoFilterDateGrouping($autoFilterDateGrouping) + { + $this->autoFilterDateGrouping = (bool) $autoFilterDateGrouping; + } + + /** + * Return the first sheet in the book view. + * + * @return int First sheet in book view + */ + public function getFirstSheetIndex() + { + return $this->firstSheetIndex; + } + + /** + * Set the first sheet in the book view. + * + * @param int $firstSheetIndex First sheet in book view + * + * @throws PhpSpreadsheetException if the given value is invalid + */ + public function setFirstSheetIndex($firstSheetIndex) + { + if ($firstSheetIndex >= 0) { + $this->firstSheetIndex = (int) $firstSheetIndex; + } else { + throw new Exception('First sheet index must be a positive integer.'); } + } - return $default; + /** + * Return the visibility status of the workbook. + * + * This may be one of the following three values: + * - visibile + * + * @return string Visible status + */ + public function getVisibility() + { + return $this->visibility; } /** - * Set a workbook bookview attribute. + * Set the visibility status of the workbook. + * + * Valid values are: + * - 'visible' (self::VISIBILITY_VISIBLE): + * Workbook window is visible + * - 'hidden' (self::VISIBILITY_HIDDEN): + * Workbook window is hidden, but can be shown by the user + * via the user interface + * - 'veryHidden' (self::VISIBILITY_VERY_HIDDEN): + * Workbook window is hidden and cannot be shown in the + * user interface. * - * @param string $attr Name of attribute to set - * @param string $value Value of the attribute + * @param string $visibility visibility status of the workbook + * + * @throws PhpSpreadsheetException if the given value is invalid */ - public function setWorkbookViewAttribute($attr, $value) + public function setVisibility($visibility) { - if ($this->workbookViewAttributes === null) { - $this->workbookViewAttributes = []; + if ($visibility === null) { + $visibility = self::VISIBILITY_VISIBLE; + } + + if (in_array($visibility, self::$workbookViewVisibilityValues)) { + $this->visibility = $visibility; + } else { + throw new PhpSpreadsheetException('Invalid visibility value.'); } + } + + /** + * Get the ratio between the workbook tabs bar and the horizontal scroll bar. + * TabRatio is assumed to be out of 1000 of the horizontal window width. + * + * @return int Ratio between the workbook tabs bar and the horizontal scroll bar + */ + public function getTabRatio() + { + return $this->tabRatio; + } - $this->workbookViewAttributes[$attr] = $value; + /** + * Set the ratio between the workbook tabs bar and the horizontal scroll bar + * TabRatio is assumed to be out of 1000 of the horizontal window width. + * + * @param int $tabRatio Ratio between the tabs bar and the horizontal scroll bar + * + * @throws PhpSpreadsheetException if the given value is invalid + */ + public function setTabRatio($tabRatio) + { + if ($tabRatio >= 0 || $tabRatio <= 1000) { + $this->tabRatio = (int) $tabRatio; + } else { + throw new PhpSpreadsheetException('Tab ratio must be between 0 and 1000.'); + } } } diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php b/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php index 72f1e5bec0..43a1916f5b 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php @@ -12,24 +12,6 @@ class Workbook extends WriterPart { - /** - * Constant associative array listing the bookview attributes to write. - * Keys are the atttribute names and values are the default value for - * those attributes. - * - * @var array - */ - private static $bookViewAttributes = [ - 'autoFilterDateGrouping' => '1', - 'firstSheet' => '0', - 'minimized' => '0', - 'showHorizontalScroll' => '1', - 'showSheetTabs' => '1', - 'showVerticalScroll' => '1', - 'tabRatio' => '600', - 'visibility' => 'visible', - ]; - /** * Write workbook to XML format. * @@ -135,10 +117,14 @@ private function writeBookViews(XMLWriter $objWriter, Spreadsheet $spreadsheet) $objWriter->startElement('workbookView'); $objWriter->writeAttribute('activeTab', $spreadsheet->getActiveSheetIndex()); - - foreach (self::$bookViewAttributes as $attribute => $defaultValue) { - $objWriter->writeAttribute($attribute, $spreadsheet->getWorkbookViewAttribute($attribute, $defaultValue)); - } + $objWriter->writeAttribute('autoFilterDateGrouping', ($spreadsheet->getAutoFilterDateGrouping() ? 'true' : 'false')); + $objWriter->writeAttribute('firstSheet', $spreadsheet->getFirstSheetIndex()); + $objWriter->writeAttribute('minimized', ($spreadsheet->getMinimized() ? 'true' : 'false')); + $objWriter->writeAttribute('showHorizontalScroll', ($spreadsheet->getShowHorizontalScroll() ? 'true' : 'false')); + $objWriter->writeAttribute('showSheetTabs', ($spreadsheet->getShowSheetTabs() ? 'true' : 'false')); + $objWriter->writeAttribute('showVerticalScroll', ($spreadsheet->getShowVerticalScroll() ? 'true' : 'false')); + $objWriter->writeAttribute('tabRatio', $spreadsheet->getTabRatio()); + $objWriter->writeAttribute('visibility', $spreadsheet->getVisibility()); $objWriter->endElement(); diff --git a/tests/PhpSpreadsheetTests/Writer/Xlsx/WorkbookViewAttrsTest.php b/tests/PhpSpreadsheetTests/Writer/Xlsx/WorkbookViewAttrsTest.php index 8287bdaca9..6485bd0888 100644 --- a/tests/PhpSpreadsheetTests/Writer/Xlsx/WorkbookViewAttrsTest.php +++ b/tests/PhpSpreadsheetTests/Writer/Xlsx/WorkbookViewAttrsTest.php @@ -44,11 +44,37 @@ public function testPreserveWorkbookViewAttributes() $worksheet2->setTitle('Tweeldedum'); $worksheet2->setCellValue('A1', 2); - // Set the workbook bookbiews to non-default values - foreach (self::$bookViewAttributes as $attr => $value) { - $workbook->setWorkbookViewAttribute($attr, $value); - } + // Check that the bookview attributes return default values + $this->assertTrue($workbook->getShowHorizontalScroll()); + $this->assertTrue($workbook->getShowVerticalScroll()); + $this->assertTrue($workbook->getShowSheetTabs()); + $this->assertTrue($workbook->getAutoFilterDateGrouping()); + $this->assertFalse($workbook->getMinimized()); + $this->assertSame(0, $workbook->getFirstSheetIndex()); + $this->assertSame(600, $workbook->getTabRatio()); + $this->assertSame(Spreadsheet::VISIBILITY_VISIBLE, $workbook->getVisibility()); + + // Set the bookview attributes to non-default values + $workbook->setShowHorizontalScroll(false); + $workbook->setShowVerticalScroll(false); + $workbook->setShowSheetTabs(false); + $workbook->setAutoFilterDateGrouping(false); + $workbook->setMinimized(true); + $workbook->setFirstSheetIndex(1); + $workbook->setTabRatio(700); + $workbook->setVisibility(Spreadsheet::VISIBILITY_HIDDEN); + + // Check that bookview attributes were set properly + $this->assertFalse($workbook->getShowHorizontalScroll()); + $this->assertFalse($workbook->getShowVerticalScroll()); + $this->assertFalse($workbook->getShowSheetTabs()); + $this->assertFalse($workbook->getAutoFilterDateGrouping()); + $this->assertTrue($workbook->getMinimized()); + $this->assertSame(1, $workbook->getFirstSheetIndex()); + $this->assertSame(700, $workbook->getTabRatio()); + $this->assertSame(Spreadsheet::VISIBILITY_HIDDEN, $workbook->getVisibility()); + // Write then read the spreadsheet Settings::setLibXmlLoaderOptions(null); // reset to default options $targetFilename = tempnam(sys_get_temp_dir(), 'xlsx'); @@ -59,11 +85,17 @@ public function testPreserveWorkbookViewAttributes() $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx(); $workbook2 = $reader->load($targetFilename); - foreach (self::$bookViewAttributes as $attr => $value) { - $this->assertEquals($value, $workbook2->getWorkbookViewAttribute($attr)); - } + // Check that the read spreadsheet has the right bookview attributes + $this->assertFalse($workbook2->getShowHorizontalScroll()); + $this->assertFalse($workbook2->getShowVerticalScroll()); + $this->assertFalse($workbook2->getShowSheetTabs()); + $this->assertFalse($workbook2->getAutoFilterDateGrouping()); + $this->assertTrue($workbook2->getMinimized()); + $this->assertSame(1, $workbook2->getFirstSheetIndex()); + $this->assertSame(700, $workbook2->getTabRatio()); + $this->assertSame(Spreadsheet::VISIBILITY_HIDDEN, $workbook2->getVisibility()); } finally { - unlink($targetFilename); + // unlink($targetFilename); } } }