338 lines
14 KiB
PHP
338 lines
14 KiB
PHP
<?php
|
|
|
|
/**
|
|
* E064_custom_hyphenation_dictionary.php
|
|
*
|
|
* @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.
|
|
|
|
// 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'));
|
|
|
|
/**
|
|
* Demonstrate external TeX hyphenation pattern loading via
|
|
* loadTexHyphenPatterns() + setTexHyphenPatterns().
|
|
*
|
|
* Background
|
|
* ──────────
|
|
* tc-lib-pdf inherits hyphenation support from the tc-lib-unicode library.
|
|
* By default NO hyphenation patterns are loaded; hyphenation only occurs
|
|
* when explicit soft-hyphens (­) are present in the source text
|
|
* (see E028_text_hyphenation.php for that approach).
|
|
*
|
|
* When you call setTexHyphenPatterns() with a loaded pattern array, the
|
|
* library automatically inserts soft-hyphen break points based on the
|
|
* pattern rules while laying out each text run. This is especially useful
|
|
* for narrow columns, justified text, or languages with long compound words
|
|
* (German, Dutch, Finnish, etc.) where manual soft-hyphen insertion would
|
|
* be impractical.
|
|
*
|
|
* API
|
|
* ───
|
|
* loadTexHyphenPatterns(string $file): array<string,string>
|
|
* Parses a TeX .tex pattern file (the standard format used by CTAN
|
|
* packages such as hyph-utf8) and returns a pattern array.
|
|
* The file must contain a \patterns{ ... } block.
|
|
*
|
|
* setTexHyphenPatterns(array $patterns): void
|
|
* Installs the pattern array as the active hyphenation dictionary.
|
|
* Pass an empty array [] to disable automatic hyphenation.
|
|
*
|
|
* Full pattern files for ~100 languages are available from CTAN:
|
|
* https://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/tex
|
|
*
|
|
* This example uses a small Dutch sample pattern file bundled with the
|
|
* examples to keep the demo self-contained. The same technique works with
|
|
* any complete CTAN TeX pattern file.
|
|
*
|
|
* Comparison layout
|
|
* ─────────────────
|
|
* The page shows the same Dutch paragraph rendered twice side by side:
|
|
* Left column — NO patterns loaded (only explicit ­ break points)
|
|
* Right column — Dutch sample patterns loaded via loadTexHyphenPatterns()
|
|
*
|
|
* Dutch is a good demonstration language because it has many long compound
|
|
* words (e.g. "woordenschat", "zelfstandigheid", "werkgelegenheid") that
|
|
* benefit greatly from automatic hyphenation in narrow columns.
|
|
*/
|
|
|
|
// Path to the bundled sample Dutch TeX pattern file.
|
|
$patternFile = \realpath(__DIR__ . '/data/hyph-nl-sample.tex');
|
|
if ($patternFile === false) {
|
|
throw new \RuntimeException('Missing pattern file: examples/data/hyph-nl-sample.tex');
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Common document setup
|
|
// -----------------------------------------------------------------------
|
|
$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: 064');
|
|
$pdf->setTitle('Custom TeX Hyphenation Dictionary');
|
|
$pdf->setKeywords('TCPDF tc-lib-pdf example hyphenation tex patterns dutch custom dictionary');
|
|
$pdf->setPDFFilename('E064_custom_hyphenation_dictionary.pdf');
|
|
|
|
$pdf->setViewerPreferences(['DisplayDocTitle' => true]);
|
|
|
|
$pdf->enableDefaultPageContent();
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Load the Dutch pattern file BEFORE generating content.
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Step 1: parse the .tex file into a pattern array.
|
|
$dutchPatterns = $pdf->loadTexHyphenPatterns(file: $patternFile);
|
|
|
|
// Step 2: install as the active dictionary.
|
|
$pdf->setTexHyphenPatterns(patterns: $dutchPatterns);
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Fonts
|
|
// -----------------------------------------------------------------------
|
|
$fontTitle = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 14);
|
|
$fontH2 = $pdf->font->insert($pdf->pon, 'helvetica', 'B', 11);
|
|
$fontBody = $pdf->font->insert($pdf->pon, 'times', '', 9);
|
|
$fontCode = $pdf->font->insert($pdf->pon, 'courier', '', 8);
|
|
$fontSmall = $pdf->font->insert($pdf->pon, 'helvetica', '', 8);
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Dutch sample text — a paragraph with long compound words.
|
|
// -----------------------------------------------------------------------
|
|
$dutchPara =
|
|
'De Nederlandse taal staat bekend om zijn lange samenstellingen. '
|
|
. 'Woorden zoals woordenschatontwikkeling, zelfstandigheidsbewegingen, '
|
|
. 'werkgelegenheidsbeleid, verantwoordelijkheidsgevoelens, '
|
|
. 'maatschappelijkontwikkelingswerk en informatietechnologieonderwijs '
|
|
. 'zijn perfecte voorbeelden van samenstellingen die automatische '
|
|
. 'woordafbreking vereisen voor een goede leesbaarheid in smalle kolommen. '
|
|
. 'Zonder patronen worden zulke woorden niet afgebroken en loopt de tekst '
|
|
. 'over de kolomrand of ontstaan er grote witruimten bij uitgevulde tekst. '
|
|
. 'Met de juiste TeX-patronen worden breekpunten automatisch ingevoegd '
|
|
. 'op de linguistisch correcte syllabegrenzen van elk woord.';
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Page 1 — Side-by-side comparison
|
|
// -----------------------------------------------------------------------
|
|
$page1 = $pdf->addPage();
|
|
|
|
// --- Title ---
|
|
$pdf->page->addContent($fontTitle['out']);
|
|
$pdf->page->addContent($pdf->getTextCell(
|
|
'Custom TeX Hyphenation Dictionary',
|
|
15.0,
|
|
18.0,
|
|
180.0,
|
|
0.0,
|
|
drawcell: false,
|
|
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
|
|
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
|
|
));
|
|
|
|
$pdf->page->addContent($fontSmall['out']);
|
|
$pdf->page->addContent($pdf->getTextCell(
|
|
'loadTexHyphenPatterns() + setTexHyphenPatterns() — Dutch sample patterns vs no patterns',
|
|
15.0,
|
|
27.0,
|
|
180.0,
|
|
0.0,
|
|
drawcell: false,
|
|
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
|
|
halign: \Com\Tecnick\Pdf\TextHAlign::Left,
|
|
));
|
|
|
|
// --- Column headers ---
|
|
$pdf->page->addContent($fontH2['out']);
|
|
$pdf->page->addContent($pdf->getTextCell(
|
|
'Without patterns',
|
|
15.0,
|
|
37.0,
|
|
87.0,
|
|
0.0,
|
|
drawcell: false,
|
|
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
|
|
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
|
|
));
|
|
$pdf->page->addContent($pdf->getTextCell(
|
|
'With Dutch patterns',
|
|
108.0,
|
|
37.0,
|
|
87.0,
|
|
0.0,
|
|
drawcell: false,
|
|
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
|
|
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
|
|
));
|
|
|
|
// Separator line between header and body
|
|
$sepStyle = [
|
|
'lineWidth' => 0.3,
|
|
'lineCap' => 'butt',
|
|
'lineJoin' => 'miter',
|
|
'dashArray' => [],
|
|
'dashPhase' => 0,
|
|
'lineColor' => '#336699',
|
|
'fillColor' => '',
|
|
];
|
|
$pdf->page->addContent($pdf->graph->getLine(15.0, 43.5, 195.0, 43.5, $sepStyle));
|
|
|
|
// Vertical divider between columns
|
|
$divStyle = [
|
|
'lineWidth' => 0.2,
|
|
'lineCap' => 'butt',
|
|
'lineJoin' => 'miter',
|
|
'dashArray' => [],
|
|
'dashPhase' => 0,
|
|
'lineColor' => '#aaaaaa',
|
|
'fillColor' => '',
|
|
];
|
|
$pdf->page->addContent($pdf->graph->getLine(103.0, 44.0, 103.0, 200.0, $divStyle));
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Left column — no automatic hyphenation
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Temporarily disable patterns.
|
|
$pdf->setTexHyphenPatterns(patterns: []);
|
|
|
|
$leftHtml = '<div style="text-align:justify; font-size:9pt; color:#222222;">' . $dutchPara . '</div>';
|
|
|
|
// Re-insert non-bold font before left column addHTMLCell.
|
|
$pdf->font->insert($pdf->pon, 'helvetica', '', 9);
|
|
$pdf->addHTMLCell(html: $leftHtml, posx: 15.0, posy: 45.0, width: 87.0);
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Right column — Dutch patterns active
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Re-enable Dutch patterns.
|
|
$pdf->setTexHyphenPatterns(patterns: $dutchPatterns);
|
|
|
|
$rightHtml = '<div style="text-align:justify; font-size:9pt; color:#222222;">' . $dutchPara . '</div>';
|
|
|
|
// Reset font stack to non-bold before second addHTMLCell.
|
|
$pdf->font->insert($pdf->pon, 'helvetica', '', 9);
|
|
$pdf->addHTMLCell(html: $rightHtml, posx: 108.0, posy: 45.0, width: 87.0);
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Pattern statistics block (below columns)
|
|
// -----------------------------------------------------------------------
|
|
$pdf->page->addContent($fontSmall['out']);
|
|
|
|
$patternCount = \count($dutchPatterns);
|
|
$pdf->page->addContent($pdf->getTextCell(
|
|
"Pattern file: examples/data/hyph-nl-sample.tex | Patterns loaded: {$patternCount}"
|
|
. ' | Source: CTAN hyph-utf8 (excerpt)',
|
|
15.0,
|
|
205.0,
|
|
180.0,
|
|
0.0,
|
|
drawcell: false,
|
|
valign: \Com\Tecnick\Pdf\TextVAlign::Top,
|
|
halign: \Com\Tecnick\Pdf\TextHAlign::Center,
|
|
));
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Page 2 — API reference and usage guidance
|
|
// -----------------------------------------------------------------------
|
|
$page2 = $pdf->addPage();
|
|
// Re-insert non-bold base font so the font stack is clean before addHTMLCell.
|
|
$pdf->font->insert($pdf->pon, 'helvetica', '', 8);
|
|
|
|
$html2 = <<<'HTML'
|
|
<h1 style="font-size:14pt; color:#003366;">API Reference and Usage Guidance</h1>
|
|
|
|
<h2 style="font-size:11pt; color:#005599;">loadTexHyphenPatterns(string $file): array</h2>
|
|
<p style="font-size:9pt; color:#333333;">
|
|
Reads a TeX hyphenation pattern file and returns an associative array mapping
|
|
pattern keys to their weighted strings. The file must contain a
|
|
<code>\patterns{ ... }</code> block in standard TeX format. Comments
|
|
(lines starting with <code>%</code>) are stripped before parsing.
|
|
</p>
|
|
<p style="font-size:9pt; color:#333333;">
|
|
The returned array can be stored and reused across multiple PDF objects
|
|
without re-reading the file:
|
|
</p>
|
|
<pre style="font-size:8pt; color:#003366; background:#f0f4ff;">
|
|
$patterns = $pdf->loadTexHyphenPatterns(lang: '/path/to/hyph-en-us.tex');
|
|
$pdf->setTexHyphenPatterns(lang: $patterns);
|
|
</pre>
|
|
|
|
<h2 style="font-size:11pt; color:#005599;">setTexHyphenPatterns(array $patterns): void</h2>
|
|
<p style="font-size:9pt; color:#333333;">
|
|
Installs the pattern array as the active hyphenation dictionary. Hyphenation
|
|
applies to all subsequent text layout calls. Pass an empty array
|
|
<code>[]</code> to disable automatic hyphenation.
|
|
</p>
|
|
|
|
<h2 style="font-size:11pt; color:#005599;">Obtaining pattern files</h2>
|
|
<p style="font-size:9pt; color:#333333;">
|
|
Full-quality pattern files for approximately 100 languages are available from
|
|
the CTAN hyph-utf8 package:<br />
|
|
<b>https://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/tex</b><br /><br />
|
|
Notable files:
|
|
</p>
|
|
<table border="0" cellpadding="2">
|
|
<tr><td style="font-size:8pt; width:55mm; color:#003366; font-weight:bold;">File</td>
|
|
<td style="font-size:8pt; color:#003366; font-weight:bold;">Language</td></tr>
|
|
<tr><td style="font-size:8pt;">hyph-en-us.tex</td><td style="font-size:8pt;">English (US)</td></tr>
|
|
<tr><td style="font-size:8pt;">hyph-de-1996.tex</td><td style="font-size:8pt;">German (1996 reform)</td></tr>
|
|
<tr><td style="font-size:8pt;">hyph-nl.tex</td><td style="font-size:8pt;">Dutch</td></tr>
|
|
<tr><td style="font-size:8pt;">hyph-fr.tex</td><td style="font-size:8pt;">French</td></tr>
|
|
<tr><td style="font-size:8pt;">hyph-pl.tex</td><td style="font-size:8pt;">Polish</td></tr>
|
|
<tr><td style="font-size:8pt;">hyph-fi.tex</td><td style="font-size:8pt;">Finnish</td></tr>
|
|
<tr><td style="font-size:8pt;">hyph-pt.tex</td><td style="font-size:8pt;">Portuguese</td></tr>
|
|
<tr><td style="font-size:8pt;">hyph-sv.tex</td><td style="font-size:8pt;">Swedish</td></tr>
|
|
</table>
|
|
|
|
<h2 style="font-size:11pt; color:#005599;">Soft hyphens vs automatic patterns — when to use each</h2>
|
|
<p style="font-size:9pt; color:#333333;">
|
|
<b>&shy; (soft hyphens in HTML)</b> — best for single documents with known content
|
|
where exact break points matter (e.g. trade-marked compound names, proper nouns
|
|
that patterns would get wrong). See E028_text_hyphenation.php.<br /><br />
|
|
<b>setTexHyphenPatterns()</b> — best for documents with large amounts of flowing
|
|
text where manual annotation is impractical (articles, books, reports). Patterns
|
|
are applied automatically to every word during layout without modifying the source
|
|
string.
|
|
</p>
|
|
|
|
<h2 style="font-size:11pt; color:#005599;">Switching languages mid-document</h2>
|
|
<p style="font-size:9pt; color:#333333;">
|
|
You can switch the active dictionary between pages or even between HTML cells
|
|
by calling <code>setTexHyphenPatterns()</code> again with a different pattern
|
|
array. Load all required pattern arrays at startup to avoid disk I/O during
|
|
layout, then swap them as needed.
|
|
</p>
|
|
HTML;
|
|
|
|
$pdf->addHTMLCell(html: $html2, posx: 15.0, posy: 20.0, width: 180.0);
|
|
|
|
$rawpdf = $pdf->getOutPDFString();
|
|
$pdf->renderPDF(rawpdf: $rawpdf);
|