Pular para o conteúdo

Ast: árvore semântica de documentos e serialização

O módulo Ast fornece a árvore de sintaxe abstrata (AST) semântica de documentos do engine. Ele modela um documento como uma hierarquia de nós tipados: Document, Section, Heading, Paragraph, List, Table, Figure, Code e FormField. O modelo registra caixas delimitadoras e âncoras de citação, e serializa tudo como JavaScript Object Notation (JSON) versionado. A camada de marcação de acessibilidade usa essa árvore para produzir uma árvore de estrutura.

Estabilidade: experimental. Esta é uma superfície de modelo interna. Suas classes não oferecem garantias de interface de programação de aplicações (API) pública congelada por versão. O conjunto de nós e os atributos de nó podem mudar. O esquema de serialização tem versionamento independente (AstDocument::CURRENT_SCHEMA_VERSION = '1.0.0'). O serializador detecta e rejeita um esquema incompatível, de modo que o JSON da AST persistido mantém um contrato estável mesmo quando a API em memória muda.

Terminal window
composer require nextpdf/core:^3

Aqui, uma AST representa a estrutura lógica de um documento. Ela não é uma árvore de sintaxe de parser para um único formato de entrada. AstDocument é o contêiner. Ele contém o AstNode raiz (que deve ser NodeType::Document), uma versão de esquema, um hash do arquivo Portable Document Format (PDF) de origem e uma contagem de páginas. Ele rejeita construções inválidas, incluindo versão de esquema vazia, contagem de páginas inferior a um ou tipo de raiz incorreto.

AstNode é o nó recursivo. NodeType enumera os tipos semânticos. Um nó carrega filhos, um BoundingBox opcional, conteúdo de texto opcional e atributos validados por NodeAttributeSchema. A API do nó oferece suporte a derivação imutável. withBboxAndText() retorna um novo nó. deepClone() copia uma subárvore. NodeId representa a identidade como objeto de valor. CitationAnchor vincula um nó a uma localização de origem para rastreabilidade. AstNodeCollection é um conjunto Countable/IteratorAggregate com filtragem por ofType().

AstSerializer é a fronteira de persistência. serialize() grava um AstDocument em JSON. deserialize() o lê de volta. canDeserialize() e extractSchemaVersion() permitem que você verifique a compatibilidade antes da análise, fazendo com que uma incompatibilidade de esquema seja uma condição detectada em vez de resultar em um carregamento corrompido. AstDocument::estimateTokenCount() ajuda a dimensionar o conteúdo para processamento posterior limitado por tokens.

ClasseMembros principaisFunção
AstDocumenttoJson(), nodeCount(), estimateTokenCount(), CURRENT_SCHEMA_VERSIONContêiner raiz; valida o tipo da raiz e o esquema
AstNodeaddChild(), children(), childCount(), totalNodeCount(), withBboxAndText(), deepClone()Nó semântico recursivo
NodeType (enum)Document, Heading, Table, Figure, FormField, …Tipo de nó semântico
AstNodeCollectionadd(), count(), isEmpty(), ofType(), toArray()Conjunto iterável de nós, filtrável por tipo
AstSerializerserialize(), deserialize(), canDeserialize(), extractSchemaVersion()Persistência JSON versionada
BoundingBoxtoArray(), equals()Objeto de valor de geometria (comparação por epsilon)
NodeId / CitationAnchortoString(), equals(), toArray()Identidade de nó e âncora de rastreabilidade de origem
NodeAttributeSchemavalidação de atributosEsquema para atributos de nó

Execute composer docs:generate-api-php -- --module=Ast para gerar a tabela PHPDoc completa.

Construa uma árvore pequena e, em seguida, serialize-a.

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

Faça round-trip de uma AST persistida de forma defensiva. Verifique a compatibilidade do esquema antes de desserializar JSON não confiável.

<?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 exige que o nó raiz seja NodeType::Document. Uma árvore com qualquer outra raiz lança uma exceção durante a construção.
  • AstNode::withBboxAndText() e deepClone() retornam novas instâncias. Os mutadores de nó disponíveis (addChild()) modificam o nó. Os auxiliares de derivação, não. Saiba qual método você está chamando.
  • Sempre proteja deserialize() com canDeserialize() para JSON de origem externa. Uma incompatibilidade de versão de esquema é uma condição esperada e detectável.
  • estimateTokenCount() é uma estimativa para dimensionar o processamento posterior, não uma contagem exata do tokenizador. Não a trate como autoritativa.
  • BoundingBox::equals() é uma comparação por epsilon (padrão 0.001). A igualdade exata de ponto flutuante não é o contrato.

A construção e a travessia da árvore são O(n) em relação à contagem de nós. A serialização é linear em relação ao tamanho da árvore. O perfil de reprodutibilidade é bitwise. A mesma árvore é serializada para os mesmos bytes JSON, o que mantém o esquema estável como contrato de persistência. A carga de trabalho de referência padrão permanece bem dentro do orçamento de 1500 ms de tempo de parede / 64 MB de pico.

AstSerializer::deserialize() analisa JSON que pode ter sido persistido ou transmitido. Valide primeiro a compatibilidade com canDeserialize(). Trate o conteúdo de texto e os atributos da árvore desserializada como strings não confiáveis quando eles voltarem a entrar na aplicação ou forem renderizados. O módulo em si não realiza nenhuma input/output (I/O) e não incorpora dados externos. Consulte o modelo de ameaças do engine em /modules/core/security/.

Este módulo não faz nenhuma afirmação normativa sobre a especificação PDF. A AST semântica é uma abstração interna do engine. Ela não implementa um modelo de documento padronizado cujas cláusulas precisem ser citadas. Onde a AST alimenta a marcação de acessibilidade, a conformidade PDF/UA e de PDF marcado da saída é documentada e validada em /modules/core/accessibility/ e /modules/core/conformance/, não aqui.