O que realmente sobrevive HTML para PDF, medido
Doze recursos de CSS de impressão renderizados através do Chromium headless e depois lidos de volta do PDF — quebras de página, tamanhos @page, cabeçalhos de tabela repetidos, links e fontes. Dois deles não funcionam, e um problema comum é culpa da API, não do CSS.
O que há de novo aqui: Uma matriz de suporte por recurso para CSS de impressão HTML-para-PDF, medida no Chromium 148 renderizando cada caso e inspecionando a contagem de páginas do PDF resultante, dimensões da página, anotações de link e texto por página — não lendo documentação. Cada veredicto é reproduzível a partir do aparato publicado e conjunto de dados, e duas técnicas amplamente recomendadas são mostradas como não funcionando.
Todo serviço HTML-to-PDF diz que sua saída é fiel. Nenhum deles publica o que isso significa. Então medimos: doze recursos de print-CSS, cada um renderizado através de Chromium headless — o mecanismo por trás de MostlyRender e por trás da maioria dos serviços HTML-to-PDF — e depois lemos novamente o PDF resultante.
Nada aqui é citado de documentação. Cada veredicto vem da inspeção do arquivo: quantas páginas ele tem, qual é o tamanho delas, qual texto acabou em qual página, se uma anotação de link existe, se uma fonte foi incorporada.
# A matriz
Medido em Chromium 148.0.7778.96, primeiro em 1º de setembro de 2026 e novamente em 27 de setembro de 2026. Ambas as execuções produziram os mesmos doze veredictos.
| Recurso | Funciona | O que observamos |
|---|---|---|
break-after: page |
Sim | 2 páginas, conforme solicitado |
page-break-after: always (herdado) |
Sim | 2 páginas — a ortografia antiga ainda é honrada |
break-inside: avoid |
Sim | O bloco se moveu por completo para a página 2 em vez de se dividir |
@page { size: A5 } |
Sim | 420×595pt — exatamente A5 |
@page { size: A4 landscape } |
Sim | 842×595pt — landscape |
Repetindo <thead> em páginas |
Sim | 3 páginas, cabeçalho presente na página 2 |
position: fixed como cabeçalho em execução |
Sim | Presente em 2 de 2 páginas |
Caixas de margem de @page + counter(page) |
Não | Sem número de página em lugar algum na saída |
<a href> como link clicável |
Sim | Anotação de link presente |
Links #anchor internos |
Sim | Destino presente |
| Incorporação de fontes | Sim | DejaVuSansMono incorporada e subconjunto |
| Grade CSS em quebra de página | Não | Todas as 8 células na página 1; página 2 vazia |
Dez de doze funcionam. Os dois que não funcionam valem a pena conhecer antes de você projetar em torno deles.
# Os dois que falham
As caixas de margem de CSS Paged Media não são renderizadas. A especificação fornece @page { @bottom-center { content: "Page " counter(page) } }, está em todos os tutoriais sobre impressão na web, e o Chromium a ignora. Não encontramos nenhum número de página na saída. Se você precisar de números de página, eles devem ser adicionados por qualquer coisa que produza o PDF — é por isso que as ferramentas que oferecem numeração de páginas fazem isso como um recurso em vez de pedir que você escreva CSS.
A grade CSS não se fragmenta. Organizamos oito itens em uma grade de duas colunas alta o suficiente para precisar de duas páginas. Todos os oito foram renderizados na página um, e a página dois ficou em branco. Grade é adequada para conteúdo que cabe em uma única página; para qualquer coisa que pagine, use fluxo de bloco ou uma tabela.
# A pegadinha que não é CSS
Ao construir o teste, registramos @page { size: A5 } como não suportado — e foi nosso próprio erro. O renderizador estava sendo informado para usar A4 pela chamada da API, e um formato de papel passado pelo chamador silenciosamente substitui o tamanho de @page do documento. Com o formato omitido e o tamanho de página CSS preferido, A5 e landscape saíram exatamente corretos.
Vale a pena afirmar isso claramente porque é quase certamente a razão mais comum pela qual as pessoas concluem que @page está quebrado: o CSS está correto, e algo upstream está silenciosamente o superando. Só detectamos isso porque um resultado que discordava da especificação nos fez reler o teste em vez de publicar o número.
# Reproduzindo isto
O teste e o conjunto de dados por trás da tabela são ambos publicados:
- bench-htmltopdf.js inicia o Chromium, renderiza cada caso e inspeciona a saída com pdf-lib e pdf.js.
- html-to-pdf.json é a saída de onde escrevemos esta tabela, marcada com a versão do Chromium que mediu.
Para executá-lo você mesmo, salve o teste em uma pasta vazia e execute:
npm i playwright pdf-lib pdfjs-dist@3
npx playwright install chromium
node bench-htmltopdf.js
Um limite que vale a pena afirmar: a detecção de fontes verifica os bytes brutos do PDF para nomes de fontes, então um resultado positivo é sólido, mas um vazio apenas significa “não visível para este método”. O teste relata esse caso como inconclusivo em vez de falha, porque publicar “fontes não estão incorporadas” com base em uma varredura que não consegue vê-las seria exatamente o tipo de afirmação que este artigo existe para evitar.
# Por que medimos
Construímos uma ferramenta HTML to PDF e API neste mecanismo. Publicar o que faz e o que não faz é mais útil do que afirmar que é fiel — e se um número aqui estiver errado, o teste que o produziu está vinculado acima para ser executado.
Perguntas frequentes
O page-break-after ainda funciona, ou preciso de break-after?
Ambos funcionam. Medimos o break-after: page moderno e o page-break-after: always legado separadamente, e cada um produziu o documento esperado de duas páginas no Chromium 148. Se você tem templates antigos emitindo a sintaxe legada, eles não são o motivo do seu problema de paginação.
Por que meu tamanho @page está sendo ignorado?
Quase sempre porque o chamador o substituiu. Se a API ou biblioteca que executa a renderização passa um formato de papel próprio, isso vence e seu @page { size: … } é descartado silenciosamente. Com o formato omitido e tamanho de página CSS preferido, A5 e landscape saíram exatamente como solicitado. Este é o relato falso mais comum de "@page size não funciona" — nos pegou enquanto construíamos o framework para este artigo.
Posso colocar números de página na margem com CSS?
Não no Chromium. @page { @bottom-center { content: counter(page) } } faz parte do CSS Paged Media e é amplamente documentado, mas a caixa de margem simplesmente não renderiza — medimos e não encontramos número de página em lugar nenhum na saída. Os números de página precisam vir da ferramenta que faz a conversão, não da sua folha de estilos.
Os layouts de grade CSS se quebram corretamente entre as páginas?
Não. Em nosso teste, todos os oito itens da grade foram renderizados na página um e a segunda página saiu vazia — a grade não fragmentou. Se seu documento é longo o suficiente para paginar, distribua a parte repetida com fluxo de bloco normal ou uma tabela e mantenha a grade para coisas que cabem em uma página.
Os hiperlinks são preservados no PDF?
Sim. Tanto um <a href> externo quanto um #anchor interno produziram anotações de link reais no arquivo de saída, portanto os links permanecem clicáveis e um índice gerado permanece navegável.