Pular para o conteúdo

O pipeline de HTML

Spec: CSS Cascade 5, §6.1 Spec: CSS Display 3, §2 Evidence: Code-backed

O NextPDF renderiza HTML e CSS em PDF dentro do processo PHP: sem navegador, sem subprocesso por padrão. Esta página explica os estágios em camadas pelos quais a conversão passa, o que o motor de CSS realmente cobre e quando delegar a um renderizador de navegador real é a escolha mais honesta.

“HTML para PDF” parece uma única operação. Na prática, envolve uma cascade, um box model, uma etapa de layout e uma etapa de pintura. Cada uma delas é um problema bem especificado, com seus próprios modos de falha. Um motor que as funde em um único procedimento é frágil. Uma alteração no parsing de cores pode deslocar uma caixa, e a única forma de perceber isso é renderizar e observar.

O modelo in-process tem uma vantagem real: nenhum navegador para instalar, nenhum sandbox para operar e nenhum limite de processo para atravessar com marshalling. Mas isso só compensa se a conversão for decomposta de forma limpa o suficiente para testar cada preocupação isoladamente. É a arquitetura que torna “renderizar HTML em PHP” confiável, não apenas possível.

  • A conversão de HTML/CSS é executada in-process via writeHtml(). O resultado é conteúdo PDF nativo, não uma imagem de uma página.
  • Ela é single-pass e streaming. O tokenizer produz uma lista de tokens. O parser consome essa lista da esquerda para a direita, e nenhuma árvore DOM completa é retida (ADR-001). Limites rígidos restringem a contagem de elementos e a profundidade de aninhamento.
  • O motor é organizado em camadas explícitas: parsing de CSS e applicators, estado de estilo, layout e formatação, pintura e paged media — com regras rígidas sobre o que cada camada pode fazer (ADR-010).
  • O motor de CSS cobre a cascade, o box model e layouts comuns (block, inline, tabelas, floats e outros) — substancial, mas um subconjunto definido daquilo que um navegador moderno implementa.
  • Quando você precisa de fidelidade exata de navegador para CSS moderno arbitrário, o NextPDF pode delegar a um renderizador de navegador headless por meio de uma extensão opcional — um ponto de junção deliberado e isolado da rede, não o caminho padrão.

A conversão é uma sequência de estágios, cada um consumindo a saída tipada do estágio anterior.

  1. Tokenize HTML becomes an ordered token list — no retained DOM tree.
  2. Resolve CSS Parse styles; the cascade and applicators compute typed values.
  3. Style state A push/pop style stack carries computed values per nesting level.
  4. Layout Block, inline, table, and float geometry computed; no paint here.
  5. Paint Borders, backgrounds, text, and decorations emit PDF operators.
  6. Paged media Page-break and @page rules applied as the cursor crosses page bounds.
O pipeline de HTML in-process: uma única passagem da esquerda para a direita sobre um fluxo de tokens, com resolução de CSS, estado de estilo, layout e pintura como camadas separadas, e quebras de paged media aplicadas conforme o cursor avança.

Duas regras arquiteturais fazem disso mais do que um fluxo.

As camadas têm contratos. O texto de CSS é lido apenas dentro das classes applicator. O código de layout calcula a geometria, mas não emite operadores de pintura. O código de pintura lê um snapshot imutável do estilo computado, nunca o estado mutável de rastreamento de layout. O código de paged media dispara quebras, mas delega a decoração de página à camada de pintura. Esses limites são impostos (ADR-010). É por isso que uma nova propriedade CSS entra como um novo applicator, em vez de uma alteração que se espalha simultaneamente pelo parser, pelo dispatch de layout e pelo painter.

Não há DOM. O pipeline é single-pass e streaming por decisão (ADR-001): no máximo um estado de estilo por nível de aninhamento, mais o cursor ativo, não um objeto por elemento. Algumas operações realmente precisam de look-ahead — dimensionamento de colunas de tabela, :has(), :last-child. Elas são tratadas por estruturas limitadas de índice de pré-varredura sobre a lista plana de tokens, não pela retenção de uma árvore. A contagem de elementos e a profundidade de aninhamento têm limites rígidos, de modo que uma entrada patológica falha rapidamente em vez de esgotar a memória.

O motor de CSS resolve a semântica real do CSS, não uma imitação. Declarações concorrentes são reduzidas a um valor por propriedade por origem, importância, camada, especificidade e ordem — a cascade de fato. O layout segue o box model. O tipo de uma caixa e o formatting context que ela estabelece determinam como ela e seus elementos irmãos in-flow são posicionados. O código-fonte do motor é organizado exatamente em torno dessas preocupações (cascade, box/display, flex, float, tabelas, fragmentação). É por isso que você pode raciocinar sobre o comportamento dele com base nas especificações, em vez de descobri-lo empiricamente.

