From a0bba38281f82f48e5fc981b4abe4396e13dbead Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Mon, 29 Jun 2020 20:15:41 +0200 Subject: [PATCH 01/29] Support pageOrder in page setup for Xlsx Reader and Writer --- src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php | 3 +++ src/PhpSpreadsheet/Worksheet/PageSetup.php | 19 +++++++++++++++++++ src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php | 3 +++ 3 files changed, 25 insertions(+) diff --git a/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php b/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php index bfb7a1f5..b556703c 100644 --- a/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php +++ b/src/PhpSpreadsheet/Reader/Xlsx/PageSetup.php @@ -69,6 +69,9 @@ class PageSetup extends BaseParserClass self::boolean((string) $xmlSheet->pageSetup['useFirstPageNumber'])) { $docPageSetup->setFirstPageNumber((int) ($xmlSheet->pageSetup['firstPageNumber'])); } + if (isset($xmlSheet->pageSetup['pageOrder'])) { + $docPageSetup->setPageOrder((string) $xmlSheet->pageSetup['pageOrder']); + } $relAttributes = $xmlSheet->pageSetup->attributes('http://schemas.openxmlformats.org/officeDocument/2006/relationships'); if (isset($relAttributes['id'])) { diff --git a/src/PhpSpreadsheet/Worksheet/PageSetup.php b/src/PhpSpreadsheet/Worksheet/PageSetup.php index f29dbad5..f52b414a 100644 --- a/src/PhpSpreadsheet/Worksheet/PageSetup.php +++ b/src/PhpSpreadsheet/Worksheet/PageSetup.php @@ -156,6 +156,9 @@ class PageSetup const SETPRINTRANGE_OVERWRITE = 'O'; const SETPRINTRANGE_INSERT = 'I'; + const PAGEORDER_OVER_THEN_DOWN = 'overThenDown'; + const PAGEORDER_DOWN_THEN_OVER = 'downThenOver'; + /** * Paper size. * @@ -246,6 +249,8 @@ class PageSetup */ private $firstPageNumber; + private $pageOrder; + /** * Create a new PageSetup. */ @@ -818,6 +823,20 @@ class PageSetup return $this->setFirstPageNumber(null); } + public function getPageOrder(): string + { + return $this->pageOrder; + } + + public function setPageOrder(?string $pageOrder): self + { + if ($pageOrder === null || $pageOrder === self::PAGEORDER_DOWN_THEN_OVER || $pageOrder === self::PAGEORDER_OVER_THEN_DOWN) { + $this->pageOrder = $pageOrder; + } + + return $this; + } + /** * Implement PHP __clone to create a deep clone, not just a shallow copy. */ diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index be064256..2a8a3add 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -875,6 +875,9 @@ class Worksheet extends WriterPart $objWriter->writeAttribute('firstPageNumber', $pSheet->getPageSetup()->getFirstPageNumber()); $objWriter->writeAttribute('useFirstPageNumber', '1'); } + if ($pSheet->getPageSetup()->getPageOrder() !== null) { + $objWriter->writeAttribute('pageOrder', $pSheet->getPageSetup()->getPageOrder()); + } $getUnparsedLoadedData = $pSheet->getParent()->getUnparsedLoadedData(); if (isset($getUnparsedLoadedData['sheets'][$pSheet->getCodeName()]['pageSetupRelId'])) { From 21b34f0afbabb30da2bd19980bb052ccf1295129 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Mon, 29 Jun 2020 20:27:47 +0200 Subject: [PATCH 02/29] pageOrder is nullable --- src/PhpSpreadsheet/Worksheet/PageSetup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpSpreadsheet/Worksheet/PageSetup.php b/src/PhpSpreadsheet/Worksheet/PageSetup.php index f52b414a..290b8349 100644 --- a/src/PhpSpreadsheet/Worksheet/PageSetup.php +++ b/src/PhpSpreadsheet/Worksheet/PageSetup.php @@ -823,7 +823,7 @@ class PageSetup return $this->setFirstPageNumber(null); } - public function getPageOrder(): string + public function getPageOrder(): ?string { return $this->pageOrder; } From 362b18ca122412eb12d2287aa9f329dba328cd54 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Tue, 30 Jun 2020 00:53:10 +0200 Subject: [PATCH 03/29] Read Print Settings Page Order in Xls Reader And eliminate switch for simple boolean portrait/landscape option --- src/PhpSpreadsheet/Reader/Xls.php | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Xls.php b/src/PhpSpreadsheet/Reader/Xls.php index 11a6195c..c60ea772 100644 --- a/src/PhpSpreadsheet/Reader/Xls.php +++ b/src/PhpSpreadsheet/Reader/Xls.php @@ -3444,6 +3444,9 @@ class Xls extends BaseReader // offset: 10; size: 2; option flags + // bit: 0; mask: 0x0001; 0=landscape, 1=portrait + $isOverThenDown= (0x0001 & self::getUInt2d($recordData, 10)); + // bit: 1; mask: 0x0002; 0=landscape, 1=portrait $isPortrait = (0x0002 & self::getUInt2d($recordData, 10)) >> 1; @@ -3453,16 +3456,8 @@ class Xls extends BaseReader if (!$isNotInit) { $this->phpSheet->getPageSetup()->setPaperSize($paperSize); - switch ($isPortrait) { - case 0: - $this->phpSheet->getPageSetup()->setOrientation(PageSetup::ORIENTATION_LANDSCAPE); - - break; - case 1: - $this->phpSheet->getPageSetup()->setOrientation(PageSetup::ORIENTATION_PORTRAIT); - - break; - } + $this->phpSheet->getPageSetup()->setPageOrder(((bool) $isOverThenDown) ? PageSetup::PAGEORDER_OVER_THEN_DOWN : PageSetup::PAGEORDER_DOWN_THEN_OVER); + $this->phpSheet->getPageSetup()->setOrientation(((bool) $isPortrait) ? PageSetup::ORIENTATION_PORTRAIT : PageSetup::ORIENTATION_LANDSCAPE); $this->phpSheet->getPageSetup()->setScale($scale, false); $this->phpSheet->getPageSetup()->setFitToPage((bool) $this->isFitToPages); From 4060cdec7e9e73919e154e8e89d0873def567588 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Tue, 30 Jun 2020 01:11:53 +0200 Subject: [PATCH 04/29] Fix comment --- src/PhpSpreadsheet/Reader/Xls.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpSpreadsheet/Reader/Xls.php b/src/PhpSpreadsheet/Reader/Xls.php index c60ea772..5e4ee225 100644 --- a/src/PhpSpreadsheet/Reader/Xls.php +++ b/src/PhpSpreadsheet/Reader/Xls.php @@ -3444,7 +3444,7 @@ class Xls extends BaseReader // offset: 10; size: 2; option flags - // bit: 0; mask: 0x0001; 0=landscape, 1=portrait + // bit: 0; mask: 0x0001; 0=down then over, 1=over then down $isOverThenDown= (0x0001 & self::getUInt2d($recordData, 10)); // bit: 1; mask: 0x0002; 0=landscape, 1=portrait From fbb04c1f8203420e9aa7171b8f2a87fe1c55b642 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Tue, 30 Jun 2020 07:01:51 +0200 Subject: [PATCH 05/29] Xls Writer changes to save print order --- src/PhpSpreadsheet/Writer/Xls/Worksheet.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php index 0dce7ba5..1a4504ed 100644 --- a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php @@ -1729,11 +1729,12 @@ class Worksheet extends BIFFwriter $numFtr = $this->phpSheet->getPageMargins()->getFooter(); // Footer Margin $iCopies = 0x01; // Number of copies - $fLeftToRight = 0x0; // Print over then down - + // Order of printing pages + $fLeftToRight = $this->phpSheet->getPageSetup()->getPageOrder() == PageSetup::PAGEORDER_OVER_THEN_DOWN + ? 0x0 : 0x1; // Page orientation - $fLandscape = ($this->phpSheet->getPageSetup()->getOrientation() == PageSetup::ORIENTATION_LANDSCAPE) ? - 0x0 : 0x1; + $fLandscape = ($this->phpSheet->getPageSetup()->getOrientation() == PageSetup::ORIENTATION_LANDSCAPE) + ? 0x0 : 0x1; $fNoPls = 0x0; // Setup not read from printer $fNoColor = 0x0; // Print black and white From 1f865c84c0d960c09b08d731edbac20797131cad Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Tue, 30 Jun 2020 07:24:12 +0200 Subject: [PATCH 06/29] Keep phpcs happy --- src/PhpSpreadsheet/Reader/Xls.php | 2 +- src/PhpSpreadsheet/Writer/Xls/Worksheet.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Xls.php b/src/PhpSpreadsheet/Reader/Xls.php index 5e4ee225..cdac07d0 100644 --- a/src/PhpSpreadsheet/Reader/Xls.php +++ b/src/PhpSpreadsheet/Reader/Xls.php @@ -3445,7 +3445,7 @@ class Xls extends BaseReader // offset: 10; size: 2; option flags // bit: 0; mask: 0x0001; 0=down then over, 1=over then down - $isOverThenDown= (0x0001 & self::getUInt2d($recordData, 10)); + $isOverThenDown = (0x0001 & self::getUInt2d($recordData, 10)); // bit: 1; mask: 0x0002; 0=landscape, 1=portrait $isPortrait = (0x0002 & self::getUInt2d($recordData, 10)) >> 1; diff --git a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php index 1a4504ed..eb6b479d 100644 --- a/src/PhpSpreadsheet/Writer/Xls/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xls/Worksheet.php @@ -1730,8 +1730,8 @@ class Worksheet extends BIFFwriter $iCopies = 0x01; // Number of copies // Order of printing pages - $fLeftToRight = $this->phpSheet->getPageSetup()->getPageOrder() == PageSetup::PAGEORDER_OVER_THEN_DOWN - ? 0x0 : 0x1; + $fLeftToRight = $this->phpSheet->getPageSetup()->getPageOrder() === PageSetup::PAGEORDER_DOWN_THEN_OVER + ? 0x1 : 0x0; // Page orientation $fLandscape = ($this->phpSheet->getPageSetup()->getOrientation() == PageSetup::ORIENTATION_LANDSCAPE) ? 0x0 : 0x1; From 3780072ae96fbe8f61c1b2109d217e62616c4f1e Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Tue, 30 Jun 2020 19:07:56 +0200 Subject: [PATCH 07/29] Retrieve basic print settings in the Ods Reader --- src/PhpSpreadsheet/Reader/Ods.php | 89 ++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Ods.php b/src/PhpSpreadsheet/Reader/Ods.php index d638d1fb..d3482716 100644 --- a/src/PhpSpreadsheet/Reader/Ods.php +++ b/src/PhpSpreadsheet/Reader/Ods.php @@ -19,11 +19,19 @@ use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Shared\File; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; +use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; +use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use XMLReader; use ZipArchive; class Ods extends BaseReader { + private $pageLayoutStyles = []; + + private $masterStylesCrossReference = []; + + private $masterPrintStylesCrossReference = []; + /** * Create a new Ods Reader instance. */ @@ -276,7 +284,45 @@ class Ods extends BaseReader (new DocumentProperties($spreadsheet))->load($xml, $namespacesMeta); - // Content + // Styles + + $dom = new DOMDocument('1.01', 'UTF-8'); + $dom->loadXML( + $this->securityScanner->scan($zip->getFromName('styles.xml')), + Settings::getLibXmlLoaderOptions() + ); + $officeNs = $dom->lookupNamespaceUri('office'); + $stylesNs = $dom->lookupNamespaceUri('style'); + + $styles = $dom->getElementsByTagNameNS($officeNs, 'automatic-styles') + ->item(0) + ->getElementsByTagNameNS($stylesNs, 'page-layout'); + + foreach ($styles as $styleSet) { + $styleName = $styleSet->getAttributeNS($stylesNs, 'name'); + $pageLayoutProperties = $styleSet->getElementsByTagNameNS($stylesNs, 'page-layout-properties')[0]; + $styleOrientation = $pageLayoutProperties->getAttributeNS($stylesNs, 'print-orientation'); + $styleScale = $pageLayoutProperties->getAttributeNS($stylesNs, 'scale-to'); + $stylePrintOrder = $pageLayoutProperties->getAttributeNS($stylesNs, 'print-page-order'); + + $this->pageLayoutStyles[$styleName] = (object) [ + 'orientation' => $styleOrientation, + 'scale' => $styleScale, + 'printOrder' => $stylePrintOrder, + ]; + } + + $styleMasterLookup = $dom->getElementsByTagNameNS($officeNs, 'master-styles') + ->item(0) + ->getElementsByTagNameNS($stylesNs, 'master-page'); + + foreach ($styleMasterLookup as $styleMasterSet) { + $styleMasterName = $styleMasterSet->getAttributeNS($stylesNs, 'name'); + $pageLayoutName = $styleMasterSet->getAttributeNS($stylesNs, 'page-layout-name'); + $this->masterPrintStylesCrossReference[$styleMasterName] = $pageLayoutName; + } + + // Main Content $dom = new DOMDocument('1.01', 'UTF-8'); $dom->loadXML( @@ -289,6 +335,20 @@ class Ods extends BaseReader $textNs = $dom->lookupNamespaceUri('text'); $xlinkNs = $dom->lookupNamespaceUri('xlink'); + $styleXReferences = $dom->getElementsByTagNameNS($officeNs, 'automatic-styles') + ->item(0) + ->getElementsByTagNameNS($stylesNs, 'style'); + + foreach ($styleXReferences as $styleXreferenceSet) { + $styleXRefName = $styleXreferenceSet->getAttributeNS($stylesNs, 'name'); + $stylePageLayoutName = $styleXreferenceSet->getAttributeNS($stylesNs, 'master-page-name'); + if (!empty($stylePageLayoutName)) { + $this->masterStylesCrossReference[$styleXRefName] = $stylePageLayoutName; + } + } + + // Content + $spreadsheets = $dom->getElementsByTagNameNS($officeNs, 'body') ->item(0) ->getElementsByTagNameNS($officeNs, 'spreadsheet'); @@ -309,6 +369,8 @@ class Ods extends BaseReader continue; } + $worksheetStyleName = $worksheetDataSet->getAttributeNS($tableNs, 'style-name'); + // Create sheet if ($worksheetID > 0) { $spreadsheet->createSheet(); // First sheet is added by default @@ -319,7 +381,7 @@ class Ods extends BaseReader // Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in // formula cells... during the load, all formulae should be correct, and we're simply // bringing the worksheet name in line with the formula, not the reverse - $spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false); + $spreadsheet->getActiveSheet()->setTitle((string) $worksheetName, false, false); } // Go through every child of table element @@ -641,6 +703,7 @@ class Ods extends BaseReader break; } } + $this->getPrintSettings($spreadsheet->getActiveSheet(), $worksheetStyleName); ++$worksheetID; } } @@ -649,6 +712,28 @@ class Ods extends BaseReader return $spreadsheet; } + private function getPrintSettings(Worksheet $worksheet, string $styleName) + { + if (!array_key_exists($styleName, $this->masterStylesCrossReference)) { + return; + } + $masterStyleName = $this->masterStylesCrossReference[$styleName]; + + if (!array_key_exists($masterStyleName, $this->masterPrintStylesCrossReference)) { + return; + } + $printSettingsIndex = $this->masterPrintStylesCrossReference[$masterStyleName]; + + if (!array_key_exists($printSettingsIndex, $this->pageLayoutStyles)) { + return; + } + $printSettings = $this->pageLayoutStyles[$printSettingsIndex]; + + $worksheet->getPageSetup()->setOrientation($printSettings->orientation ?? PageSetup::ORIENTATION_DEFAULT); + $worksheet->getPageSetup()->setScale((int) trim($printSettings->scale, '%')); + $worksheet->getPageSetup()->setPageOrder($printSettings->printOrder === 'ltr' ? PageSetup::PAGEORDER_OVER_THEN_DOWN : PageSetup::PAGEORDER_DOWN_THEN_OVER); + } + /** * Recursively scan element. * From 13a3363410e44c7d30814be3e7c94c2e68ddbff7 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Tue, 30 Jun 2020 19:15:08 +0200 Subject: [PATCH 08/29] Return void, just to keep phpcs happy --- src/PhpSpreadsheet/Reader/Ods.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpSpreadsheet/Reader/Ods.php b/src/PhpSpreadsheet/Reader/Ods.php index d3482716..040bfffa 100644 --- a/src/PhpSpreadsheet/Reader/Ods.php +++ b/src/PhpSpreadsheet/Reader/Ods.php @@ -712,7 +712,7 @@ class Ods extends BaseReader return $spreadsheet; } - private function getPrintSettings(Worksheet $worksheet, string $styleName) + private function getPrintSettings(Worksheet $worksheet, string $styleName): void { if (!array_key_exists($styleName, $this->masterStylesCrossReference)) { return; From 4dadf4a5c8d816e2f9ff04d9f803b5b1ad18f680 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Tue, 30 Jun 2020 22:23:33 +0200 Subject: [PATCH 09/29] Refactor reading Ods Page Settings into a separate class --- src/PhpSpreadsheet/Reader/Ods.php | 69 +----------- .../Reader/Ods/PageSettings.php | 104 ++++++++++++++++++ 2 files changed, 108 insertions(+), 65 deletions(-) create mode 100644 src/PhpSpreadsheet/Reader/Ods/PageSettings.php diff --git a/src/PhpSpreadsheet/Reader/Ods.php b/src/PhpSpreadsheet/Reader/Ods.php index 040bfffa..ad87192f 100644 --- a/src/PhpSpreadsheet/Reader/Ods.php +++ b/src/PhpSpreadsheet/Reader/Ods.php @@ -11,6 +11,7 @@ use DOMNode; use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Cell\DataType; +use PhpOffice\PhpSpreadsheet\Reader\Ods\PageSettings; use PhpOffice\PhpSpreadsheet\Reader\Ods\Properties as DocumentProperties; use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner; use PhpOffice\PhpSpreadsheet\RichText\RichText; @@ -19,8 +20,6 @@ use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Shared\File; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\NumberFormat; -use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; -use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use XMLReader; use ZipArchive; @@ -291,36 +290,8 @@ class Ods extends BaseReader $this->securityScanner->scan($zip->getFromName('styles.xml')), Settings::getLibXmlLoaderOptions() ); - $officeNs = $dom->lookupNamespaceUri('office'); - $stylesNs = $dom->lookupNamespaceUri('style'); - $styles = $dom->getElementsByTagNameNS($officeNs, 'automatic-styles') - ->item(0) - ->getElementsByTagNameNS($stylesNs, 'page-layout'); - - foreach ($styles as $styleSet) { - $styleName = $styleSet->getAttributeNS($stylesNs, 'name'); - $pageLayoutProperties = $styleSet->getElementsByTagNameNS($stylesNs, 'page-layout-properties')[0]; - $styleOrientation = $pageLayoutProperties->getAttributeNS($stylesNs, 'print-orientation'); - $styleScale = $pageLayoutProperties->getAttributeNS($stylesNs, 'scale-to'); - $stylePrintOrder = $pageLayoutProperties->getAttributeNS($stylesNs, 'print-page-order'); - - $this->pageLayoutStyles[$styleName] = (object) [ - 'orientation' => $styleOrientation, - 'scale' => $styleScale, - 'printOrder' => $stylePrintOrder, - ]; - } - - $styleMasterLookup = $dom->getElementsByTagNameNS($officeNs, 'master-styles') - ->item(0) - ->getElementsByTagNameNS($stylesNs, 'master-page'); - - foreach ($styleMasterLookup as $styleMasterSet) { - $styleMasterName = $styleMasterSet->getAttributeNS($stylesNs, 'name'); - $pageLayoutName = $styleMasterSet->getAttributeNS($stylesNs, 'page-layout-name'); - $this->masterPrintStylesCrossReference[$styleMasterName] = $pageLayoutName; - } + $pageSettings = new PageSettings($dom); // Main Content @@ -335,17 +306,7 @@ class Ods extends BaseReader $textNs = $dom->lookupNamespaceUri('text'); $xlinkNs = $dom->lookupNamespaceUri('xlink'); - $styleXReferences = $dom->getElementsByTagNameNS($officeNs, 'automatic-styles') - ->item(0) - ->getElementsByTagNameNS($stylesNs, 'style'); - - foreach ($styleXReferences as $styleXreferenceSet) { - $styleXRefName = $styleXreferenceSet->getAttributeNS($stylesNs, 'name'); - $stylePageLayoutName = $styleXreferenceSet->getAttributeNS($stylesNs, 'master-page-name'); - if (!empty($stylePageLayoutName)) { - $this->masterStylesCrossReference[$styleXRefName] = $stylePageLayoutName; - } - } + $pageSettings->readStyleCrossReferences($dom); // Content @@ -703,7 +664,7 @@ class Ods extends BaseReader break; } } - $this->getPrintSettings($spreadsheet->getActiveSheet(), $worksheetStyleName); + $pageSettings->setPrintSettingsForWorksheet($spreadsheet->getActiveSheet(), $worksheetStyleName); ++$worksheetID; } } @@ -712,28 +673,6 @@ class Ods extends BaseReader return $spreadsheet; } - private function getPrintSettings(Worksheet $worksheet, string $styleName): void - { - if (!array_key_exists($styleName, $this->masterStylesCrossReference)) { - return; - } - $masterStyleName = $this->masterStylesCrossReference[$styleName]; - - if (!array_key_exists($masterStyleName, $this->masterPrintStylesCrossReference)) { - return; - } - $printSettingsIndex = $this->masterPrintStylesCrossReference[$masterStyleName]; - - if (!array_key_exists($printSettingsIndex, $this->pageLayoutStyles)) { - return; - } - $printSettings = $this->pageLayoutStyles[$printSettingsIndex]; - - $worksheet->getPageSetup()->setOrientation($printSettings->orientation ?? PageSetup::ORIENTATION_DEFAULT); - $worksheet->getPageSetup()->setScale((int) trim($printSettings->scale, '%')); - $worksheet->getPageSetup()->setPageOrder($printSettings->printOrder === 'ltr' ? PageSetup::PAGEORDER_OVER_THEN_DOWN : PageSetup::PAGEORDER_DOWN_THEN_OVER); - } - /** * Recursively scan element. * diff --git a/src/PhpSpreadsheet/Reader/Ods/PageSettings.php b/src/PhpSpreadsheet/Reader/Ods/PageSettings.php new file mode 100644 index 00000000..5ff1d58b --- /dev/null +++ b/src/PhpSpreadsheet/Reader/Ods/PageSettings.php @@ -0,0 +1,104 @@ +setDomNameSpaces($styleDom); + $this->readPageSettingStyles($styleDom); + $this->readStyleMasterLookup($styleDom); + } + + private function setDomNameSpaces(DOMDocument $styleDom): void + { + $this->officeNs = $styleDom->lookupNamespaceUri('office'); + $this->stylesNs = $styleDom->lookupNamespaceUri('style'); + } + + private function readPageSettingStyles(DOMDocument $styleDom): void + { + $styles = $styleDom->getElementsByTagNameNS($this->officeNs, 'automatic-styles') + ->item(0) + ->getElementsByTagNameNS($this->stylesNs, 'page-layout'); + + foreach ($styles as $styleSet) { + $styleName = $styleSet->getAttributeNS($this->stylesNs, 'name'); + $pageLayoutProperties = $styleSet->getElementsByTagNameNS($this->stylesNs, 'page-layout-properties')[0]; + $styleOrientation = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'print-orientation'); + $styleScale = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'scale-to'); + $stylePrintOrder = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'print-page-order'); + + $this->pageLayoutStyles[$styleName] = (object) [ + 'orientation' => $styleOrientation, + 'scale' => $styleScale, + 'printOrder' => $stylePrintOrder, + ]; + } + } + + private function readStyleMasterLookup(DOMDocument $styleDom): void + { + $styleMasterLookup = $styleDom->getElementsByTagNameNS($this->officeNs, 'master-styles') + ->item(0) + ->getElementsByTagNameNS($this->stylesNs, 'master-page'); + + foreach ($styleMasterLookup as $styleMasterSet) { + $styleMasterName = $styleMasterSet->getAttributeNS($this->stylesNs, 'name'); + $pageLayoutName = $styleMasterSet->getAttributeNS($this->stylesNs, 'page-layout-name'); + $this->masterPrintStylesCrossReference[$styleMasterName] = $pageLayoutName; + } + } + + public function readStyleCrossReferences(DOMDocument $contentDom): void + { + $styleXReferences = $contentDom->getElementsByTagNameNS($this->officeNs, 'automatic-styles') + ->item(0) + ->getElementsByTagNameNS($this->stylesNs, 'style'); + + foreach ($styleXReferences as $styleXreferenceSet) { + $styleXRefName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'name'); + $stylePageLayoutName = $styleXreferenceSet->getAttributeNS($this->stylesNs, 'master-page-name'); + if (!empty($stylePageLayoutName)) { + $this->masterStylesCrossReference[$styleXRefName] = $stylePageLayoutName; + } + } + } + + public function setPrintSettingsForWorksheet(Worksheet $worksheet, string $styleName): void + { + if (!array_key_exists($styleName, $this->masterStylesCrossReference)) { + return; + } + $masterStyleName = $this->masterStylesCrossReference[$styleName]; + + if (!array_key_exists($masterStyleName, $this->masterPrintStylesCrossReference)) { + return; + } + $printSettingsIndex = $this->masterPrintStylesCrossReference[$masterStyleName]; + + if (!array_key_exists($printSettingsIndex, $this->pageLayoutStyles)) { + return; + } + $printSettings = $this->pageLayoutStyles[$printSettingsIndex]; + + $worksheet->getPageSetup() + ->setOrientation($printSettings->orientation ?? PageSetup::ORIENTATION_DEFAULT) + ->setPageOrder($printSettings->printOrder === 'ltr' ? PageSetup::PAGEORDER_OVER_THEN_DOWN : PageSetup::PAGEORDER_DOWN_THEN_OVER) + ->setScale((int) trim($printSettings->scale, '%')); + } +} From 736d9ffd3b03667a2038a12876b8a0c5df0e54d9 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Tue, 30 Jun 2020 22:45:08 +0200 Subject: [PATCH 10/29] Gotta keep phpcs happy, even if it is only a blank line between property declarations --- src/PhpSpreadsheet/Reader/Ods/PageSettings.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PhpSpreadsheet/Reader/Ods/PageSettings.php b/src/PhpSpreadsheet/Reader/Ods/PageSettings.php index 5ff1d58b..6810ef5f 100644 --- a/src/PhpSpreadsheet/Reader/Ods/PageSettings.php +++ b/src/PhpSpreadsheet/Reader/Ods/PageSettings.php @@ -9,6 +9,7 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; class PageSettings { private $officeNs; + private $stylesNs; private $pageLayoutStyles = []; From 163da065054345959b25550702c532738b046d5e Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Wed, 1 Jul 2020 14:50:13 +0200 Subject: [PATCH 11/29] Horizontal an dVertical centering for the Ods Reader --- src/PhpSpreadsheet/Reader/Ods/PageSettings.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/PhpSpreadsheet/Reader/Ods/PageSettings.php b/src/PhpSpreadsheet/Reader/Ods/PageSettings.php index 6810ef5f..4a1fc065 100644 --- a/src/PhpSpreadsheet/Reader/Ods/PageSettings.php +++ b/src/PhpSpreadsheet/Reader/Ods/PageSettings.php @@ -43,11 +43,14 @@ class PageSettings $styleOrientation = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'print-orientation'); $styleScale = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'scale-to'); $stylePrintOrder = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'print-page-order'); + $centered = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'table-centering'); $this->pageLayoutStyles[$styleName] = (object) [ 'orientation' => $styleOrientation, 'scale' => $styleScale, 'printOrder' => $stylePrintOrder, + 'horizontalCentered' => $centered === 'horizontal' || $centered === 'both', + 'verticalCentered' => $centered === 'vertical' || $centered === 'both', ]; } } @@ -100,6 +103,8 @@ class PageSettings $worksheet->getPageSetup() ->setOrientation($printSettings->orientation ?? PageSetup::ORIENTATION_DEFAULT) ->setPageOrder($printSettings->printOrder === 'ltr' ? PageSetup::PAGEORDER_OVER_THEN_DOWN : PageSetup::PAGEORDER_DOWN_THEN_OVER) - ->setScale((int) trim($printSettings->scale, '%')); + ->setScale((int) trim($printSettings->scale, '%')) + ->setHorizontalCentered($printSettings->horizontalCentered) + ->setVerticalCentered($printSettings->verticalCentered); } } From e644cc72d03df950e211c0b711e0a8b9f550c9fe Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Wed, 1 Jul 2020 19:55:25 +0200 Subject: [PATCH 12/29] Additional print information for Gnumeric --- src/PhpSpreadsheet/Reader/Gnumeric.php | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Gnumeric.php b/src/PhpSpreadsheet/Reader/Gnumeric.php index 81096730..4d4988e9 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric.php @@ -17,6 +17,7 @@ use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Borders; use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\Font; +use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use SimpleXMLElement; use XMLReader; @@ -431,15 +432,35 @@ class Gnumeric extends BaseReader } } + private function printInformation(SimpleXMLElement $sheet) + { + if (!$this->readDataOnly && isset($sheet->PrintInformation)) { + $printInformation = $sheet->PrintInformation[0]; + $scale = $printInformation->Scale->attributes()['percentage']; + $pageOrder = (string) $printInformation->order; + $orientation = (string) $printInformation->orientation; + $horizontalCentered = (bool) $printInformation->hcenter; + $verticalCentered = (bool) $printInformation->vcenter; + + $this->spreadsheet->getActiveSheet()->getPageSetup() + ->setPageOrder($pageOrder === 'r_then_d' ? PageSetup::PAGEORDER_OVER_THEN_DOWN : PageSetup::PAGEORDER_DOWN_THEN_OVER) + ->setScale((int) $scale) + ->setOrientation($orientation ?? PageSetup::ORIENTATION_DEFAULT) + ->setHorizontalCentered($horizontalCentered) + ->setVerticalCentered($verticalCentered); + } + } + private function sheetMargins(SimpleXMLElement $sheet): void { if (!$this->readDataOnly && isset($sheet->PrintInformation, $sheet->PrintInformation->Margins)) { foreach ($sheet->PrintInformation->Margins->children($this->gnm, true) as $key => $margin) { $marginAttributes = $margin->attributes(); - $marginSize = 72 / 100; // Default + $marginSize = $marginAttributes['Points'] ?? 72 / 100; // Default switch ($marginAttributes['PrefUnit']) { + case 'inch': case 'mm': - $marginSize = (int) ($marginAttributes['Points']) / 100; + $marginSize = ($marginAttributes['Points']) / 100; break; } @@ -513,6 +534,7 @@ class Gnumeric extends BaseReader // name in line with the formula, not the reverse $this->spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false); + $this->printInformation($sheet); $this->sheetMargins($sheet); foreach ($sheet->Cells->Cell as $cell) { From c288c11d0150521e9383cc9cfa217f6891bf733b Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Wed, 1 Jul 2020 20:06:36 +0200 Subject: [PATCH 13/29] Failed to avoid the void trap --- src/PhpSpreadsheet/Reader/Gnumeric.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpSpreadsheet/Reader/Gnumeric.php b/src/PhpSpreadsheet/Reader/Gnumeric.php index 4d4988e9..d96d562e 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric.php @@ -432,7 +432,7 @@ class Gnumeric extends BaseReader } } - private function printInformation(SimpleXMLElement $sheet) + private function printInformation(SimpleXMLElement $sheet): void { if (!$this->readDataOnly && isset($sheet->PrintInformation)) { $printInformation = $sheet->PrintInformation[0]; From aecef1372f9582153b42df9b0e4fb8d67f5ef21c Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Fri, 3 Jul 2020 19:26:57 +0200 Subject: [PATCH 14/29] Read Ods Margins --- src/PhpSpreadsheet/Reader/Ods.php | 6 ----- .../Reader/Ods/PageSettings.php | 27 +++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Ods.php b/src/PhpSpreadsheet/Reader/Ods.php index ad87192f..4f164b6f 100644 --- a/src/PhpSpreadsheet/Reader/Ods.php +++ b/src/PhpSpreadsheet/Reader/Ods.php @@ -25,12 +25,6 @@ use ZipArchive; class Ods extends BaseReader { - private $pageLayoutStyles = []; - - private $masterStylesCrossReference = []; - - private $masterPrintStylesCrossReference = []; - /** * Create a new Ods Reader instance. */ diff --git a/src/PhpSpreadsheet/Reader/Ods/PageSettings.php b/src/PhpSpreadsheet/Reader/Ods/PageSettings.php index 4a1fc065..05095752 100644 --- a/src/PhpSpreadsheet/Reader/Ods/PageSettings.php +++ b/src/PhpSpreadsheet/Reader/Ods/PageSettings.php @@ -12,6 +12,8 @@ class PageSettings private $stylesNs; + private $stylesFo; + private $pageLayoutStyles = []; private $masterStylesCrossReference = []; @@ -29,6 +31,7 @@ class PageSettings { $this->officeNs = $styleDom->lookupNamespaceUri('office'); $this->stylesNs = $styleDom->lookupNamespaceUri('style'); + $this->stylesFo = $styleDom->lookupNamespaceUri('fo'); } private function readPageSettingStyles(DOMDocument $styleDom): void @@ -44,6 +47,16 @@ class PageSettings $styleScale = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'scale-to'); $stylePrintOrder = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'print-page-order'); $centered = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'table-centering'); + $marginLeft = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-left'); + $marginRight = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-right'); + $marginTop = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-top'); + $marginBottom = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-bottom'); + $header = $styleSet->getElementsByTagNameNS($this->stylesNs, 'header-style')[0]; + $headerProperties = $header->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties')[0]; + $marginHeader = $headerProperties->getAttributeNS($this->stylesFo, 'min-height'); + $footer = $styleSet->getElementsByTagNameNS($this->stylesNs, 'footer-style')[0]; + $footerProperties = $footer->getElementsByTagNameNS($this->stylesNs, 'header-footer-properties')[0]; + $marginFooter = $footerProperties->getAttributeNS($this->stylesFo, 'min-height'); $this->pageLayoutStyles[$styleName] = (object) [ 'orientation' => $styleOrientation, @@ -51,6 +64,12 @@ class PageSettings 'printOrder' => $stylePrintOrder, 'horizontalCentered' => $centered === 'horizontal' || $centered === 'both', 'verticalCentered' => $centered === 'vertical' || $centered === 'both', + 'marginLeft' => round((float) $marginLeft ?? 0.7, 5), + 'marginRight' => round((float) $marginRight ?? 0.7, 5), + 'marginTop' => round((float) $marginTop ?? 0.7, 5), + 'marginBottom' => round((float) $marginBottom ?? 0.7, 5), + 'marginHeader' => round((float) $marginHeader ?? 0.0, 5), + 'marginFooter' => round((float) $marginFooter ?? 0.0, 5), ]; } } @@ -106,5 +125,13 @@ class PageSettings ->setScale((int) trim($printSettings->scale, '%')) ->setHorizontalCentered($printSettings->horizontalCentered) ->setVerticalCentered($printSettings->verticalCentered); + + $worksheet->getPageMargins() + ->setLeft($printSettings->marginLeft) + ->setRight($printSettings->marginRight) + ->setTop($printSettings->marginTop) + ->setBottom($printSettings->marginBottom) + ->setHeader($printSettings->marginHeader) + ->setFooter($printSettings->marginFooter); } } From 84ba21400c5043b7bc68addbc7a6fc8ba7f6a2d7 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sat, 4 Jul 2020 00:28:16 +0200 Subject: [PATCH 15/29] Fix datatype conversion for Gnumeric values --- src/PhpSpreadsheet/Reader/Gnumeric.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PhpSpreadsheet/Reader/Gnumeric.php b/src/PhpSpreadsheet/Reader/Gnumeric.php index d96d562e..8375b37e 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric.php @@ -436,18 +436,18 @@ class Gnumeric extends BaseReader { if (!$this->readDataOnly && isset($sheet->PrintInformation)) { $printInformation = $sheet->PrintInformation[0]; - $scale = $printInformation->Scale->attributes()['percentage']; + $scale = (string) $printInformation->Scale->attributes()['percentage']; $pageOrder = (string) $printInformation->order; $orientation = (string) $printInformation->orientation; - $horizontalCentered = (bool) $printInformation->hcenter; - $verticalCentered = (bool) $printInformation->vcenter; + $horizontalCentered = (string) $printInformation->hcenter->attributes()['value']; + $verticalCentered = (string) $printInformation->vcenter->attributes()['value']; $this->spreadsheet->getActiveSheet()->getPageSetup() ->setPageOrder($pageOrder === 'r_then_d' ? PageSetup::PAGEORDER_OVER_THEN_DOWN : PageSetup::PAGEORDER_DOWN_THEN_OVER) ->setScale((int) $scale) ->setOrientation($orientation ?? PageSetup::ORIENTATION_DEFAULT) - ->setHorizontalCentered($horizontalCentered) - ->setVerticalCentered($verticalCentered); + ->setHorizontalCentered((bool) $horizontalCentered) + ->setVerticalCentered((bool) $verticalCentered); } } From 8629337101fef7bf51fb7e062080de510438a94b Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Sun, 5 Jul 2020 16:22:35 +0200 Subject: [PATCH 16/29] Retrieving print/page setup for the Xml Reader --- src/PhpSpreadsheet/Reader/Gnumeric.php | 106 ++++++++----- .../Reader/Ods/PageSettings.php | 18 ++- src/PhpSpreadsheet/Reader/Xml.php | 102 ++++++++++++ src/PhpSpreadsheet/Worksheet/PageMargins.php | 30 ++++ src/PhpSpreadsheet/Worksheet/PageSetup.php | 6 +- .../Reader/Gnumeric/PageSetupTest.php | 147 +++++++++++++++++ .../Reader/Ods/PageSetupTest.php | 149 ++++++++++++++++++ .../Reader/Xls/PageSetupTest.php | 149 ++++++++++++++++++ .../Reader/Xlsx/PageSetupTest.php | 149 ++++++++++++++++++ .../Reader/Xml/PageSetupTest.php | 148 +++++++++++++++++ .../Worksheet/PageMarginsTest.php | 89 +++++++++++ 11 files changed, 1045 insertions(+), 48 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Reader/Gnumeric/PageSetupTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Ods/PageSetupTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Xls/PageSetupTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Xlsx/PageSetupTest.php create mode 100644 tests/PhpSpreadsheetTests/Reader/Xml/PageSetupTest.php create mode 100644 tests/PhpSpreadsheetTests/Worksheet/PageMarginsTest.php diff --git a/src/PhpSpreadsheet/Reader/Gnumeric.php b/src/PhpSpreadsheet/Reader/Gnumeric.php index 8375b37e..32105477 100644 --- a/src/PhpSpreadsheet/Reader/Gnumeric.php +++ b/src/PhpSpreadsheet/Reader/Gnumeric.php @@ -17,6 +17,7 @@ use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Borders; use PhpOffice\PhpSpreadsheet\Style\Fill; use PhpOffice\PhpSpreadsheet\Style\Font; +use PhpOffice\PhpSpreadsheet\Worksheet\PageMargins; use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use SimpleXMLElement; @@ -24,6 +25,8 @@ use XMLReader; class Gnumeric extends BaseReader { + private const UOM_CONVERSION_POINTS_TO_CENTIMETERS = 0.03527777778; + /** * Shared Expressions. * @@ -402,6 +405,72 @@ class Gnumeric extends BaseReader } } + private function printInformation(SimpleXMLElement $sheet): void + { + if (!$this->readDataOnly && isset($sheet->PrintInformation)) { + $printInformation = $sheet->PrintInformation[0]; + $scale = (string) $printInformation->Scale->attributes()['percentage']; + $pageOrder = (string) $printInformation->order; + $orientation = (string) $printInformation->orientation; + $horizontalCentered = (string) $printInformation->hcenter->attributes()['value']; + $verticalCentered = (string) $printInformation->vcenter->attributes()['value']; + + $this->spreadsheet->getActiveSheet()->getPageSetup() + ->setPageOrder($pageOrder === 'r_then_d' ? PageSetup::PAGEORDER_OVER_THEN_DOWN : PageSetup::PAGEORDER_DOWN_THEN_OVER) + ->setScale((int) $scale) + ->setOrientation($orientation ?? PageSetup::ORIENTATION_DEFAULT) + ->setHorizontalCentered((bool) $horizontalCentered) + ->setVerticalCentered((bool) $verticalCentered); + } + } + + private function sheetMargins(SimpleXMLElement $sheet): void + { + if (!$this->readDataOnly && isset($sheet->PrintInformation, $sheet->PrintInformation->Margins)) { + $marginSet = [ + // Default Settings + 'top' => 0.75, + 'header' => 0.3, + 'left' => 0.7, + 'right' => 0.7, + 'bottom' => 0.75, + 'footer' => 0.3, + ]; + + foreach ($sheet->PrintInformation->Margins->children($this->gnm, true) as $key => $margin) { + $marginAttributes = $margin->attributes(); + $marginSize = ($marginAttributes['Points']) ?? 72; // Default is 72pt + // Convert value in points to inches + $marginSize = PageMargins::fromPoints((float) $marginSize); + $marginSet[$key] = $marginSize; + } + + foreach ($marginSet as $key => $marginSize) { + // Gnumeric is quirky in the way it displays the header/footer values: + // header is actually the sum of top and header; footer is actually the sum of bottom and footer + // then top is actually the header value, and bottom is actually the footer value + switch ($key) { + case 'left': + case 'right': + $this->sheetMargin($key, $marginSize); + break; + case 'top': + $this->sheetMargin($key, $marginSet['header'] ?? 0); + break; + case 'bottom': + $this->sheetMargin($key, $marginSet['footer'] ?? 0); + break; + case 'header': + $this->sheetMargin($key, ($marginSet['top'] ?? 0) - $marginSize); + break; + case 'footer': + $this->sheetMargin($key, ($marginSet['bottom'] ?? 0) - $marginSize); + break; + } + } + } + } + private function sheetMargin(string $key, float $marginSize): void { switch ($key) { @@ -432,43 +501,6 @@ class Gnumeric extends BaseReader } } - private function printInformation(SimpleXMLElement $sheet): void - { - if (!$this->readDataOnly && isset($sheet->PrintInformation)) { - $printInformation = $sheet->PrintInformation[0]; - $scale = (string) $printInformation->Scale->attributes()['percentage']; - $pageOrder = (string) $printInformation->order; - $orientation = (string) $printInformation->orientation; - $horizontalCentered = (string) $printInformation->hcenter->attributes()['value']; - $verticalCentered = (string) $printInformation->vcenter->attributes()['value']; - - $this->spreadsheet->getActiveSheet()->getPageSetup() - ->setPageOrder($pageOrder === 'r_then_d' ? PageSetup::PAGEORDER_OVER_THEN_DOWN : PageSetup::PAGEORDER_DOWN_THEN_OVER) - ->setScale((int) $scale) - ->setOrientation($orientation ?? PageSetup::ORIENTATION_DEFAULT) - ->setHorizontalCentered((bool) $horizontalCentered) - ->setVerticalCentered((bool) $verticalCentered); - } - } - - private function sheetMargins(SimpleXMLElement $sheet): void - { - if (!$this->readDataOnly && isset($sheet->PrintInformation, $sheet->PrintInformation->Margins)) { - foreach ($sheet->PrintInformation->Margins->children($this->gnm, true) as $key => $margin) { - $marginAttributes = $margin->attributes(); - $marginSize = $marginAttributes['Points'] ?? 72 / 100; // Default - switch ($marginAttributes['PrefUnit']) { - case 'inch': - case 'mm': - $marginSize = ($marginAttributes['Points']) / 100; - - break; - } - $this->sheetMargin($key, (float) $marginSize); - } - } - } - private function processComments(SimpleXMLElement $sheet): void { if ((!$this->readDataOnly) && (isset($sheet->Objects))) { diff --git a/src/PhpSpreadsheet/Reader/Ods/PageSettings.php b/src/PhpSpreadsheet/Reader/Ods/PageSettings.php index 05095752..77341aab 100644 --- a/src/PhpSpreadsheet/Reader/Ods/PageSettings.php +++ b/src/PhpSpreadsheet/Reader/Ods/PageSettings.php @@ -47,6 +47,7 @@ class PageSettings $styleScale = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'scale-to'); $stylePrintOrder = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'print-page-order'); $centered = $pageLayoutProperties->getAttributeNS($this->stylesNs, 'table-centering'); + $marginLeft = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-left'); $marginRight = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-right'); $marginTop = $pageLayoutProperties->getAttributeNS($this->stylesFo, 'margin-top'); @@ -59,17 +60,18 @@ class PageSettings $marginFooter = $footerProperties->getAttributeNS($this->stylesFo, 'min-height'); $this->pageLayoutStyles[$styleName] = (object) [ - 'orientation' => $styleOrientation, - 'scale' => $styleScale, + 'orientation' => $styleOrientation ?: PageSetup::ORIENTATION_DEFAULT, + 'scale' => $styleScale ?: 100, 'printOrder' => $stylePrintOrder, 'horizontalCentered' => $centered === 'horizontal' || $centered === 'both', 'verticalCentered' => $centered === 'vertical' || $centered === 'both', - 'marginLeft' => round((float) $marginLeft ?? 0.7, 5), - 'marginRight' => round((float) $marginRight ?? 0.7, 5), - 'marginTop' => round((float) $marginTop ?? 0.7, 5), - 'marginBottom' => round((float) $marginBottom ?? 0.7, 5), - 'marginHeader' => round((float) $marginHeader ?? 0.0, 5), - 'marginFooter' => round((float) $marginFooter ?? 0.0, 5), + // margin size is already stored in inches, so no UOM conversion is required + 'marginLeft' => (float) $marginLeft ?? 0.7, + 'marginRight' => (float) $marginRight ?? 0.7, + 'marginTop' => (float) $marginTop ?? 0.3, + 'marginBottom' => (float) $marginBottom ?? 0.3, + 'marginHeader' => (float) $marginHeader ?? 0.45, + 'marginFooter' => (float) $marginFooter ?? 0.45, ]; } } diff --git a/src/PhpSpreadsheet/Reader/Xml.php b/src/PhpSpreadsheet/Reader/Xml.php index f9ad5475..9ea0a82d 100644 --- a/src/PhpSpreadsheet/Reader/Xml.php +++ b/src/PhpSpreadsheet/Reader/Xml.php @@ -15,6 +15,7 @@ use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Style\Alignment; use PhpOffice\PhpSpreadsheet\Style\Border; use PhpOffice\PhpSpreadsheet\Style\Font; +use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; use SimpleXMLElement; /** @@ -626,6 +627,27 @@ class Xml extends BaseReader ++$rowID; } + + $xml_x = $worksheet->children($namespaces['x']); + if (isset($xml_x->WorksheetOptions)) { + $printSettings = $this->pageSetup($xml_x, $namespaces, $this->getPrintDefaults()); + $printSettings = $this->printSetup($xml_x, $namespaces, $printSettings); + + $spreadsheet->getActiveSheet()->getPageSetup() + ->setPaperSize($printSettings->paperSize) + ->setOrientation($printSettings->orientation) + ->setScale($printSettings->scale) + ->setVerticalCentered($printSettings->verticalCentered) + ->setHorizontalCentered($printSettings->horizontalCentered) + ->setPageOrder($printSettings->printOrder); + $spreadsheet->getActiveSheet()->getPageMargins() + ->setTop($printSettings->topMargin) + ->setHeader($printSettings->headerMargin) + ->setLeft($printSettings->leftMargin) + ->setRight($printSettings->rightMargin) + ->setBottom($printSettings->bottomMargin) + ->setFooter($printSettings->footerMargin); + } } ++$worksheetID; } @@ -855,4 +877,84 @@ class Xml extends BaseReader } } } + + private function getPrintDefaults() + { + return (object) [ + 'paperSize' => 9, + 'orientation' => PageSetup::ORIENTATION_DEFAULT, + 'scale' => 100, + 'horizontalCentered' => false, + 'verticalCentered' => false, + 'printOrder' => PageSetup::PAGEORDER_DOWN_THEN_OVER, + 'topMargin' => 0.75, + 'headerMargin' => 0.3, + 'leftMargin' => 0.7, + 'rightMargin' => 0.7, + 'bottomMargin' => 0.75, + 'footerMargin' => 0.3, + ]; + } + + private function pageSetup(SimpleXMLElement $xmlX, array $namespaces, \stdClass $printDefaults): \stdClass + { + if (isset($xmlX->WorksheetOptions->PageSetup)) { + foreach ($xmlX->WorksheetOptions->PageSetup as $pageSetupData) { + foreach ($pageSetupData as $pageSetupKey => $pageSetupValue) { + $pageSetupAttributes = $pageSetupValue->attributes($namespaces['x']); + switch ($pageSetupKey) { + case 'Layout': + $printDefaults->orientation = (string) strtolower($pageSetupAttributes->Orientation) ?: PageSetup::ORIENTATION_PORTRAIT; + $printDefaults->horizontalCentered = (bool)$pageSetupAttributes->CenterHorizontal ?: false; + $printDefaults->verticalCentered = (bool)$pageSetupAttributes->CenterVertical ?: false; + + break; + case 'Header': + $printDefaults->headerMargin = (float)$pageSetupAttributes->Margin ?: 1.0; + + break; + case 'Footer': + $printDefaults->footerMargin = (float)$pageSetupAttributes->Margin ?: 1.0; + + break; + case 'PageMargins': + $printDefaults->leftMargin = (float) $pageSetupAttributes->Left ?: 1.0; + $printDefaults->rightMargin = (float) $pageSetupAttributes->Right ?: 1.0; + $printDefaults->topMargin = (float) $pageSetupAttributes->Top ?: 1.0; + $printDefaults->bottomMargin = (float) $pageSetupAttributes->Bottom ?: 1.0; + + break; + } + } + } + } + + return $printDefaults; + } + + private function printSetup(SimpleXMLElement $xmlX, array $namespaces, \stdClass $printDefaults): \stdClass + { + if (isset($xmlX->WorksheetOptions->Print)) { + foreach ($xmlX->WorksheetOptions->Print as $printData) { + foreach ($printData as $printKey => $printValue) { + switch ($printKey) { + case 'LeftToRight': + $printDefaults->printOrder = PageSetup::PAGEORDER_OVER_THEN_DOWN; + + break; + case 'PaperSizeIndex': + $printDefaults->paperSize = (int) $printValue ?: 9; + + break; + case 'Scale': + $printDefaults->scale = (int) $printValue ?: 100; + + break; + } + } + } + } + + return $printDefaults; + } } diff --git a/src/PhpSpreadsheet/Worksheet/PageMargins.php b/src/PhpSpreadsheet/Worksheet/PageMargins.php index 9ebfb648..d59c6afd 100644 --- a/src/PhpSpreadsheet/Worksheet/PageMargins.php +++ b/src/PhpSpreadsheet/Worksheet/PageMargins.php @@ -211,4 +211,34 @@ class PageMargins } } } + + public static function fromCentimeters($value): float + { + return $value / 2.54; + } + + public static function toCentimeters($value): float + { + return $value * 2.54; + } + + public static function fromMillimeters($value): float + { + return $value / 25.4; + } + + public static function toMillimeters($value): float + { + return $value * 25.4; + } + + public static function fromPoints($value): float + { + return $value / 72; + } + + public static function toPoints($value): float + { + return $value * 72; + } } diff --git a/src/PhpSpreadsheet/Worksheet/PageSetup.php b/src/PhpSpreadsheet/Worksheet/PageSetup.php index 290b8349..d8d5098f 100644 --- a/src/PhpSpreadsheet/Worksheet/PageSetup.php +++ b/src/PhpSpreadsheet/Worksheet/PageSetup.php @@ -249,7 +249,7 @@ class PageSetup */ private $firstPageNumber; - private $pageOrder; + private $pageOrder = self::PAGEORDER_DOWN_THEN_OVER; /** * Create a new PageSetup. @@ -823,7 +823,7 @@ class PageSetup return $this->setFirstPageNumber(null); } - public function getPageOrder(): ?string + public function getPageOrder(): string { return $this->pageOrder; } @@ -831,7 +831,7 @@ class PageSetup public function setPageOrder(?string $pageOrder): self { if ($pageOrder === null || $pageOrder === self::PAGEORDER_DOWN_THEN_OVER || $pageOrder === self::PAGEORDER_OVER_THEN_DOWN) { - $this->pageOrder = $pageOrder; + $this->pageOrder = $pageOrder ?? self::PAGEORDER_DOWN_THEN_OVER; } return $this; diff --git a/tests/PhpSpreadsheetTests/Reader/Gnumeric/PageSetupTest.php b/tests/PhpSpreadsheetTests/Reader/Gnumeric/PageSetupTest.php new file mode 100644 index 00000000..057dbf69 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Gnumeric/PageSetupTest.php @@ -0,0 +1,147 @@ +spreadsheet = $reader->load($filename); + } + + public function testPageSetup() + { + $assertions = $this->pageSetupAssertions(); + + foreach ($this->spreadsheet->getAllSheets() as $worksheet) { + if (!array_key_exists($worksheet->getTitle(), $assertions)) { + continue; + } + + $sheetAssertions = $assertions[$worksheet->getTitle()]; + foreach ($sheetAssertions as $test => $expectedResult) { + $testMethodName = 'get' . ucfirst($test); + $actualResult = $worksheet->getPageSetup()->$testMethodName(); + $this->assertSame( + $expectedResult, + $actualResult, + "Failed assertion for Worksheet '{$worksheet->getTitle()}' {$test}" + ); + } + } + } + + public function testPageMargins() + { + $assertions = $this->pageMarginAssertions(); + + foreach ($this->spreadsheet->getAllSheets() as $worksheet) { + if (!array_key_exists($worksheet->getTitle(), $assertions)) { + continue; + } + + $sheetAssertions = $assertions[$worksheet->getTitle()]; + foreach ($sheetAssertions as $test => $expectedResult) { + $testMethodName = 'get' . ucfirst($test); + $actualResult = $worksheet->getPageMargins()->$testMethodName(); + $this->assertEqualsWithDelta( + $expectedResult, + $actualResult, + self::MARGIN_PRECISION, + "Failed assertion for Worksheet '{$worksheet->getTitle()}' {$test} margin" + ); + } + } + } + + public function pageSetupAssertions() + { + return [ + 'Sheet1' => [ + 'orientation' => PageSetup::ORIENTATION_PORTRAIT, + 'scale' => 75, + 'horizontalCentered' => true, + 'verticalCentered' => false, + 'pageOrder' => PageSetup::PAGEORDER_DOWN_THEN_OVER, + ], + 'Sheet2' => [ + 'orientation' => PageSetup::ORIENTATION_LANDSCAPE, + 'scale' => 100, + 'horizontalCentered' => false, + 'verticalCentered' => true, + 'pageOrder' => PageSetup::PAGEORDER_OVER_THEN_DOWN, + ], + 'Sheet3' => [ + 'orientation' => PageSetup::ORIENTATION_PORTRAIT, + 'scale' => 90, + 'horizontalCentered' => true, + 'verticalCentered' => true, + 'pageOrder' => PageSetup::PAGEORDER_DOWN_THEN_OVER, + ], + 'Sheet4' => [ + // Default Settings + 'orientation' => PageSetup::ORIENTATION_PORTRAIT, + 'scale' => 100, + 'horizontalCentered' => false, + 'verticalCentered' => false, + 'pageOrder' => PageSetup::PAGEORDER_DOWN_THEN_OVER, + ], + ]; + } + + public function pageMarginAssertions() + { + return [ + 'Sheet1' => [ + // Here the values are in inches + 'top' => 0.315, + 'header' => 0.630, + 'left' => 0.512, + 'right' => 0.512, + 'bottom' => 0.315, + 'footer' => 0.433, + ], + 'Sheet2' => [ + // Here the values are in inches + 'top' => 0.315, + 'header' => 0.433, + 'left' => 0.709, + 'right' => 0.709, + 'bottom' => 0.315, + 'footer' => 0.433, + ], + 'Sheet3' => [ + // Here the values are in inches + 'top' => 0.512, + 'header' => 0.433, + 'left' => 0.709, + 'right' => 0.709, + 'bottom' => 0.512, + 'footer' => 0.433, + ], + 'Sheet4' => [ + // Default Settings (in inches) + 'top' => 0.3, + 'header' => 0.45, + 'left' => 0.7, + 'right' => 0.7, + 'bottom' => 0.3, + 'footer' => 0.45, + ], + ]; + } +} diff --git a/tests/PhpSpreadsheetTests/Reader/Ods/PageSetupTest.php b/tests/PhpSpreadsheetTests/Reader/Ods/PageSetupTest.php new file mode 100644 index 00000000..74eaae8f --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Ods/PageSetupTest.php @@ -0,0 +1,149 @@ +spreadsheet = $reader->load($filename); + } + + public function testPageSetup() + { + $assertions = $this->pageSetupAssertions(); + + foreach ($this->spreadsheet->getAllSheets() as $worksheet) { + if (!array_key_exists($worksheet->getTitle(), $assertions)) { + continue; + } + + $sheetAssertions = $assertions[$worksheet->getTitle()]; + foreach ($sheetAssertions as $test => $expectedResult) { + $testMethodName = 'get' . ucfirst($test); + $actualResult = $worksheet->getPageSetup()->$testMethodName(); + $this->assertSame( + $expectedResult, + $actualResult, + "Failed assertion for Worksheet '{$worksheet->getTitle()}' {$test}" + ); + } + } + } + + public function testPageMargins() + { + $assertions = $this->pageMarginAssertions(); + + foreach ($this->spreadsheet->getAllSheets() as $worksheet) { + if (!array_key_exists($worksheet->getTitle(), $assertions)) { + continue; + } + + $sheetAssertions = $assertions[$worksheet->getTitle()]; + foreach ($sheetAssertions as $test => $expectedResult) { + $testMethodName = 'get' . ucfirst($test); + $actualResult = $worksheet->getPageMargins()->$testMethodName(); + $this->assertEqualsWithDelta( + $expectedResult, + $actualResult, + self::MARGIN_PRECISION, + "Failed assertion for Worksheet '{$worksheet->getTitle()}' {$test} margin" + ); + } + } + } + + public function pageSetupAssertions() + { + return [ + 'Sheet1' => [ + 'orientation' => PageSetup::ORIENTATION_PORTRAIT, + 'scale' => 75, + 'horizontalCentered' => true, + 'verticalCentered' => false, + 'pageOrder' => PageSetup::PAGEORDER_DOWN_THEN_OVER, + ], + 'Sheet2' => [ + 'orientation' => PageSetup::ORIENTATION_LANDSCAPE, + 'scale' => 100, + 'horizontalCentered' => false, + 'verticalCentered' => true, + 'pageOrder' => PageSetup::PAGEORDER_OVER_THEN_DOWN, + ], + 'Sheet3' => [ + 'orientation' => PageSetup::ORIENTATION_PORTRAIT, + 'scale' => 90, + 'horizontalCentered' => true, + 'verticalCentered' => true, + 'pageOrder' => PageSetup::PAGEORDER_DOWN_THEN_OVER, + ], + 'Sheet4' => [ + // Default Settings + 'orientation' => PageSetup::ORIENTATION_DEFAULT, + 'scale' => 100, + 'horizontalCentered' => false, + 'verticalCentered' => false, + 'pageOrder' => PageSetup::PAGEORDER_DOWN_THEN_OVER, + ], + ]; + } + + public function pageMarginAssertions() + { + return [ + 'Sheet1' => [ + // Here the values are in cm + 'top' => 0.8 / self::MARGIN_UNIT_CONVERSION, + 'header' => 1.6 / self::MARGIN_UNIT_CONVERSION, + 'left' => 1.3 / self::MARGIN_UNIT_CONVERSION, + 'right' => 1.3 / self::MARGIN_UNIT_CONVERSION, + 'bottom' => 0.8 / self::MARGIN_UNIT_CONVERSION, + 'footer' => 1.1 / self::MARGIN_UNIT_CONVERSION, + ], + 'Sheet2' => [ + // Here the values are in cm + 'top' => 0.8 / self::MARGIN_UNIT_CONVERSION, + 'header' => 1.1 / self::MARGIN_UNIT_CONVERSION, + 'left' => 1.8 / self::MARGIN_UNIT_CONVERSION, + 'right' => 1.8 / self::MARGIN_UNIT_CONVERSION, + 'bottom' => 0.8 / self::MARGIN_UNIT_CONVERSION, + 'footer' => 1.1 / self::MARGIN_UNIT_CONVERSION, + ], + 'Sheet3' => [ + // Here the values are in cm + 'top' => 1.3 / self::MARGIN_UNIT_CONVERSION, + 'header' => 1.1 / self::MARGIN_UNIT_CONVERSION, + 'left' => 1.8 / self::MARGIN_UNIT_CONVERSION, + 'right' => 1.8 / self::MARGIN_UNIT_CONVERSION, + 'bottom' => 1.3 / self::MARGIN_UNIT_CONVERSION, + 'footer' => 1.1 / self::MARGIN_UNIT_CONVERSION, + ], + 'Sheet4' => [ + // Default Settings (already in inches) + 'top' => 0.3, + 'header' => 0.45, + 'left' => 0.7, + 'right' => 0.7, + 'bottom' => 0.3, + 'footer' => 0.45, + ], + ]; + } +} diff --git a/tests/PhpSpreadsheetTests/Reader/Xls/PageSetupTest.php b/tests/PhpSpreadsheetTests/Reader/Xls/PageSetupTest.php new file mode 100644 index 00000000..2cfbbb3f --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xls/PageSetupTest.php @@ -0,0 +1,149 @@ +spreadsheet = $reader->load($filename); + } + + public function testPageSetup() + { + $assertions = $this->pageSetupAssertions(); + + foreach ($this->spreadsheet->getAllSheets() as $worksheet) { + if (!array_key_exists($worksheet->getTitle(), $assertions)) { + continue; + } + + $sheetAssertions = $assertions[$worksheet->getTitle()]; + foreach ($sheetAssertions as $test => $expectedResult) { + $testMethodName = 'get' . ucfirst($test); + $actualResult = $worksheet->getPageSetup()->$testMethodName(); + $this->assertSame( + $expectedResult, + $actualResult, + "Failed assertion for Worksheet '{$worksheet->getTitle()}' {$test}" + ); + } + } + } + + public function testPageMargins() + { + $assertions = $this->pageMarginAssertions(); + + foreach ($this->spreadsheet->getAllSheets() as $worksheet) { + if (!array_key_exists($worksheet->getTitle(), $assertions)) { + continue; + } + + $sheetAssertions = $assertions[$worksheet->getTitle()]; + foreach ($sheetAssertions as $test => $expectedResult) { + $testMethodName = 'get' . ucfirst($test); + $actualResult = $worksheet->getPageMargins()->$testMethodName(); + $this->assertEqualsWithDelta( + $expectedResult, + $actualResult, + self::MARGIN_PRECISION, + "Failed assertion for Worksheet '{$worksheet->getTitle()}' {$test} margin" + ); + } + } + } + + public function pageSetupAssertions() + { + return [ + 'Sheet1' => [ + 'orientation' => PageSetup::ORIENTATION_PORTRAIT, + 'scale' => 75, + 'horizontalCentered' => true, + 'verticalCentered' => false, + 'pageOrder' => PageSetup::PAGEORDER_DOWN_THEN_OVER, + ], + 'Sheet2' => [ + 'orientation' => PageSetup::ORIENTATION_LANDSCAPE, + 'scale' => 100, + 'horizontalCentered' => false, + 'verticalCentered' => true, + 'pageOrder' => PageSetup::PAGEORDER_OVER_THEN_DOWN, + ], + 'Sheet3' => [ + 'orientation' => PageSetup::ORIENTATION_PORTRAIT, + 'scale' => 90, + 'horizontalCentered' => true, + 'verticalCentered' => true, + 'pageOrder' => PageSetup::PAGEORDER_DOWN_THEN_OVER, + ], + 'Sheet4' => [ + // Default Settings + 'orientation' => PageSetup::ORIENTATION_DEFAULT, + 'scale' => 100, + 'horizontalCentered' => false, + 'verticalCentered' => false, + 'pageOrder' => PageSetup::PAGEORDER_DOWN_THEN_OVER, + ], + ]; + } + + public function pageMarginAssertions() + { + return [ + 'Sheet1' => [ + // Here the values are in cm, so we convert to inches for comparison with internal uom + 'top' => 2.4 / self::MARGIN_UNIT_CONVERSION, + 'header' => 0.8 / self::MARGIN_UNIT_CONVERSION, + 'left' => 1.3 / self::MARGIN_UNIT_CONVERSION, + 'right' => 1.3 / self::MARGIN_UNIT_CONVERSION, + 'bottom' => 1.9 / self::MARGIN_UNIT_CONVERSION, + 'footer' => 0.8 / self::MARGIN_UNIT_CONVERSION, + ], + 'Sheet2' => [ + // Here the values are in cm, so we convert to inches for comparison with internal uom + 'top' => 1.9 / self::MARGIN_UNIT_CONVERSION, + 'header' => 0.8 / self::MARGIN_UNIT_CONVERSION, + 'left' => 1.8 / self::MARGIN_UNIT_CONVERSION, + 'right' => 1.8 / self::MARGIN_UNIT_CONVERSION, + 'bottom' => 1.9 / self::MARGIN_UNIT_CONVERSION, + 'footer' => 0.8 / self::MARGIN_UNIT_CONVERSION, + ], + 'Sheet3' => [ + // Here the values are in cm, so we convert to inches for comparison with internal uom + 'top' => 2.4 / self::MARGIN_UNIT_CONVERSION, + 'header' => 1.3 / self::MARGIN_UNIT_CONVERSION, + 'left' => 1.8 / self::MARGIN_UNIT_CONVERSION, + 'right' => 1.8 / self::MARGIN_UNIT_CONVERSION, + 'bottom' => 2.4 / self::MARGIN_UNIT_CONVERSION, + 'footer' => 1.3 / self::MARGIN_UNIT_CONVERSION, + ], + 'Sheet4' => [ + // Default Settings (already in inches for comparison) + 'top' => 0.75, + 'header' => 0.3, + 'left' => 0.7, + 'right' => 0.7, + 'bottom' => 0.75, + 'footer' => 0.3, + ], + ]; + } +} diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/PageSetupTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/PageSetupTest.php new file mode 100644 index 00000000..a5b287fe --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/PageSetupTest.php @@ -0,0 +1,149 @@ +spreadsheet = $reader->load($filename); + } + + public function testPageSetup() + { + $assertions = $this->pageSetupAssertions(); + + foreach ($this->spreadsheet->getAllSheets() as $worksheet) { + if (!array_key_exists($worksheet->getTitle(), $assertions)) { + continue; + } + + $sheetAssertions = $assertions[$worksheet->getTitle()]; + foreach ($sheetAssertions as $test => $expectedResult) { + $testMethodName = 'get' . ucfirst($test); + $actualResult = $worksheet->getPageSetup()->$testMethodName(); + $this->assertSame( + $expectedResult, + $actualResult, + "Failed assertion for Worksheet '{$worksheet->getTitle()}' {$test}" + ); + } + } + } + + public function testPageMargins() + { + $assertions = $this->pageMarginAssertions(); + + foreach ($this->spreadsheet->getAllSheets() as $worksheet) { + if (!array_key_exists($worksheet->getTitle(), $assertions)) { + continue; + } + + $sheetAssertions = $assertions[$worksheet->getTitle()]; + foreach ($sheetAssertions as $test => $expectedResult) { + $testMethodName = 'get' . ucfirst($test); + $actualResult = $worksheet->getPageMargins()->$testMethodName(); + $this->assertEqualsWithDelta( + $expectedResult, + $actualResult, + self::MARGIN_PRECISION, + "Failed assertion for Worksheet '{$worksheet->getTitle()}' {$test} margin" + ); + } + } + } + + public function pageSetupAssertions() + { + return [ + 'Sheet1' => [ + 'orientation' => PageSetup::ORIENTATION_PORTRAIT, + 'scale' => 75, + 'horizontalCentered' => true, + 'verticalCentered' => false, + 'pageOrder' => PageSetup::PAGEORDER_DOWN_THEN_OVER, + ], + 'Sheet2' => [ + 'orientation' => PageSetup::ORIENTATION_LANDSCAPE, + 'scale' => 100, + 'horizontalCentered' => false, + 'verticalCentered' => true, + 'pageOrder' => PageSetup::PAGEORDER_OVER_THEN_DOWN, + ], + 'Sheet3' => [ + 'orientation' => PageSetup::ORIENTATION_PORTRAIT, + 'scale' => 90, + 'horizontalCentered' => true, + 'verticalCentered' => true, + 'pageOrder' => PageSetup::PAGEORDER_DOWN_THEN_OVER, + ], + 'Sheet4' => [ + // Default Settings + 'orientation' => PageSetup::ORIENTATION_DEFAULT, + 'scale' => 100, + 'horizontalCentered' => false, + 'verticalCentered' => false, + 'pageOrder' => PageSetup::PAGEORDER_DOWN_THEN_OVER, + ], + ]; + } + + public function pageMarginAssertions() + { + return [ + 'Sheet1' => [ + // Here the values are in cm, so we convert to inches for comparison with internal uom + 'top' => 2.4 / self::MARGIN_UNIT_CONVERSION, + 'header' => 0.8 / self::MARGIN_UNIT_CONVERSION, + 'left' => 1.3 / self::MARGIN_UNIT_CONVERSION, + 'right' => 1.3 / self::MARGIN_UNIT_CONVERSION, + 'bottom' => 1.9 / self::MARGIN_UNIT_CONVERSION, + 'footer' => 0.8 / self::MARGIN_UNIT_CONVERSION, + ], + 'Sheet2' => [ + // Here the values are in cm, so we convert to inches for comparison with internal uom + 'top' => 1.9 / self::MARGIN_UNIT_CONVERSION, + 'header' => 0.8 / self::MARGIN_UNIT_CONVERSION, + 'left' => 1.8 / self::MARGIN_UNIT_CONVERSION, + 'right' => 1.8 / self::MARGIN_UNIT_CONVERSION, + 'bottom' => 1.9 / self::MARGIN_UNIT_CONVERSION, + 'footer' => 0.8 / self::MARGIN_UNIT_CONVERSION, + ], + 'Sheet3' => [ + // Here the values are in cm, so we convert to inches for comparison with internal uom + 'top' => 2.4 / self::MARGIN_UNIT_CONVERSION, + 'header' => 1.3 / self::MARGIN_UNIT_CONVERSION, + 'left' => 1.8 / self::MARGIN_UNIT_CONVERSION, + 'right' => 1.8 / self::MARGIN_UNIT_CONVERSION, + 'bottom' => 2.4 / self::MARGIN_UNIT_CONVERSION, + 'footer' => 1.3 / self::MARGIN_UNIT_CONVERSION, + ], + 'Sheet4' => [ + // Default Settings (already in inches for comparison) + 'top' => 0.75, + 'header' => 0.3, + 'left' => 0.7, + 'right' => 0.7, + 'bottom' => 0.75, + 'footer' => 0.3, + ], + ]; + } +} diff --git a/tests/PhpSpreadsheetTests/Reader/Xml/PageSetupTest.php b/tests/PhpSpreadsheetTests/Reader/Xml/PageSetupTest.php new file mode 100644 index 00000000..8b934be6 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xml/PageSetupTest.php @@ -0,0 +1,148 @@ +spreadsheet = $reader->load($filename); + } + + public function testPageSetup() + { + $assertions = $this->pageSetupAssertions(); + + foreach ($this->spreadsheet->getAllSheets() as $worksheet) { + if (!array_key_exists($worksheet->getTitle(), $assertions)) { + continue; + } + + $sheetAssertions = $assertions[$worksheet->getTitle()]; + foreach ($sheetAssertions as $test => $expectedResult) { + $testMethodName = 'get' . ucfirst($test); + $actualResult = $worksheet->getPageSetup()->$testMethodName(); + $this->assertSame( + $expectedResult, + $actualResult, + "Failed assertion for Worksheet '{$worksheet->getTitle()}' {$test}" + ); + } + } + } + + public function testPageMargins() + { + $assertions = $this->pageMarginAssertions(); + + foreach ($this->spreadsheet->getAllSheets() as $worksheet) { + if (!array_key_exists($worksheet->getTitle(), $assertions)) { + continue; + } + + $sheetAssertions = $assertions[$worksheet->getTitle()]; + foreach ($sheetAssertions as $test => $expectedResult) { + $testMethodName = 'get' . ucfirst($test); + $actualResult = $worksheet->getPageMargins()->$testMethodName(); + $this->assertEqualsWithDelta( + $expectedResult, + $actualResult, + self::MARGIN_PRECISION, + "Failed assertion for Worksheet '{$worksheet->getTitle()}' {$test} margin" + ); + } + } + } + + public function pageSetupAssertions() + { + return [ + 'Sheet1' => [ + 'orientation' => PageSetup::ORIENTATION_PORTRAIT, + 'scale' => 75, + 'horizontalCentered' => true, + 'verticalCentered' => false, + 'pageOrder' => PageSetup::PAGEORDER_DOWN_THEN_OVER, + ], + 'Sheet2' => [ + 'orientation' => PageSetup::ORIENTATION_LANDSCAPE, + 'scale' => 100, + 'horizontalCentered' => false, + 'verticalCentered' => true, + 'pageOrder' => PageSetup::PAGEORDER_OVER_THEN_DOWN, + ], + 'Sheet3' => [ + 'orientation' => PageSetup::ORIENTATION_PORTRAIT, + 'scale' => 90, + 'horizontalCentered' => true, + 'verticalCentered' => true, + 'pageOrder' => PageSetup::PAGEORDER_DOWN_THEN_OVER, + ], + 'Sheet4' => [ + // Default Settings + 'orientation' => PageSetup::ORIENTATION_DEFAULT, + 'scale' => 100, + 'horizontalCentered' => false, + 'verticalCentered' => false, + 'pageOrder' => PageSetup::PAGEORDER_DOWN_THEN_OVER, + ], + ]; + } + + public function pageMarginAssertions() + { + return [ + 'Sheet1' => [ + // Here the values are in cm, so we convert to inches for comparison with internal uom + 'top' => 2.4 / self::MARGIN_UNIT_CONVERSION, + 'header' => 0.8 / self::MARGIN_UNIT_CONVERSION, + 'left' => 1.3 / self::MARGIN_UNIT_CONVERSION, + 'right' => 1.3 / self::MARGIN_UNIT_CONVERSION, + 'bottom' => 1.9 / self::MARGIN_UNIT_CONVERSION, + 'footer' => 0.8 / self::MARGIN_UNIT_CONVERSION, + ], + 'Sheet2' => [ + // Here the values are in cm, so we convert to inches for comparison with internal uom + 'top' => 1.9 / self::MARGIN_UNIT_CONVERSION, + 'header' => 0.8 / self::MARGIN_UNIT_CONVERSION, + 'left' => 1.8 / self::MARGIN_UNIT_CONVERSION, + 'right' => 1.8 / self::MARGIN_UNIT_CONVERSION, + 'bottom' => 1.9 / self::MARGIN_UNIT_CONVERSION, + 'footer' => 0.8 / self::MARGIN_UNIT_CONVERSION, + ], + 'Sheet3' => [ + // Here the values are in cm, so we convert to inches for comparison with internal uom + 'top' => 2.4 / self::MARGIN_UNIT_CONVERSION, + 'header' => 1.3 / self::MARGIN_UNIT_CONVERSION, + 'left' => 1.8 / self::MARGIN_UNIT_CONVERSION, + 'right' => 1.8 / self::MARGIN_UNIT_CONVERSION, + 'bottom' => 2.4 / self::MARGIN_UNIT_CONVERSION, + 'footer' => 1.3 / self::MARGIN_UNIT_CONVERSION, + ], + 'Sheet4' => [ + // Default Settings (already in inches for comparison) + 'top' => 0.75, + 'header' => 0.3, + 'left' => 0.7, + 'right' => 0.7, + 'bottom' => 0.75, + 'footer' => 0.3, + ], + ]; + } +} diff --git a/tests/PhpSpreadsheetTests/Worksheet/PageMarginsTest.php b/tests/PhpSpreadsheetTests/Worksheet/PageMarginsTest.php new file mode 100644 index 00000000..b9713707 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Worksheet/PageMarginsTest.php @@ -0,0 +1,89 @@ + Date: Sun, 5 Jul 2020 16:28:46 +0200 Subject: [PATCH 17/29] Forgot to check in the test files for the unit tests --- tests/data/Reader/Gnumeric/PageSetup.gnumeric | Bin 0 -> 2347 bytes tests/data/Reader/Ods/PageSetup.ods | Bin 0 -> 4814 bytes tests/data/Reader/XLS/PageSetup.xls | Bin 0 -> 34816 bytes tests/data/Reader/XLSX/PageSetup.xlsx | Bin 0 -> 15225 bytes tests/data/Reader/Xml/PageSetup.xml | 250 ++++++++++++++++++ 5 files changed, 250 insertions(+) create mode 100644 tests/data/Reader/Gnumeric/PageSetup.gnumeric create mode 100644 tests/data/Reader/Ods/PageSetup.ods create mode 100644 tests/data/Reader/XLS/PageSetup.xls create mode 100644 tests/data/Reader/XLSX/PageSetup.xlsx create mode 100644 tests/data/Reader/Xml/PageSetup.xml diff --git a/tests/data/Reader/Gnumeric/PageSetup.gnumeric b/tests/data/Reader/Gnumeric/PageSetup.gnumeric new file mode 100644 index 0000000000000000000000000000000000000000..53359154866fec0215c55f3144d34ea5444f4014 GIT binary patch literal 2347 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 literal 0 HcmV?d00001 diff --git a/tests/data/Reader/XLS/PageSetup.xls b/tests/data/Reader/XLS/PageSetup.xls new file mode 100644 index 0000000000000000000000000000000000000000..559d05326f7887f1c31f07f70c82e30a8f858e62 GIT binary patch literal 34816 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 literal 0 HcmV?d00001 diff --git a/tests/data/Reader/Xml/PageSetup.xml b/tests/data/Reader/Xml/PageSetup.xml new file mode 100644 index 00000000..8f996a93 --- /dev/null +++ b/tests/data/Reader/Xml/PageSetup.xml @@ -0,0 +1,250 @@ + + + + + Mark Baker + Mark Baker + 2020-07-04T11:51:41Z + 2020-06-29T17:37:00Z + 2020-07-04T11:52:32Z + 16.00 + + + + + + 13170 + 21600 + 2145 + 2145 + False + False + + + + + + + + 1 + 2 + 3 + + + 4 + 5 + 6 + + + 7 + 8 + 9 + + + 30 + 6 + +
+ + + +
+