diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cba4f68..72447054 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -- Cell formats with escaped spaces were causing incorrect date formatting - [#557](https://github.com/PHPOffice/PhpSpreadsheet/issues/557) +### Added + +- Support workbook view attributes for Xlsx format - [#523](https://github.com/PHPOffice/PhpSpreadsheet/issues/523) ### Fixed - Xlsx reader crashed when reading a file with workbook protection - [#553](https://github.com/PHPOffice/PhpSpreadsheet/pull/553) +- Cell formats with escaped spaces were causing incorrect date formatting - [#557](https://github.com/PHPOffice/PhpSpreadsheet/issues/557) ## [1.3.1] - 2018-06-12 diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php index 7a8f48b1..2b50bbd3 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx.php +++ b/src/PhpSpreadsheet/Reader/Xlsx.php @@ -1973,8 +1973,10 @@ class Xlsx extends BaseReader } if ((!$this->readDataOnly) || (!empty($this->loadSheetsOnly))) { + $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) { @@ -1985,6 +1987,46 @@ class Xlsx extends BaseReader } $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; @@ -2475,4 +2517,27 @@ 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; + } } diff --git a/src/PhpSpreadsheet/Spreadsheet.php b/src/PhpSpreadsheet/Spreadsheet.php index ff63223a..150f71b1 100644 --- a/src/PhpSpreadsheet/Spreadsheet.php +++ b/src/PhpSpreadsheet/Spreadsheet.php @@ -9,6 +9,17 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; 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. * @@ -123,6 +134,67 @@ class Spreadsheet */ private $unparsedLoadedData = []; + /** + * Controls visibility of the horizonal scroll bar in the application. + * + * @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 $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 ? * @@ -1216,4 +1288,203 @@ class Spreadsheet { return $this->uniqueID; } + + /** + * Get the visibility of the horizonal scroll bar in the application. + * + * @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 bool True if vertical scroll bar is visible + */ + public function getShowVerticalScroll() + { + 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 Exception 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 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 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 $visibility visibility status of the workbook + * + * @throws Exception if the given value is invalid + */ + public function setVisibility($visibility) + { + if ($visibility === null) { + $visibility = self::VISIBILITY_VISIBLE; + } + + if (in_array($visibility, self::$workbookViewVisibilityValues)) { + $this->visibility = $visibility; + } else { + throw new Exception('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; + } + + /** + * 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 Exception if the given value is invalid + */ + public function setTabRatio($tabRatio) + { + if ($tabRatio >= 0 || $tabRatio <= 1000) { + $this->tabRatio = (int) $tabRatio; + } else { + throw new Exception('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 cede7edb..43a1916f 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Workbook.php @@ -117,14 +117,14 @@ class Workbook extends WriterPart $objWriter->startElement('workbookView'); $objWriter->writeAttribute('activeTab', $spreadsheet->getActiveSheetIndex()); - $objWriter->writeAttribute('autoFilterDateGrouping', '1'); - $objWriter->writeAttribute('firstSheet', '0'); - $objWriter->writeAttribute('minimized', '0'); - $objWriter->writeAttribute('showHorizontalScroll', '1'); - $objWriter->writeAttribute('showSheetTabs', '1'); - $objWriter->writeAttribute('showVerticalScroll', '1'); - $objWriter->writeAttribute('tabRatio', '600'); - $objWriter->writeAttribute('visibility', 'visible'); + $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/Functional/WorkbookViewAttributesTest.php b/tests/PhpSpreadsheetTests/Functional/WorkbookViewAttributesTest.php new file mode 100644 index 00000000..cce2a95b --- /dev/null +++ b/tests/PhpSpreadsheetTests/Functional/WorkbookViewAttributesTest.php @@ -0,0 +1,80 @@ +getActiveSheet(); + $worksheet1->setTitle('Tweedledee'); + $worksheet1->setCellValue('A1', 1); + $worksheet2 = $workbook->createSheet(); + $worksheet2->setTitle('Tweeldedum'); + $worksheet2->setCellValue('A1', 2); + + // 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()); + + $workbook2 = $this->writeAndReload($workbook, $format); + + // 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()); + } +}