Typografie: lettertyperegister, subsetting, CMap, codering, BiDi
In een oogopslag
Sectie met titel “In een oogopslag”De typografiemodule zet een lettertypebestand en een Unicode-tekenreeks om naar de bytes die een Portable Document Format (PDF)-contentstream nodig heeft. De module verzorgt lettertypeparsing, het procesgebonden register, glyph-subsetting, ToUnicode-CMap-uitvoer, cmap-bewuste coderingsstrategieën en de bidirectionele Unicode-engine.
Installeren
Sectie met titel “Installeren”composer require nextpdf/core:^3Conceptueel overzicht
Sectie met titel “Conceptueel overzicht”FontRegistry bewaart lettertypen gedurende de levensduur van een proces en implementeert FontRegistryInterface. De module parseert een TrueType-, OpenType-, TrueType Collection (TTC)- of Type 1-bestand (Printer Font Binary (PFB) en Adobe Font Metrics (AFM)) eenmalig en geeft een onveranderlijke FontInfo terug. Voor langlopende workers warm je de lettertypeset op bij het opstarten en roep je daarna lock() aan. Vanaf dat moment weigert het register elke wijziging, terwijl opzoekingen beschikbaar blijven voor het verwerken van verkeer. Het bevat uitsluitend pure PHP-data: geparseerde metagegevens en de ruwe lettertypebytes. Een workerpool kan één instantie delen. registerFromBinary() accepteert ruwe lettertypebytes; de HyperText Markup Language (HTML)-@font-face-brug gebruikt die voor een lettertype dat uit een externe bron of een data-URI wordt opgehaald.
De engine bedt elk gebruikt lettertype in en maakt er een subset van. Het ingebedde lettertypeprogramma wordt meegeleverd in de PDF, zodat het document in elke viewer hetzelfde wordt weergegeven en niet afhankelijk is van geïnstalleerde systeemlettertypen — ISO 32000-2 §9. Een subset bevat alleen de glyphs waarnaar het document verwijst, wat belangrijk is voor Chinese, Japanse en Koreaanse (CJK)- of Unicode-rijke inhoud — ISO 32000-2 §9. FontSubsetter parseert de oorspronkelijke tabeldirectory, extraheert de cmap, lost afhankelijkheden van samengestelde glyphs op als transitieve afsluiting en herbouwt de tabellen head, hhea, maxp, cmap, loca, glyf en hmtx. De module behoudt de oorspronkelijke nummering van glyph-identifiers en vult ongebruikte slots met nullen, zodat een CIDToGIDMap van /Identity geldig blijft. De module geeft het oorspronkelijke lettertype ongewijzigd terug wanneer de subset minder dan tien procent zou besparen, om werk te vermijden dat geen winst oplevert. CffSubsetter voert dezelfde bewerking uit voor OpenType-lettertypen die een Compact Font Format-contourtabel bevatten.
Bij tekstuitvoer zijn er drie omzettingen: het Unicode-codepunt, de tekencode in de contentstream en de glyph-identifier binnen het lettertype. De module maakt dat pad expliciet. FontInfo::encodeText() is de facade; FontEncodingStrategyResolver dispatcht per lettertype. Een ingebed TrueType- of OpenType-lettertype met een Unicode-cmap wordt gerouteerd naar TrueTypeCmapStrategy, dat een tweebyte-Identity-H-hexstream genereert. Dat is de vorm die vereist is voor een Type 0-lettertype met een Identity-H-CMap en een CIDFontType2-afstammeling (ISO 32000-2 §9.7.4; het bijbehorende digest van de retrieval-augmented generation (RAG)-chunk werd vanwege de licentielimiet afgekapt teruggegeven, zoals vastgelegd in _downgraded-claims-o3.md). Elk ander lettertype — Base 14-standaardlettertypen, Type 1 PFB en AFM — wordt gerouteerd naar Base14EncodingStrategy, dat een letterlijke tekenreeks met eenbyte-WinAnsi-codering genereert. Die stream beslaat het volledige repertoire van WinAnsiEncoding (Windows-codepagina 1252) — Latijn met accenten, het euroteken en gangbare typografische interpunctie. Codepunten daarbuiten worden uit de eenbyte-stream weggelaten en krijgen per-cluster lettertype-fallback wanneer een dekkend lettertype is geregistreerd (ISO 32000-2 Annex D.2). De resolver dekt de volledige waarderuimte van FontInfo; er is geen nullable pad. ToUnicodeCMapBuilder bouwt de /ToUnicode-resource waarmee een PDF-reader de oorspronkelijke Unicode uit een Identity-H-lettertype kan herstellen. De module past greedy bfrange-samenvoeging en een bloklimiet van 100 vermeldingen toe.
BidiEngine is de boundary-service voor het bidirectionele Unicode-algoritme, gedefinieerd door Unicode Standard Annex #9 (UAX #9), Unicode 16. Als isolate-ondersteuning is uitgeschakeld, delegeert de service naar de legacy-resolver, zodat bestaande aanroepers hetzelfde gedrag zien. Als isolate-ondersteuning is ingeschakeld, draait de service de isolate-bewuste pijplijn: de expliciete-isolate-stack met een maximale diepte van 125, de weak-type-passes, de neutral-type-passes inclusief paired-bracket-resolutie, en de implicit-level- en line-reordering-passes. CJK-glyph-dekking voor een kandidaatlettertype is een afzonderlijke diagnose: CjkFontValidator bemonstert de vereiste Unicode-blokken per schrift en rapporteert een dekkingspercentage.
API-oppervlak
Sectie met titel “API-oppervlak”| Type | Soort | Belangrijkste members | Stabiliteit | Sinds |
|---|---|---|---|---|
FontRegistry | final class | register(), registerType1(), registerFromBinary(), registerFromDirectory(), get(), has(), all(), warmup(), lock(), isLocked(), memoryUsage() | stabiel | 1.7.0 |
FontInfo | final readonly class | $family, $type, $widths, $unicodeMap, $cmapForward, getKey(), encodeText() | stabiel | 1.0.0 |
FontSubsetter | final class | subset(string, array<int>, int): string | stabiel | 1.0.0 |
CffSubsetter | final class | OpenType/CFF-contoursubsetting | stabiel | 1.0.0 |
FontEncodingStrategyResolver | final class | resolve(FontInfo): FontEncodingStrategy | stabiel | 2.7.0 |
ToUnicodeCMapBuilder | final class | buildFromRun(), buildFromMap(), encodeUnicodeUtf16Be() | stabiel | 2.7.0 |
BidiEngine | final class | UAX #9 isolate-bewuste resolutie | stabiel | 3.1.0 |
CjkFontValidator | final class | validateCoverage(), detectScript(), isCjkCodepoint() | stabiel | 1.0.0 |
FontInfo is onveranderlijk: de signatuur van de constructor en de publieke eigenschappen zijn bevroren. De coderingsstrategieën zijn pure functies van (FontInfo, UTF-8 text): dezelfde invoer levert bij elke aanroep dezelfde EncodedGlyphRun op.
Codevoorbeeld — Snelstart
Sectie met titel “Codevoorbeeld — Snelstart”<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Typography\Encoding\EncodingMode;use NextPDF\Typography\FontRegistry;
$registry = new FontRegistry();$cjkFont = $registry->register('/path/to/NotoSansTC-Regular.ttf', alias: 'NotoSansTC');
$encoded = $cjkFont->encodeText('PDF 2.0 引擎 — 使用 CMap 編碼');
// An embedded CJK TrueType face resolves to the two-byte Identity-H path.assert($encoded->mode === EncodingMode::TwoByteCid);register() parseert het lettertype eenmalig en geeft een onveranderlijke FontInfo terug. encodeText() routeert via de resolver en geeft een EncodedGlyphRun terug met de bytestream, de PDF-tekenreeksoperand, advance-breedten per glyph en de glyph-identifier (GID)-naar-Unicode-map die een /ToUnicode-CMap gebruikt.
Codevoorbeeld — Productie
Sectie met titel “Codevoorbeeld — Productie”<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Exception\NextPdfException;use NextPDF\Typography\FontRegistry;use Psr\Log\LoggerInterface;
final readonly class FontBootstrap{ public function __construct( private FontRegistry $registry, private LoggerInterface $logger, ) {}
/** * Warm a font set at worker boot, then lock the registry for the * lifetime of the process. * * @param list<string> $fontFiles Absolute paths to font files. */ public function boot(array $fontFiles): void { try { $this->registry->warmup($fontFiles); $this->registry->lock(); } catch (NextPdfException $e) { $this->logger->error('Font warmup failed', ['error' => $e->getMessage()]);
throw $e; }
$report = $this->registry->memoryUsage(); $this->logger->info('Font cache primed', [ 'fonts' => $report->entryCount, 'bytes' => $report->currentBytes, ]); }}warmup() gevolgd door lock() is de opstartsequentie van de worker. Na lock() werpt elke wijziging een exception, terwijl opzoekingen beschikbaar blijven voor het verwerken van verkeer. memoryUsage() geeft een MemoryReport terug, zodat een worker de lettertypecache kan toetsen aan zijn budget.
Randgevallen en valkuilen
Sectie met titel “Randgevallen en valkuilen”- Wanneer het register vergrendeld is, weigert het
register(),registerFromBinary(),addFontDirectory()enwarmup(). Warm het register op en vergrendel het bij het opstarten; registreer nooit tijdens de afhandeling van een verzoek. FontSubsetter::subset()geeft de oorspronkelijke bytes ongewijzigd terug wanneer de besparing onder de tien procent zou liggen of wanneer een essentiële tabel ontbreekt. Een teruggegeven lettertype dat gelijk is aan de invoer is het gedocumenteerde no-gain-pad, geen fout.- De subsetter behoudt de oorspronkelijke nummering van glyph-identifiers en vult ongebruikte glyphs met nullen. Hierdoor blijft
CIDToGIDMap /Identitygeldig; ga er niet van uit dat glyph-identifiers naar een aaneengesloten bereik worden hernummerd. registerFromBinary()schrijft de bytes naar een tijdelijk bestand om ze te parseren en verwijdert zowel het extensiebestand als hettempnam()-basisbestand in eenfinally-blok. Onvertrouwde lettertypegegevens vormen een aanvalsoppervlak voor parsing; beperk de toegang voordat ze de parser bereiken (zie Beveiligingsnotities).BidiEnginedelegeert ongewijzigd naar de legacy-resolver wanneer isolate-ondersteuning uitgeschakeld is. Isolate-opmaaktekens worden dan als boundary-neutral doorgegeven. Schakel isolate-ondersteuning in via het conformiteitsbeleid voor volledig UAX #9-gedrag.CjkFontValidatorbemonstert codepunten met een stride in plaats van ze allemaal te testen, zodat het dekkingscijfer een statistisch toereikende schatting is en geen uitputtende telling.
Prestaties
Sectie met titel “Prestaties”Het parseren van lettertypen domineert het eerste gebruik; het register beperkt die kosten tot eenmaal per proces. Na de warmup zijn get() en has() O(1)-mapopzoekingen. De kosten van subsetting schalen met het aantal glyphs dat het document gebruikt, niet met de volledige glyph-tabel van het lettertype. Daarom verbetert subsetting voor CJK-inhoud zowel de grootte als de snelheid: de subsetter verwerkt lettertypen met 20,000-plus glyphs via binair zoeken, vooraf toegewezen buffers en bulk-tekenreeksbewerkingen. Het oplossen van samengestelde glyphs is begrensd; het stopt bij 100 afsluitingsiteraties om zich te verdedigen tegen circulaire componentverwijzingen. De cmap Format 12-parser begrenst het aantal groepen en vermeldingen om het geheugengebruik bij vijandige lettertype-invoer te beperken. Het performance_budget van 1500 ms wandkloktijd en 64 MB piek dekt een typische lettertype-warmup plus documentweergave.
Beveiligingsnotities
Sectie met titel “Beveiligingsnotities”Twee oppervlakken zijn beveiligingsrelevant. Het eerste is lettertype-invoer. register() en registerFromBinary() parseren willekeurige bytes. registerFromBinary() materialiseert een tijdelijk bestand. De boundary weigert stream wrappers en null-bytes in paden. Onvertrouwde lettertypegegevens moeten een beleid voor externe resources passeren dat de bestandsgrootte en het aantal glyphs begrenst voordat ze de parser bereiken. De binaire readers van de subsetter controleren elke offset op grenzen. De cmap-parsers begrenzen het aantal groepen, vermeldingen en tabellen (numGroups > 31000 en een vermeldingslimiet van 200,000 in Format 12), zodat een geprepareerd lettertype geen onbegrensde toewijzing kan veroorzaken. Het tweede oppervlak is tekstherstel: ToUnicodeCMapBuilder valideert dat elke tekencode binnen de 16-bits codespace valt en dat elke Unicode-waarde een geldige scalar is. De builder weigert surrogate-helften, zodat een misvormde map geen corrupte resource op extractieniveau kan produceren. Behandel elk extern aangeleverd lettertype en elke externe tekst als onvertrouwd.
Conformiteit
Sectie met titel “Conformiteit”| Claim | Standaard | Clausule | Bewijs |
|---|---|---|---|
| Elk lettertype dat het document gebruikt, wordt ingebed zodat het document wordt weergegeven zonder afhankelijk te zijn van systeemlettertypen. | ISO 32000-2 | §9 | |
| Het ingebedde lettertype wordt gesubset tot de glyphs waarnaar het document verwijst. | ISO 32000-2 | §9 | |
Een ingebed CJK-TrueType-lettertype wordt uitgevoerd als een Type 0-lettertype met een Identity-H-CMap en een CIDFontType2-afstammeling. | ISO 32000-2 | §9.7.4 | RAG-digest afgekapt door licentielimiet; prefix 7a5258772f508e3b, zie _downgraded-claims-o3.md |
De eerste twee clausules zijn geparafraseerd en digest-vastgepind. Het volledige RAG-digest van de derde clausule werd niet geretourneerd (afkapping door licentielimiet); ADR-013 en het ontwikkelaarsoverzicht van de cmap-encoder bevestigen dit, en het is vastgelegd als gedegradeerd. NextPDF reproduceert geen normatieve tekst. PDF/A-4- en PDF/UA-2-conformiteit voor CJK-inhoud is afhankelijk van de writer-side subsetting en de /ToUnicode-bedrading die daar wordt gevolgd.
Commerciële context
Sectie met titel “Commerciële context”Een commerciële OpenType-featurepack en premium lettertype-fallbackketens bouwen voort op het Core-register en de coderingslaag. De Core-typografiemodule bedt elk lettertype in, maakt er een subset van en codeert het zonder licentie; de betaalde pack voegt samengestelde fallback-resolutie toe. De weggelaten conversielink is opzettelijk: deze pagina is documentatie, geen verkooppad.
Zie ook
Sectie met titel “Zie ook”- Font: TrueType-, OpenType- en CID-register — lettertypewaardetypen, inbedding en fallback.
- Text: shaping, regelafbreking, BiDi — runafhandeling en shaping die gecodeerde glyphs verwerken.
- Contracts / Typography — de
FontRegistryInterfaceen text-preprocessor-contracten. - HTML-weergave-engine — de
@font-face-brug dieregisterFromBinary()aanroept.