322 lines
11 KiB
PHP
322 lines
11 KiB
PHP
|
|
<?php
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* E005_header_footer.php
|
|||
|
|
*
|
|||
|
|
* @since 2026-04-19
|
|||
|
|
* @category Library
|
|||
|
|
* @package Pdf
|
|||
|
|
* @author Nicola Asuni <info@tecnick.com>
|
|||
|
|
* @copyright 2002-2026 Nicola Asuni - Tecnick.com LTD
|
|||
|
|
* @license https://www.gnu.org/copyleft/lesser.html GNU-LGPL v3 (see LICENSE)
|
|||
|
|
* @link https://github.com/tecnickcom/tc-lib-pdf
|
|||
|
|
*
|
|||
|
|
* This file is part of tc-lib-pdf software library.
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// NOTE: local file reads (images, fonts, attachments) are restricted to an allowlist of
|
|||
|
|
// trusted paths that covers this package tree, so run the examples in place. To read assets
|
|||
|
|
// from other locations, list them in the 'allowedPaths' entry of the fileOptions constructor
|
|||
|
|
// parameter (see E047_remote_resources_security.php).
|
|||
|
|
|
|||
|
|
// NOTE: run make fonts in the project root to generate the dependencies and example fonts.
|
|||
|
|
|
|||
|
|
// autoloader when using Composer
|
|||
|
|
require __DIR__ . '/../vendor/autoload.php';
|
|||
|
|
|
|||
|
|
// define fonts directory
|
|||
|
|
\define('K_PATH_FONTS', \realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
|
|||
|
|
|
|||
|
|
// autoloader when using RPM or DEB package installation
|
|||
|
|
//require ('/usr/share/php/Com/Tecnick/Pdf/autoload.php');
|
|||
|
|
|
|||
|
|
// ----------
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Custom PDF class with a repeating page header and footer.
|
|||
|
|
*
|
|||
|
|
* Extends Tcpdf and overrides defaultPageContent() so that every
|
|||
|
|
* new page automatically receives a branded header and a numbered footer.
|
|||
|
|
* The mechanism is activated by calling enableDefaultPageContent(true).
|
|||
|
|
*/
|
|||
|
|
class PdfWithHeaderFooter extends \Com\Tecnick\Pdf\Tcpdf
|
|||
|
|
{
|
|||
|
|
/** Horizontal margin used by the header and footer bands (mm). */
|
|||
|
|
private const HF_MARGIN = 10.0;
|
|||
|
|
|
|||
|
|
/** Height of the header band (mm). */
|
|||
|
|
private const HEADER_H = 12.0;
|
|||
|
|
|
|||
|
|
/** Height of the footer band (mm). */
|
|||
|
|
private const FOOTER_H = 10.0;
|
|||
|
|
|
|||
|
|
/** Document title shown left-aligned in the header. */
|
|||
|
|
private string $headerTitle = '';
|
|||
|
|
|
|||
|
|
/** Subtitle / date shown right-aligned in the header. */
|
|||
|
|
private string $headerSubtitle = '';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Set the text displayed in the page header.
|
|||
|
|
*
|
|||
|
|
* @param string $title Left-aligned title.
|
|||
|
|
* @param string $subtitle Right-aligned subtitle (e.g. date or company name).
|
|||
|
|
*/
|
|||
|
|
public function setHeaderText(string $title, string $subtitle): void
|
|||
|
|
{
|
|||
|
|
$this->headerTitle = $title;
|
|||
|
|
$this->headerSubtitle = $subtitle;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Generates the repeating header and footer for every page.
|
|||
|
|
*
|
|||
|
|
* This method is called automatically by setPageContext() whenever a new
|
|||
|
|
* page is added, provided enableDefaultPageContent(true) has been called.
|
|||
|
|
*
|
|||
|
|
* @param int $pid Page index (0-based).
|
|||
|
|
*
|
|||
|
|
* @return string Raw PDF stream prepended to the page content.
|
|||
|
|
*/
|
|||
|
|
public function defaultPageContent(int $pid = -1): string
|
|||
|
|
{
|
|||
|
|
if ($pid < 0) {
|
|||
|
|
$pid = $this->page->getPageId();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Insert the default font once and cache it for subsequent pages.
|
|||
|
|
if ($this->defaultfont === null) {
|
|||
|
|
$this->defaultfont = $this->font->insert($this->pon, 'helvetica', '', 9);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$page = $this->page->getPage($pid);
|
|||
|
|
$pw = $page['width'];
|
|||
|
|
$ph = $page['height'];
|
|||
|
|
|
|||
|
|
$lm = self::HF_MARGIN; // left margin x
|
|||
|
|
$rm = $pw - self::HF_MARGIN; // right margin x
|
|||
|
|
$tw = $pw - (2 * self::HF_MARGIN); // usable band width
|
|||
|
|
|
|||
|
|
$lineStyle = [
|
|||
|
|
'lineWidth' => 0.25,
|
|||
|
|
'lineCap' => 'butt',
|
|||
|
|
'lineJoin' => 'miter',
|
|||
|
|
'dashArray' => [],
|
|||
|
|
'dashPhase' => 0,
|
|||
|
|
'lineColor' => '#555555',
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
$out = '';
|
|||
|
|
|
|||
|
|
// ---- HEADER ------------------------------------------------
|
|||
|
|
|
|||
|
|
$headerY = self::HF_MARGIN;
|
|||
|
|
$headerOut = $this->graph->getStartTransform();
|
|||
|
|
$headerOut .= $this->defaultfont['out'];
|
|||
|
|
|
|||
|
|
// Title – left-aligned, bold
|
|||
|
|
if ($this->headerTitle !== '') {
|
|||
|
|
$bfontBold = $this->font->insert($this->pon, 'helvetica', 'B', 10);
|
|||
|
|
$headerOut .= $bfontBold['out'];
|
|||
|
|
$headerOut .= $this->color->getPdfColor('#1a3a6b');
|
|||
|
|
$headerOut .= $this->getTextCell(
|
|||
|
|
txt: $this->headerTitle,
|
|||
|
|
posx: $lm,
|
|||
|
|
posy: $headerY,
|
|||
|
|
width: $tw * 0.65,
|
|||
|
|
height: self::HEADER_H,
|
|||
|
|
offset: 0,
|
|||
|
|
linespace: 0,
|
|||
|
|
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
|
|||
|
|
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
|
|||
|
|
);
|
|||
|
|
$headerOut .= $this->defaultfont['out'];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Subtitle – right-aligned
|
|||
|
|
if ($this->headerSubtitle !== '') {
|
|||
|
|
$headerOut .= $this->color->getPdfColor('#555555');
|
|||
|
|
$headerOut .= $this->getTextCell(
|
|||
|
|
txt: $this->headerSubtitle,
|
|||
|
|
posx: $lm + ($tw * 0.65),
|
|||
|
|
posy: $headerY,
|
|||
|
|
width: $tw * 0.35,
|
|||
|
|
height: self::HEADER_H,
|
|||
|
|
offset: 0,
|
|||
|
|
linespace: 0,
|
|||
|
|
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
|
|||
|
|
halign: \Com\Tecnick\Pdf\TextHAlign::Right,
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Separator line below the header
|
|||
|
|
$headerLineY = $headerY + self::HEADER_H;
|
|||
|
|
$headerOut .= $this->graph->getLine($lm, $headerLineY, $rm, $headerLineY, $lineStyle);
|
|||
|
|
$headerOut .= $this->graph->getStopTransform();
|
|||
|
|
|
|||
|
|
$out .= $this->beginArtifact('Pagination', 'Header');
|
|||
|
|
$out .= $headerOut;
|
|||
|
|
$out .= $this->endArtifact();
|
|||
|
|
|
|||
|
|
// ---- FOOTER ------------------------------------------------
|
|||
|
|
|
|||
|
|
$footerLineY = $ph - self::HF_MARGIN - self::FOOTER_H;
|
|||
|
|
$footerOut = $this->graph->getStartTransform();
|
|||
|
|
$footerOut .= $this->defaultfont['out'];
|
|||
|
|
|
|||
|
|
// Separator line above the footer
|
|||
|
|
$footerOut .= $this->graph->getLine($lm, $footerLineY, $rm, $footerLineY, $lineStyle);
|
|||
|
|
|
|||
|
|
// Page number – centred
|
|||
|
|
$footerOut .= $this->color->getPdfColor('#555555');
|
|||
|
|
$footerOut .= $this->getTextCell(
|
|||
|
|
txt: 'Page ' . ($pid + 1),
|
|||
|
|
posx: $lm,
|
|||
|
|
posy: $footerLineY,
|
|||
|
|
width: $tw,
|
|||
|
|
height: self::FOOTER_H,
|
|||
|
|
offset: 0,
|
|||
|
|
linespace: 0,
|
|||
|
|
valign: \Com\Tecnick\Pdf\TextVAlign::Center,
|
|||
|
|
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
|
|||
|
|
);
|
|||
|
|
$footerOut .= $this->graph->getStopTransform();
|
|||
|
|
|
|||
|
|
$out .= $this->beginArtifact('Pagination', 'Footer');
|
|||
|
|
$out .= $footerOut;
|
|||
|
|
$out .= $this->endArtifact();
|
|||
|
|
|
|||
|
|
return $out;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ----------
|
|||
|
|
|
|||
|
|
// main PDF object using the custom subclass
|
|||
|
|
$pdf = new PdfWithHeaderFooter(
|
|||
|
|
'mm', // string $unit = 'mm',
|
|||
|
|
true, // bool $isunicode = true,
|
|||
|
|
false, // bool $subsetfont = false,
|
|||
|
|
true, // bool $compress = true,
|
|||
|
|
'pdfua1', // string $mode = 'pdfua1',
|
|||
|
|
null, // ?ObjEncrypt $objEncrypt = null,
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// ----------
|
|||
|
|
|
|||
|
|
$pdf->setCreator('tc-lib-pdf');
|
|||
|
|
$pdf->setAuthor('Nicola Asuni');
|
|||
|
|
$pdf->setSubject('tc-lib-pdf example: 005');
|
|||
|
|
$pdf->setTitle('Header & Footer Example');
|
|||
|
|
$pdf->setKeywords('TCPDF tc-lib-pdf example header footer');
|
|||
|
|
$pdf->setPDFFilename('005_header_footer.pdf');
|
|||
|
|
|
|||
|
|
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
|
|||
|
|
$pdf->setLanguageArray(['a_meta_language' => 'en-US']);
|
|||
|
|
|
|||
|
|
// Set the text that will appear in the header on every page.
|
|||
|
|
$pdf->setHeaderText('My Document Title', \date('Y-m-d'));
|
|||
|
|
|
|||
|
|
// Enable automatic header/footer: calls defaultPageContent() for every new page.
|
|||
|
|
$pdf->enableDefaultPageContent();
|
|||
|
|
|
|||
|
|
// ----------
|
|||
|
|
// Insert fonts
|
|||
|
|
|
|||
|
|
$bfont = $pdf->font->insert($pdf->pon, 'helvetica', '', 11);
|
|||
|
|
$bfontB = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 13);
|
|||
|
|
|
|||
|
|
// ----------
|
|||
|
|
// Add first page
|
|||
|
|
|
|||
|
|
$page01 = $pdf->addPage();
|
|||
|
|
$pdf->setBookmark(name: 'Page 1', link: '', level: 0, page: -1, posx: 0, posy: 0, fstyle: 'B', color: 'blue');
|
|||
|
|
|
|||
|
|
// Page content starts below the header band (HF_MARGIN + HEADER_H + separator ≈ 25 mm).
|
|||
|
|
$contentY = 28.0;
|
|||
|
|
|
|||
|
|
$pdf->page->addContent($bfontB['out']);
|
|||
|
|
|
|||
|
|
$title1 = $pdf->getTextCell(
|
|||
|
|
txt: 'Page 1 — Custom Repeating Header & Footer',
|
|||
|
|
posx: 10,
|
|||
|
|
posy: $contentY,
|
|||
|
|
width: 190,
|
|||
|
|
height: 0,
|
|||
|
|
offset: 0,
|
|||
|
|
linespace: 0,
|
|||
|
|
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
|
|||
|
|
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
|
|||
|
|
);
|
|||
|
|
$pdf->page->addContent($title1);
|
|||
|
|
|
|||
|
|
$pdf->page->addContent($bfont['out']);
|
|||
|
|
|
|||
|
|
$body1 = 'This is the first page of a document that demonstrates how to add a custom page header and footer that automatically repeats on every new page using tc-lib-pdf.
|
|||
|
|
|
|||
|
|
The header displays the document title (left) and the date (right), separated from the page content by a thin horizontal rule. The footer shows the page number centred at the bottom, also separated by a horizontal rule.
|
|||
|
|
|
|||
|
|
This is achieved by subclassing \Com\Tecnick\Pdf\Tcpdf and overriding the public defaultPageContent() method, then activating the mechanism once with enableDefaultPageContent(true) before adding any pages.
|
|||
|
|
|
|||
|
|
Because setPageContext() calls defaultPageContent() automatically every time addPage() is used, the header and footer appear on every page without any additional code in the page-building section.';
|
|||
|
|
|
|||
|
|
$txt1 = $pdf->getTextCell(
|
|||
|
|
txt: $body1,
|
|||
|
|
posx: 10,
|
|||
|
|
posy: $contentY + 10,
|
|||
|
|
width: 190,
|
|||
|
|
height: 0,
|
|||
|
|
offset: 15,
|
|||
|
|
linespace: 2,
|
|||
|
|
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
|
|||
|
|
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
|
|||
|
|
);
|
|||
|
|
$pdf->page->addContent($txt1);
|
|||
|
|
|
|||
|
|
// ----------
|
|||
|
|
// Add second page
|
|||
|
|
|
|||
|
|
$page02 = $pdf->addPage();
|
|||
|
|
$pdf->setBookmark(name: 'Page 2', link: '', level: 0, page: -1, posx: 0, posy: 0, fstyle: 'B', color: 'green');
|
|||
|
|
|
|||
|
|
$pdf->page->addContent($bfontB['out']);
|
|||
|
|
|
|||
|
|
$title2 = $pdf->getTextCell(
|
|||
|
|
txt: 'Page 2 — Continued Content',
|
|||
|
|
posx: 10,
|
|||
|
|
posy: $contentY,
|
|||
|
|
width: 190,
|
|||
|
|
height: 0,
|
|||
|
|
offset: 0,
|
|||
|
|
linespace: 0,
|
|||
|
|
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
|
|||
|
|
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
|
|||
|
|
);
|
|||
|
|
$pdf->page->addContent($title2);
|
|||
|
|
|
|||
|
|
$pdf->page->addContent($bfont['out']);
|
|||
|
|
|
|||
|
|
$body2 = 'This is the second page. The header and footer are present here too without any manual intervention — the overridden defaultPageContent() method takes care of them automatically.
|
|||
|
|
|
|||
|
|
Any further pages added with addPage() would also receive the same header and footer, making the approach suitable for multi-page reports, invoices, or any document type that requires consistent page decoration.';
|
|||
|
|
|
|||
|
|
$txt2 = $pdf->getTextCell(
|
|||
|
|
txt: $body2,
|
|||
|
|
posx: 10,
|
|||
|
|
posy: $contentY + 10,
|
|||
|
|
width: 190,
|
|||
|
|
height: 0,
|
|||
|
|
offset: 15,
|
|||
|
|
linespace: 2,
|
|||
|
|
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
|
|||
|
|
halign: \Com\Tecnick\Pdf\TextHAlign::Justify,
|
|||
|
|
);
|
|||
|
|
$pdf->page->addContent($txt2);
|
|||
|
|
|
|||
|
|
// ----------
|
|||
|
|
// Output the PDF
|
|||
|
|
|
|||
|
|
$rawpdf = $pdf->getOutPDFString();
|
|||
|
|
|
|||
|
|
$pdf->renderPDF(rawpdf: $rawpdf);
|