Pular para o conteúdo

HTML: subsistema de renderização de HTML+CSS em PDF

O subsistema HTML converte HyperText Markup Language (HTML) e Cascading Style Sheets (CSS) em fluxos de conteúdo Portable Document Format (PDF) em uma única passagem para frente. É o maior subsistema do mecanismo e também o de maior risco, com 324 arquivos em src/Html/.

Terminal window
composer require nextpdf/core:^3

O subsistema HTML é um renderizador de streaming de passagem única que converte HTML+CSS em PDF. A superfície pública é um único método: Document::writeHtml(). Internamente, o HtmlParser tokeniza a entrada, resolve estilos, calcula o layout e emite operadores PDF em uma única passagem para frente, sem reter uma árvore de documento.

Tenha clareza sobre o escopo. Este subsistema não é um renderizador de documento retido. Ele não mantém um grafo de elementos, não refaz o layout de conteúdo já gravado e não permite modificar a entrada depois que a análise começa. Ele implementa um subconjunto curado de CSS em pins de especificação fixos. Dois Architecture Decision Records (ADRs) o governam. O ADR-001 define o modelo de streaming de passagem única e seus limites. O ADR-010 define o contrato de quatro camadas (análise de CSS, estado de estilo, layout, pintura), além dos adjuntos de mídia paginada e de medição.

HtmlParser é classificado como risco crítico no manifesto do módulo. Cinco arquivos têm anotações documentadas de zona de perigo: o orquestrador HtmlParser (tokenizador de streaming, mais de 1000 linhas de código (LOC)), o HtmlStyleState (mais de 100 campos de propriedade CSS com um modelo de herança em pilha), o HtmlBlockHandler (despacho de blocos acoplado ao estado de estilo), o FlexLayoutEngine (medição e layout flex completos) e o TableParser (paginação de colspan/rowspan ao longo de quebras de página). Trate alterações aqui como trabalho em modo de planejamento.

Use esta página como ponto de entrada. Consulte pipeline para a sequência de etapas, css-resolver para cascata e especificidade, layer-contracts-adr010 para os limites das camadas e streaming-constraints-adr001 para o modelo sem árvore retida e os respectivos limites.

