Aller au contenu

Content : modèle de contenu textuel et structuré

Le module Content construit les opérateurs d’affichage et d’état de texte, les ombres de texte, le JavaScript au niveau du document et les dictionnaires de propriétés de contenu marqué. Il forme la couche entre la mise en page et le flux de contenu.

Fenêtre de terminal
composer require nextpdf/core:^3

Content contient les primitives qui transforment le texte résolu en opérateurs PDF. TextRenderer est le composant central. Il construit l’opérateur d’affichage de texte pour une chaîne, ainsi que les opérateurs d’état de texte qui le précèdent. L’opérateur Tj peint les glyphes d’une chaîne avec la police courante et les autres paramètres graphiques liés au texte — ISO 32000-2 §9. TextRenderer choisit entre un opérateur d’affichage unique et un tableau TJ positionné selon le TypographyMode actif. Il applique les ajustements de crénage lorsque le mode utilise des tableaux TJ.

L’état de texte est modélisé de bout en bout. setTextRenderingMode() accepte une énumération TextRenderingMode. Ses huit cas correspondent un à un aux modes de rendu de texte ISO 32000-2 : remplissage, contour, remplissage puis contour, invisible, et les quatre variantes de détourage (Table 104). Le renderer contrôle aussi la largeur du contour, l’espacement des caractères et des mots, l’étirement horizontal, l’élévation du texte, la direction de droite à gauche et un Hyphenator optionnel. L’appel à buildTextStateOperators() émet l’état accumulé sous la forme d’un seul bloc d’opérateurs.

TextShadow est un objet valeur : couleur, décalages X et Y en unités utilisateur, et opacité. Le renderer l’utilise pour émettre une seconde passe de tracé décalée. Les décalages par défaut sont des valeurs discrètes de 0.5/−0.5 avec une opacité de 0.5, ce qui donne une ombre douce de style CSS.

JavaScriptManager gère le scripting au niveau du document. includeJs() enregistre un script de document. addJsObject() enregistre un objet script nommé. writeJavaScript() / writeOpenAction() les sérialisent dans le catalogue et l’OpenAction. Le gestionnaire vérifie et encode chaque corps de script au format chaîne PDF avant émission.

PropertiesRegistry est le magasin des propriétés de contenu marqué. register() renvoie un index de balise stable pour un dictionnaire de propriétés. registerOcg() / registerOcgs() lient les groupes de contenu optionnel par numéro d’objet. writeProperties() sérialise le registre dans le dictionnaire de ressources de la page. Ce sont les données que le module ContentStream référence lorsqu’il ouvre une séquence marquée avec une liste de propriétés.

Deux décodeurs d’image se trouvent ici, car il s’agit de formats natifs PDF traités en pass-through. JBig2Loader et JpxLoader analysent les structures de segments JBIG2 et JPEG 2000, puis renvoient un ImageData sans jamais rastériser de pixels. Les octets encodés sont transmis au viewer sans modification. Lorsqu’une source JBIG2 contient un segment de globals distinct, JBig2Loader l’intègre via une référence de flux /JBIG2Globals sur l’XObject image ; la forme in-stream/in-line continue d’être préservée à l’aller-retour comme auparavant. Il s’agit uniquement de câblage structurel — les octets de globals sont transmis sans rastérisation, et non décodés.

ClasseMéthodes clésRôle
TextRendererbuildTextShowOperator(), buildTextStateOperators(), setTextRenderingMode(), setTextStrokeWidth(), setTextShadow(), setFontSpacing(), setWordSpacing(), setFontStretching(), setTextRise(), setRTL(), setHyphenation()Constructeur d’opérateurs d’affichage de texte et d’état de texte
TextRenderingMode (énumération)Fill, Stroke, FillStroke, Invisible, FillClip, StrokeClip, FillStrokeClip, ClipModes de rendu de texte ISO 32000-2
TextShadow__construct(Color, offsetX, offsetY, opacity)Objet valeur pour une passe de tracé décalée
JavaScriptManagerincludeJs(), addJsObject(), hasJavaScript(), writeJavaScript(), writeOpenAction()Câblage du catalogue pour le JavaScript au niveau du document
PropertiesRegistryregister(), getTagIndex(), registerOcg(), registerOcgs(), getAll(), writeProperties()Magasin de propriétés de contenu marqué et d’OCG
JBig2Loaderload(), loadFromString(), parseSegments()Décodeur JBIG2 en pass-through
JpxLoaderload(), loadFromString(), parseBoxes()Décodeur JPEG 2000 en pass-through

Exécute composer docs:generate-api-php -- --module=Content pour obtenir le tableau PHPDoc complet.

