Pipeline de renderização de HTML
Visão geral
Seção intitulada “Visão geral”Quando você chama writeHtml(), ele executa uma única passagem direta sobre a HyperText Markup Language (HTML): tokeniza a entrada, resolve @page e os estilos, posiciona o conteúdo e pinta os operadores do Portable Document Format (PDF). Ele não mantém uma árvore de elementos entre as etapas.
Instalação
Seção intitulada “Instalação”composer require nextpdf/core:^3Visão conceitual
Seção intitulada “Visão conceitual”O pipeline de renderização de HTML converte HTML+CSS, ou seja, HTML mais Cascading Style Sheets (CSS), em operadores de content-stream PDF em uma única passagem direta. Ele não constrói uma árvore de documento retida. As etapas abaixo refletem HtmlParser::parse() no branch main.
Etapa 1 — Sanitizar e normalizar. HtmlParser::parse() rejeita entradas com mais de 10 MB, remove caracteres de controle e normaliza as quebras de linha: tanto CRLF quanto CR isolado tornam-se LF, em conformidade com a normalização de quebras de linha do HTML na origem. Em seguida, ele redefine todos os campos de instância, de modo que o estado de uma chamada anterior não possa ser carregado para a próxima execução.
Etapa 2 — Extrair blocos @page e de estilo. O parser primeiro extrai os blocos <style> e, em seguida, aplica as regras @page descobertas para reconfigurar a geometria da página. Ele faz isso antes de processar qualquer token, porque o tamanho da página afeta toda decisão de layout posterior.
Etapa 3 — Tokenizar. HtmlTokenizer::cleanHtml() normaliza os espaços em branco enquanto preserva o conteúdo de <pre>. Em seguida, tokenize() produz uma list<HtmlToken> plana. Trata-se de uma lista de tokens, não de um grafo de nós. O pipeline descarta imediatamente os tokens de texto compostos apenas por espaços em branco. HtmlChildScanner::scan() constrói mapas de índice (contagem de filhos, contagem de tags, ausência de conteúdo) sobre a lista plana, de modo que os seletores estruturais não dependam de uma árvore.
Etapa 4 — Pré-varredura opcional de :has(). Quando você habilita o recurso experimental css.has, CssResolver::resolveHasSelectors() executa uma pré-varredura limitada sobre a lista de tokens para resolver o seletor relacional. Essa etapa documentada e limitada é a exceção à regra de passo único.
Etapa 5 — Processar tokens (estilo, layout, pintura). HtmlParser::processTokens() percorre a lista de tokens uma única vez. Para cada elemento, ele resolve a cascata (os aplicadores da Camada 1 escrevem em HtmlStyleState), calcula a geometria (layout da Camada 3) e emite operadores PDF (pintura da Camada 4). A herança de estilos usa uma pilha HtmlStyleState com push e pop. O cursor (x, y, margens, deslocamento no fluxo) passa entre os handlers por meio de snapshots de HtmlBlockCursor.
Etapa 6 — Retornar o resultado. parse() retorna um HtmlRenderResult imutável com o content stream emitido, a posição final do cursor e as chaves de fonte usadas. O chamador (writeHtml()) devolve o cursor ao sistema de coordenadas da página.
Para a separação em quatro camadas dentro da Etapa 5, consulte a página contratos de camada. Para a propriedade de ausência de árvore retida e seus limites, consulte restrições de streaming.
Superfície da API
Seção intitulada “Superfície da API”| Símbolo | Localização | Etapa |
|---|---|---|
Document::writeHtml(string $html): static | src/Core/Concerns/HasTextOutput.php | Ponto de entrada público |
HtmlParser::parse(string $html): HtmlRenderResult | src/Html/HtmlParser.php | Orquestra todas as etapas |
HtmlTokenizer::cleanHtml() / tokenize() | src/Html/HtmlTokenizer.php | Etapa 3 |
HtmlChildScanner::scan() | src/Html/HtmlChildScanner.php | Mapas de índice da Etapa 3 |
CssResolver::resolveHasSelectors() | src/Html/CssResolver.php | Etapa 4 (controlada por flag) |
HtmlRenderResult (stream, endX, endY, usedFontKeys) | src/Html/HtmlRenderResult.php | Etapa 6 |
Exemplo de código — Início rápido
Seção intitulada “Exemplo de código — Início rápido”Extraído de examples/08-html-basic.php.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('HTML Basic');$doc->addPage();$doc->writeHtml('<h1 style="color:#1E3A8A;">HTML Rendering</h1><p>One pass.</p>');$doc->save(__DIR__ . '/output/08-html-basic.pdf');Exemplo de código — Produção
Seção intitulada “Exemplo de código — Produção”Renderize um relatório estilizado com um bloco <style> incorporado. O pipeline extrai e aplica esse bloco de estilo antes de processar qualquer token.
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;use NextPDF\Exception\HtmlParsingException;
function renderInvoice(string $bodyHtml, string $out): void{ $doc = Document::createStandalone(); $doc->setTitle('Invoice'); $doc->addPage();
$html = '<style>@page { margin: 20mm; } ' . 'h1 { color: #1E3A8A; } ' . 'table { width: 100%; }</style>' . $bodyHtml;
try { $doc->writeHtml($html); } catch (HtmlParsingException $e) { // Sanitize/cap failures surface here. Do not retry. throw $e; }
$doc->save($out);}Casos extremos e pegadinhas
Seção intitulada “Casos extremos e pegadinhas”@pageé lido antes dos tokens. Uma regra@pagedepois do conteúdo ainda se aplica, porque a extração de estilos ocorre antes da tokenização. A geometria da página é fixada antes da Etapa 5.- Os espaços em branco de
<pre>são preservados.cleanHtml()protege o conteúdo de<pre>; o pipeline reduz os espaços em branco nos demais locais. :has()é controlado por flag. Se você não habilitar o recurso experimentalcss.has, a Etapa 4 não é executada e os seletores:has()não correspondem.- Um único buffer de fluxo. O pipeline escreve em um único buffer de string. Ele nunca move conteúdo já escrito. Não há novo cálculo de layout.
- Os limites se aplicam no meio da passagem. Os limites de elementos e de aninhamento lançam exceção durante a Etapa 5, não antes. Um documento pode falhar no meio do processo.
Desempenho
Seção intitulada “Desempenho”O pipeline percorre em O(número de tokens). O dimensionamento de colunas de tabela acrescenta uma varredura de linhas limitada por tabela (Etapa 5, TableParser). Quando habilitada, a pré-varredura de :has() acrescenta uma passagem limitada sobre a lista de tokens (Etapa 4). A memória é O(profundidade de aninhamento) para a pilha de estilos, não O(número de elementos); consulte restrições de streaming. O benchmark de desempenho do pipeline de renderização de HTML protege contra regressões com um limite de 5% (trabalho mesclado, PR #564). O performance_budget por página (wall_ms: 1500, peak_mb: 64) é o teto operacional.
Notas de segurança
Seção intitulada “Notas de segurança”A Etapa 1 é o primeiro limite de segurança: o limite de entrada de 10 MB, a remoção de caracteres de controle e a normalização de quebras de linha são todos executados antes da tokenização. Durante a Etapa 5, DefaultHtmlSecurityPolicy controla as tags, os atributos, as propriedades CSS e os esquemas de URL permitidos. Consulte o modelo de segurança do módulo HTML.
Conformidade
Seção intitulada “Conformidade”A normalização de quebras de linha segue o tratamento de quebras de linha do padrão HTML: CRLF e CR isolado tornam-se LF. A conformidade CSS por propriedade está documentada na matriz de suporte CSS, e o comportamento da cascata está documentado em css-resolver. Esta página não repete o suporte por propriedade.
Contexto comercial
Seção intitulada “Contexto comercial”Recurso Enterprise. O Premium amplia a cobertura de CSS no mesmo pipeline. A sequência de seis etapas não muda entre as edições. Consulte a matriz de suporte CSS.