From 580c741c8e355a818e0d59cdba29f9bcf81d8b6f Mon Sep 17 00:00:00 2001 From: oleibman Date: Wed, 1 Dec 2021 23:00:02 -0800 Subject: [PATCH] Improve PDF Support for Page Size and Orientation (#2410) Fix #1691. PhpSpreadsheet allows the setting of different page size and orientation on each worksheet. It also allows the setting of page size and orientation on the PDF writer. It isn't clear which is supposed to prevail when the two are in conflict. In the cited issue, the user expects the PDF writer setting to prevail, and I tend to agree. Code is changed to do this, and handling things in this manner is now explicitly documented. PhpSpreadsheet uses a default paper size of Letter, and a default orientation of Default (which Excel treats as Portrait). New static routines are added to change the default for sheets created subsequent to such calls. This could allow users to configure these defaults better for their environments. The new functions are added to the documentation. --- docs/topics/reading-and-writing-to-file.md | 10 +++ docs/topics/recipes.md | 15 ++++ phpstan-baseline.neon | 25 ------- src/PhpSpreadsheet/Worksheet/PageSetup.php | 55 +++++++++++++-- src/PhpSpreadsheet/Writer/Html.php | 11 +-- src/PhpSpreadsheet/Writer/Pdf.php | 10 ++- src/PhpSpreadsheet/Writer/Pdf/Dompdf.php | 31 ++------- src/PhpSpreadsheet/Writer/Pdf/Mpdf.php | 35 ++-------- src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php | 32 ++------- .../Worksheet/DefaultPaperSizeTest.php | 62 +++++++++++++++++ .../Writer/Mpdf/OrientationTest.php | 69 +++++++++++++++++++ 11 files changed, 234 insertions(+), 121 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Worksheet/DefaultPaperSizeTest.php create mode 100644 tests/PhpSpreadsheetTests/Writer/Mpdf/OrientationTest.php diff --git a/docs/topics/reading-and-writing-to-file.md b/docs/topics/reading-and-writing-to-file.md index 8bb48cc5..eb743e85 100644 --- a/docs/topics/reading-and-writing-to-file.md +++ b/docs/topics/reading-and-writing-to-file.md @@ -918,6 +918,16 @@ which sheet to write to PDF: $writer->setSheetIndex(0); ``` +#### Setting Orientation and PaperSize + +PhpSpreadsheet will attempt to honor the orientation and paper size specified +in the worksheet for each page it prints, if the renderer supports that. However, you can set all pages +to have the same orientation and paper size, e.g. + +```php +$writer->setOrientation(\PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_LANDSCAPE); +``` + #### Formula pre-calculation By default, this writer pre-calculates all formulas in the spreadsheet. diff --git a/docs/topics/recipes.md b/docs/topics/recipes.md index 25d4bb50..c90e607c 100644 --- a/docs/topics/recipes.md +++ b/docs/topics/recipes.md @@ -303,6 +303,21 @@ $spreadsheet->getActiveSheet()->getPageSetup() Note that there are additional page settings available. Please refer to the [API documentation](https://phpoffice.github.io/PhpSpreadsheet) for all possible options. +The default papersize is initially PAPERSIZE_LETTER. However, this default +can be changed for new sheets with the following call: +```php +\PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::setPaperSizeDefault( + \PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::PAPERSIZE_A4 +); +``` + +The default orientation is ORIENTATION_DEFAULT, which will be treated as Portrait in Excel. However, this default can be changed for new sheets with the following call: +```php +\PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::setOrientationDefault( + \PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_LANDSCAPE +); +``` + ### Page Setup: Scaling options The page setup scaling options in PhpSpreadsheet relate directly to the diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 04b2a61f..9943f81a 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -6530,11 +6530,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Writer/Html.php - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Html\\:\\:getSheetIndex\\(\\) should return int but returns int\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Html.php - - message: "#^Parameter \\#1 \\$borderStyle of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Html\\:\\:mapBorderStyle\\(\\) expects int, string given\\.$#" count: 1 @@ -6650,26 +6645,6 @@ parameters: count: 2 path: src/PhpSpreadsheet/Writer/Ods/Settings.php - - - message: "#^Parameter \\#2 \\$str of function fwrite expects string, string\\|null given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Pdf/Dompdf.php - - - - message: "#^Strict comparison using \\=\\=\\= between int and null will always evaluate to false\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Pdf/Dompdf.php - - - - message: "#^Strict comparison using \\=\\=\\= between null and int will always evaluate to false\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Pdf/Mpdf.php - - - - message: "#^Strict comparison using \\=\\=\\= between int and null will always evaluate to false\\.$#" - count: 1 - path: src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php - - message: "#^Cannot call method getHashCode\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#" count: 1 diff --git a/src/PhpSpreadsheet/Worksheet/PageSetup.php b/src/PhpSpreadsheet/Worksheet/PageSetup.php index ec276b64..96407828 100644 --- a/src/PhpSpreadsheet/Worksheet/PageSetup.php +++ b/src/PhpSpreadsheet/Worksheet/PageSetup.php @@ -160,18 +160,32 @@ class PageSetup const PAGEORDER_DOWN_THEN_OVER = 'downThenOver'; /** - * Paper size. + * Paper size default. * * @var int */ - private $paperSize = self::PAPERSIZE_LETTER; + private static $paperSizeDefault = self::PAPERSIZE_LETTER; + + /** + * Paper size. + * + * @var ?int + */ + private $paperSize; + + /** + * Orientation default. + * + * @var string + */ + private static $orientationDefault = self::ORIENTATION_DEFAULT; /** * Orientation. * * @var string */ - private $orientation = self::ORIENTATION_DEFAULT; + private $orientation; /** * Scale (Print Scale). @@ -256,6 +270,7 @@ class PageSetup */ public function __construct() { + $this->orientation = self::$orientationDefault; } /** @@ -265,7 +280,7 @@ class PageSetup */ public function getPaperSize() { - return $this->paperSize; + return $this->paperSize ?? self::$paperSizeDefault; } /** @@ -282,6 +297,22 @@ class PageSetup return $this; } + /** + * Get Paper Size default. + */ + public static function getPaperSizeDefault(): int + { + return self::$paperSizeDefault; + } + + /** + * Set Paper Size Default. + */ + public static function setPaperSizeDefault(int $paperSize): void + { + self::$paperSizeDefault = $paperSize; + } + /** * Get Orientation. * @@ -301,11 +332,25 @@ class PageSetup */ public function setOrientation($orientation) { - $this->orientation = $orientation; + if ($orientation === self::ORIENTATION_LANDSCAPE || $orientation === self::ORIENTATION_PORTRAIT || $orientation === self::ORIENTATION_DEFAULT) { + $this->orientation = $orientation; + } return $this; } + public static function getOrientationDefault(): string + { + return self::$orientationDefault; + } + + public static function setOrientationDefault(string $orientation): void + { + if ($orientation === self::ORIENTATION_LANDSCAPE || $orientation === self::ORIENTATION_PORTRAIT || $orientation === self::ORIENTATION_DEFAULT) { + self::$orientationDefault = $orientation; + } + } + /** * Get Scale. * diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index a59ea1f9..d0266cc5 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -291,10 +291,8 @@ class Html extends BaseWriter /** * Get sheet index. - * - * @return int */ - public function getSheetIndex() + public function getSheetIndex(): ?int { return $this->sheetIndex; } @@ -1784,6 +1782,11 @@ class Html extends BaseWriter return $result; } + public function getOrientation(): ?string + { + return null; + } + /** * Generate @page declarations. * @@ -1819,7 +1822,7 @@ class Html extends BaseWriter $htmlPage .= 'margin-top: ' . $top; $bottom = StringHelper::FormatNumber($worksheet->getPageMargins()->getBottom()) . 'in; '; $htmlPage .= 'margin-bottom: ' . $bottom; - $orientation = $worksheet->getPageSetup()->getOrientation(); + $orientation = $this->getOrientation() ?? $worksheet->getPageSetup()->getOrientation(); if ($orientation === \PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_LANDSCAPE) { $htmlPage .= 'size: landscape; '; } elseif ($orientation === \PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_PORTRAIT) { diff --git a/src/PhpSpreadsheet/Writer/Pdf.php b/src/PhpSpreadsheet/Writer/Pdf.php index c419e1e2..493bbba3 100644 --- a/src/PhpSpreadsheet/Writer/Pdf.php +++ b/src/PhpSpreadsheet/Writer/Pdf.php @@ -26,14 +26,14 @@ abstract class Pdf extends Html /** * Orientation (Over-ride). * - * @var string + * @var ?string */ protected $orientation; /** * Paper size (Over-ride). * - * @var int + * @var ?int */ protected $paperSize; @@ -155,7 +155,7 @@ abstract class Pdf extends Html /** * Get Paper Size. * - * @return int + * @return ?int */ public function getPaperSize() { @@ -178,10 +178,8 @@ abstract class Pdf extends Html /** * Get Orientation. - * - * @return string */ - public function getOrientation() + public function getOrientation(): ?string { return $this->orientation; } diff --git a/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php b/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php index e49c22c7..fc96f904 100644 --- a/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php +++ b/src/PhpSpreadsheet/Writer/Pdf/Dompdf.php @@ -30,33 +30,14 @@ class Dompdf extends Pdf $paperSize = 'LETTER'; // Letter (8.5 in. by 11 in.) // Check for paper size and page orientation - if ($this->getSheetIndex() === null) { - $orientation = ($this->spreadsheet->getSheet(0)->getPageSetup()->getOrientation() - == PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P'; - $printPaperSize = $this->spreadsheet->getSheet(0)->getPageSetup()->getPaperSize(); - } else { - $orientation = ($this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getOrientation() - == PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P'; - $printPaperSize = $this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getPaperSize(); - } + $setup = $this->spreadsheet->getSheet($this->getSheetIndex() ?? 0)->getPageSetup(); + $orientation = $this->getOrientation() ?? $setup->getOrientation(); + $orientation = ($orientation === PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P'; + $printPaperSize = $this->getPaperSize() ?? $setup->getPaperSize(); + $paperSize = self::$paperSizes[$printPaperSize] ?? PageSetup::getPaperSizeDefault(); $orientation = ($orientation == 'L') ? 'landscape' : 'portrait'; - // Override Page Orientation - if ($this->getOrientation() !== null) { - $orientation = ($this->getOrientation() == PageSetup::ORIENTATION_DEFAULT) - ? PageSetup::ORIENTATION_PORTRAIT - : $this->getOrientation(); - } - // Override Paper Size - if ($this->getPaperSize() !== null) { - $printPaperSize = $this->getPaperSize(); - } - - if (isset(self::$paperSizes[$printPaperSize])) { - $paperSize = self::$paperSizes[$printPaperSize]; - } - // Create PDF $pdf = $this->createExternalWriterInstance(); $pdf->setPaper($paperSize, $orientation); @@ -65,7 +46,7 @@ class Dompdf extends Pdf $pdf->render(); // Write to file - fwrite($fileHandle, $pdf->output()); + fwrite($fileHandle, $pdf->output() ?? ''); parent::restoreStateAfterSave(); } diff --git a/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php b/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php index 8d0eda91..96067dbb 100644 --- a/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php +++ b/src/PhpSpreadsheet/Writer/Pdf/Mpdf.php @@ -28,37 +28,12 @@ class Mpdf extends Pdf { $fileHandle = parent::prepareForSave($filename); - // Default PDF paper size - $paperSize = 'LETTER'; // Letter (8.5 in. by 11 in.) - // Check for paper size and page orientation - if (null === $this->getSheetIndex()) { - $orientation = ($this->spreadsheet->getSheet(0)->getPageSetup()->getOrientation() - == PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P'; - $printPaperSize = $this->spreadsheet->getSheet(0)->getPageSetup()->getPaperSize(); - } else { - $orientation = ($this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getOrientation() - == PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P'; - $printPaperSize = $this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getPaperSize(); - } - $this->setOrientation($orientation); - - // Override Page Orientation - if (null !== $this->getOrientation()) { - $orientation = ($this->getOrientation() == PageSetup::ORIENTATION_DEFAULT) - ? PageSetup::ORIENTATION_PORTRAIT - : $this->getOrientation(); - } - $orientation = strtoupper($orientation); - - // Override Paper Size - if (null !== $this->getPaperSize()) { - $printPaperSize = $this->getPaperSize(); - } - - if (isset(self::$paperSizes[$printPaperSize])) { - $paperSize = self::$paperSizes[$printPaperSize]; - } + $setup = $this->spreadsheet->getSheet($this->getSheetIndex() ?? 0)->getPageSetup(); + $orientation = $this->getOrientation() ?? $setup->getOrientation(); + $orientation = ($orientation === PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P'; + $printPaperSize = $this->getPaperSize() ?? $setup->getPaperSize(); + $paperSize = self::$paperSizes[$printPaperSize] ?? PageSetup::getPaperSizeDefault(); // Create PDF $config = ['tempDir' => $this->tempDir . '/mpdf']; diff --git a/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php b/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php index 6ed16a5d..d29d4764 100644 --- a/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php +++ b/src/PhpSpreadsheet/Writer/Pdf/Tcpdf.php @@ -46,32 +46,12 @@ class Tcpdf extends Pdf $paperSize = 'LETTER'; // Letter (8.5 in. by 11 in.) // Check for paper size and page orientation - if ($this->getSheetIndex() === null) { - $orientation = ($this->spreadsheet->getSheet(0)->getPageSetup()->getOrientation() - == PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P'; - $printPaperSize = $this->spreadsheet->getSheet(0)->getPageSetup()->getPaperSize(); - $printMargins = $this->spreadsheet->getSheet(0)->getPageMargins(); - } else { - $orientation = ($this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getOrientation() - == PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P'; - $printPaperSize = $this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getPaperSize(); - $printMargins = $this->spreadsheet->getSheet($this->getSheetIndex())->getPageMargins(); - } - - // Override Page Orientation - if ($this->getOrientation() !== null) { - $orientation = ($this->getOrientation() == PageSetup::ORIENTATION_LANDSCAPE) - ? 'L' - : 'P'; - } - // Override Paper Size - if ($this->getPaperSize() !== null) { - $printPaperSize = $this->getPaperSize(); - } - - if (isset(self::$paperSizes[$printPaperSize])) { - $paperSize = self::$paperSizes[$printPaperSize]; - } + $setup = $this->spreadsheet->getSheet($this->getSheetIndex() ?? 0)->getPageSetup(); + $orientation = $this->getOrientation() ?? $setup->getOrientation(); + $orientation = ($orientation === PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P'; + $printPaperSize = $this->getPaperSize() ?? $setup->getPaperSize(); + $paperSize = self::$paperSizes[$printPaperSize] ?? PageSetup::getPaperSizeDefault(); + $printMargins = $this->spreadsheet->getSheet($this->getSheetIndex() ?? 0)->getPageMargins(); // Create PDF $pdf = $this->createExternalWriterInstance($orientation, 'pt', $paperSize); diff --git a/tests/PhpSpreadsheetTests/Worksheet/DefaultPaperSizeTest.php b/tests/PhpSpreadsheetTests/Worksheet/DefaultPaperSizeTest.php new file mode 100644 index 00000000..5ab3c282 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Worksheet/DefaultPaperSizeTest.php @@ -0,0 +1,62 @@ +paperSize = PageSetup::getPaperSizeDefault(); + $this->orientation = PageSetup::getOrientationDefault(); + } + + protected function tearDown(): void + { + PageSetup::setPaperSizeDefault($this->paperSize); + PageSetup::setOrientationDefault($this->orientation); + } + + public function testChangeDefault(): void + { + PageSetup::setPaperSizeDefault(PageSetup::PAPERSIZE_A4); + PageSetup::setOrientationDefault(PageSetup::ORIENTATION_LANDSCAPE); + $spreadsheet = new Spreadsheet(); + $sheet1 = $spreadsheet->getActiveSheet(); + $sheet2 = $spreadsheet->createSheet(); + $sheet3 = $spreadsheet->createSheet(); + self::assertSame(PageSetup::PAPERSIZE_A4, $sheet1->getPageSetup()->getPaperSize()); + self::assertSame(PageSetup::PAPERSIZE_A4, $sheet2->getPageSetup()->getPaperSize()); + self::assertSame(PageSetup::PAPERSIZE_A4, $sheet3->getPageSetup()->getPaperSize()); + self::assertSame(PageSetup::ORIENTATION_LANDSCAPE, $sheet1->getPageSetup()->getOrientation()); + self::assertSame(PageSetup::ORIENTATION_LANDSCAPE, $sheet2->getPageSetup()->getOrientation()); + self::assertSame(PageSetup::ORIENTATION_LANDSCAPE, $sheet3->getPageSetup()->getOrientation()); + $spreadsheet->disconnectWorksheets(); + } + + public function testUnchangedDefault(): void + { + //PageSetup::setPaperSizeDefault(PageSetup::PAPERSIZE_A4); + //PageSetup::setOrientationDefault(PageSetup::ORIENTATION_LANDSCAPE); + $spreadsheet = new Spreadsheet(); + $sheet1 = $spreadsheet->getActiveSheet(); + $sheet2 = $spreadsheet->createSheet(); + $sheet3 = $spreadsheet->createSheet(); + self::assertSame(PageSetup::PAPERSIZE_LETTER, $sheet1->getPageSetup()->getPaperSize()); + self::assertSame(PageSetup::PAPERSIZE_LETTER, $sheet2->getPageSetup()->getPaperSize()); + self::assertSame(PageSetup::PAPERSIZE_LETTER, $sheet3->getPageSetup()->getPaperSize()); + self::assertSame(PageSetup::ORIENTATION_DEFAULT, $sheet1->getPageSetup()->getOrientation()); + self::assertSame(PageSetup::ORIENTATION_DEFAULT, $sheet2->getPageSetup()->getOrientation()); + self::assertSame(PageSetup::ORIENTATION_DEFAULT, $sheet3->getPageSetup()->getOrientation()); + $spreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/PhpSpreadsheetTests/Writer/Mpdf/OrientationTest.php b/tests/PhpSpreadsheetTests/Writer/Mpdf/OrientationTest.php new file mode 100644 index 00000000..c04e8432 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Mpdf/OrientationTest.php @@ -0,0 +1,69 @@ +getActiveSheet(); + $sheet1->fromArray(self::INITARRAY); + $sheet1->getPageSetup()->setOrientation(PageSetup::ORIENTATION_LANDSCAPE); + $sheet2 = $spreadsheet->createSheet(); + $sheet2->fromArray(self::INITARRAY); + $sheet2->getPageSetup()->setOrientation(PageSetup::ORIENTATION_PORTRAIT); + $sheet3 = $spreadsheet->createSheet(); + $sheet3->fromArray(self::INITARRAY); + $sheet3->getPageSetup()->setOrientation(PageSetup::ORIENTATION_LANDSCAPE); + + return $spreadsheet; + } + + public static function testSheetOrientation(): void + { + $spreadsheet = self::setupSheet(); + $writer = new Mpdf($spreadsheet); + //$writer->setOrientation( PageSetup::ORIENTATION_LANDSCAPE ); + $writer->writeAllSheets(); + $html = $writer->generateHtmlAll(); + self::assertSame(2, substr_count($html, 'size: landscape;')); + self::assertSame(1, substr_count($html, 'size: portrait;')); + $spreadsheet->disconnectWorksheets(); + } + + public static function testLandscape(): void + { + $spreadsheet = self::setupSheet(); + $writer = new Mpdf($spreadsheet); + $writer->setOrientation(PageSetup::ORIENTATION_LANDSCAPE); + $writer->writeAllSheets(); + $html = $writer->generateHtmlAll(); + self::assertSame(3, substr_count($html, 'size: landscape;')); + self::assertSame(0, substr_count($html, 'size: portrait;')); + $spreadsheet->disconnectWorksheets(); + } + + public static function testPortrait(): void + { + $spreadsheet = self::setupSheet(); + $writer = new Mpdf($spreadsheet); + $writer->setOrientation(PageSetup::ORIENTATION_PORTRAIT); + $writer->writeAllSheets(); + $html = $writer->generateHtmlAll(); + self::assertSame(0, substr_count($html, 'size: landscape;')); + self::assertSame(3, substr_count($html, 'size: portrait;')); + $spreadsheet->disconnectWorksheets(); + } +}