Zum Inhalt springen

TrueType-Schriftart einbetten und subsetten

Registrieren Sie eine TrueType-Schriftart, rendern Sie damit Text und lassen Sie den Writer ein Subset der Schriftart einbetten. Dieses Recipe folgt demselben Content-Pfad wie examples/04-text-and-fonts.php und ergänzt ihn um eine registrierte TrueType-Schriftart (.ttf).

Terminal-Fenster
composer require nextpdf/core:^3

Dieser Constraint ist auf das Paket nextpdf/core abgestimmt, und das Beispiel läuft mit PHP 8.4. Die mitgelieferte Test-Fixture LiberationSans-Regular.ttf sorgt dafür, dass dieses Recipe in sich geschlossen bleibt.

Registrieren Sie eine Schriftart mit FontRegistry::register($path, $alias). Die Registry parst die Datei, liefert ein FontInfo-Objekt zurück und nutzt dabei TrueTypeParser für .ttf- und .otf-Dateien. Um diese Schriftart zu aktivieren, wählen Sie ihren Alias mit setFont($alias, ...) aus. Dieser Aufruf erfasst außerdem die verwendeten Codepoints.

Das Subsetting erfolgt automatisch beim Aufruf von save(). Der PDF-Font-Writer sammelt die verwendeten Codepoints und ruft FontSubsetter::subset() auf beziehungsweise nutzt CffSubsetter für Schriftarten im Compact Font Format (CFF) oder OpenType-Schriftarten. Wenn das Subset kleiner ist als das vollständige Programm, bettet der Writer das Subset ein und schreibt BaseFont und FontName mit einem per Pluszeichen angefügten Subset-Tag aus sechs Großbuchstaben um. Das ergibt die Form ABCDEF+FontName, die ISO 32000-2 für ein Font-Subset vorschreibt. Der Writer speichert das eingebettete TrueType-Programm als FontFile2 im Font-Deskriptor (ISO 32000-2).

Das Subset-Prefix wird deterministisch aus dem PostScript-Namen erzeugt, sodass ein deterministischer Build ein stabiles Tag liefert. Deshalb ist das Reproduzierbarkeitsprofil des Recipes structural. Das structural-Profil klammert das Subset-Prefix und das /ID im Trailer durch Normalisierung aus, statt sie bytegenau zu vergleichen.

  • FontRegistry::register(string $fontFile, string $alias = '', int $fontIndex = 0): FontInfoNextPDF\Typography\FontRegistry.
  • setFont(string $family, string $style = '', float $size = 12.0): staticNextPDF\Core\Concerns\HasTypography; übergeben Sie den registrierten Alias als $family.
  • Das Subsetting ist writer-intern (NextPDF\Writer\PdfFontWriter -> NextPDF\Typography\FontSubsetter). Es gibt keinen öffentlichen An-/Aus-Schalter: Der Writer subsettet immer dann, wenn die Codepoints bekannt sind und das Subset kleiner ist.

Die vollständige PHPDoc-Tabelle wird aus dem Quellcode generiert.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
use NextPDF\Typography\FontRegistry;
$registry = new FontRegistry();
$registry->register(__DIR__ . '/MyFont-Regular.ttf', alias: 'MyFont');
$doc = Document::createStandalone();
$doc->addPage();
$doc->setFont('MyFont', '', 14);
$doc->cell(0, 10, 'Rendered with an embedded, subset TrueType face.', newLine: true);
$doc->save(__DIR__ . '/out.pdf');

Document::createStandalone() baut seine eigene Registry auf. Um eine selbst befüllte Registry zu verwenden, erstellen Sie das Dokument über DocumentFactory, wie das Produktionsbeispiel zeigt. Die Factory sorgt dafür, dass der Writer genau die registrierte Schriftart liest.

Dieses Beispiel ist in sich geschlossen und im Harness ausführbar. Es registriert die mitgelieferte LiberationSans-Regular.ttf, rendert über DocumentFactory, sodass die befüllte Registry verwendet wird, und verlässt sich auf das automatische Subsetting beim Speichern.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\DocumentFactory;
use NextPDF\Graphics\ImageRegistry;
use NextPDF\Typography\FontRegistry;
// A bundled TrueType test fixture keeps this recipe self-contained.
// Replace with a font you have the right to embed.
$fontPath = __DIR__ . '/../../fonts/test-fixtures/LiberationSans/LiberationSans-Regular.ttf';
if (!is_file($fontPath)) {
// Fall back to the repository-relative fixture location.
$fontPath = dirname(__DIR__, 2) . '/fonts/test-fixtures/LiberationSans/LiberationSans-Regular.ttf';
}
$fontRegistry = new FontRegistry();
$fontRegistry->register($fontPath, alias: 'LiberationSans');
$imageRegistry = new ImageRegistry(maxCacheBytes: 0);
$documentFactory = new DocumentFactory($fontRegistry, $imageRegistry);
$doc = $documentFactory->create();
$doc->setTitle('Embedded Subset Font');
$doc->addPage();
$doc->setFont('LiberationSans', '', 20);
$doc->cell(0, 14, 'Embedded TrueType face', newLine: true);
$doc->setFont('LiberationSans', '', 12);
$doc->multiCell(0, 7, 'Only the glyphs used by this document are embedded. '
. 'The writer subsets the font program and rewrites the BaseFont with a '
. 'deterministic six-letter subset prefix, for example ABCDEF+LiberationSans.');
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');
$doc->save($out !== false ? $out : __DIR__ . '/embed-and-subset-fonts.pdf');
echo "Wrote embed-and-subset-fonts.pdf\n";

