Zum Inhalt springen

AST: Semantischer Dokumentbaum und Serialisierung

Das AST-Modul bildet den semantischen Dokumentbaum der Engine. Es modelliert ein Dokument als typisierte Knotenhierarchie aus Document, Section, Heading, Paragraph, List, Table, Figure, Code, FormField mit Bounding-Boxen, Zitatankern und einer versionierten JSON-Serialisierung. Die Accessibility-Tagging-Schicht nutzt ihn, um einen Strukturbaum zu erzeugen.

Stabilität: experimentell. Dies ist eine interne Modelloberfläche. Ihre Klassen haben keine versionsfixierten Public-API-Garantien, und die Knotenmenge sowie die Knoten- attribute werden weiterentwickelt. Das Serialisierungsschema wird unabhängig versioniert (AstDocument::CURRENT_SCHEMA_VERSION = '1.0.0'). Der Serializer kann ein inkompatibles Schema erkennen und ablehnen, sodass persistiertes AST-JSON einen stabilen Vertrag hat, auch wenn die In-Memory-API dies nicht garantiert.

Terminal-Fenster
composer require nextpdf/core:^3

Ein AST ist hier eine semantische Abstraktion über die logische Struktur eines Dokuments und kein Parser-Syntaxbaum für ein einzelnes Eingabeformat. AstDocument ist der Container. Er enthält den Wurzel-AstNode (der NodeType::Document sein muss), eine Schemaversion, einen Quell-PDF-Hash und eine Seitenanzahl. Ungültige Konstruktionen lehnt er ab (leere Schemaversion, Seitenanzahl unter eins, falscher Wurzeltyp).

AstNode ist der rekursive Knotentyp. NodeType zählt die semantischen Arten auf. Ein Knoten enthält Kindknoten, eine optionale BoundingBox, optionalen Textinhalt und schemavalidierte Attribute über NodeAttributeSchema. Die Knoten-API ist auf unveränderliche Ableitungen ausgelegt. withBboxAndText() gibt einen neuen Knoten zurück. deepClone() kopiert einen Teilbaum. NodeId ist die Value-Object-Identität. CitationAnchor verknüpft einen Knoten zur Nachverfolgbarkeit mit einem Quellort. AstNodeCollection ist eine Countable-/IteratorAggregate-Sammlung mit ofType()-Filterung.

AstSerializer bildet die Persistenzgrenze. serialize() schreibt ein AstDocument als JSON. deserialize() liest es zurück. canDeserialize() und extractSchemaVersion() ermöglichen Konsumenten, die Kompatibilität vor dem Parsen zu prüfen, sodass eine Schemadiskrepanz eine erkannte Bedingung ist und kein beschädigter Ladevorgang. AstDocument::estimateTokenCount() existiert, weil der Baum auch dazu dient, Inhalte für die nachgelagerte tokenbegrenzte Verarbeitung zu dimensionieren.

KlasseWichtige MemberRolle
AstDocumenttoJson(), nodeCount(), estimateTokenCount(), CURRENT_SCHEMA_VERSIONWurzelcontainer; validiert Wurzeltyp und Schema
AstNodeaddChild(), children(), childCount(), totalNodeCount(), withBboxAndText(), deepClone()Rekursiver semantischer Knoten
NodeType (Enum)Document, Heading, Table, Figure, FormField, …Semantische Knotenart
AstNodeCollectionadd(), count(), isEmpty(), ofType(), toArray()Iterierbare, typgefilterte Knotenmenge
AstSerializerserialize(), deserialize(), canDeserialize(), extractSchemaVersion()Versionierte JSON-Persistenz
BoundingBoxtoArray(), equals()Geometrie-Value-Object (Epsilon-Vergleich)
NodeId / CitationAnchortoString(), equals(), toArray()Knotenidentität und Quell-Nachverfolgbarkeitsanker
NodeAttributeSchemaAttributvalidierungSchema für Knotenattribute

