apply($doc); $doc->addPage(); $template->masthead($doc); // The shared nine brand faces are painted across these two pages — the // specimen is the one sanctioned caller of the eager probe. Noto Sans TC is // specimen-only, so register its directory here and resolve it lazily at the // exact point where the Traditional Chinese sample is painted. $cjkFontFamily = 'NotoSansTC'; $cjkFontDirectory = \dirname(__DIR__) . '/assets/fonts/notosanstc'; $cjkFontPath = $cjkFontDirectory . '/NotoSansTC-Regular.ttf'; if (!\is_file($cjkFontPath)) { throw new RuntimeException("Vendored Traditional Chinese font missing: {$cjkFontPath}"); } $doc->addFontDirectory($cjkFontDirectory); Typography::probeAllFaces($doc); $css = <<<'CSS' CSS; // ── Section 1: Inter weight & style grid ──────────────────────────────── $weightGrid = [ ['label' => 'Regular', 'weight' => 'normal', 'style' => 'normal', 'text' => 'The quick brown fox jumps over the lazy dog.'], ['label' => 'Bold', 'weight' => 'bold', 'style' => 'normal', 'text' => 'Pack my box with five dozen liquor jugs.'], ['label' => 'Italic', 'weight' => 'normal', 'style' => 'italic', 'text' => 'How vexingly quick daft zebras jump!'], ['label' => 'Bold Italic', 'weight' => 'bold', 'style' => 'italic', 'text' => 'Waltz, bad nymph, for quick jigs vex.'], ]; $weightRows = ''; foreach ($weightGrid as $row) { $weightRows .= '' . '' . \htmlspecialchars($row['label'], ENT_QUOTES) . '' . '' . \htmlspecialchars($row['text'], ENT_QUOTES) . '' . ''; } $section1 = <<1. Weight & Style — Inter

{$weightRows}
HTML; $doc->writeHtml(SampleTemplate::sharedCss() . $css . $section1); // ── Section 2: Barlow display weights (raw API — see engine-gotcha note above) + JetBrains Mono ── $doc->writeHtml( SampleTemplate::sharedCss() . $css . '

2. Display & Monospace — Barlow & JetBrains Mono

', ); $barlowRows = [ ['label' => 'Regular', 'family' => 'Barlow', 'style' => '', 'text' => 'Precision typesetting, engineered.'], ['label' => 'Medium', 'family' => 'Barlow-Medium', 'style' => '', 'text' => 'Precision typesetting, engineered.'], ['label' => 'Bold', 'family' => 'Barlow', 'style' => 'B', 'text' => 'Precision typesetting, engineered.'], ]; // Label + specimen columns (22% / 78%, mirroring table.np-specimen's // td.np-specimen-label split) so each weight is named, not merely implied // by visual heft. A hairline under every row but the last mirrors the // section-1 HTML specimen table's row rule. $margins = $doc->getMargins(); $labelWidth = ($doc->getPageWidth() - $margins->left - $margins->right) * 0.22; $doc->setDrawColor(...PrintPalette::rgb(PrintPalette::HAIRLINE)); $doc->setLineWidth(0.4); foreach ($barlowRows as $i => $row) { $border = $i === \count($barlowRows) - 1 ? false : 'B'; $doc->setFont(Typography::FAMILY_TEXT, '', 8.0); $doc->setTextColor(...PrintPalette::rgb(PrintPalette::MUTED)); $doc->cell($labelWidth, 16.0, $row['label'], border: $border, newLine: false); $doc->setFont($row['family'], $row['style'], 13.0); $doc->setTextColor(...PrintPalette::rgb(PrintPalette::INK)); $doc->cell(0, 16.0, $row['text'], border: $border, newLine: true); } $doc->ln(4.0); $doc->setFont(Typography::FAMILY_TEXT, '', Typography::SIZE_BODY); $monoBlock = <<<'HTML'

function render(): PdfBytes { return $doc->save(); }

HTML; $doc->writeHtml(SampleTemplate::sharedCss() . $css . $monoBlock); // ── Section 3: Unicode script coverage ─────────────────────────────────── $section3 = <<3. Unicode Script Coverage

LATIN EXTENDED — GERMAN PANGRAM

Zwölf Boxkämpfer jagen Viktor quer über den großen Sylter Deich.

GREEK — PANGRAM

Ξεσκεπάζω την ψυχοφθόρα βδελυγμία.

CYRILLIC — RUSSIAN PANGRAM

Съешь же ещё этих мягких французских булок, да выпей чаю.

TRADITIONAL CHINESE — TAIWAN

