Zum Inhalt springen

Eigene Layout-Engines und Text-Interception zur Layout-Zeit

NextPDF stellt keine austauschbare Layout-Engine-Schnittstelle bereit. Der öffentliche Layout-Extension-Vertrag ist TextPreprocessorInterface; diese Schnittstelle fängt Text zur Layout-Zeit ab. Außerdem erhalten Sie die Content-Lifecycle-Events, mit denen Sie beobachten können, was das Layout erzeugt.

Terminal-Fenster
composer require nextpdf/core:^3

Die Layout-Pipeline ist intern. Sie umfasst Glyphen-Layout, Font-Subsetting, die Ausgabe der ToUnicode-CMap und den Strukturbaum; NextPDF erlaubt nicht, sie zu ersetzen. Stabile Byte-Ausgabe und die Konformität für getaggtes PDF beruhen auf einem einzigen kontrollierten Build.

NextPDF legt jedoch sehr wohl den Punkt vor dem Layout offen: TextPreprocessorInterface. Eine Implementierung erhält Rohtext und gibt ein segmentiertes Ergebnis zurück, bevor dieser Text in Glyphen-Layout, Font-Subsetting, die ToUnicode-CMap oder den Strukturbaum eingeht. Das ist der unterstützte Weg, um Textinhalt zu ändern, ohne die Layout-Engine anzufassen.

Der Vertrag enthält in seinem PHPDoc des Quellcodes eine harte Regel: Eine Implementierung darf nicht verändern, wie das Layout arbeitet. Sie darf keine layoutbeeinflussenden Zeichen wie Line Feed, Carriage Return oder Tab hinzufügen, und sie muss die logische Lesereihenfolge erhalten. Der Präprozessor beschreibt einen Inhaltsaustausch; Layout-Entscheidungen trifft er nicht. Halten Sie diese Regel ein, sonst zerstören Sie die stabile Ausgabe und die Barrierefreiheit.

Um das Ergebnis des Layouts zu beobachten — nicht, um es zu ändern — nutzen Sie die Content-Lifecycle-Events in Action-Trigger und Event-Listener. ContentRenderedEvent wird ausgelöst, nachdem Inhalt auf eine Seite gezeichnet wurde. FontLoadedEvent wird einmal pro Schriftfamilie und Stil ausgelöst.

NextPDF\Contracts\TextPreprocessorInterface (stabil, seit 1.9.0):

MethodeRückgabeZweck
process(string $text)TextPreprocessResultWandelt Rohtext um, bevor er in die Render-Pipeline gelangt; gibt ein segmentiertes Ergebnis mit Redaction-Metadaten zurück.

Das zurückgegebene NextPDF\Contracts\TextPreprocessResult ist ein eingefrorenes Value Object. Seine Konstruktorsignatur und seine öffentlichen Eigenschaften sind stabil und ändern sich nicht in einem Minor- oder Patch-Release. Neue Methoden können hinzukommen.

Dieser kleine Präprozessor maskiert ein festes Token. Er fügt keine layoutbeeinflussenden Zeichen hinzu und bewahrt die Lesereihenfolge.

<?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),
]);
}
}

Ein produktionsreifer Präprozessor hält die Matching-Regeln an einer Stelle. Bei einem fehlerhaften Pattern verhält er sich fail-closed und protokolliert niemals den Originaltext.

<?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),
]);
}
}
  • Kein Ersatz des Layouts. Es gibt keinen Vertrag, um Box-Layout, Zeilenumbruch oder Pagination zu ersetzen. Eine Anfrage, eine Layout-Engine eines Drittanbieters einzuklinken, ist per Design nicht abgedeckt.
  • Durchsetzung der Regel. Das Hinzufügen von \n, \r oder \t in process() beschädigt das Layout und zerstört die stabile Ausgabe. Die Engine vertraut auf diese Regel; sie prüft Ihre Ausgabe nicht erneut auf layoutbeeinflussende Zeichen.
  • Lesereihenfolge. Das Umordnen von Segmenten zerstört die Lesereihenfolge von getaggtem PDF und die PDF/UA-Konformität.
  • Eine Verantwortung. Der Präprozessor beschreibt einen Inhaltsaustausch. Nutzen Sie Lifecycle-Events zum Beobachten und schleusen Sie keine Seiteneffekte über process() ein.

process() läuft einmal pro Text-Run auf dem kritischen Layout-Pfad. Halten Sie es speicherschonend. Kompilieren Sie Patterns einmal im Konstruktor, nicht bei jedem Aufruf. Die Content-Lifecycle-Events verursachen keinen Aufwand, wenn kein Listener gebunden ist.

TextPreprocessorInterface ist die unterstützte Stelle, um sensiblen Inhalt zu entfernen, bevor er den Content-Stream, die Font-Subsets oder die Metadaten erreicht. Weil die Vorverarbeitung vor dem Subsetting und der ToUnicode-CMap läuft, gelangen redigierte Glyphen niemals in die Datei. Behandeln Sie ein Scheitern des Präprozessors als fail-closed und nutzen Sie leeren oder maskierten Text, statt das Original auszugeben.

Auf diese Seite treffen keine normativen Signatur- oder Archivierungs-Aussagen zu. Die Regel zur Lesereihenfolge bringt den Vertrag mit den Anforderungen von getaggtem PDF in Einklang. Die Konformität auf Tag-Ebene wird in der Barrierefreiheits-Referenz behandelt.

NextPDF Pro liefert produktionsreife Text-Preprocessing-Strategien, darunter PII-Redaction, abgestimmt auf gängige Dokumenttypen. In Core schreiben Sie TextPreprocessorInterface selbst, oder Sie nutzen einen verifizierten Build der kostenpflichtigen Edition über denselben öffentlichen Vertrag.

Das Glossar definiert Text-Präprozessor und Erweiterungspunkt; die jeweilige kanonische Definition finden Sie im veröffentlichten Glossar.