ContentStream: emettitore di content stream PDF
In breve
Sezione intitolata “In breve”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.
Installazione
Sezione intitolata “Installazione”composer require nextpdf/core:^3Panoramica concettuale
Sezione intitolata “Panoramica concettuale”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.
Superficie API
Sezione intitolata “Superficie API”| Metodo | Firma | Ruolo |
|---|---|---|
append() | append(string $raw): void | Aggiunge i byte grezzi degli operatori così come sono (senza escape) |
beginTag() | beginTag(string $structType, int $mcid): void | Apre una sequenza BDC di struttura |
endTag() | endTag(): void | Chiude la sequenza più interna con EMC |
beginArtifact() | beginArtifact(ArtifactSubtype|string $type): void | Apre una sequenza di artifact |
endArtifact() | endArtifact(): void | Chiude l’artifact più interno |
getMarkedContentDepth() | getMarkedContentDepth(): int | Restituisce la profondità di annidamento corrente |
relabelTag() | relabelTag(string $old, string $new, int $mcid): void | Riscrive sul posto un tag emesso |
finish() | finish(): string | Restituisce l’intero buffer; genera un’eccezione se non è bilanciato |
drain() | drain(): string | Restituisce il buffer senza il controllo di bilanciamento |
peek() | peek(): string | Restituisce il buffer senza consumarlo |
reset() | reset(): void | Azzera tutto lo stato |
Eseguire composer docs:generate-api-php -- --module=ContentStream per ottenere la tabella PHPDoc completa.
Esempio di codice — Avvio rapido
Sezione intitolata “Esempio di codice — Avvio rapido”<?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();Esempio di codice — Produzione
Sezione intitolata “Esempio di codice — Produzione”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();Casi limite e insidie
Sezione intitolata “Casi limite e insidie”append()non applica l’escape all’input. Passare soltanto byte di operatori validi. Il builder si fida del chiamante.endTag()edendArtifact()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. Usaredrain()solo per lo streaming incrementale.- Il contatore di profondità non distingue i tag dagli artifact.
EMCchiude 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 stessomcidimpiegato per emetterlo.
Prestazioni
Sezione intitolata “Prestazioni”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.
Note sulla sicurezza
Sezione intitolata “Note sulla sicurezza”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.
Conformità
Sezione intitolata “Conformità”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.