mirror of
https://github.com/PHPOffice/PhpSpreadsheet.git
synced 2026-08-17 11:30:22 +00:00
Headers and Footers in Pdf
Fix #3159, at least as far as we can. Requester would probably prefer that we automatically translate the PhpSpreadsheet header and footer information to forms that the Pdf vendors can handle. That seems like it would be very difficult. On the other hand, we can add some new properties to the Pdf writers to make it *relatively* easy for the user to define their own headers and footers for the writers. Mpdf, as mentioned in the issue, can already handle this in conjunction with the `editHtmlCallback` property. This is demonstraded in Sample21c, which is now renamed to something more descriptive, `Mpdf_Custom_Headers`. That is the only change needed for Mpdf. Tcpdf, which currently automatically prevents any footers or headers, needs some new properties to allow them. Even with the new properties, a user would need to extend both PhpSpreadsheet's Tcpdf Writer, and the vendor's own class (extending that class is actually the vendor's recommendation). A new sample `Tcpdf_Custom_Headers` demonstrates how this would be done. Dompdf has 2 ways to add headers and footers. One is pretty good at styling, but doesn't offer much flexibility for dynamic properties other than the current page number; it can't handle different odd/even/first headers, nor the number of pages in the document. The second makes the dynamic properties possible, but seems difficult to style. Two new samples `Dompdf_Custom_Headers` (using `editHtmlCallback`) and `Dompdf_Canvas_Headers` (using a new protected method `callPageScript`) illustrate the use of each. None of the samples demonstrates the full capabilities of what can go into a header or footer. I consider that out of scope; enabling the ability is one thing, perfecting it is quite another.
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
use Dompdf\Canvas;
|
||||
use Dompdf\FontMetrics;
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Pdf\Dompdf;
|
||||
|
||||
require __DIR__ . '/../Header.php';
|
||||
|
||||
// Issue 2432 - styles were too large to fit in first Mpdf chunk, causing problems.
|
||||
|
||||
// Override PhpSpreadsheet class.
|
||||
class Dompdf_Canvas_Headers extends Dompdf // phpcs:ignore
|
||||
{
|
||||
protected function callPageScript(\Dompdf\Dompdf $dompdf): void
|
||||
{
|
||||
$dompdf->getCanvas()->page_script(function (
|
||||
int $pageNumber,
|
||||
int $pageCount,
|
||||
Canvas $canvas,
|
||||
FontMetrics $fontMetrics
|
||||
): void {
|
||||
// Get font metrics for dynamic content like page numbers
|
||||
$fontName = 'helvetica'; // note lowercase
|
||||
$fontType = 'bold'; // note lowercase
|
||||
$font = $fontMetrics->getFont($fontName, $fontType) ?? throw new Exception("no font metrics for $fontName $fontType");
|
||||
// Example text placement (adjust coordinates and font as needed)
|
||||
if ($pageNumber === 1) {
|
||||
$header = 'Dompdf First Page';
|
||||
} elseif ($pageNumber % 2 === 0) {
|
||||
$header = 'Dompdf Even Page';
|
||||
} else {
|
||||
$header = 'Dompdf Odd Page';
|
||||
}
|
||||
$text = "$header Page $pageNumber of $pageCount";
|
||||
$canvas->text(40, 20, $text, $font, 12);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$counter = 0;
|
||||
/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */
|
||||
$helper->log('Populate spreadsheet');
|
||||
for ($row = 1; $row < 1001; ++$row) {
|
||||
$sheet->getCell("A$row")->setValue(++$counter);
|
||||
// Add many styles by using slight variations of font color for each.
|
||||
$sheet->getCell("A$row")->getStyle()->getFont()
|
||||
->getColor()->setRgb(sprintf('%06x', $counter));
|
||||
$sheet->getCell("B$row")->setValue(++$counter);
|
||||
$sheet->getCell("C$row")->setValue(++$counter);
|
||||
}
|
||||
|
||||
$helper->log('Write to Dompdf with headers added by page_script');
|
||||
IOFactory::registerWriter('Pdf', Dompdf_Canvas_Headers::class);
|
||||
$helper->write($spreadsheet, __FILE__, ['Pdf']);
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Pdf\Dompdf;
|
||||
|
||||
require __DIR__ . '/../Header.php';
|
||||
|
||||
// Issue 2432 - styles were too large to fit in first Mpdf chunk, causing problems.
|
||||
|
||||
function addHeadersFootersDompdf2000(string $html): string
|
||||
{
|
||||
$headerFooter = <<<EOF
|
||||
<style type='text/css'>
|
||||
header {
|
||||
position: fixed;
|
||||
top: -0.5in; /* Position in top margin area */
|
||||
height: 0.5in;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
footer {
|
||||
position: fixed;
|
||||
bottom: -0.5in; /* Position in bottom margin area */
|
||||
height: 0.5in;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
}
|
||||
.pagenum:before {content: counter(page);}
|
||||
</style>
|
||||
|
||||
EOF;
|
||||
$endhead = '</head>';
|
||||
$html = str_replace($endhead, "$headerFooter$endhead", $html);
|
||||
$bodystring = '<body>';
|
||||
$bodyrepl = <<<EOF
|
||||
<body>
|
||||
<header>Dompdf Fixed header</header>
|
||||
<footer>Page <span class="pagenum"></span></footer>
|
||||
EOF;
|
||||
|
||||
return str_replace($bodystring, $bodyrepl, $html);
|
||||
}
|
||||
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$counter = 0;
|
||||
/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */
|
||||
$helper->log('Populate spreadsheet');
|
||||
for ($row = 1; $row < 1001; ++$row) {
|
||||
$sheet->getCell("A$row")->setValue(++$counter);
|
||||
// Add many styles by using slight variations of font color for each.
|
||||
$sheet->getCell("A$row")->getStyle()->getFont()
|
||||
->getColor()->setRgb(sprintf('%06x', $counter));
|
||||
$sheet->getCell("B$row")->setValue(++$counter);
|
||||
$sheet->getCell("C$row")->setValue(++$counter);
|
||||
}
|
||||
|
||||
$helper->log('Write to Dompdf');
|
||||
IOFactory::registerWriter('Pdf', Dompdf::class);
|
||||
$helper->write(
|
||||
$spreadsheet,
|
||||
__FILE__,
|
||||
['Pdf'],
|
||||
false,
|
||||
function (Dompdf $writer): void {
|
||||
$writer->setEditHtmlCallback('addHeadersFootersDompdf2000');
|
||||
}
|
||||
);
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
@@ -52,14 +52,14 @@ $helper->log('Populate spreadsheet');
|
||||
for ($row = 1; $row < 1001; ++$row) {
|
||||
$sheet->getCell("A$row")->setValue(++$counter);
|
||||
// Add many styles by using slight variations of font color for each.
|
||||
$sheet->getCell("A$row")->getStyle()->getFont()->getColor()->setRgb(sprintf('%06x', $counter));
|
||||
$sheet->getCell("A$row")->getStyle()->getFont()
|
||||
->getColor()->setRgb(sprintf('%06x', $counter));
|
||||
$sheet->getCell("B$row")->setValue(++$counter);
|
||||
$sheet->getCell("C$row")->setValue(++$counter);
|
||||
}
|
||||
|
||||
$helper->log('Write to Mpdf');
|
||||
IOFactory::registerWriter('Pdf', Mpdf::class);
|
||||
$helper->write($spreadsheet, __FILE__, ['Pdf']);
|
||||
$helper->write(
|
||||
$spreadsheet,
|
||||
__FILE__,
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Pdf\TcpdfNoDie;
|
||||
use TCPDF as VendorTcpdf;
|
||||
|
||||
require __DIR__ . '/../Header.php';
|
||||
|
||||
// Override PhpSpreadsheet class.
|
||||
class Tcpdf2 extends TcpdfNoDie // phpcs:ignore
|
||||
{
|
||||
protected bool $writeHeader = true;
|
||||
|
||||
protected bool $writeFooter = true;
|
||||
|
||||
protected function createExternalWriterInstance(string $orientation, string $unit, $paperSize): VendorTcpdf
|
||||
{
|
||||
$this->defines();
|
||||
|
||||
return new Tcpdf2Class($orientation, $unit, $paperSize);
|
||||
}
|
||||
}
|
||||
|
||||
// Override vendor class.
|
||||
class Tcpdf2Class extends VendorTcpdf // phpcs:ignore
|
||||
{
|
||||
// Page header
|
||||
public function Header(): void // phpcs:ignore
|
||||
{
|
||||
// Position at 15 mm from top
|
||||
$this->setY(15);
|
||||
// Set font
|
||||
$this->setFont('helvetica', 'B', 12);
|
||||
// Title
|
||||
$pageNum = $this->getPage();
|
||||
if ($pageNum === 1) {
|
||||
$this->Cell(0, 15, 'Tcpdf first header', 0, 0, 'C', false, '', 0, false, 'M', 'M');
|
||||
} elseif ($pageNum % 2 === 0) {
|
||||
$this->Cell(0, 15, 'Tcpdf even header', 0, 0, 'C', false, '', 0, false, 'M', 'M');
|
||||
} else {
|
||||
$this->Cell(0, 15, 'Tcpdf odd header', 0, 0, 'C', false, '', 0, false, 'M', 'M');
|
||||
}
|
||||
}
|
||||
|
||||
// Page footer
|
||||
public function Footer(): void // phpcs:ignore
|
||||
{
|
||||
// Position at 15 mm from bottom
|
||||
$this->setY(-15);
|
||||
// Set font
|
||||
$this->setFont('helvetica', 'I', 8);
|
||||
// Page number
|
||||
$this->Cell(0, 10, 'Page ' . $this->getAliasNumPage() . '/' . $this->getAliasNbPages(), 0, 0, 'C', false, '', 0, false, 'T', 'M');
|
||||
}
|
||||
}
|
||||
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$counter = 0;
|
||||
/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */
|
||||
$helper->log('Populate spreadsheet');
|
||||
for ($row = 1; $row < 1001; ++$row) {
|
||||
$sheet->getCell("A$row")->setValue(++$counter);
|
||||
// Add many styles by using slight variations of font color for each.
|
||||
$sheet->getCell("A$row")->getStyle()->getFont()
|
||||
->getColor()->setRgb(sprintf('%06x', $counter));
|
||||
$sheet->getCell("B$row")->setValue(++$counter);
|
||||
$sheet->getCell("C$row")->setValue(++$counter);
|
||||
}
|
||||
|
||||
$helper->log('Write to Tcpdf with headers and footers');
|
||||
IOFactory::registerWriter('Pdf', Tcpdf2::class);
|
||||
$helper->write($spreadsheet, __FILE__, ['Pdf']);
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
@@ -49,10 +49,15 @@ class Dompdf extends Pdf
|
||||
|
||||
$pdf->loadHtml($this->generateHTMLAll());
|
||||
$pdf->render();
|
||||
$this->callPageScript($pdf);
|
||||
|
||||
// Write to file
|
||||
fwrite($fileHandle, $pdf->output());
|
||||
|
||||
parent::restoreStateAfterSave();
|
||||
}
|
||||
|
||||
protected function callPageScript(\Dompdf\Dompdf $pdf): void
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,10 @@ use PhpOffice\PhpSpreadsheet\Writer\Pdf;
|
||||
|
||||
class Tcpdf extends Pdf
|
||||
{
|
||||
protected bool $writeHeader = false;
|
||||
|
||||
protected bool $writeFooter = false;
|
||||
|
||||
/**
|
||||
* Create a new PDF Writer instance.
|
||||
*
|
||||
@@ -66,8 +70,8 @@ class Tcpdf extends Pdf
|
||||
$pdf->SetMargins($printMargins->getLeft() * 72, $printMargins->getTop() * 72, $printMargins->getRight() * 72);
|
||||
$pdf->SetAutoPageBreak(true, $printMargins->getBottom() * 72);
|
||||
|
||||
$pdf->setPrintHeader(false);
|
||||
$pdf->setPrintFooter(false);
|
||||
$pdf->setPrintHeader($this->writeHeader);
|
||||
$pdf->setPrintFooter($this->writeFooter);
|
||||
|
||||
$pdf->AddPage();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user