Führen Sie composer docs:generate-api-php -- --module=Ast aus, um die vollständige PHPDoc-Tabelle zu erhalten.

Erstellen Sie einen kleinen Baum und serialisieren Sie ihn.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Ast\AstNode;
use NextPDF\Ast\AstSerializer;
use NextPDF\Ast\NodeType;
$root = new AstNode(NodeType::Document);
$heading = new AstNode(NodeType::Heading);
$root->addChild($heading);
$root->addChild(new AstNode(NodeType::Paragraph));
echo "Nodes: {$root->totalNodeCount()}\n";
$json = (new AstSerializer())->serialize(/* an AstDocument wrapping $root */);

Verarbeiten Sie persistierten AST defensiv im Round-Trip und prüfen Sie die Schemakompatibilität vor dem Deserialisieren nicht vertrauenswürdiger JSON-Daten.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Ast\AstDocument;
use NextPDF\Ast\AstSerializer;
use Psr\Log\LoggerInterface;
final readonly class AstStore
{
public function __construct(
private AstSerializer $serializer,
private LoggerInterface $logger,
) {}
public function load(string $json): ?AstDocument
{
if (!$this->serializer->canDeserialize($json)) {
$this->logger->warning('AST JSON schema incompatible; rejected.', [
'found_schema' => $this->serializer->extractSchemaVersion($json),
'expected' => AstDocument::CURRENT_SCHEMA_VERSION,
]);
return null;
}
return $this->serializer->deserialize($json);
}
}
  • AstDocument erfordert, dass der Wurzelknoten NodeType::Document ist. Ein Baum mit einer anderen Wurzel wirft bei der Konstruktion.
  • AstNode::withBboxAndText() und deepClone() geben neue Instanzen zurück. Die vorhandenen Knotenmutatoren (addChild()) mutieren. Die Ableitungshelfer tun dies nicht. Achten Sie darauf, welche Methode Sie aufrufen.
  • Schützen Sie deserialize() bei extern bezogenem JSON immer mit canDeserialize(). Eine Schemaversionsdiskrepanz ist eine erkennbare, erwartete Bedingung.
  • estimateTokenCount() ist eine Schätzung zur Dimensionierung der nachgelagerten Verarbeitung, keine exakte Tokenizer-Zählung. Behandeln Sie den Wert nicht als maßgeblich.
  • BoundingBox::equals() ist ein Epsilon-Vergleich (Standard 0.001). Exakte Float-Gleichheit gehört nicht zum Vertrag.

Baumkonstruktion und -traversierung sind O(n) in der Anzahl der Knoten. Die Serialisierung ist linear in der Baumgröße. Das Reproduzierbarkeitsprofil ist bitwise. Derselbe Baum wird zu denselben JSON-Bytes serialisiert, was das Schema zu einem stabilen Persistenzvertrag macht. Die standardmäßige Referenzlast liegt deutlich innerhalb des Budgets von 1500 ms Wall / 64 MB Peak.

AstSerializer::deserialize() parst JSON, das persistiert oder übertragen worden sein kann. Validieren Sie die Kompatibilität zuerst mit canDeserialize(). Behandeln Sie Textinhalt und Attribute des deserialisierten Baums als nicht vertrauenswürdige Strings, wenn sie wieder in die Anwendung gelangen oder gerendert werden. Das Modul selbst führt keine I/O aus und bettet keine externen Daten ein. Siehe das Bedrohungsmodell der Engine in /modules/core/security/.

Dieses Modul erhebt keinen normativen Anspruch im Sinne der PDF-Spezifikation. Der semantische AST ist eine Engine-interne Abstraktion. Er implementiert kein standardisiertes Dokumentmodell, dessen Klauseln zitiert werden müssten. Wo der AST das Accessibility-Tagging speist, werden die PDF/UA- und Tagged-PDF-Konformität der Ausgabe unter /modules/core/accessibility/ und /modules/core/conformance/ dokumentiert und validiert, nicht hier.