Files
baseProject/vendor/tecnickcom/tc-lib-pdf/examples/E054_page_groups_numbering.php
T

289 lines
9.7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* E054_page_groups_numbering.php
*
* Demonstrates publishing-style mixed page numbering: Roman-numeral front matter
* followed by Arabic body pages, with a generated table of contents and
* multi-level bookmarks as cross-reference anchors.
*
* What this demonstrates:
* - setBookmark(): hierarchical outline entries with styles and colors.
* - setNamedDestination(): stable jump targets independent of page order.
* - addTOC(): auto-generated TOC page linked back to bookmarked sections.
* - setDisplayMode(): open with outlines panel showing.
* - Logical vs physical page numbering: inject visible Roman/Arabic labels
* via page header content while the PDF page object index remains 0-based.
*
* @since 2026-05-01
* @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.
require __DIR__ . '/../vendor/autoload.php';
define('K_PATH_FONTS', (string) realpath(__DIR__ . '/../vendor/tecnickcom/tc-lib-pdf-font/target/fonts'));
$pdf = new \Com\Tecnick\Pdf\Tcpdf(
unit: \Com\Tecnick\Pdf\Page\Unit::Millimeter,
isunicode: true,
subsetfont: false,
compress: true,
mode: \Com\Tecnick\Pdf\PdfConformance::None,
objEncrypt: null,
);
$pdf->setCreator('tc-lib-pdf');
$pdf->setAuthor('Nicola Asuni');
$pdf->setSubject('tc-lib-pdf example: 054');
$pdf->setTitle('Page Groups & Mixed Numbering');
$pdf->setKeywords('TCPDF tc-lib-pdf page groups numbering roman arabic TOC bookmarks');
$pdf->setPDFFilename('054_page_groups_numbering.pdf');
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
// Open with the outline panel visible and full-width zoom.
$pdf->setDisplayMode(
zoom: \Com\Tecnick\Pdf\DisplayZoom::FullWidth,
layout: \Com\Tecnick\Pdf\Page\PageLayout::SinglePage,
mode: \Com\Tecnick\Pdf\Page\PageDisplayMode::UseOutlines,
);
$pdf->enableDefaultPageContent();
// ----------
// Fonts
$titleFont = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 16);
$chFont = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 14);
$bodyFont = $pdf->font->insert($pdf->pon, 'helvetica', '', 12);
$smallFont = $pdf->font->insert($pdf->pon, 'helvetica', '', 10);
// ----------
// Helpers
/** Convert an integer 13999 to an uppercase Roman numeral. */
$toRoman = static function (int $n): string {
$map = [
1000 => 'M',
900 => 'CM',
500 => 'D',
400 => 'CD',
100 => 'C',
90 => 'XC',
50 => 'L',
40 => 'XL',
10 => 'X',
9 => 'IX',
5 => 'V',
4 => 'IV',
1 => 'I',
];
$result = '';
foreach ($map as $val => $sym) {
while ($n >= $val) {
$result .= $sym;
$n -= $val;
}
}
return $result;
};
/**
* Render a page number footer for the current page.
* @param \Com\Tecnick\Pdf\Tcpdf $pdf
* @param int $pid Page PID
* @param string $label Visible page number string (e.g. "I", "1")
* @param array{out: string} $font Font descriptor
*/
$addPageFooter = static function (\Com\Tecnick\Pdf\Tcpdf $pdf, int $pid, string $label, array $font): void {
$pdf->page->addContent($font['out'], $pid);
$pdf->page->addContent(
$pdf->getTextCell(
txt: '— ' . $label . ' —',
posx: 0,
posy: 282,
width: 210,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
),
$pid,
);
};
// ===| FRONT MATTER |========================================================
// Pages i, ii (TOC placeholder + Preface)
// These pages use Roman numerals in the footer.
// ----- Page i: TOC placeholder -----
$tocPage = $pdf->addPage(['format' => 'A4']);
$pdf->setBookmark(
name: 'Table of Contents',
link: '',
level: 0,
page: $tocPage['pid'],
posx: 0,
posy: 0,
fstyle: 'B',
color: 'gray',
);
$addPageFooter($pdf, $tocPage['pid'], $toRoman(1), $smallFont);
// Reserve header text for this page
$pdf->page->addContent($titleFont['out'], $tocPage['pid']);
$pdf->page->addContent(
$pdf->getTextCell(
txt: 'TABLE OF CONTENTS',
posx: 15,
posy: 20,
width: 180,
height: 0,
offset: 0,
linespace: 1,
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
),
$tocPage['pid'],
);
// The actual TOC content is injected by addTOC() at the end of the script.
// ----- Page ii: Preface -----
$prefacePage = $pdf->addPage(['format' => 'A4']);
$pdf->setBookmark(
name: 'Preface',
link: '',
level: 0,
page: $prefacePage['pid'],
posx: 0,
posy: 0,
fstyle: '',
color: 'gray',
);
$addPageFooter($pdf, $prefacePage['pid'], $toRoman(2), $smallFont);
$prefaceHtml = '<h2 style="font-family: helvetica; text-align: center;">Preface</h2>
<p style="font-family: helvetica; font-size: 10pt;">
This document demonstrates mixed page numbering conventions used in publishing workflows.
Front matter (title, preface, table of contents) carries Roman numerals (i, ii, iii …),
while the body text uses Arabic numerals (1, 2, 3 …).
</p>
<p style="font-family: helvetica; font-size: 10pt;">
In tc-lib-pdf, both logical page numbers displayed in footers and structural bookmarks are
controlled independently. The PDF page object index is always zero-based; the visible label
is rendered as regular text content. Named destinations remain stable regardless of the
visible numbering scheme.
</p>';
$pdf->addHTMLCell(html: $prefaceHtml, posx: 15, posy: 30, width: 175, height: $prefacePage['pid']);
// ===| BODY |================================================================
// Chapters 13, Arabic page numbers 13
/** @var array<int, array{title: string, sub: list<string>, color: string, bodyText: string}> $chapters */
$chapters = [
[
'title' => 'Chapter 1 — Foundations',
'sub' => ['1.1 History', '1.2 Key Concepts'],
'color' => '#1a3c6e',
'bodyText' => 'This chapter introduces the foundational ideas. Page number 1 (Arabic) appears in the footer. The bookmark entry for this chapter points to this page via a named destination.',
],
[
'title' => 'Chapter 2 — Methods',
'sub' => ['2.1 Approach A', '2.2 Approach B'],
'color' => '#1a6e3c',
'bodyText' => 'This chapter details the methodology. Page number 2 (Arabic) appears below. Bookmark level-1 entries for subsections use the same page pid with a vertical offset.',
],
[
'title' => 'Chapter 3 — Results',
'sub' => ['3.1 Findings', '3.2 Conclusions'],
'color' => '#6e1a1a',
'bodyText' => 'Final chapter with results and conclusions. Page number 3 (Arabic). The TOC on page i links back to all three chapter bookmarks.',
],
];
foreach ($chapters as $idx => $ch) {
$arabicNum = $idx + 1;
$chPage = $pdf->addPage(['format' => 'A4']);
// Register named destination so TOC links are stable.
$destName = $pdf->setNamedDestination(name: 'chapter-' . $arabicNum, page: $chPage['pid'], posx: 0, posy: 0);
// Top-level bookmark (bold, chapter color)
$pdf->setBookmark(
name: $ch['title'],
link: $destName,
level: 0,
page: $chPage['pid'],
posx: 0,
posy: 0,
fstyle: 'B',
color: $ch['color'],
);
// Sub-section bookmarks (indented, normal weight)
foreach ($ch['sub'] as $si => $subTitle) {
$subY = 70.0 + ($si * 20.0);
$pdf->setBookmark(
name: $subTitle,
link: '',
level: 1,
page: $chPage['pid'],
posx: 0,
posy: $subY,
fstyle: '',
color: $ch['color'],
);
}
// Page footer with Arabic numeral
$addPageFooter($pdf, $chPage['pid'], (string) $arabicNum, $smallFont);
// Chapter content
$chHtml =
'<h2 style="font-family: helvetica; color: '
. $ch['color']
. ';">'
. htmlspecialchars($ch['title'])
. '</h2>
<p style="font-family: helvetica; font-size: 10pt;">'
. htmlspecialchars($ch['bodyText'])
. '</p>';
foreach ($ch['sub'] as $subTitle) {
$chHtml .=
'<h3 style="font-family: helvetica; color: '
. $ch['color']
. ';">'
. htmlspecialchars($subTitle)
. '</h3>
<p style="font-family: helvetica; font-size: 10pt;">Section content would go here.</p>';
}
$pdf->addHTMLCell(html: $chHtml, posx: 15, posy: 20, width: 175, height: $chPage['pid']);
}
// ===| GENERATE TOC |========================================================
// addTOC() injects the outline entries onto the reserved TOC page (page i),
// starting at y=35 (below the "TABLE OF CONTENTS" heading).
// It uses the current font for rendering — activate bodyFont first.
$pdf->page->addContent($bodyFont['out'], $tocPage['pid']);
$pdf->addTOC(page: $tocPage['pid'], posx: 15, posy: 35, width: 175);
// ----------
$rawpdf = $pdf->getOutPDFString();
$pdf->renderPDF(rawpdf: $rawpdf);