Ga naar inhoud

Contracts / Typografie

Het typografiedomein definieert de contracten voor het lettertyperegister en de tekstvoorbewerking: FontRegistryInterface, TextPreprocessorInterface en de onveranderlijke waardeobjecten TextPreprocessResult en TextSegment. Alle zijn stable.

Terminal window
composer require nextpdf/core:^3

FontRegistryInterface is het lettertyperegister met procesbrede levensduur. Het registreert een lettertype in TrueType-, OpenType-, TrueType Collection- (TTC) of Printer Font Binary- (PFB)-formaat en geeft geparseerde FontInfo-metadata terug. Omdat het register langer bestaat dan afzonderlijke documenten, parseert een worker elk lettertype slechts één keer. Je kunt bij het opstarten een reeks lettertypen voorverwarmen en vervolgens het register vergrendelen, zodat productieverkeer het niet kan wijzigen. Een vergrendeld register werpt LogicException bij register(), addFontDirectory() of warmup(); opzoekacties blijven beschikbaar. Het register accepteert ook ruwe lettertypebytes via registerFromBinary(). De @font-face-brug gebruikt deze methode om een lettertype te registreren dat is opgehaald van een externe bron of een data-URI (uniform resource identifier). Het register bewaart uitsluitend pure PHP-gegevens, zonder resource-handles, zodat het over een workerpool gedeeld kan worden.

De engine embedt elk gebruikt lettertype en maakt er een subset van. Een ingebed lettertypeprogramma reist mee binnen het Portable Document Format- (PDF)-bestand, zodat het document in elke viewer hetzelfde wordt weergegeven, onafhankelijk van de geïnstalleerde systeemlettertypen — ISO 32000-2 §9. Een lettertypesubset bevat alleen de glyphs waarnaar het document daadwerkelijk verwijst. Dat is het belangrijkst voor Chinese, Japanse en Koreaanse (CJK)-inhoud of andere Unicode-rijke inhoud — ISO 32000-2 §9. Het registercontract stelt de geparseerde metadata beschikbaar die de subset- en embeddingfasen gebruiken.

TextPreprocessorInterface onderschept tekst voordat deze de glyph-layout, de lettertypesubset, de ToUnicode-tekentabel (CMap) en de structuurboom binnenkomt. Juist die plaatsing vormt de beveiligingseigenschap: een preprocessor die inhoud redacteert, verwijdert die inhoud voordat deze de content stream, de lettertypesubset of de metadata kan bereiken. Het contract legt twee invarianten vast. Een preprocessor mag geen layout-beïnvloedende tekens introduceren en moet de logische leesvolgorde behouden; de verantwoordelijkheid ligt bij inhoudsvervanging, niet bij lay-out. Het resultaat is een onveranderlijke TextPreprocessResult met een geordende lijst van TextSegment-waarden. Een segment wordt ofwel doorgegeven, ofwel geredacteerd. Voor een geredacteerd segment hangt de weergavetekst af van de maskeringsmodus: leeg voor een zwarte rechthoek, asterisken volgens de oorspronkelijke lengte, of een vast label. De originalCharCount op een segment is een niet-omkeerbare meethint die alleen wordt gebruikt om een redactierechthoek te dimensioneren. Die mag nooit worden gebruikt om de oorspronkelijke inhoud te reconstrueren.

TypeSoortBelangrijkste membersStabiliteitSinds
FontRegistryInterfaceinterfaceregister(), get(), has(), all(), addFontDirectory(), warmup(), lock(), isLocked(), registerBase14(), registerFromBinary(), memoryUsage()stable1.7.0
TextPreprocessorInterfaceinterfaceprocess(string): TextPreprocessResultstable1.9.0
TextPreprocessResultfinal readonly class$segments, hasRedactions(), getDisplayText()stable1.9.0
TextSegmentfinal readonly class$displayText, $isRedacted, $originalCharCount, $fillColorstable1.9.0

TextPreprocessResult en TextSegment bevriezen hun constructorsignaturen en publieke eigenschappen; nieuwe methoden mogen worden toegevoegd, maar eigenschappen mogen niet veranderen.

examples/04-text-and-fonts.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->addPage();
$doc->setFont('helvetica', 'B', 18);
$doc->cell(0, 12, 'Bold heading', newLine: true);
$doc->setFont('helvetica', '', 11);
$doc->multiCell(0, 7, 'Body text rendered with a registered font.');
$doc->save(__DIR__ . '/output/04-text-and-fonts.pdf');

setFont() zoekt de familie op via FontRegistryInterface. Een standalone document gebruikt een privéregister. Deel binnen een worker één register; zie de documentpagina.