Source : examples/28-text-rendering.php.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Content\TextRenderer;
use NextPDF\Content\TextRenderingMode;
$renderer = new TextRenderer();
$renderer
->setTextRenderingMode(TextRenderingMode::FillStroke)
->setTextStrokeWidth(0.3)
->setWordSpacing(0.5);
$stateOps = $renderer->buildTextStateOperators();

Cet exemple ajoute une ombre douce et un césureur, puis construit l’opérateur d’affichage avec une fonction d’échappement fournie par l’appelant (le seam canonique PdfStringEscaper de l’ADR-015).

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Content\TextRenderer;
use NextPDF\Content\TextShadow;
use NextPDF\Graphics\Color;
use NextPDF\Support\PdfStringEscaper;
$renderer = new TextRenderer();
$renderer
->setTextShadow(new TextShadow(Color::rgb(0, 0, 0), 0.4, -0.4, 0.45))
->setRTL(false);
$showOp = $renderer->buildTextShowOperator(
text: 'Quarterly report',
fontKey: 'F1',
metrics: $fontMetrics,
escapeSegment: static fn (string $s): string => PdfStringEscaper::escapeLiteral($s),
);
$pageContent = $renderer->buildTextStateOperators() . $showOp;
  • buildTextShowOperator() renvoie une chaîne vide pour une entrée vide. N’émets pas de Tj vide — ajoute une protection en amont si ta mise en page peut produire des passages vides.
  • Le callback d’échappement est obligatoire et porte la responsabilité de la sécurité des chaînes. Passe le PdfStringEscaper::escapeLiteral() canonique (ADR-015). Un échappeur partiel produit une chaîne littérale syntaxiquement invalide.
  • TextShadow::offsetY est négatif vers le bas avec une origine en haut à gauche. Un Y positif pousse l’ombre vers le haut, ce qui est rarement voulu.
  • JavaScriptManager valide l’entrée des scripts. Un corps de script invalide est rejeté à l’enregistrement, et non ignoré silencieusement au moment de l’écriture.
  • JBig2Loader et JpxLoader ne rastérisent jamais. Ils valident et transmettent les octets encodés. Un segment corrompu est remonté sous la forme d’une erreur d’analyse, et non d’une image vide.
  • PropertiesRegistry::register() est idempotente pour un même dictionnaire — des dictionnaires de propriétés identiques réutilisent un seul index de balise.

La construction des opérateurs est en O(n) par rapport à la longueur de la chaîne, plus une passe de crénage en O(n) lorsque le mode typographique utilise des tableaux TJ. Aucun coût de mise en page ni de shaping n’est payé ici : ces étapes restent dans les modules Typography et Layout. La sérialisation du JavaScript et des propriétés est en O(entrées). Les chargeurs d’image en pass-through analysent en O(octets) avec un coût de décodage nul. C’est leur principal avantage pour les charges de travail de documents numérisés. Le performance_budget pour la charge de travail de référence est de 1500 ms en temps réel et 64 Mo en pic.

JavaScriptManager accepte des corps de script qui peuvent provenir de templates non fiables. Il vérifie chaque corps et l’encode au format chaîne PDF, mais le JavaScript de document reste une surface de contenu actif. Désactive-le pour une sortie non fiable, ou retire-le avec le chemin d’assainissement décrit dans /modules/core/security/. JBig2Loader et JpxLoader analysent des structures de segments non fiables : limite la taille d’entrée et le temps d’analyse, et exécute l’extraction dans un worker contraint lorsque la source est fournie par l’utilisateur. La frontière d’échappement du texte se situe au niveau du callback fourni par l’appelant. Passe toujours l’échappeur canonique afin que les octets de contrôle ne puissent pas s’échapper d’une chaîne littérale.

Le module émet des opérateurs d’affichage de texte et d’état de texte cohérents avec le modèle de texte ISO 32000-2 §9. Cela inclut la sémantique de l’opérateur Tj et les modes de rendu de la Table 104 que l’énumération TextRenderingMode reflète. Ce sont des faits d’implémentation : les formes des opérateurs sont produites par src/Content/TextRenderer.php et l’énumération TextRenderingMode, et couvertes par tests/Unit/Content/TextRenderer*, JavaScriptManagerIsoTest, et PropertiesRegistryTest. Ces faits n’affirment pas la conformité PDF 2.0 de bout en bout. Le contrat d’échappement des chaînes suit l’ADR-015 et l’ISO 32000-2 §7.3.4.2. Les chemins en pass-through JBIG2 et JPEG 2000 préservent les flux encodés sans modification. Un segment de globals JBIG2 distinct est intégré sous la forme d’une référence de flux /JBIG2Globals sur l’XObject image — vérifié comme câblage structurel, et non comme une affirmation de fidélité de décodage. La conformité au niveau du document est validée par l’oracle et les suites golden dans /modules/core/conformance/.