Aller au contenu

Moteurs de mise en page personnalisés et interception du texte pendant la mise en page

NextPDF n’expose pas d’interface enfichable pour un moteur de mise en page. Le contrat public d’extension de mise en page est TextPreprocessorInterface, qui intervient sur le texte pendant la mise en page. Tu disposes aussi des événements de cycle de vie du contenu, qui te permettent d’observer ce que la mise en page produit.

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

Le pipeline de mise en page est interne. Il couvre la mise en page des glyphes, la création des sous-ensembles de polices, la génération de la CMap ToUnicode et l’arbre de structure, et NextPDF ne te laisse pas le remplacer. La stabilité de la sortie en octets et la conformité du PDF balisé reposent sur une build unique et contrôlée.

Ce que NextPDF expose bien, c’est le point d’intervention avant la mise en page : TextPreprocessorInterface. Une implémentation reçoit du texte brut et renvoie un résultat segmenté avant que ce texte n’entre dans la mise en page des glyphes, la création des sous-ensembles de polices, la CMap ToUnicode ou l’arbre de structure. C’est la méthode prise en charge pour modifier le contenu textuel sans toucher au moteur de mise en page.

La PHPDoc du contrat énonce une règle stricte : une implémentation ne doit pas changer le fonctionnement de la mise en page. Elle ne doit pas ajouter de caractères affectant la mise en page, tels que le saut de ligne, le retour chariot ou la tabulation, et elle doit conserver l’ordre de lecture logique. Le préprocesseur déclare un remplacement de contenu ; il ne fait pas de choix de mise en page. Respecte cette règle : sinon tu casses la stabilité de la sortie et l’accessibilité.

Pour observer le résultat de la mise en page sans le modifier, utilise les événements de cycle de vie du contenu dans Déclencheurs d’action et écouteurs d’événements. ContentRenderedEvent se déclenche après le dessin du contenu sur une page. FontLoadedEvent se déclenche une fois par famille et style de police.

NextPDF\Contracts\TextPreprocessorInterface (stable, depuis la version 1.9.0) :

MéthodeRetourneRôle
process(string $text)TextPreprocessResultTransforme le texte brut avant le pipeline de rendu ; renvoie un résultat segmenté avec des métadonnées de caviardage.

Le NextPDF\Contracts\TextPreprocessResult renvoyé est un objet-valeur figé. La signature de son constructeur et ses propriétés publiques sont stables, et elles ne changent pas lors d’une version mineure ou corrective. De nouvelles méthodes peuvent être ajoutées.

Ce petit préprocesseur masque un jeton fixe. Il n’ajoute aucun caractère affectant la mise en page et conserve l’ordre de lecture.

<?php
declare(strict_types=1);
use NextPDF\Contracts\TextPreprocessorInterface;
use NextPDF\Contracts\TextPreprocessResult;
use NextPDF\Contracts\TextSegment;
final class TokenMaskingPreprocessor implements TextPreprocessorInterface
{
public function process(string $text): TextPreprocessResult
{
$masked = \str_replace('SECRET-TOKEN', '••••••••••••', $text);
return new TextPreprocessResult([
new TextSegment($masked, redacted: $masked !== $text),
]);
}
}

Un préprocesseur de production conserve les règles de correspondance au même endroit. Il échoue en mode fermé en cas de motif incorrect et ne journalise jamais le texte d’origine.

<?php
declare(strict_types=1);
use NextPDF\Contracts\TextPreprocessorInterface;
use NextPDF\Contracts\TextPreprocessResult;
use NextPDF\Contracts\TextSegment;
use Psr\Log\LoggerInterface;
final class PatternRedactionPreprocessor implements TextPreprocessorInterface
{
/**
* @param non-empty-string $pattern A valid PCRE pattern for sensitive spans
*/
public function __construct(
private readonly string $pattern,
private readonly LoggerInterface $logger,
) {}
public function process(string $text): TextPreprocessResult
{
$result = \preg_replace($this->pattern, '[REDACTED]', $text);
if ($result === null) {
// Fail closed: never emit unredacted text on a pattern error.
$this->logger->error('Redaction pattern failed; substituting empty text');
return new TextPreprocessResult([new TextSegment('', redacted: true)]);
}
return new TextPreprocessResult([
new TextSegment($result, redacted: $result !== $text),
]);
}
}
  • Aucun remplacement de la mise en page. Il n’existe aucun contrat pour remplacer la mise en page en boîtes, la coupure de lignes ou la pagination. Une demande visant à brancher un moteur de mise en page tiers est hors périmètre par conception.
  • Respect de la règle. Ajouter \n, \r ou \t dans process() corrompt la mise en page et casse la stabilité de la sortie. Le moteur part du principe que la règle est respectée ; il ne revérifie pas ta sortie pour détecter les caractères affectant la mise en page.
  • Ordre de lecture. Réordonner les segments casse l’ordre de lecture du PDF balisé et la conformité PDF/UA.
  • Une seule responsabilité. Le préprocesseur déclare un remplacement de contenu. Utilise les événements de cycle de vie pour observer et ne fais pas transiter d’effets de bord par process().

process() s’exécute une fois par séquence de texte sur le chemin critique de la mise en page. Veille à ce qu’il reste léger en mémoire. Compile les motifs une seule fois dans le constructeur, pas à chaque appel. Les événements de cycle de vie du contenu ne coûtent rien quand aucun écouteur n’est enregistré.

TextPreprocessorInterface est l’endroit pris en charge pour retirer le contenu sensible avant qu’il n’atteigne le flux de contenu, les sous-ensembles de polices ou les métadonnées. Comme il s’exécute avant la création des sous-ensembles de polices et la CMap ToUnicode, les glyphes caviardés n’entrent jamais dans le fichier. Traite un échec du préprocesseur comme un échec en mode fermé et utilise du texte vide ou masqué plutôt que d’émettre l’original.

Aucune assertion normative de signature ou d’archivage ne s’applique à cette page. La règle d’ordre de lecture met le contrat en phase avec les besoins du PDF balisé. La conformité au niveau des balises est traitée dans la référence d’accessibilité.

NextPDF Pro fournit des stratégies de prétraitement de texte prêtes pour la production, dont le caviardage des données personnelles adapté aux types de documents courants. Dans le Core, tu écris TextPreprocessorInterface toi-même, ou tu utilises une build vérifiée d’une édition payante à travers le même contrat public.

Le glossaire définit text preprocessor et extension point ; consulte le glossaire publié pour leurs définitions canoniques.