Przejdź do głównej zawartości

Ast: semantyczne drzewo dokumentu i serializacja

Moduł Ast udostępnia semantyczne drzewo składniowe dokumentu (AST) używane przez silnik. Modeluje dokument jako typowaną hierarchię węzłów: Document, Section, Heading, Paragraph, List, Table, Figure, Code oraz FormField. Model przechowuje prostokąty ograniczające i kotwice cytowań oraz umożliwia serializację do wersjonowanego formatu JavaScript Object Notation (JSON). Warstwa tagowania dostępności używa tego drzewa do tworzenia drzewa struktury.

Stabilność: eksperymentalna. To wewnętrzna powierzchnia modelu. Jej klasy nie są objęte gwarancjami publicznego interfejsu programowania aplikacji (API) zamrożonymi dla danej wersji. Zestaw węzłów i atrybuty węzłów mogą ulec zmianie. Schemat serializacji jest wersjonowany niezależnie (AstDocument::CURRENT_SCHEMA_VERSION = '1.0.0'). Serializator wykrywa i odrzuca niezgodny schemat, więc utrwalony AST w JSON zachowuje stabilny kontrakt nawet wtedy, gdy API w pamięci się zmienia.

Okno terminala
composer require nextpdf/core:^3

W tym kontekście AST reprezentuje logiczną strukturę dokumentu. Nie jest drzewem składniowym parsera konkretnego formatu wejściowego. AstDocument jest kontenerem. Przechowuje korzeniowy AstNode (który musi być NodeType::Document), wersję schematu, skrót źródłowego pliku Portable Document Format (PDF) oraz liczbę stron. Odrzuca nieprawidłowe konstrukcje, w tym pustą wersję schematu, liczbę stron poniżej jednego lub niewłaściwy typ korzenia.

AstNode jest węzłem rekurencyjnym. NodeType definiuje rodzaje semantyczne. Węzeł zawiera węzły podrzędne, opcjonalny BoundingBox, opcjonalną treść tekstową oraz atrybuty walidowane przez NodeAttributeSchema. API węzła obsługuje niezmienne wyprowadzanie nowych instancji. withBboxAndText() zwraca nowy węzeł. deepClone() kopiuje poddrzewo. NodeId reprezentuje tożsamość jako obiekt wartości. CitationAnchor wiąże węzeł z lokalizacją źródłową na potrzeby śledzenia. AstNodeCollection jest zbiorem zgodnym z Countable/IteratorAggregate, z filtrowaniem za pomocą ofType().

AstSerializer stanowi granicę trwałości. serialize() zapisuje AstDocument do formatu JSON. deserialize() odtwarza dokument z JSON. canDeserialize() oraz extractSchemaVersion() pozwalają sprawdzić zgodność przed parsowaniem, dzięki czemu niezgodność schematu jest kontrolowanym warunkiem, a nie uszkodzonym odczytem. AstDocument::estimateTokenCount() pomaga oszacować rozmiar treści na potrzeby dalszego przetwarzania ograniczonego liczbą tokenów.

KlasaKluczowe składoweRola
AstDocumenttoJson(), nodeCount(), estimateTokenCount(), CURRENT_SCHEMA_VERSIONKontener główny; waliduje typ korzenia i schemat
AstNodeaddChild(), children(), childCount(), totalNodeCount(), withBboxAndText(), deepClone()Rekurencyjny węzeł semantyczny
NodeType (enum)Document, Heading, Table, Figure, FormField, …Rodzaj węzła semantycznego
AstNodeCollectionadd(), count(), isEmpty(), ofType(), toArray()Iterowalny zbiór węzłów z filtrowaniem po typie
AstSerializerserialize(), deserialize(), canDeserialize(), extractSchemaVersion()Wersjonowane utrwalanie w JSON
BoundingBoxtoArray(), equals()Obiekt wartości geometrii (porównanie z epsilonem)
NodeId / CitationAnchortoString(), equals(), toArray()Tożsamość węzła i kotwica śledzenia źródła
NodeAttributeSchemawalidacja atrybutówSchemat atrybutów węzła

Uruchom composer docs:generate-api-php -- --module=Ast, aby wygenerować pełną tabelę PHPDoc.

Zbuduj niewielkie drzewo, a następnie je zserializuj.

<?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 */);

Obsługuj cykl zapisu i odczytu utrwalonego AST w sposób defensywny. Sprawdź zgodność schematu, zanim zdeserializujesz niezaufany JSON.

<?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 wymaga, aby węzeł korzeniowy był typu NodeType::Document. Dla drzewa z dowolnym innym korzeniem konstruktor zgłasza wyjątek.
  • AstNode::withBboxAndText() oraz deepClone() zwracają nowe instancje. Dostępne metody mutujące węzeł (addChild()) zmieniają tę instancję. Pomocnicze metody wyprowadzające pozostawiają ją bez zmian. Pamiętaj, którą metodę wywołujesz.
  • Zawsze zabezpieczaj deserialize() wywołaniem canDeserialize(), gdy JSON pochodzi z zewnątrz. Niezgodność wersji schematu to wykrywalny, oczekiwany warunek.
  • estimateTokenCount() to szacunek rozmiaru na potrzeby dalszego przetwarzania, a nie dokładna liczba tokenów z tokenizera. Nie traktuj go jako wartości miarodajnej.
  • BoundingBox::equals() to porównanie z epsilonem (domyślnie 0.001). Dokładna równość liczb zmiennoprzecinkowych nie jest częścią kontraktu.

Budowanie i przechodzenie po drzewie mają złożoność O(n) względem liczby węzłów. Serializacja jest liniowa względem rozmiaru drzewa. Profil odtwarzalności to bitwise. To samo drzewo jest serializowane do tych samych bajtów JSON, co utrzymuje stabilność schematu jako kontraktu trwałości. Domyślne obciążenie referencyjne mieści się z dużym zapasem w budżecie 1500 ms czasu rzeczywistego / 64 MB szczytowego zużycia pamięci.

AstSerializer::deserialize() parsuje JSON, który może być utrwalany lub przesyłany. Najpierw sprawdź zgodność za pomocą canDeserialize(). Treść tekstową i atrybuty zdeserializowanego drzewa traktuj jako niezaufane ciągi znaków, jeśli ponownie trafiają do aplikacji lub są renderowane. Sam moduł nie wykonuje operacji input/output (I/O) i nie osadza żadnych danych zewnętrznych. Zobacz model zagrożeń silnika w /modules/core/security/.

Ten moduł nie formułuje żadnego normatywnego oświadczenia względem specyfikacji PDF. Semantyczne AST jest abstrakcją wewnętrzną silnika. Nie implementuje znormalizowanego modelu dokumentu, dla którego należałoby cytować klauzule. Tam, gdzie AST zasila tagowanie dostępności, zgodność wyniku z PDF/UA i tagowanym PDF jest udokumentowana i walidowana na /modules/core/accessibility/ oraz /modules/core/conformance/, a nie tutaj.