Pular para o conteúdo

Renderizar HTML em uma página PDF

Use writeHtml() para renderizar um fragmento de Hypertext Markup Language (HTML) e Cascading Style Sheets (CSS) como conteúdo de página em Portable Document Format (PDF). Forneça a marcação, e o NextPDF renderiza uma página formatada. A versão completa e executável deste código está em examples/08-html-basic.php. Siga as etapas abaixo ou copie o exemplo diretamente.

O NextPDF lê o HTML uma vez e transmite o resultado diretamente para a página. Este é um pipeline de streaming de passagem única. Você não precisa entender esse modelo para usar esta receita. Ainda assim, vale mantê-lo em mente, porque ele explica algumas regras mais adiante nesta página.

Terminal window
composer require nextpdf/core:^3

Este comando instala o pacote nextpdf/core. Os exemplos desta página são executados em PHP 8.4, e o runtime compatível é >=8.4 <9.0.

writeHtml() recebe uma string HTML e a desenha na página atual, começando pela posição atual do cursor. Internamente, o NextPDF varre o HTML uma vez e o divide em tokens (HtmlTokenizer). Em seguida, percorre essa lista da esquerda para a direita (HtmlParser). Para cada elemento, escreve em um buffer as instruções de desenho PDF correspondentes, chamadas de operadores de fluxo de conteúdo (content-stream operators). O mecanismo nunca constrói nem mantém uma árvore de elementos na memória entre chamadas. Esse design deliberado é o modelo de streaming de passagem única registrado no ADR-001.

Cada elemento de bloco suportado se torna uma caixa de layout, e cada trecho de texto se torna um operador de exibição de texto (text-show operator). Os estilos de atributos style inline e de um bloco <style> são resolvidos pela cascata CSS, ou seja, pelas regras que decidem qual estilo prevalece quando mais de um se aplica. A quebra de texto, o alinhamento e o espaçamento seguem o modelo CSS Text, que define como o texto de origem se transforma em texto formatado e quebrado em linhas (W3C CSS Text Level 3).

Se você não escolher uma fonte, o texto do corpo usa uma face padrão. Esse padrão é uma fonte Type 1 padrão, uma das 14 fontes padrão nomeadas na ISO 32000-2. Ele só muda quando você registra e seleciona a sua própria fonte, ou quando um perfil de conformidade exige que o NextPDF incorpore uma substituta.

Defina essa expectativa desde o início: o NextPDF suporta um subconjunto de HTML e CSS, não a totalidade de nenhum dos dois. Esta receita cobre o subconjunto suportado. Ela não afirma suporte completo a HTML nem a CSS. Para ver o status exato e verificado de cada módulo, consulte a matriz de suporte a CSS.

A assinatura do método é writeHtml(string $html): static. Ele é declarado na interface NextPDF\Contracts\PdfDocumentInterface e implementado em NextPDF\Core\Concerns\HasTextOutput. O método renderiza na página atual e cria uma para você caso ainda não exista nenhuma. A tabela PHPDoc completa do método é gerada a partir do código-fonte.

<?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>HTML Rendering in NextPDF</h1><p>Rendered with <strong>writeHtml()</strong>.</p>');
$doc->save(__DIR__ . '/out.pdf');

Este exemplo completo e autossuficiente é o que o harness de testes executa. Ele espelha examples/08-html-basic.php. Em vez de codificar um caminho de saída fixo, ele grava no caminho fornecido pelo harness. Isso permite que o harness de reprodutibilidade execute o script duas vezes e compare os resultados.

<?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 content</strong> directly into PDF pages.
This is the recommended approach for <em>mixed formatting</em>.</p>
<h2>Supported elements</h2>
<ul>
<li>Headings (h1-h6)</li>
<li>Paragraphs with <strong>bold</strong> and <em>italic</em></li>
<li>Ordered and unordered lists</li>
<li>Tables with borders and alignment</li>
<li>Inline styles (color, font-size, margin)</li>
</ul>
<h2>Ordered list</h2>
<ol>
<li>Create a Document instance</li>
<li>Add pages and content</li>
<li>Call save() or output()</li>
</ol>
HTML;
$doc->writeHtml($html);
// The harness sets NEXTPDF_COOKBOOK_OUTPUT and runs this script twice.
// Honour it: do not hard-code a path, do not echo the PDF to STDOUT.
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');
$doc->save($out !== false ? $out : __DIR__ . '/render-html-to-pdf.pdf');
echo "Wrote render-html-to-pdf.pdf\n";

STDOUT esperado:

