Zum Inhalt springen

Schrift: Werttypen, Einbettung und Ersatz

Eine Schrift in NextPDF besteht aus dem unveränderlichen Wertobjekt FontInfo und dem Technologietyp, der entscheidet, wie die Engine sie einbettet. Jede von der Engine verwendete Schrift wird eingebettet. Ein veralteter Base-14-Verweis greift auf eine mitgelieferte, metrikkompatible Ersatzschrift zurück.

Terminal-Fenster
composer require nextpdf/core:^3

FontInfo ist das einzige unveränderliche Wertobjekt und enthält alles, was die Engine zum Einbetten einer Schrift benötigt: Familie und Stil, den PostScript-Namen, Deskriptor-Flags, auf ein 1000-Einheiten-Em skalierte Metriken, Zeichenbreiten, die Glyphen-zu-Unicode-Zuordnung, die Vorwärts-cmap (Unicode zu Glyphen-Identifikator), rohe Schriftbytes und — sofern vorhanden — Variationsachsen, benannte Instanzen, Variationsselektoren, Kerning-Paare und vertikale Metriken. Es ist final readonly. Die Konstruktor-Signatur und die öffentlichen Eigenschaften sind eingefroren, sodass eine geparste Schrift als stabile, gemeinsam nutzbare Tatsache vorliegt. FontInfo::encodeText() ist die einzige Methode, die tatsächlich Arbeit verrichtet. Sie nutzt den Encoding-Resolver und gibt einen EncodedGlyphRun zurück.

FontType zählt die Technologien auf, die die Engine einbettet: TrueType (Einzelbyte-Encoding), TrueTypeUnicode (Mehrbyte-CID-Encoding für Unicode-reiche Schriftsysteme), OpenType (Compact-Font-Format-Konturen), Type1 (PostScript Type 1, registriert aus einem PFB-und-AFM-Paar) und CidFont0 (eine PostScript-basierte CID-Schrift). Der vom Parser zugewiesene Typ bestimmt die Form des Schrift-Dictionarys, das der Writer ausgibt.

Die Engine bettet das Schriftprogramm ein, damit das Dokument in jedem Viewer gleich rendert, unabhängig von den installierten Systemschriften — ISO 32000-2 §9. Ein TrueType-Programm wird über den Schriftdeskriptor-Eintrag FontFile2 eingebettet und muss die Tabellen glyf, head, hhea, hmtx, loca und maxp enthalten — ISO 32000-2 §9.6.5 (RAG-Digest durch das Lizenz-Limit gekürzt; festgehalten in _downgraded-claims-o3.md). Ein OpenType-Programm mit einer Compact-Font-Format-Konturtabelle wird über FontFile3 eingebettet — ISO 32000-2 §9.6.5 (RAG-Digest gekürzt; siehe dasselbe Log). Der Subsetter baut genau diesen erforderlichen Tabellensatz neu auf, sodass das eingebettete Subset ein konformes Programm bleibt.

Der Ersatzmechanismus behandelt den veralteten Base-14-Fall. Base14SubstituteFonts ordnet einen normalisierten Base-14-Schlüssel — helvetica, helveticab, times, courier und die übrigen — einer mitgelieferten Datei aus den Liberation Fonts zu. Liberation Sans, Serif und Mono sind metrikkompatibel mit Helvetica beziehungsweise Arial, Times Roman und Courier. Jede davon ist ein eingebetteter TrueType-Schnitt und rendert daher das vollständige lateinische WinAnsiEncoding-Repertoire (Windows-1252), das ein Standard-14-Verweis verlangt — lateinische Zeichen mit Akzenten, das Euro-Zeichen und gängige typografische Satzzeichen (ISO 32000-2 Annex D.2). Symbol und ZapfDingbats haben keinen permissiv lizenzierten, metrikkompatiblen Ersatz und werden bewusst nicht ersetzt; ein Dokument, das sie benötigt, muss eine einbettbare Schrift registrieren. Der Resolver ist nebenwirkungsfrei — er liefert nur die Datei, auf die ein Schlüssel verweist, und nichts darüber hinaus. Die Registrierung in der Registry bleibt in der Verantwortung des Aufrufers, wodurch die Lock-Semantik und die Warmup-Pipeline erhalten bleiben.