HTML; $doc->writeHtml(SampleTemplate::sharedCss() . $css . $section3); $traditionalChineseSample = '繁體中文排版測試,涵蓋常用字與臺灣慣用詞。'; $doc->setFont($cjkFontFamily, '', 11.0); if (!$doc->fontMetrics()->isFontRegistered(Typography::fontKey($cjkFontFamily))) { throw new RuntimeException('Vendored Traditional Chinese font failed to resolve via setFont().'); } $doc->setTextColor(...PrintPalette::rgb(PrintPalette::INK)); $doc->cell(0, 15.0, $traditionalChineseSample, newLine: true); $doc->setFont(Typography::FAMILY_TEXT, '', Typography::SIZE_BODY); // ── Section 4: Type scale ladder ───────────────────────────────────────── $scale = [ ['pt' => Typography::SIZE_TITLE, 'role' => 'Title — Barlow Bold', 'family' => 'Barlow', 'weight' => 'bold'], ['pt' => Typography::SIZE_SECTION, 'role' => 'Section — Barlow Bold', 'family' => 'Barlow', 'weight' => 'bold'], ['pt' => Typography::SIZE_SUBSECTION, 'role' => 'Subsection — Barlow Bold', 'family' => 'Barlow', 'weight' => 'bold'], ['pt' => Typography::SIZE_BODY, 'role' => 'Body — Inter Regular, 1.45 leading', 'family' => 'Inter', 'weight' => 'normal'], ['pt' => Typography::SIZE_TABLE, 'role' => 'Table — Inter Regular', 'family' => 'Inter', 'weight' => 'normal'], ['pt' => Typography::SIZE_MONO, 'role' => 'Mono — JetBrains Mono', 'family' => 'JetBrainsMono', 'weight' => 'normal'], ['pt' => Typography::SIZE_FOOTER, 'role' => 'Footer — Inter Regular', 'family' => 'Inter', 'weight' => 'normal'], ]; $scaleRows = ''; foreach ($scale as $step) { $ptLabel = \rtrim(\rtrim(\sprintf('%.1F', $step['pt']), '0'), '.'); $scaleRows .= '' . '' . $ptLabel . ' pt' . '' . \htmlspecialchars($step['role'], ENT_QUOTES) . '' . 'Aa Bb Cc 0123' . ''; } $section4 = <<4. Type Scale

{$scaleRows}
HTML; $doc->writeHtml(SampleTemplate::sharedCss() . $css . $section4); // ═══════════════════════════════════════════════════════════════════════ // Page 2 — measure, alignment, tracking // ═══════════════════════════════════════════════════════════════════════ $doc->addPage(); $measureText = 'A comfortable measure keeps the reader’s eye anchored on the line. ' . 'Narrower columns break more often and slow the cadence; wider columns flow ' . 'faster but demand more from the eye travelling back to the next line start.'; $measureCols = [ ['label' => 'Full measure — 100%', 'width' => '100%'], ['label' => '120 mm column — 66.7%', 'width' => '66.7%'], ['label' => '90 mm column — 50%', 'width' => '50%'], ]; $measureBlocks = ''; foreach ($measureCols as $col) { $measureBlocks .= '

' . \htmlspecialchars($col['label'], ENT_QUOTES) . '

' . '' . ($col['width'] === '100%' ? '' : '') . '
' . $measureText . '
'; } $section5 = <<5. Measure & Line Length

{$measureBlocks} HTML; $doc->writeHtml(SampleTemplate::sharedCss() . $css . $section5); // ── Section 6: alignment quartet ───────────────────────────────────────── $alignText = 'NextPDF composes documents with the same care a typographer brings to ' . 'print, while developers keep the simplicity of a single, predictable API.'; $aligns = [ ['label' => 'LEFT', 'align' => 'left'], ['label' => 'CENTER', 'align' => 'center'], ['label' => 'RIGHT', 'align' => 'right'], ['label' => 'JUSTIFY', 'align' => 'justify'], ]; $alignRows = ''; foreach ($aligns as $i => $a) { $isLast = $i === \count($aligns) - 1; $alignRows .= '

' . $a['label'] . '

' . '

' . $alignText . '

'; } $section6 = <<6. Alignment Quartet

{$alignRows} HTML; $doc->writeHtml(SampleTemplate::sharedCss() . $css . $section6); // ── Section 7: letter-spacing & case ───────────────────────────────────── $caseRows = [ ['label' => 'Uppercase, wide tracking', 'transform' => 'uppercase', 'spacing' => '1.5pt'], ['label' => 'Capitalize, normal tracking', 'transform' => 'capitalize', 'spacing' => '0pt'], ['label' => 'Lowercase, tight tracking', 'transform' => 'lowercase', 'spacing' => '-0.3pt'], ]; $caseHtmlRows = ''; foreach ($caseRows as $row) { $caseHtmlRows .= '' . '' . \htmlspecialchars($row['label'], ENT_QUOTES) . '' . 'NextPDF Type System' . ''; } $section7 = <<7. Letter-Spacing & Case

{$caseHtmlRows}
HTML; $doc->writeHtml(SampleTemplate::sharedCss() . $css . $section7); // ── Section 8: tracking on kerning-sensitive pairs ─────────────────────── $kernText = 'AVIATION WATER TOTAL WAVY TASTE'; $section8 = <<8. Kerning-Sensitive Pairs — Tracking Off / On

Character-spacing (Tc) control applied to letter pairs — AV, WA, TO, TA — that read loosest at zero tracking. This is manual tracking, not automatic glyph-pair kerning.

0 pt tracking (off) {$kernText}
−0.4 pt tracking (on) {$kernText}
HTML; $doc->writeHtml(SampleTemplate::sharedCss() . $css . $section8); $output = \getenv('NEXTPDF_SAMPLE_OUTPUT') ?: __DIR__ . '/../output/typography.pdf'; $doc->save($output); echo "Created: {$output}\n";