Wrote render-html-to-pdf.pdf
  • Transferência do cursor. writeHtml() move o cursor para o fim do conteúdo renderizado. Um cell() seguinte ou um segundo writeHtml() continua a partir desse ponto, não do topo da página.
  • Ainda sem página. Se não existir nenhuma página, writeHtml() adiciona uma antes de renderizar. Chame addPage() primeiro quando você precisar definir um tamanho de página específico.
  • Limites de elementos e de aninhamento. O mecanismo de streaming impõe um limite de 50,000 elementos e um limite de aninhamento de 100 níveis (ADR-001). Um documento que excede qualquer um desses limites é rejeitado, em vez de ser truncado silenciosamente.
  • Marcação não suportada. Elementos e propriedades fora do subconjunto suportado são ignorados ou usam um fallback; eles não geram erros. Verifique a cobertura na matriz de suporte a CSS antes de depender de uma propriedade.
  • Recursos externos. Imagens e folhas de estilo remotas seguem a política de recursos externos; a política padrão não busca URLs remotas arbitrárias.

A tokenização e a renderização acontecem em uma única passagem pela entrada, então o custo cresce linearmente com o número de tokens, O(n). O orçamento padrão para esta receita é wall_ms: 1500, peak_mb: 96. Como o mecanismo transmite a saída e não mantém nenhum Document Object Model (DOM) na memória, o pico de memória acompanha o buffer de fluxo de conteúdo e a pilha de estilos ativa, não o tamanho total do documento.

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

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

Este trecho inclui apenas as linhas classificadas como Verificado na matriz de suporte a CSS auditada quanto à veracidade. “Verificado” significa que há uma implementação em src/Html/ e um conjunto substancial e dedicado de fixtures que passa de forma determinística sob o perfil estrutural.

Módulo W3CNívelStatusEvidência
CSS Flexible Box Layout (css_flexbox_1)1Verificadosrc/Html/Flex/, tests/Unit/Html/Flex/
CSS Grid Layout (css_grid_1)1Verificadosrc/Html/Grid/, corpus WPT
CSS Cascading and Inheritance (css_cascade_3)3Verificadosrc/Html/Cascade/, tests/Unit/Html/Cascade/
CSS Table (css_tables_3)3Verificadosrc/Html/Table/, fixtures de tabela + PDFs golden
CSS Fonts (css_fonts_4)4Verificadosrc/Html/FontFace/, tests/Unit/Html/FontFace/

Propriedades como text-align, text-indent e color são classificadas como “Reivindicado” na matriz (implementadas, mas sem fixture de módulo dedicada), por isso não aparecem aqui como Verificado.

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

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

O mecanismo HTML não retém nenhum DOM. Seu estado é um cursor escalar mais uma pilha de estilos push/pop; nós de texto compostos apenas por espaços em branco são descartados na tokenização. Uma consequência é que um elemento posterior não pode reestilizar um anterior, e seletores que precisam do contexto da árvore completa (por exemplo, casos complexos de :has()) ficam restritos conforme o ADR-006. Planeje um layout que dependa apenas da ordem do documento.

Análise (parsing), layout e pintura são camadas separadas. O parser não emite operadores de pintura brutos, e o despacho de layout não analisa CSS. Cruzar essas fronteiras gera a dívida de acoplamento que o ADR-010 proíbe. Para autores de receitas, isso significa que o ponto de entrada público é writeHtml(). Não acesse os internos do parser.

Conforme o ADR-020, contextos de formatação com escopo de contêiner (flex, table) podem construir uma subárvore efêmera, limitada a 5,000 nós por contexto, 20 níveis de profundidade, com um teto de memória ativa de 50 MB em todos os contextos ativos e 10 níveis de aninhamento. Fora desses contextos, o modelo de streaming não mantém nenhuma árvore. Mantenha tabelas individuais e contêineres flex dentro do limite de nós para ter consumo de memória previsível.

Trate a entrada HTML como não confiável. O NextPDF não executa scripts, e a política padrão de recursos externos não busca URLs remotas arbitrárias, portanto o próprio mecanismo é conservador. Ainda assim, valide ou sanitize qualquer HTML que você monte a partir da entrada do usuário antes de renderizá-lo. Os limites de elementos e de aninhamento também protegem você: eles delimitam quanto trabalho um documento hostil ou malformado pode exigir.

DeclaraçãoEspecificaçãoCláusulareference_id
O CSS Text controla a tradução do texto de origem em texto formatado e quebrado em linhas.W3C CSS Text Level 3css_text_3#x1.x2.p4
A face padrão do corpo é resolvida para uma fonte Type 1 padrão.ISO 32000-2iso32000_2_sec9#x1.x29

Esta receita mostra como o NextPDF renderiza um subconjunto suportado de HTML e CSS. Ela não afirma suporte completo a HTML nem a CSS. O status verificado por módulo está na matriz de suporte a CSS.

Não aplicável.