Blog

gPdf vs Puppeteer: quando 800 MB de Chromium é a resposta errada

Puppeteer transforma qualquer página em PDF, mas muitas vezes você paga por um navegador headless que não precisa. Comparação prática para 2026.

Se você pesquisou “Puppeteer PDF alternative” hoje, a pergunta real provavelmente é:

“Por que minha função serverless leva 2 seconds de cold start e usa 900 MB de RAM só para imprimir uma fatura?”

Puppeteer é uma ferramenta excelente. Também é grande demais para o trabalho que muitos times fazem com ela: transformar dados estruturados em um PDF previsível. Esta comparação é para quem está prestes a levar Puppeteer para produção e desconfia que deve existir um caminho mais simples.

O que você realmente entrega com Puppeteer

Ao rodar npm install puppeteer, você baixa cerca de 170 MB de Chromium antes das dependências. Em runtime, headless Chromium normalmente precisa de 600-900 MB de memória residente para renderizar uma página e 1-2 seconds de cold start para iniciar o navegador. Cada render precisa:

  1. Subir o processo do browser, ou reutilizar um pool.
  2. Abrir uma aba.
  3. Navegar para seu HTML / URL.
  4. Esperar domcontentloaded e, muitas vezes, fontes, imagens e web components.
  5. Executar page.pdf(), serializando a página pintada pelo PDF engine do Chromium.
  6. Fechar a aba.

Esse é o imposto da web platform inteira. Você paga igual para um contrato jurídico de 90 páginas ou para uma etiqueta de envio de uma página com cinco linhas.

Quando o input realmente precisa de HTML, CSS layout, JavaScript e web fonts, o imposto faz sentido. Para faturas, etiquetas, recibos, tickets, extratos e certificados, costuma ser desperdício.

Onde Puppeteer vence

Seja honesto antes de migrar:

  • HTML/CSS fiel. Se seu design system emite HTML e o PDF precisa ser pixel-identical, Puppeteer é o melhor. É o Chrome imprimindo.
  • Web-platform features. SVG filters, casos extremos de CSS Grid, web components, conteúdo calculado por JavaScript e iframes de terceiros funcionam.
  • Debug visual. Você pode tirar screenshots no meio do render e usar DevTools em headless mode.
  • Sem tradução de schema. Se o conteúdo já é uma página, page.goto(url); await page.pdf() é a pipeline inteira.

Se dois desses pontos descrevem seu workload real, não troque. Puppeteer é a resposta certa.

Onde Puppeteer perde

Nos outros casos, o custo empilha rápido.

Memória e cold start em serverless

Um Node 20 Lambda ou Cloudflare Container típico com Puppeteer:

Metric Typical value
Container image size 250-400 MB (Chromium + Node + your code)
Cold-start time 1.8 - 2.5 seconds
Warm RAM per render 600 - 900 MB
Concurrent renders per 1 GB instance 1 (sometimes 2 if pages are tiny)

Se seu serviço de faturas faz 100.000 renderizações por mês, você paga pelo boot do browser em cada contêiner frio, embora nenhuma dessas faturas precise executar JavaScript.

A armadilha das fontes em containers

Chromium vem com um conjunto padrão de fontes, geralmente insuficiente para CJK, cirílico, devanagari, árabe e muitas escritas específicas. Em produção, aparece assim:

A fatura Q3 2025 do escritório de Tokyo imprime ▢▢▢▢ 2025年第3四半期. O cliente escala. O time passa um sprint em Dockerfile fonts e CSS fallback.

Só NotoSans CJK adiciona ~50 MB à imagem. Um fallback global Noto pode adicionar ~250 MB. Você paga por Chromium e por uma catedral de fontes para imprimir uma fatura japonesa.

Determinismo

Renders de Puppeteer não são byte-identical entre versões de Chromium. Um patch pode mover kerning, baseline ou quebras de página. Se você tem PDF diff em CI, cada upgrade vira uma investigação.

JavaScript no render time

Até HTML “estático” precisa ser parseado, layout-computed, pintado e serializado. Na prática, isso dá 80-400 ms por página em processo quente. O mesmo documento enviado como JSON a um renderer binário pode sair em 3-8 ms.

Onde gPdf se encaixa

gPdf inverte o modelo: você não descreve o documento como HTML para um browser pintar. Você o descreve como JSON estruturado (DocumentRequest), e um renderer Rust compilado para WebAssembly emite o PDF diretamente. Não há browser, DOM ou layout pass em JavaScript.