TypArtWichtige MitgliederStabilitätSeit
FontInfofinal readonly class$family, $style, $type, $unitsPerEm, $widths, $unicodeMap, $cmapForward, $fileData, $variationAxes, $kernPairs, getKey(), encodeText()stabil1.0.0
FontTypeenum (string)TrueType, TrueTypeUnicode, OpenType, Type1, CidFont0stabil1.0.0
Base14SubstituteFontsfinal class (intern)normalisierter Base-14-Schlüssel zum Pfad der mitgelieferten Liberation-Dateistabil2.7.0
ShaperFactoryfinal classdefault(), create(), wouldUseRealShaper()stabil3.2.0
ShapingResultfinal readonly class$glyphRuns, $originalText, $script, $direction, $shaperImplstabil3.2.0

Base14SubstituteFonts ist @internal — nur zur Framework-Nutzung und ohne Abwärtskompatibilitätsgarantie für seine Oberfläche.

examples/35-cjk-cmap-demo.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Typography\FontRegistry;
use NextPDF\Typography\FontType;
$registry = new FontRegistry();
$font = $registry->register('/path/to/NotoSansTC-Regular.ttf', alias: 'NotoSansTC');
// FontInfo is the immutable parsed fact about the face.
echo $font->family, ' / ', $font->type->value, "\n"; // e.g. "Noto Sans TC / TrueTypeUnicode"
assert($font->type === FontType::TrueTypeUnicode);

Der Parser befüllt FontInfo und weist den FontType zu. Eine TrueType-Schrift mit Unicode-cmap erhält TrueTypeUnicode; diesen Typ gibt der Writer als zusammengesetzte Type-0-Schrift aus.

examples/font/base14-fallback.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Typography\Base14SubstituteFonts;
use NextPDF\Typography\FontRegistry;
final readonly class Base14EmbeddingResolver
{
public function __construct(private FontRegistry $registry) {}
/**
* Register an embeddable substitute for a legacy Base 14 key so the
* output document embeds every font (PDF/A-4 and PDF/UA-2 require it).
*/
public function ensureEmbeddable(string $base14Key): void
{
$path = Base14SubstituteFonts::resolve($base14Key);
if ($path === null) {
// Symbol / ZapfDingbats have no permissive substitute — the
// caller must supply its own embeddable font.
throw new \RuntimeException("No bundled substitute for {$base14Key}");
}
if (!$this->registry->has($base14Key)) {
$this->registry->register($path, alias: $base14Key);
}
}
}

Der Resolver ist nebenwirkungsfrei. Die Registrierung bleibt explizit, damit die Lock- und Warmup-Verträge der Registry gelten. Symbol und ZapfDingbats liefern absichtlich keinen Pfad zurück.

  • Symbol und ZapfDingbats werden absichtlich nicht ersetzt. Ein Null-Ergebnis für diese Schlüssel ist dokumentiertes Verhalten und kein Fehler aufgrund einer fehlenden Schrift.
  • FontInfo ist final readonly. Behandeln Sie eine geparste Schrift als Wert: Erwarten Sie nie, Breiten oder Metriken an Ort und Stelle zu mutieren; registrieren Sie neu, wenn sich die Quelle ändert.
  • Eine Type-1-Schrift braucht sowohl die PFB-Kontur als auch die AFM-Metriken. FontRegistry::registerType1() nimmt das Paar entgegen; die automatische Erkennung leitet den AFM-Pfad anhand der Endung aus dem PFB-Pfad ab.
  • FontType::TrueType gegenüber FontType::TrueTypeUnicode ist die Unterscheidung zwischen Einzelbyte und Mehrbyte. Der Encoding-Resolver richtet sich nach der gefüllten Vorwärts-cmap, nicht nach dem Familiennamen, sodass eine Unicode-TrueType-Schrift automatisch auf den Identity-H-Pfad geleitet wird.
  • Achsen von Variationsschriften und benannte Instanzen werden, sofern vorhanden, in FontInfo geparst, doch das ausführliche CJK-Beispiel verwendet bewusst die statische Schrift, um das geparste FontInfo deterministisch zu halten.

