Salta ai contenuti

ContentStream: emettitore di content stream PDF

Il modulo ContentStream emette operatori di marked content: apre e chiude tag di struttura e artifact, tiene traccia della profondità di annidamento e restituisce il buffer degli operatori.

Terminal window
composer require nextpdf/core:^3

ContentStreamBuilder è l’unica classe del modulo. Costruisce il livello di marked content di un content stream di pagina. Un content stream codifica il contenuto della pagina come sequenza di operatori — ISO 32000-2 §8. Il builder emette gli operatori di marked content attorno a quel contenuto.

append() aggiunge i byte grezzi degli operatori così come sono. Il builder non applica l’escape a questo input; la validità dell’input resta responsabilità del chiamante. È qui che la pipeline HTML e il modulo Graphics intercalano i propri operatori.

beginTag() apre una sequenza contrassegnata da un tag di struttura. Emette un operatore BDC con una property list MCID, come da ISO 32000-2 §14.6. endTag() emette l’operatore EMC corrispondente. Il builder tiene traccia della profondità di annidamento. Un endTag() senza sequenze aperte genera PageLayoutException anziché scrivere un EMC non bilanciato.

beginArtifact() apre una sequenza di artifact. Un artifact contiene decorazioni di impaginazione — intestazioni, piè di pagina, numeri di pagina, righe — che devono restare al di fuori dell’albero della struttura, come da ISO 32000-2 §14.8.2.2. Il sottotipo è uno dei quattro valori ISO: Pagination, Layout, Page o Background. È preferibile usare l’enum tipizzato ArtifactSubtype. L’overload a stringa viene convalidato rispetto all’enum; pertanto un valore non standard fallisce immediatamente.

relabelTag() riscrive sul posto un tag già emesso. finish() restituisce l’intero buffer e genera un’eccezione se il marked content non è bilanciato. drain() restituisce il buffer accumulato finora senza il controllo di bilanciamento, per lo streaming incrementale. peek() restituisce il buffer senza consumarlo. reset() azzera lo stato.

MetodoFirmaRuolo
append()append(string $raw): voidAggiunge i byte grezzi degli operatori così come sono (senza escape)
beginTag()beginTag(string $structType, int $mcid): voidApre una sequenza BDC di struttura
endTag()endTag(): voidChiude la sequenza più interna con EMC
beginArtifact()beginArtifact(ArtifactSubtype|string $type): voidApre una sequenza di artifact
endArtifact()endArtifact(): voidChiude l’artifact più interno
getMarkedContentDepth()getMarkedContentDepth(): intRestituisce la profondità di annidamento corrente
relabelTag()relabelTag(string $old, string $new, int $mcid): voidRiscrive sul posto un tag emesso
finish()finish(): stringRestituisce l’intero buffer; genera un’eccezione se non è bilanciato
drain()drain(): stringRestituisce il buffer senza il controllo di bilanciamento
peek()peek(): stringRestituisce il buffer senza consumarlo
reset()reset(): voidAzzera tutto lo stato

Eseguire composer docs:generate-api-php -- --module=ContentStream per ottenere la tabella PHPDoc completa.

<?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();

Racchiude un paragrafo in un tag di struttura e un piè di pagina in un artifact. Esegue lo streaming incrementale del buffer tramite 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() non applica l’escape all’input. Passare soltanto byte di operatori validi. Il builder si fida del chiamante.
  • endTag() ed endArtifact() generano un’eccezione in caso di underflow. Non chiudere mai una sequenza che non sia aperta.
  • finish() verifica il bilanciamento e genera un’eccezione quando la profondità non è zero. drain() non esegue il controllo. Usare drain() solo per lo streaming incrementale.
  • Il contatore di profondità non distingue i tag dagli artifact. EMC chiude la sequenza più interna di entrambi i tipi. Annidarli in ordine rigoroso.
  • L’overload a stringa di beginArtifact() viene convalidato rispetto all’enum. Un sottotipo non standard fallisce al momento della chiamata, non nell’output.
  • relabelTag() riscrive un tag emesso. Usare lo stesso mcid impiegato per emetterlo.

Ogni operazione consiste in un’append di stringa O(1) oppure, per relabelTag(), in una riscrittura O(buffer). Il modulo mantiene un buffer di stringa e un contatore di profondità intero. Non vengono eseguiti parsing né allocazioni oltre al buffer. Il budget del carico di lavoro di riferimento è di 1500 ms di tempo reale e 64 MB di picco. Questo modulo rimane ampiamente entro tali limiti.

append() è il confine di attendibilità. Il builder scrive i byte così come sono, pertanto il codice a monte deve applicare l’escape a qualsiasi stringa che raggiunga un operatore di stringa letterale. L’escaper canonico è PdfStringEscaper::escapeLiteral() (ADR-015). Non passare mai testo utente senza escape tramite append(). I controlli di bilanciamento in endTag(), endArtifact() e finish() impediscono che un albero di marked content malformato raggiunga il Writer. Vedere /modules/core/security/ per il modello delle minacce del documento.

Il modulo emette strutture di operatori di marked content coerenti con ISO 32000-2: coppie BDC/EMC con un property list MCID come da §14.6, e sequenze di artifact come da §14.8.2.2. Si tratta di fatti di implementazione. L’evidenza è src/ContentStream/ContentStreamBuilder.php, l’enum src/Accessibility/ArtifactSubtype.php e tests/Unit/ContentStream/ContentStreamBuilderMarkedContentBalanceCoverageTest oltre a ContentStreamBuilderRelabelTagInvariantTest. Non costituiscono un’affermazione di conformità PDF/UA-2 o PDF 2.0 end-to-end. La struttura tagged-PDF a cui questi operatori partecipano viene convalidata da un oracolo esterno: tests/Integration/Accessibility/VeraPdfUa2GoldenTest verifica un fixture generato rispetto a veraPDF per il profilo PDF/UA-2. Tale test oracolo viene saltato quando il binario veraPDF è assente, pertanto è un gate facoltativo (opt-in). Dichiarare che questo modulo «produce strutture di marked content; la conformità PDF/UA-2 è convalidata da veraPDF» anziché asserire una conformità senza qualifiche.