Isso é restritivo para problemas que são HTML de verdade. Mas para faturas, etiquetas, recibos, extratos e certificados, o modelo JSON-first combina melhor:

  • Seus dados já são estruturados. A fatura já existe como { customer, lines, totals, taxes, notes }. Você não quer transformá-la em HTML para o browser re-interpretar como layout.
  • Layout vira contrato. font_size: 11 sempre são 11 points; gap: 8 sempre são 8 points.
  • Saída byte-identical. Mesmo input, mesmos bytes. PDF diff fica útil.
  • Cold start é o runtime, não o browser. Um V8 isolate em Cloudflare Workers inicia em 5-20 ms e o módulo WASM fica quente no mesmo isolate.

Um render típico de fatura de uma página no gPdf fica em 3-5 ms p50 wall-clock no edge. É cerca de duas ordens de grandeza mais rápido que o warm path do Puppeteer e três ordens mais rápido que o cold path.

Matriz de decisão

Workload Use Puppeteer Use gPdf
Existing HTML report → PDF ✅ first choice ⚠️ requires rewrite
Invoices, statements, receipts ⚠️ heavy hammer ✅ first choice
Shipping labels with barcodes ❌ avoid (font issues) ✅ first choice
E-invoice (Factur-X / ZUGFeRD / EN 16931) ❌ no built-in support ✅ built-in
PDF/A long-term archival ⚠️ needs Ghostscript pass ✅ built-in profiles
Pixel-faithful design system mockups ✅ first choice ❌ wrong tool
Charts that need real D3 / Recharts ✅ first choice ❌ wrong tool
Tickets, certificates, name-tags ⚠️ overkill ✅ first choice
Anything that needs JavaScript at render time ✅ only choice ❌ wrong tool

Se a coluna da direita ganha em mais de três linhas, a economia não é sutil.

Comparação real: fatura de uma página

Mesmo conteúdo, papel, fontes NotoSans e perfil PDF/A-3b:

Puppeteer (warm Lambda, 1 GB) gPdf (warm Cloudflare Worker)
p50 latency 180 ms 3.4 ms
p99 latency 420 ms 8 ms
Cold-start penalty +1800 ms first render +12 ms first render
Memory at peak 720 MB 18 MB
Image / module size 280 MB 4.5 MB
CJK glyphs ❌ unless explicit install ✅ embedded NotoSans CJK
Custo / 100.000 renderizações ~$240 (Lambda compute) ~$5 (gPdf Basic plan)

O último item surpreende, mas não é preço-isca. É estrutural. Não precisamos amortizar boot do Chromium, memória do browser ou cold starts de contêiner.

“Mas $5/100.000 páginas parece barato demais. Qual é a pegadinha?”

A pegadinha é que não rodamos um browser. O custo de um renderer binário quente em V8 isolate é milissegundos de CPU e kilobytes de memória. Cobrar como Puppeteer seria cobrar por infraestrutura que não usamos.

Quando ainda escolher Puppeteer

A resposta honesta nem sempre é “use gPdf”:

  1. Você já roda Puppeteer em produção e funciona. Não migre por esporte. Avalie gPdf quando Puppeteer doer: conta compute acima de $400/mês ou cold start quebrando SLA.
  2. Seus documentos são páginas web existentes. Um relatório de 60 páginas gerado por usuários, com charts e conteúdo dinâmico, não é migração JSON; é redesign.
  3. Você precisa de paridade pixel-perfect com preview web. Alguns fluxos precisam que Chromium seja o renderer nos dois lados.

Se nada disso se aplica, a conta é direta: deploy menor, menor latência, menor custo, saída determinística e menos drama com fontes.

Como migrar um workload real

Normalmente é um spike de 1-2 dias por tipo de documento:

  1. Escolha um documento, de preferência o de maior volume, não o mais complexo.
  2. Mapeie as seções lógicas do HTML para elementos gPdf JSON (text, box, table, barcode, image).
  3. Itere no Playground com um DocumentRequest real.
  4. Escreva um mapper pequeno que emita JSON a partir dos seus dados atuais.
  5. Rode A/B contra o endpoint Puppeteer por uma semana. Faça diff dos PDFs. Decida.

A maioria dos times entende o modelo JSON em um dia. O difícil não é a nova ferramenta; é desfazer anos de ginástica HTML/CSS no template antigo.

TL;DR

Puppeteer é a resposta certa para páginas web. Para documentos, você paga uma taxa de 100-200× por render para evitar o pequeno custo inicial de descrever o documento como dados. Se você gera faturas, etiquetas, recibos, extratos ou tickets com forma fixa e valores variáveis, um renderizador nativo da edge como o gPdf será mais rápido, menor, mais barato e determinístico.

Teste no Playground. É um Worker real no edge, sem signup, com resposta no navegador abaixo de 5 ms.