FontInfo wird von der Registry einmal pro Schrift und Prozess allokiert und danach per Referenz geteilt. Es trägt die rohen Schriftbytes, die den größten Teil des Speicherbedarfs ausmachen. Ein Worker sollte nur die Schriften vorwärmen, die er benötigt, und memoryUsage() überwachen. Der Resolver für Base-14-Ersatzschriften ist eine Map-Suche in konstanter Zeit ohne I/O, bis der Aufrufer die aufgelöste Datei registriert. Das performance_budget von 1500 ms Wall-Zeit und 64 MB Spitze deckt einen typischen Schriftsatz-Warmup plus Rendering ab. Der Speicherbedarf pro Schrift skaliert mit der Größe der Schriftdatei, nicht mit der Glyphenzahl, bis der Subsetter ausgeführt wird.

FontInfo selbst ist inert — es handelt sich um geparste Daten ohne Verhalten über die reine encodeText()-Transformation hinaus. Die Angriffsfläche liegt vorgelagert, beim Parsen: beliebige Schriftbytes, die den TrueType- oder Type-1-Parser erreichen. Die Parser prüfen jeden binären Offset auf Grenzüberschreitungen und weisen Stream-Wrapper sowie Null-Bytes in Pfaden zurück. Nicht vertrauenswürdige Schrifteingaben müssen vor der Registrierung eine Richtlinie für externe Ressourcen durchlaufen, die Größe und Glyphenzahl begrenzt. Die mitgelieferten Liberation-Ersatzschriften sind vertrauenswürdige Assets, die mit dem Paket ausgeliefert werden, sodass der Ersatzpfad keine neue, nicht vertrauenswürdige Eingabe einführt.

AussageStandardKlauselNachweis
Jede vom Dokument verwendete Schrift wird eingebettet, sodass das Dokument rendert, ohne auf Systemschriften angewiesen zu sein.ISO 32000-2§9
Ein TrueType-Programm wird über FontFile2 mit den Tabellen glyf, head, hhea, hmtx, loca, maxp eingebettet.ISO 32000-2§9.6.5RAG-Digest durch das Lizenz-Limit gekürzt; Präfix 7b26f37996239b2a, siehe _downgraded-claims-o3.md
Ein OpenType-Programm (CFF) wird über FontFile3 eingebettet.ISO 32000-2§9.6.5RAG-Digest durch das Lizenz-Limit gekürzt; Präfix 801549ee00623baf, siehe _downgraded-claims-o3.md

Die erste Klausel ist per Digest fixiert und durch B1 belegt. Die FontFile2- und FontFile3-Klauseln sind paraphrasiert. Ihre vollständigen RAG-Digests wurden nicht zurückgegeben (Kürzung durch das Lizenz-Limit) und sind durch FontSubsetter (der genau den Satz glyf/head/hhea/hmtx/loca/maxp neu aufbaut) sowie das FontType-Enum belegt. NextPDF gibt keinen normativen Text wieder. Base14SubstituteFonts zitiert im Quelltext ISO 32000-2 §9.6.2.2 (Standard-Behandlung von Type-1-Schriften), ISO 14289-2:2024 §8.4.5.5.1 (PDF/UA-2 Schrifteinbettung) und ISO 19005-4:2020 §6.3.5 (PDF/A-4 Schrifteinbettung). Die Seiten zu Barrierefreiheit und Konformität enthalten die vollständige Profilkonformität.

Ein kommerzielles Paket für Schriftlizenzen und ein dynamischer Subsetting-Dienst bauen auf FontInfo im Core und auf der Registry auf. Das Core-Schriftmodul bettet ein, subsettet und greift auf Ersatzschriften zurück, ohne dass dafür eine Lizenz erforderlich ist. Das Weglassen eines Verweises auf eine Konvertierung ist beabsichtigt.