Esta página é Evidence: Code-backed . Os estágios e regras correspondem ao repositório core:

  • O ponto de entrada in-process é writeHtml(string $html): static em src/Core/Concerns/HasTextOutput.php.
  • O design single-pass, sem DOM retido e com limites de elementos e de aninhamento, é descrito pelo ADR-001 e pelo código de tokenizer/parser/style-stack em src/Html/.
  • O contrato do motor em camadas — parsing/applicators de CSS, estado de estilo, layout, pintura, paged media — é descrito pelo ADR-010 e refletido na estrutura de src/Html/ (por exemplo Cascade/, Css/, Flex/, Float/, Fragmentation/ e as classes applicator).
  • O ponto de junção de delegação ao navegador é writeHtmlChrome() no mesmo arquivo, documentado como exigindo a extensão de renderizador opcional, além de um binário Chrome/Chromium.

Os padrões dão base a uma afirmação honesta de cobertura. A cascade reduz declarações concorrentes a um único valor por propriedade — origem, importância, camada, especificidade, ordem — conforme Spec: CSS Cascade 5, §6.1 , e o posicionamento in-flow segue as regras de box e de formatting context conforme Spec: CSS Display 3, §2 . Igualmente importante é o limite: uma feature query existe justamente porque nem todo processador suporta todos os recursos, conforme Spec: CSS Conditional 5, §2 . O motor de CSS do NextPDF é um subconjunto definido e alinhado às especificações, e afirmar isso claramente faz parte do contrato.

A renderização in-process é uma única chamada. A saída é texto PDF selecionável, não uma página rasterizada:

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('HTML Basic');
$doc->addPage();
$html = <<<'HTML'
<h1 style="color: #1E3A8A;">HTML Rendering in NextPDF</h1>
<p>NextPDF renders <strong>HTML and CSS</strong> directly into PDF pages,
<em>in-process</em>.</p>
<ul>
<li>Headings, paragraphs, bold and italic</li>
<li>Lists, tables, inline styles</li>
</ul>
HTML;
$doc->writeHtml($html);
$doc->save(__DIR__ . '/html-basic.pdf');

Se o mesmo documento exigisse CSS moderno arbitrário com fidelidade exata de navegador, a chamada seria writeHtmlChrome($html) — mesmo documento, caminho de renderização diferente e uma dependência deliberada do renderizador de navegador opcional.

O equívoco recorrente é tratar um motor de HTML para PDF como “basicamente um navegador.” Ele não é, e não afirma ser. Um navegador é uma implementação vasta e continuamente atualizada de toda a plataforma web. O motor in-process do NextPDF é um subconjunto alinhado às especificações e focado em layout de documentos. O modelo mental honesto é “um motor de CSS competente para documentos de impressão”, não “Chrome em PHP.” Quando você realmente precisa da plataforma completa, é para isso que serve writeHtmlChrome(). É um caminho separado, de adesão explícita, com sua própria pegada operacional, não um fallback silencioso.

Um segundo equívoco é presumir que o caminho do navegador apenas “renderiza a página pela rede.” É o oposto, por construção. O ponto de junção de delegação sempre renderiza com o acesso de rede a sub-recursos bloqueado — sem imagens, fontes, folhas de estilo ou frames remotos — de modo que não pode se tornar um vetor de requisições de saída. Fidelidade de pixels, sim; saída de rede aberta, não.

Esta página explica o formato do pipeline e a escolha entre in-process e navegador. Ela não é uma matriz de suporte a CSS. As propriedades, módulos e seletores exatos que o motor in-process cobre são definidos pelo código e por seus testes de conformidade, não por esta visão geral. Essa cobertura evolui. O caminho de delegação ao navegador exige uma extensão opcional e um binário Chrome/Chromium. A configuração, as características operacionais e a estrutura interna dessa extensão estão fora do escopo aqui e são documentadas junto com aquele pacote. “In-process” descreve o caminho padrão writeHtml(). Não é uma afirmação de que todo caminho de renderização evita um subprocesso. As afirmações arquiteturais estão corretas na data de revisão desta página. As fontes autoritativas são src/Html/, o ADR-001 e o ADR-010 no repositório core.

O motor de CSS in-process é uma capacidade do Core. O ponto de junção de delegação ao navegador é uma extensão opcional, apresentada aqui apenas no nível de capacidade:

HTML rendering paths — edition availability
Edition Availability
Core O Core fornece o motor de HTML/CSS in-process (writeHtml).
Pro O caminho de delegação ao navegador é uma extensão complementar opcional, independente do nível de edição.
Enterprise O caminho de delegação ao navegador é uma extensão complementar opcional, independente do nível de edição.
  • Renderização in-process — converter HTML/CSS em PDF dentro do processo PHP, sem navegador ou subprocesso padrão (writeHtml()).
  • Single-pass / streaming — consumir um fluxo de tokens da esquerda para a direita sem reter uma árvore DOM completa (ADR-001).
  • Cascade — o processo do CSS que resolve declarações concorrentes em um valor por propriedade por origem, importância, camada, especificidade e ordem.
  • Formatting context — o ambiente de layout que uma caixa estabelece e que governa como o conteúdo in-flow dela é posicionado.
  • Contrato de camadas do motor — o conjunto de regras imposto (ADR-010) que define o que cada uma das camadas de parsing, estilo, layout, pintura e paged media pode fazer.
  • Ponto de junção de delegação ao navegador — o caminho opcional writeHtmlChrome() que renderiza por meio de um navegador headless com o acesso de rede a sub-recursos bloqueado.