Erwartetes STDOUT:

Wrote embed-and-subset-fonts.pdf

Um das Subset zu prüfen, öffnen Sie die Ausgabe und sehen Sie sich das Font-Dictionary an. Der BaseFont lautet <TAG>+LiberationSans, und der Deskriptor trägt ein FontFile2. Ein Aufruf von qpdf --check meldet keine strukturellen Fehler.

  • Registry-Besitz. Document::createStandalone() baut seine eigene Registry auf. Eine Schriftart, die Sie auf einer separaten FontRegistry registriert haben, ist für das Dokument nicht sichtbar. Verwenden Sie DocumentFactory, um Ihre Registry zu übergeben, wie es das Produktionsbeispiel macht.
  • Einbettungsrechte. Subsetting ändert nichts an der Lizenzierung. Betten Sie nur Schriftarten ein, für die Sie eine Einbettungslizenz haben. Manche Schriftarten setzen Einbettungsrestriktionsbits; der Parser liest diese Bits aus, aber für die Compliance bleiben Sie verantwortlich.
  • CFF-/OpenType-Pfad. .otf- und CFF-Schriftarten werden von CffSubsetter gesubsettet, nicht von FontSubsetter. Das Verhalten und das Umschreiben des Subset-Tags sind gleichwertig, nur der Codepfad unterscheidet sich.
  • Kein Größenvorteil. Manchmal ist das Subset nicht kleiner als das Original, etwa bei winzigen Schriftarten oder wenn alle Glyphen verwendet werden. In diesem Fall bettet der Writer das Originalprogramm ohne Subset-Tag ein. Das ist korrekt und kein Fehler.
  • CJK-Schriftarten. Große chinesische, japanische und koreanische (CJK) Schriftarten nutzen gemäß ADR-008 eine gestufte Subsetting-Strategie mit einem isolierten Subprozess und einem PHP-nativen Fallback. Details zu CJK und zum aktuellen Pipelinestatus finden Sie unter CJK-Text mit cmap-bewusster Kodierung setzen.

Das Parsen erfolgt in einem einzigen Durchlauf über die Font-Tabellen, und die Kosten des Subsettings steigen mit der Anzahl der Glyphen. Lateinische Nicht-CJK-Schriftarten werden im laufenden Prozess innerhalb des Budgets wall_ms: 1500, peak_mb: 96 gesubsettet. Große CJK-Schriftarten werden an einen isolierten Subprozess mit einem Wall-Clock-Timeout von zwei Sekunden und einem PHP-nativen Fallback geleitet (ADR-008). Dieses Routing bedeutet, dass ein langsamer oder abstürzender Subsetting-Lauf den Aufrufer nicht blockieren kann.

Eine Schriftdatei ist nicht vertrauenswürdige Binäreingabe. Der Parser weist Stream-Wrapper-Pfade und Nullbytes ab. Der CJK-Subsetting-Subprozess läuft ohne geerbte Datenbankverbindungen, File-Handles oder Framework-State und fällt bei Absturz oder Timeout sicher zurück (ADR-008). Prüfen Sie die Herkunft von Schriftarten, die Sie von Endnutzern annehmen.

AussageSpezifikationAbschnittreference_id
Der BaseFont/FontName eines Font-Subsets trägt ein Subset-Prefix aus sechs Großbuchstaben, das per Pluszeichen angefügt wird.ISO 32000-2iso32000_2_sec9#x1.x66.p2
Ein eingebettetes TrueType-Font-Programm wird als FontFile2 im Font-Deskriptor gespeichert.ISO 32000-2iso32000_2_sec9#x1.x65.p15

Dieses Recipe zeigt, wie NextPDF eine TrueType-Schriftart einbettet, subsettet und ein konformes Subset-Prefix ausgibt. Es sichert keine Font-Lizenz-Compliance zu; die Einbettungsrechte liegen in der Verantwortung des Integrators.

Nicht zutreffend.