Contratos de camadas do engine HTML (ADR-010)
Visão geral
Seção intitulada “Visão geral”O subsistema Hypertext Markup Language (HTML) separa a análise de Cascading Style Sheets (CSS), o estado de estilo, o layout e a pintura em quatro camadas. Os dados fluem em uma única direção por essas camadas. O Architecture Decision Record 010 (ADR-010) define os limites e as regras de extensão.
Instalação
Seção intitulada “Instalação”composer require nextpdf/core:^3Visão geral conceitual
Seção intitulada “Visão geral conceitual”O Architecture Decision Record 010 (ADR-010) (“Engine Layer Contracts, Hot Path Ownership, and Extension Rules”, aprovado em 2026-04-12) formaliza a divisão em camadas do subsistema HTML. O contrato de renderização do core tem quatro camadas: análise de CSS e applicators, estado de estilo, layout e formatação, e pintura. O ADR-010 também documenta duas camadas adjuntas: mídia paginada e o harness de medição. Essas camadas envolvem o core de quatro camadas sem alterar o fluxo de dados. O termo canônico do glossário para o core é “pipeline HTML”, um pipeline de quatro camadas.
Os dados fluem em uma única direção. O texto CSS é convertido em valores tipados na Camada 1. A Camada 1 grava esses valores nos campos de HtmlStyleState na Camada 2. A Camada 3 lê os campos de estado de estilo e calcula a geometria. A Camada 4 lê um snapshot imutável de ComputedStyle mais a geometria e emite operadores Portable Document Format (PDF). Nenhuma camada lê dados de uma camada posterior.
A separação em quatro camadas é mais que documentação. O ADR-010 registra dois refactors pontuais feitos na v1.2.0 que moveram código para a camada correta. PageBorderPainter foi extraído de HtmlParser, de modo que os operadores de pintura não residem mais no orquestrador. O docblock da classe HtmlStyleState agora traz o contrato formal de camadas e declara quais campos cada camada pode gravar ou ler.
Há um limite explícito. FormattingContextFactory::startTable() ainda lê cinco chaves CSS brutas diretamente. O ADR-010 registra isso como débito técnico conhecido, adiado para um futuro TableApplicator, e não como o contrato pretendido. Documentar a exceção faz parte do contrato.
As quatro camadas do core
Seção intitulada “As quatro camadas do core”| Camada | Arquivos (representativos) | Grava | Lê | Não deve |
|---|---|---|---|---|
| 1 — Análise de CSS & applicators | CssValueParser, CssResolver, HtmlCssApplicator, src/Html/Applicator/* | HtmlStyleState campos CSS | Texto CSS bruto | Calcular geometria; emitir operadores |
| 2 — Estado de estilo | HtmlStyleState, State/ComputedStyle, State/LayoutState | — (contêiner passivo de valores) | — | Analisar CSS; decidir layout; emitir operadores |
| 3 — Layout & formatação | FormattingContextFactory, HtmlBlockHandler, FlexLayoutEngine, TableParser, FloatContext | Geometria do cursor | HtmlStyleState campos | Ler $css[...] bruto; emitir operadores de pintura |
| 4 — Pintura & renderização | BorderRenderer, BackgroundImageRenderer, src/Html/Paint/*, src/Html/Gradient/* | Fluxo de operadores PDF | ComputedStyle (imutável) + geometria | Calcular geometria; analisar CSS; decidir quebras de página |
As duas camadas adjuntas
Seção intitulada “As duas camadas adjuntas”| Camada | Arquivos (representativos) | Função |
|---|---|---|
| 5 — Mídia paginada | PageBreakController, PageBorderPainter, PageRule, PageRuleParser, ParserConfigurator | Resolver regras @page; avaliar restrições de quebra e de orphan/widow; delegar a decoração da página à pintura. |
| 6 — Medição & harness | Scripts classificadores de Web Platform Tests (WPT), tests/Support/* | Classificar resultados de testes; produzir snapshots de regressão; fornecer auxiliares de asserção. Não contém lógica de renderização. |
Superfície da API
Seção intitulada “Superfície da API”O contrato é imposto pelo posicionamento das classes e pelo docblock de HtmlStyleState. Verifique-o em relação a src/Html/.
| Símbolo | Camada | Função no contrato |
|---|---|---|
PropertyApplicatorInterface | 1 | Interface de estratégia; o único ponto que grava campos computados de CSS. |
ParserConfigurator::buildCssApplicator() | 1 (wiring) | Registra cada applicator. Uma nova propriedade CSS é registrada aqui. |
HtmlStyleState | 2 | Contêiner de grupo duplo; o docblock da classe declara a camada proprietária de cada campo. |
HtmlStyleState::toComputedStyle() | 2 | Produz o ComputedStyle imutável para a camada de pintura. |
FormattingContextFactory::dispatchOpenTag() | 3 | Ponto único de roteamento para novo comportamento de layout. |
PageBorderPainter::buildStream() | 4 | Decoração de página; chamada a partir da Camada 5, não incorporada em HtmlParser. |
Exemplo de código — Início rápido
Seção intitulada “Exemplo de código — Início rápido”Você nunca interage diretamente com as camadas. O fluxo de quatro camadas roda dentro de uma única chamada.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->addPage();$doc->writeHtml('<p style="color:#1E3A8A;border:1px solid #999;">Layered render.</p>');$doc->save(__DIR__ . '/output/layers.pdf');Exemplo de código — Produção
Seção intitulada “Exemplo de código — Produção”O contrato importa quando você contribui, não quando chama a biblioteca. Para adicionar uma propriedade CSS, use o ponto de extensão da Camada 1: crie um applicator, adicione um campo tipado de HtmlStyleState com um docblock de camada e registre o applicator em ParserConfigurator. A ilustração abaixo mostra o formato do contrato do applicator. Use src/Html/Applicator/ como modelo para uma classe concreta.
<?php
declare(strict_types=1);
// Layer 1 extension contract (see ADR-010 §C "New CSS property").// A new property group ships as a PropertyApplicatorInterface// implementation registered in ParserConfigurator::buildCssApplicator().// It writes a typed HtmlStyleState field and never computes geometry// or emits PDF operators — those belong to Layers 3 and 4.Casos extremos & pegadinhas
Seção intitulada “Casos extremos & pegadinhas”FormattingContextFactory::startTable()lê CSS bruto. Esta é a única exceção de contrato documentada, adiada para um futuroTableApplicator. Não copie o padrão.- Seis camadas, core de quatro camadas. O ADR-010 numera seis camadas. O contrato de fluxo de dados é o core de quatro camadas; mídia paginada e medição são camadas adjuntas.
HtmlStyleStateé de grupo duplo. Ele carrega campos computados de CSS e campos de rastreamento de layout. Apenas os applicators gravam o grupo CSS. A pintura lêComputedStyle, nunca os campos de rastreamento de layout.HtmlParsernão tem camada. Ele é o orquestrador. A análise de CSS, os cálculos de geometria e a emissão de pintura não devem residir nele.
Desempenho
Seção intitulada “Desempenho”O contrato de camadas é estrutural, portanto não adiciona custo de tempo de execução. HtmlStyleState::toComputedStyle() produz um snapshot imutável para cada elemento que precisa de pintura. Esse snapshot permite que o código de pintura evite o contêiner de estado mutável. O custo de renderização é governado pelo modelo de streaming, não pelas camadas. O performance_budget por página (wall_ms: 1500, peak_mb: 64) permanece como teto operacional.
Notas de segurança
Seção intitulada “Notas de segurança”A separação em camadas sustenta o modelo de segurança. A Camada 1 analisa os valores CSS e os filtra por política antes que o código de layout ou de pintura os veja, de modo que DefaultHtmlSecurityPolicy::isCssPropertyAllowed() permanece o único portão. A pintura nunca lê CSS bruto controlado por atacante. Consulte o modelo de segurança do módulo HTML.
Conformidade
Seção intitulada “Conformidade”Esta página não cita nenhum padrão externo. Os limites de camada vêm do ADR-010 e do docblock da classe HtmlStyleState, que codifica o contrato no código-fonte. A conformidade comportamental de CSS está documentada em css-resolver.
Contexto comercial
Seção intitulada “Contexto comercial”Capacidade Enterprise. Os recursos CSS Premium usam essas mesmas quatro camadas por meio dos pontos de extensão documentados. Não há um pipeline Premium separado. Consulte a matriz de suporte a CSS.