Pular para o conteúdo

Pipeline de renderização de HTML

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.

Terminal window
composer require nextpdf/core:^3

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.

SímboloLocalizaçãoEtapa
Document::writeHtml(string $html): staticsrc/Core/Concerns/HasTextOutput.phpPonto de entrada público
HtmlParser::parse(string $html): HtmlRenderResultsrc/Html/HtmlParser.phpOrquestra todas as etapas
HtmlTokenizer::cleanHtml() / tokenize()src/Html/HtmlTokenizer.phpEtapa 3
HtmlChildScanner::scan()src/Html/HtmlChildScanner.phpMapas de índice da Etapa 3
CssResolver::resolveHasSelectors()src/Html/CssResolver.phpEtapa 4 (controlada por flag)
HtmlRenderResult (stream, endX, endY, usedFontKeys)src/Html/HtmlRenderResult.phpEtapa 6

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');

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);
}
  • @page é lido antes dos tokens. Uma regra @page depois 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 experimental css.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.

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.

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.

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.

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.