ContentStream : émetteur de flux de contenu PDF
Le module ContentStream émet des opérateurs de contenu balisé. Il ouvre et ferme les balises de structure et les artefacts, suit la profondeur d’imbrication et renvoie le tampon d’opérateurs.
Installation
Section intitulée « Installation »composer require nextpdf/core:^3Vue d’ensemble conceptuelle
Section intitulée « Vue d’ensemble conceptuelle »ContentStreamBuilder est la seule classe de ce module. Il construit, dans un flux de contenu de page, la couche de contenu balisé. Un flux de contenu encode le contenu de la page sous la forme d’une séquence d’opérateurs, conformément à ISO 32000-2 §8. Le constructeur émet les opérateurs de contenu balisé autour de ce contenu.
append() ajoute des octets d’opérateur bruts tels quels. Le constructeur n’échappe pas cette entrée. L’appelant est responsable de sa validité. C’est là que le pipeline HTML et le module Graphics intercalent leurs propres opérateurs.
beginTag() ouvre une séquence balisée de structure. Il émet un opérateur BDC accompagné d’une liste de propriétés MCID, conformément à ISO 32000-2 §14.6. endTag() émet l’opérateur EMC correspondant. Le constructeur comptabilise la profondeur d’imbrication. Un appel à endTag() sans séquence ouverte lève PageLayoutException au lieu d’écrire un EMC déséquilibré.
beginArtifact() ouvre une séquence d’artefact. Un artefact contient les éléments de décoration de pagination — en-têtes, pieds de page, numéros de page, filets — qui doivent rester hors de l’arbre de structure, conformément à ISO 32000-2 §14.8.2.2. Le sous-type est l’une des quatre valeurs ISO : Pagination, Layout, Page ou Background. Privilégie l’enum typé ArtifactSubtype. La surcharge sous forme de chaîne est validée par rapport à l’enum, si bien qu’une valeur non standard échoue immédiatement.
relabelTag() réécrit sur place une balise déjà émise. finish() renvoie le tampon complet et lève une exception si le contenu balisé est déséquilibré. drain() renvoie le tampon accumulé jusque-là sans vérifier l’équilibre, pour un streaming incrémental. peek() renvoie le tampon sans le consommer. reset() efface l’état.
Surface de l’API
Section intitulée « Surface de l’API »| Méthode | Signature | Rôle |
|---|---|---|
append() | append(string $raw): void | Ajoute des octets d’opérateur bruts tels quels (sans échappement) |
beginTag() | beginTag(string $structType, int $mcid): void | Ouvre une séquence BDC de structure |
endTag() | endTag(): void | Ferme la séquence la plus interne avec EMC |
beginArtifact() | beginArtifact(ArtifactSubtype|string $type): void | Ouvre une séquence d’artefact |
endArtifact() | endArtifact(): void | Ferme l’artefact le plus interne |
getMarkedContentDepth() | getMarkedContentDepth(): int | Renvoie la profondeur d’imbrication actuelle |
relabelTag() | relabelTag(string $old, string $new, int $mcid): void | Réécrit sur place une balise émise |
finish() | finish(): string | Renvoie le tampon complet ; lève une exception si déséquilibré |
drain() | drain(): string | Renvoie le tampon sans la vérification d’équilibre |
peek() | peek(): string | Renvoie le tampon sans le consommer |
reset() | reset(): void | Efface tout l’état |
Lance composer docs:generate-api-php -- --module=ContentStream pour obtenir le tableau PHPDoc complet.
Exemple de code — Démarrage rapide
Section intitulée « Exemple de code — Démarrage rapide »<?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();Exemple de code — Production
Section intitulée « Exemple de code — Production »Cet exemple encapsule un paragraphe dans une balise de structure et un pied de page dans un artefact. Il récupère le tampon de manière incrémentale avec 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();Cas limites et pièges
Section intitulée « Cas limites et pièges »append()n’échappe pas l’entrée. Ne passe que des octets d’opérateur valides. Le constructeur fait confiance à l’appelant.endTag()etendArtifact()lèvent une exception en cas de sous-dépassement. Ne ferme jamais une séquence qui n’est pas ouverte.finish()vérifie l’équilibre et lève une exception si la profondeur n’est pas nulle.drain()ne vérifie pas. N’utilisedrain()que pour un streaming incrémental.- Le compteur de profondeur ne distingue pas les balises des artefacts.
EMCferme la séquence la plus interne, quel que soit son type. Imbrique-les dans un ordre strict. - La surcharge sous forme de chaîne de
beginArtifact()est validée par rapport à l’enum. Un sous-type non standard échoue à l’appel, pas dans la sortie. relabelTag()réécrit une balise émise. Utilise le mêmemcidque celui utilisé lors de son émission.
Performance
Section intitulée « Performance »Chaque opération correspond à un ajout de chaîne en O(1), ou à une réécriture en O(tampon) pour relabelTag(). Le module ne conserve qu’un seul tampon de chaîne et un seul compteur entier de profondeur. Il n’effectue aucune analyse et n’alloue rien au-delà du tampon. Le budget de la charge de travail de référence est de 1500 ms de temps écoulé et de 64 Mo en pic. Ce module reste très en dessous.
Notes de sécurité
Section intitulée « Notes de sécurité »append() est la frontière de confiance. Le constructeur écrit les octets tels quels, donc le code en amont doit échapper toute chaîne destinée à un opérateur de chaîne littérale. L’échappeur canonique est PdfStringEscaper::escapeLiteral() (ADR-015). Ne fais jamais transiter de texte utilisateur non échappé par append(). Les vérifications d’équilibre dans endTag(), endArtifact() et finish() empêchent un arbre de contenu balisé malformé d’atteindre le Writer. Consulte /modules/core/security/ pour le modèle de menace du document.
Conformité
Section intitulée « Conformité »Le module émet des structures d’opérateurs de contenu balisé cohérentes avec ISO 32000-2 : paires BDC/EMC assorties d’une liste de propriétés MCID selon §14.6, et séquences d’artefact selon §14.8.2.2. Il s’agit de faits d’implémentation. La preuve réside dans src/ContentStream/ContentStreamBuilder.php, dans l’enum src/Accessibility/ArtifactSubtype.php, ainsi que dans tests/Unit/ContentStream/ContentStreamBuilderMarkedContentBalanceCoverageTest et ContentStreamBuilderRelabelTagInvariantTest. Ces éléments ne constituent pas une affirmation de conformité PDF/UA-2 ou PDF 2.0 de bout en bout. La structure de PDF balisé à laquelle participent ces opérateurs est validée par un oracle externe : tests/Integration/Accessibility/VeraPdfUa2GoldenTest vérifie une fixture générée contre veraPDF pour le profil PDF/UA-2. Ce test oracle est ignoré lorsque le binaire veraPDF est absent ; c’est donc un garde-fou facultatif. Indique que ce module « produit des structures de contenu balisé ; la conformité PDF/UA-2 est validée par veraPDF » plutôt que d’affirmer une conformité sans réserve.