writeHtml() renderiza conteúdo da direita para a esquerda (RTL). Defina a propriedade CSS direction: rtl no body, em uma tabela ou em qualquer elemento. O mecanismo resolve a ordem visual com o algoritmo bidirecional Unicode (UAX #9) por meio do motor bidirecional da camada de tipografia — consulte Typography para os detalhes do BidiEngine. Conteúdo misto de latino, árabe e numérico é ordenado corretamente, e um número que vem após o árabe mantém seus dígitos da esquerda para a direita.

O árabe também recebe shaping contextual: o mecanismo seleciona a forma inicial, medial, final ou isolada de cada letra e aplica a ligadura Lam-Alef. O shaping precisa de uma fonte registrada cujo mapa de caracteres cubra o bloco Arabic Presentation Forms-B; uma face somente latina, incluindo as fontes standard-14, não consegue desenhar árabe. Em tabelas, cada célula é reordenada e passa por shaping de forma independente e alinha-se à borda de início (direita) sob direction: rtl. O RTL aplica-se a árabe, hebraico, persa e urdu; o hebraico é reordenado, mas não passa por shaping.

Defina a direção com a propriedade CSS direction — o atributo HTML dir não é mapeado para ela. O alinhamento horizontal de texto de bloco e em linha fora de tabela, e o text-align: justify, ainda não são aplicados. Para uma fatura em árabe executável e a lista completa das limitações atuais, consulte Render right-to-left Arabic HTML.

SímboloLocalizaçãoFunção
Document::writeHtml(string $html): staticsrc/Core/Concerns/HasTextOutput.phpPonto de entrada público. Renderiza HTML no cursor atual.
Document::createStandalone(): selfsrc/Core/Document.phpConstrói um documento autônomo.
HtmlParser::parse(string $html): HtmlRenderResultsrc/Html/HtmlParser.phpOrquestrador interno do fluxo.
HtmlRenderResultsrc/Html/HtmlRenderResult.phpResultado imutável: fluxo (stream), cursor final e fontes usadas.
DefaultHtmlSecurityPolicysrc/Html/DefaultHtmlSecurityPolicy.phpPolítica padrão de tags, atributos, CSS e Uniform Resource Locator (URL).
HtmlSecurityPolicyInterfacesrc/Contracts/HtmlSecurityPolicyInterface.phpContrato de política para políticas personalizadas.

Fonte: 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>Direct to PDF.</p>');
$doc->save(__DIR__ . '/output/08-html-basic.pdf');

Este exemplo mostra um relatório em tabela com um bloco de estilo incorporado, baseado em examples/09-html-table.php.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
use NextPDF\Exception\HtmlParsingException;
function renderInventory(string $rowsHtml, string $out): void
{
$doc = Document::createStandalone();
$doc->setTitle('Inventory');
$doc->addPage();
$html = '<style>table { width: 100%; } '
. 'th { background-color: #1E3A8A; color: #FFFFFF; }</style>'
. '<table border="1" cellpadding="5">' . $rowsHtml . '</table>';
try {
$doc->writeHtml($html);
} catch (HtmlParsingException $e) {
// Input cap, element cap (50,000), or nesting cap (100). Do not retry.
throw $e;
}
$doc->save($out);
}
  • Subconjunto curado de CSS. O suporte é fixado por módulo. Verifique a matriz de suporte de CSS antes de depender de uma propriedade.
  • Limites rígidos lançam exceção. Os limites de 10 MB de entrada, 50,000 elementos e 100 níveis de aninhamento lançam, cada um, uma HtmlParsingException. Consulte restrições de streaming.
  • Sem refazer o layout. O renderizador grava a saída uma única vez na ordem do documento; estilos posteriores não podem alterar a saída anterior.
  • :has() é controlado pelo recurso experimental css.has.
  • Subsistema de risco crítico. Cinco arquivos são marcados como zonas de perigo. Use o modo de planejamento para alterações em src/Html/.

Restrições de streaming de passagem única (ADR-001)

Seção intitulada “Restrições de streaming de passagem única (ADR-001)”

O renderizador não mantém árvore de documento e executa uma única passagem para frente. Os limites de elementos, aninhamento e entrada são rígidos. Para todos os detalhes e o contrato de segurança em workers, consulte restrições de streaming (ADR-001).

A análise de CSS, o estado de estilo, o layout e a pintura são separados em quatro camadas com contratos unidirecionais, além dos adjuntos de mídia paginada e de medição. Para todos os detalhes, consulte contratos de camada (ADR-010).

A memória de estado de estilo e cursor é O(profundidade de aninhamento), não O(contagem de elementos). O performance_budget por página é peak_mb: 64. O limite de 50,000 elementos é o teto rígido; divida entradas maiores em várias chamadas de writeHtml(). Para mais detalhes, consulte restrições de streaming.

A travessia é O(contagem de tokens). O dimensionamento de colunas de tabela adiciona uma varredura limitada de linhas por tabela. A pré-varredura opcional de :has() adiciona uma passagem limitada pela lista de tokens. O benchmark de desempenho do pipeline de renderização HTML impõe um portão de regressão de 5% (trabalho mesclado, pull request (PR) #564). O performance_budget por página (wall_ms: 1500, peak_mb: 64) é o teto operacional.

DefaultHtmlSecurityPolicy impõe uma lista de permissões de tags, atributos, propriedades CSS e esquemas de URL, além de um teto de 10 MB de entrada e um teto de 100 níveis de aninhamento, independentemente do analisador. A lista de permissões de propriedades CSS é o teto de segurança. A tabela de suporte em tempo de execução é um teto de capacidade separado. Implemente HtmlSecurityPolicyInterface para fornecer uma política mais rígida. O DefaultExternalResourcePolicy governa separadamente a busca de recursos externos.

Em valores de href e de src de imagem, a lista de permissões de URL também rejeita caminhos com raiz de barra invertida (\…) e caminhos Universal Naming Convention (UNC) (\\host\share), além da rejeição já existente de caminhos relativos ao protocolo (//) e da lista de permissões que aceita apenas http(s) ou relativos. As barras invertidas são normalizadas para barras antes da verificação, de modo que a inclusão de um arquivo local por caminho absoluto do Windows ou a busca em um compartilhamento Server Message Block (SMB) não consiga passar pelo ramo “sem esquema, portanto relativo”. Nenhum dos caminhos carrega um esquema Uniform Resource Identifier (URI).

Trecho da matriz de suporte de CSS (apenas linhas verificadas)

Seção intitulada “Trecho da matriz de suporte de CSS (apenas linhas verificadas)”

Esta página não reapresenta o suporte por propriedade. A matriz de suporte de CSS é a única autoridade para o status verificado por módulo do World Wide Web Consortium (W3C), incluindo quais módulos estão Verificados versus Declarados.

O subsistema implementa um subconjunto curado de CSS em pins de especificação fixos. Os mapeamentos comportamentais da especificação para a cascata são documentados com identificadores de cláusula e chunk em css-resolver. O status de conformidade por módulo está na matriz de suporte de CSS.

Capacidade Enterprise. O Premium amplia a cobertura de CSS (impressão avançada e módulos adicionais) no mesmo pipeline de passagem única. A arquitetura, os limites e os contratos de camada permanecem os mesmos entre as edições. Consulte a matriz de suporte de CSS.