Ga naar inhoud

ContentStream: emitter voor PDF-contentstreams

De ContentStream-module emitteert marked-content-operatoren voor Portable Document Format (PDF). De module opent en sluit structuurtags en artifacts, houdt de nestdiepte bij en retourneert de operatorbuffer.

Terminal window
composer require nextpdf/core:^3

ContentStreamBuilder is de enige klasse in de module. De klasse bouwt de marked-content-laag van de content stream van een pagina op. Een content stream codeert de pagina-inhoud als een reeks operatoren — ISO 32000-2 §8. De builder emitteert vervolgens marked-content-operatoren rond die inhoud.

append() voegt ruwe operatorbytes ongewijzigd toe. De builder escapet deze invoer niet. De aanroeper is verantwoordelijk voor de geldigheid ervan. Gebruik deze grens wanneer de HTML-pipeline en de Graphics-module hun eigen operatoren moeten invoegen.

beginTag() opent een reeks met structuurtag. De methode emitteert een BDC-operator met een MCID-eigenschappenlijst, conform ISO 32000-2 §14.6. endTag() emitteert de bijbehorende EMC-operator. De builder houdt de nestdiepte bij. Een aanroep van endTag() zonder open reeks werpt PageLayoutException op in plaats van een ongebalanceerde EMC te schrijven.

beginArtifact() opent een artifactreeks. Gebruik artifacts voor pagineringsdecoratie — kop- en voetteksten, paginanummers en lijnen — die buiten de structuurboom moet blijven, conform ISO 32000-2 §14.8.2.2. Het subtype is een van vier ISO-waarden: Pagination, Layout, Page of Background. Gebruik bij voorkeur de getypeerde ArtifactSubtype-enum. De string-overload wordt gevalideerd tegen de enum, zodat een niet-standaardwaarde meteen tot een fout leidt.

relabelTag() herschrijft een eerder geëmitteerde tag op zijn plaats. finish() retourneert de volledige buffer en werpt een uitzondering als de marked content ongebalanceerd is. drain() retourneert de buffer tot dusver zonder balanscontrole, voor incrementele streaming. peek() retourneert de buffer zonder deze te verbruiken. reset() wist de status.

MethodeSignatuurRol
append()append(string $raw): voidVoegt ruwe operatorbytes ongewijzigd toe (geen escaping)
beginTag()beginTag(string $structType, int $mcid): voidOpent een BDC-reeks met structuurtag
endTag()endTag(): voidSluit de binnenste reeks af met EMC
beginArtifact()beginArtifact(ArtifactSubtype|string $type): voidOpent een artifactreeks
endArtifact()endArtifact(): voidSluit het binnenste artifact af
getMarkedContentDepth()getMarkedContentDepth(): intRetourneert de huidige nestdiepte
relabelTag()relabelTag(string $old, string $new, int $mcid): voidHerschrijft een geëmitteerde tag op zijn plaats
finish()finish(): stringRetourneert de volledige buffer; werpt een uitzondering bij onbalans
drain()drain(): stringRetourneert de buffer zonder balanscontrole
peek()peek(): stringRetourneert de buffer zonder deze te verbruiken
reset()reset(): voidWist alle status

Voer composer docs:generate-api-php -- --module=ContentStream uit om de volledige PHPDoc-tabel te genereren.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\ContentStream\ContentStreamBuilder;
$builder = new ContentStreamBuilder();
$builder->beginTag('P', mcid: 0);
$builder->append("BT /F1 12 Tf 72 720 Td (Hello) Tj ET\n");
$builder->endTag();
$pageContent = $builder->finish();

Gebruik dit patroon om een alinea in een structuurtag en een voettekst in een artifact te omsluiten. Het patroon streamt de buffer incrementeel met drain().

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Accessibility\ArtifactSubtype;
use NextPDF\ContentStream\ContentStreamBuilder;
$builder = new ContentStreamBuilder();
$builder->beginTag('H1', mcid: 0);
$builder->append($titleOperators);
$builder->endTag();
$builder->beginArtifact(ArtifactSubtype::Pagination);
$builder->append($footerOperators);
$builder->endArtifact();
if ($builder->getMarkedContentDepth() !== 0) {
throw new RuntimeException('Unbalanced marked content before flush.');
}
$chunk = $builder->drain();
  • append() escapet de invoer niet. Geef uitsluitend geldige operatorbytes door. De builder vertrouwt de aanroeper.
  • endTag() en endArtifact() werpen een uitzondering bij onderloop. Sluit nooit een reeks die niet open is.
  • finish() controleert de balans en werpt een uitzondering als de diepte niet nul is. drain() controleert dit niet. Gebruik drain() uitsluitend voor incrementele streaming.
  • De nestdiepteteller maakt geen onderscheid tussen tags en artifacts. EMC sluit de binnenste reeks van beide soorten af. Houd bij het nesten de strikte volgorde aan.
  • De string-overload van beginArtifact() wordt gevalideerd tegen de enum. Een niet-standaardsubtype leidt bij de aanroep tot een fout, niet pas in de uitvoer.
  • relabelTag() herschrijft een geëmitteerde tag. Gebruik dezelfde mcid waarmee de tag is geëmitteerd.

Elke bewerking is een O(1)-string-append, behalve relabelTag(), dat een O(buffer)-herschrijving uitvoert. De module houdt één stringbuffer en één integer-nestdiepteteller aan. De module voert geen parsing uit en alloceert alleen de buffer. Het budget voor de referentieworkload is 1500 ms wandkloktijd en 64 MB piekgeheugen. Deze module blijft daar ruim onder.

append() is de vertrouwensgrens. De builder schrijft bytes ongewijzigd weg, dus upstream-code moet elke string escapen voordat die een literal-string-operator bereikt. De canonieke escaper is PdfStringEscaper::escapeLiteral() (ADR-015). Geef nooit niet-geëscapete gebruikerstekst door via append(). De balanscontroles in endTag(), endArtifact() en finish() voorkomen dat een ongeldige marked-content-boom de Writer bereikt. Zie /modules/core/security/ voor het dreigingsmodel van het document.

De module emitteert marked-content-operatorstructuren die consistent zijn met ISO 32000-2: BDC/EMC-paren met een MCID-eigenschappenlijst conform §14.6, en artifactreeksen conform §14.8.2.2. Dit zijn implementatiefeiten. Het bewijs bestaat uit src/ContentStream/ContentStreamBuilder.php, de src/Accessibility/ArtifactSubtype.php-enum en tests/Unit/ContentStream/ContentStreamBuilderMarkedContentBalanceCoverageTest plus ContentStreamBuilderRelabelTagInvariantTest. Deze vormen geen claim van end-to-end PDF/UA-2- of PDF 2.0-conformiteit. Een externe oracle valideert de getagde PDF-structuur waaraan deze operatoren deelnemen: tests/Integration/Accessibility/VeraPdfUa2GoldenTest controleert een gegenereerde fixture met veraPDF tegen het PDF/UA-2-profiel. Die oracle-test wordt overgeslagen wanneer het veraPDF-binary ontbreekt, dus het is een opt-in-gate. Formuleer het als: deze module „produceert marked-content-structuren; PDF/UA-2-conformiteit wordt door veraPDF gevalideerd”, in plaats van onvoorwaardelijke conformiteit te beweren.