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.
This commit is contained in:
oleibman 2021-12-01 23:00:02 -08:00 committed by GitHub
parent d5825a6682
commit 580c741c8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 234 additions and 121 deletions

View File

@ -918,6 +918,16 @@ which sheet to write to PDF:
$writer->setSheetIndex(0); $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 #### Formula pre-calculation
By default, this writer pre-calculates all formulas in the spreadsheet. By default, this writer pre-calculates all formulas in the spreadsheet.

View File

@ -303,6 +303,21 @@ $spreadsheet->getActiveSheet()->getPageSetup()
Note that there are additional page settings available. Please refer to Note that there are additional page settings available. Please refer to
the [API documentation](https://phpoffice.github.io/PhpSpreadsheet) for all possible options. 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 ### Page Setup: Scaling options
The page setup scaling options in PhpSpreadsheet relate directly to the The page setup scaling options in PhpSpreadsheet relate directly to the

View File

@ -6530,11 +6530,6 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Writer/Html.php 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\\.$#" message: "#^Parameter \\#1 \\$borderStyle of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Html\\:\\:mapBorderStyle\\(\\) expects int, string given\\.$#"
count: 1 count: 1
@ -6650,26 +6645,6 @@ parameters:
count: 2 count: 2
path: src/PhpSpreadsheet/Writer/Ods/Settings.php 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\\.$#" message: "#^Cannot call method getHashCode\\(\\) on PhpOffice\\\\PhpSpreadsheet\\\\Style\\\\Font\\|null\\.$#"
count: 1 count: 1

View File

@ -160,18 +160,32 @@ class PageSetup
const PAGEORDER_DOWN_THEN_OVER = 'downThenOver'; const PAGEORDER_DOWN_THEN_OVER = 'downThenOver';
/** /**
* Paper size. * Paper size default.
* *
* @var int * @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. * Orientation.
* *
* @var string * @var string
*/ */
private $orientation = self::ORIENTATION_DEFAULT; private $orientation;
/** /**
* Scale (Print Scale). * Scale (Print Scale).
@ -256,6 +270,7 @@ class PageSetup
*/ */
public function __construct() public function __construct()
{ {
$this->orientation = self::$orientationDefault;
} }
/** /**
@ -265,7 +280,7 @@ class PageSetup
*/ */
public function getPaperSize() public function getPaperSize()
{ {
return $this->paperSize; return $this->paperSize ?? self::$paperSizeDefault;
} }
/** /**
@ -282,6 +297,22 @@ class PageSetup
return $this; 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. * Get Orientation.
* *
@ -301,11 +332,25 @@ class PageSetup
*/ */
public function setOrientation($orientation) 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; 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. * Get Scale.
* *

View File

@ -291,10 +291,8 @@ class Html extends BaseWriter
/** /**
* Get sheet index. * Get sheet index.
*
* @return int
*/ */
public function getSheetIndex() public function getSheetIndex(): ?int
{ {
return $this->sheetIndex; return $this->sheetIndex;
} }
@ -1784,6 +1782,11 @@ class Html extends BaseWriter
return $result; return $result;
} }
public function getOrientation(): ?string
{
return null;
}
/** /**
* Generate @page declarations. * Generate @page declarations.
* *
@ -1819,7 +1822,7 @@ class Html extends BaseWriter
$htmlPage .= 'margin-top: ' . $top; $htmlPage .= 'margin-top: ' . $top;
$bottom = StringHelper::FormatNumber($worksheet->getPageMargins()->getBottom()) . 'in; '; $bottom = StringHelper::FormatNumber($worksheet->getPageMargins()->getBottom()) . 'in; ';
$htmlPage .= 'margin-bottom: ' . $bottom; $htmlPage .= 'margin-bottom: ' . $bottom;
$orientation = $worksheet->getPageSetup()->getOrientation(); $orientation = $this->getOrientation() ?? $worksheet->getPageSetup()->getOrientation();
if ($orientation === \PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_LANDSCAPE) { if ($orientation === \PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_LANDSCAPE) {
$htmlPage .= 'size: landscape; '; $htmlPage .= 'size: landscape; ';
} elseif ($orientation === \PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_PORTRAIT) { } elseif ($orientation === \PhpOffice\PhpSpreadsheet\Worksheet\PageSetup::ORIENTATION_PORTRAIT) {

View File

@ -26,14 +26,14 @@ abstract class Pdf extends Html
/** /**
* Orientation (Over-ride). * Orientation (Over-ride).
* *
* @var string * @var ?string
*/ */
protected $orientation; protected $orientation;
/** /**
* Paper size (Over-ride). * Paper size (Over-ride).
* *
* @var int * @var ?int
*/ */
protected $paperSize; protected $paperSize;
@ -155,7 +155,7 @@ abstract class Pdf extends Html
/** /**
* Get Paper Size. * Get Paper Size.
* *
* @return int * @return ?int
*/ */
public function getPaperSize() public function getPaperSize()
{ {
@ -178,10 +178,8 @@ abstract class Pdf extends Html
/** /**
* Get Orientation. * Get Orientation.
*
* @return string
*/ */
public function getOrientation() public function getOrientation(): ?string
{ {
return $this->orientation; return $this->orientation;
} }

View File

@ -30,33 +30,14 @@ class Dompdf extends Pdf
$paperSize = 'LETTER'; // Letter (8.5 in. by 11 in.) $paperSize = 'LETTER'; // Letter (8.5 in. by 11 in.)
// Check for paper size and page orientation // Check for paper size and page orientation
if ($this->getSheetIndex() === null) { $setup = $this->spreadsheet->getSheet($this->getSheetIndex() ?? 0)->getPageSetup();
$orientation = ($this->spreadsheet->getSheet(0)->getPageSetup()->getOrientation() $orientation = $this->getOrientation() ?? $setup->getOrientation();
== PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P'; $orientation = ($orientation === PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P';
$printPaperSize = $this->spreadsheet->getSheet(0)->getPageSetup()->getPaperSize(); $printPaperSize = $this->getPaperSize() ?? $setup->getPaperSize();
} else { $paperSize = self::$paperSizes[$printPaperSize] ?? PageSetup::getPaperSizeDefault();
$orientation = ($this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getOrientation()
== PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P';
$printPaperSize = $this->spreadsheet->getSheet($this->getSheetIndex())->getPageSetup()->getPaperSize();
}
$orientation = ($orientation == 'L') ? 'landscape' : 'portrait'; $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 // Create PDF
$pdf = $this->createExternalWriterInstance(); $pdf = $this->createExternalWriterInstance();
$pdf->setPaper($paperSize, $orientation); $pdf->setPaper($paperSize, $orientation);
@ -65,7 +46,7 @@ class Dompdf extends Pdf
$pdf->render(); $pdf->render();
// Write to file // Write to file
fwrite($fileHandle, $pdf->output()); fwrite($fileHandle, $pdf->output() ?? '');
parent::restoreStateAfterSave(); parent::restoreStateAfterSave();
} }

View File

@ -28,37 +28,12 @@ class Mpdf extends Pdf
{ {
$fileHandle = parent::prepareForSave($filename); $fileHandle = parent::prepareForSave($filename);
// Default PDF paper size
$paperSize = 'LETTER'; // Letter (8.5 in. by 11 in.)
// Check for paper size and page orientation // Check for paper size and page orientation
if (null === $this->getSheetIndex()) { $setup = $this->spreadsheet->getSheet($this->getSheetIndex() ?? 0)->getPageSetup();
$orientation = ($this->spreadsheet->getSheet(0)->getPageSetup()->getOrientation() $orientation = $this->getOrientation() ?? $setup->getOrientation();
== PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P'; $orientation = ($orientation === PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P';
$printPaperSize = $this->spreadsheet->getSheet(0)->getPageSetup()->getPaperSize(); $printPaperSize = $this->getPaperSize() ?? $setup->getPaperSize();
} else { $paperSize = self::$paperSizes[$printPaperSize] ?? PageSetup::getPaperSizeDefault();
$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];
}
// Create PDF // Create PDF
$config = ['tempDir' => $this->tempDir . '/mpdf']; $config = ['tempDir' => $this->tempDir . '/mpdf'];

View File

@ -46,32 +46,12 @@ class Tcpdf extends Pdf
$paperSize = 'LETTER'; // Letter (8.5 in. by 11 in.) $paperSize = 'LETTER'; // Letter (8.5 in. by 11 in.)
// Check for paper size and page orientation // Check for paper size and page orientation
if ($this->getSheetIndex() === null) { $setup = $this->spreadsheet->getSheet($this->getSheetIndex() ?? 0)->getPageSetup();
$orientation = ($this->spreadsheet->getSheet(0)->getPageSetup()->getOrientation() $orientation = $this->getOrientation() ?? $setup->getOrientation();
== PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P'; $orientation = ($orientation === PageSetup::ORIENTATION_LANDSCAPE) ? 'L' : 'P';
$printPaperSize = $this->spreadsheet->getSheet(0)->getPageSetup()->getPaperSize(); $printPaperSize = $this->getPaperSize() ?? $setup->getPaperSize();
$printMargins = $this->spreadsheet->getSheet(0)->getPageMargins(); $paperSize = self::$paperSizes[$printPaperSize] ?? PageSetup::getPaperSizeDefault();
} else { $printMargins = $this->spreadsheet->getSheet($this->getSheetIndex() ?? 0)->getPageMargins();
$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];
}
// Create PDF // Create PDF
$pdf = $this->createExternalWriterInstance($orientation, 'pt', $paperSize); $pdf = $this->createExternalWriterInstance($orientation, 'pt', $paperSize);

View File

@ -0,0 +1,62 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
use PHPUnit\Framework\TestCase;
class DefaultPaperSizeTest extends TestCase
{
/** @var int */
private $paperSize;
/** @var string */
private $orientation;
protected function setUp(): void
{
$this->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();
}
}

View File

@ -0,0 +1,69 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Writer\Mpdf;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
use PhpOffice\PhpSpreadsheet\Writer\Pdf\Mpdf;
use PHPUnit\Framework\TestCase;
class OrientationTest extends TestCase
{
private const INITARRAY = [
[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10],
[11, 12, 13, 14, 15],
];
private static function setupSheet(): Spreadsheet
{
$spreadsheet = new Spreadsheet();
$sheet1 = $spreadsheet->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();
}
}