examples/contracts/typography-production.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\FontRegistryInterface;
use NextPDF\Contracts\TextPreprocessorInterface;
use NextPDF\Exception\NextPdfException;
use Psr\Log\LoggerInterface;
final readonly class FontWarmupService
{
public function __construct(
private FontRegistryInterface $fonts,
private TextPreprocessorInterface $preprocessor,
private LoggerInterface $logger,
) {}
/**
* Warm a font set at boot, then lock the registry.
*
* @param list<string> $fontFiles Absolute paths to font files.
*/
public function boot(array $fontFiles): void
{
try {
$this->fonts->warmup($fontFiles);
$this->fonts->lock();
} catch (NextPdfException $e) {
$this->logger->error('Font warmup failed', ['error' => $e->getMessage()]);
throw $e;
}
}
public function redact(string $text): string
{
$result = $this->preprocessor->process($text);
return $result->hasRedactions()
? $result->getDisplayText()
: $text;
}
}

warmup() gevolgd door lock() is de opstartvolgorde van de worker. Na lock() veroorzaakt elke wijziging een uitzondering. Opzoekacties blijven beschikbaar voor verkeer.

  • Een vergrendeld register weigert elke wijzigingsmethode. Voorverwarm en vergrendel het register bij het opstarten; roep register() nooit aan tijdens de afhandeling van een verzoek.
  • registerFromBinary() schrijft de lettertypebytes naar een tijdelijk bestand voordat ze worden geparseerd. Niet-vertrouwde lettertypegegevens vormen een aanvalsoppervlak voor de parser — beheer deze via ExternalResourcePolicyInterface (zie de pagina over het beveiligingsbeleid).
  • Een TextPreprocessor mag geen regeleinden, regelterugloop of tabs toevoegen. Die tekens veranderen de lay-out en doorbreken de eerste invariant van het contract.
  • TextSegment::$originalCharCount is alleen een breedtehint. Het gebruiken om de oorspronkelijke inhoud af te leiden, ondermijnt de redactie en schendt de derde invariant van het contract.
  • TextPreprocessResult::getDisplayText() geeft per ontwerp een lege string terug voor zwarte-rechthoeksegmenten. Beschouw een leeg segment niet als een voorbewerkingsfout.

Lettertypeparsing domineert het eerste gebruik; het register beperkt die kosten tot één keer per proces. Na het voorverwarmen zijn get() en has() O(1)-mapopzoekacties. memoryUsage() geeft een MemoryReport terug, zodat een worker de lettertypecache kan volgen ten opzichte van zijn budget. Tekstvoorbewerking is lineair in de invoerlengte. De segmentlijst voegt begrensde overhead toe, evenredig aan het aantal redactietreffers. Het performance_budget van 1500 ms wandkloktijd en 64 MB piek dekt het voorverwarmen van een typische lettertypeset plus documentweergave. De subsetkosten schalen met het aantal daadwerkelijk gebruikte glyphs, niet met de volledige glyphtabel van het lettertype. Subsetting verkleint daarom de uitvoergrootte en de weergavekosten voor CJK-inhoud.

Het typografiedomein heeft twee beveiligingsrelevante oppervlakken. Het eerste is lettertype-invoer: registerFromBinary() parseert willekeurige bytes. Niet-vertrouwde lettertypegegevens moeten een ExternalResourcePolicyInterface passeren die de bestandsgrootte en het glyphaantal begrenst voordat ze de parser bereiken. Het tweede is redactie: TextPreprocessorInterface draait vóór de glyph-layout, de lettertypesubset, de ToUnicode-CMap en de structuurboom, zodat geredacteerde inhoud nooit in het weergegeven artefact terechtkomt. Een redactie als overlay tijdens het tekenen lekt de oorspronkelijke tekst in de content stream en de subset. De plaatsing van het contract voorkomt die klasse van defecten. De meethint op een segment is bewust niet-omkeerbaar. Beschouw elk extern aangeleverd lettertype of elke externe tekst als niet-vertrouwd.

BeweringStandaardClausuleBewijs
Elk lettertype dat het document gebruikt, is ingebed zodat het document weergegeven wordt zonder afhankelijk te zijn van systeemlettertypen.ISO 32000-2§9
Het ingebedde lettertype is gesubset tot de glyphs waarnaar het document verwijst.ISO 32000-2§9

Beide clausules zijn geparafraseerd. NextPDF reproduceert geen normatieve tekst. PDF/A-4 verplicht inbedding voor elk lettertype. Die conformiteit is gedocumenteerd op de extractie- en toegankelijkheidspagina’s.