Comparativo de bibliotecas Go PDF 2026
Todas as bibliotecas Go PDF ainda vivas em 2026, comparadas em 4 cargas de trabalho, com licenças, dependências e estado de manutenção.
TL;DR
Há cinco anos, pesquisar "biblioteca Go PDF" levava quase sempre a jung-kurt/gofpdf. Hoje, está arquivada. O fork da comunidade também. O terreno real é bem menor do que os resultados de busca sugerem:
- Ativamente mantidas: gpdf (este time), signintech/gopdf, johnfercher/maroto v2 — mas Maroto ainda depende de um gofpdf arquivado.
- Arquivadas: jung-kurt/gofpdf (2021), go-pdf/fpdf (2025).
- Comercial / AGPL: unidoc/unipdf.
Este post submete as bibliotecas mantidas a quatro cargas de trabalho, lista licenças e grafos de dependências, e faz uma recomendação por caso de uso. Rodamos tudo de novo no ano que vem.
Aviso de viés: somos o time do gpdf. O código do benchmark é público (_benchmark/benchmark_test.go) — clone, rode de novo, conte onde os números divergem.
O que estamos realmente comparando
A frase "biblioteca Go PDF" cobre pelo menos três ferramentas diferentes se passando pela mesma categoria:
- Escritores PDF de baixo nível — você empurra bytes e desenha com primitivas.
jung-kurt/gofpdf,signintech/gopdf. - Bibliotecas de layout que envolvem um escritor — linhas/colunas declarativas por cima.
johnfercher/maroto v2,gpdf. - Suítes documentais completas — parsing, assinatura, PDF/A, OCR, redação.
unidoc/unipdf.
Perguntar "qual é a melhor biblioteca Go PDF" sem dizer de qual categoria você precisa é o motivo de a maioria das threads de recomendação no Reddit saírem dos trilhos. Nas comparações abaixo tentamos manter essa distinção visível.
Fora da lista: qualquer coisa que suba um Chromium headless (go-rod, chromedp). Isso não é biblioteca PDF; é um navegador que imprime. Bom para fidelidade em CSS complexo, ruim para cold start, ruim para memória, ruim para distroless. Se o requisito é "o designer me entrega HTML+CSS e quer impressão pixel-perfect", essas ferramentas existem — não competimos com elas neste post.
Placar
| Biblioteca | Último release | Arquivada | Licença | Deps. do núcleo | CJK | Grid de layout | Status 2026 |
|---|---|---|---|---|---|---|---|
| gpdf (este time) | ativa | — | MIT | 0 | nativo | 12 colunas | mantida |
| signintech/gopdf | ativa | — | MIT | 0 | TTF manual | não | mantida |
| johnfercher/maroto v2 | ativa | — | MIT | gofpdf (arquivada) | via gofpdf | linha/coluna | mantida sobre base morta |
| jung-kurt/gofpdf | 2021 | 2021-09-08 | MIT | 0 | AddUTF8Font | não | arquivada |
| go-pdf/fpdf | 2023 | 2025 | MIT | 0 | AddUTF8Font | não | arquivada |
| unidoc/unipdf | ativa | — | AGPL-3.0 / Comercial | várias | sim | não | comercial |
Três pontos a notar. Um: metade do campo está arquivado. Dois: Maroto está mantida, mas a fundação não — é problema de supply chain mesmo que compile hoje. Três: se você não pode aceitar AGPL, unidoc vira decisão de licença comercial, não técnica.
O benchmark
Código: _benchmark/benchmark_test.go no repo do gpdf. Ambiente: Apple M1 (Max, 32 GB, macOS 14.5), Go 1.25, sem CGO. Cada caso roda por pelo menos cinco segundos de wall time. -benchmem ligado; reportamos ns/op e alocações.
Escolhemos esses quatro casos porque refletem o que as pessoas de fato geram, não algo micro-sintético:
- Hello world de uma página. Uma página, uma linha de texto, uma fonte. Define o piso de overhead por documento.
- Tabela de nota fiscal 4×10. Linha de cabeçalho, dez linhas de corpo, alinhamentos de coluna, bordas finas. A forma "gerar minha nota".
- Relatório paginado de 100 páginas. Cabeçalho repetido, rodapé, números de página, corpo em cada página. Mede custo de paginação.
- Nota fiscal CJK complexa. Japonês misto (Hiragana, Katakana, Kanji), tabela de itens 4×15, cabeçalho, rodapé com número de página, subconjunto TrueType NotoSansJP embarcado.
Não incluído: unidoc/unipdf. O binário é gated por licença, e reproduzir a metodologia de benchmark publicada dentro de um repo público seria mais enganoso do que útil. Se está avaliando unidoc, rode os benchmarks oficiais deles.
Resultados
| Carga | gpdf | signintech/gopdf | Maroto v2 | gofpdf | go-pdf/fpdf |
|---|---|---|---|---|---|
| Hello world de uma página | 13 µs | 423 µs | 237 µs | 132 µs | 135 µs |
| Tabela de nota fiscal 4×10 | 108 µs | 835 µs | 8.600 µs | 241 µs | 243 µs |
| Relatório paginado de 100 pgs. | 683 µs | 8.600 µs | 19.800 µs | 11.700 µs | 11.900 µs |
| Nota fiscal CJK complexa | 133 µs | 997 µs | 10.400 µs | 254 µs | n/d |
n/d em go-pdf/fpdf no caso CJK: o caminho padrão do AddUTF8Font dá panic ao ler a tabela cmap formato 12 do NotoSansJP na versão testada. Dá para patchear, mas o fork está arquivado — ninguém publica a correção.
Lendo os números
A ordem é estável entre cargas. gpdf é 10–30× mais rápido que o segundo colocado em todo caso testado — não é exótico nem acidental. Três escolhas de design se somam:
Layout em passada única. gpdf não constrói um AST intermediário para depois serializar. Os builders escrevem direto no content stream do PDF à medida que resolvem, o que elimina cerca de metade das alocações das outras bibliotecas. É o que move o ponteiro no benchmark de 100 páginas, onde a pressão de alocação bate mais no GC — 683 µs contra 19.800 µs não é diferença de tuning, é arquitetura diferente.
Sem reflection no hot path. Todo tipo que a engine de layout toca é concreto. Isoladamente soa a microotimização — e é — mas acumulado num relatório de 100 páginas, o dispatch por interface aparece no perfil. Deixamos de fora.
Subsetador TrueType que não varre toda vez. A máquina de fontes do gofpdf relê a tabela cmap a cada lookup de glifo; gpdf resolve uma vez e cacheia. Para conteúdo só em Latin quase não importa. Para CJK, onde um parágrafo pode tocar 150 glifos únicos entre Kanji, Hiragana, Katakana e pontuação, é a diferença entre "rápido o suficiente para geração síncrona" e "empurra pra fila".
Uma ressalva que a tabela não dá: velocidade absoluta importa menos do que se pensa na maioria das cargas PDF. O limiar interessante é "rápido o suficiente para gerar no caminho da requisição". Toda biblioteca aqui cruza esse limiar para um hello world de uma página. Só gpdf cruza para um relatório de 100 páginas com chrome repetido. Se seu maior documento é um recibo de uma página, as quatro mantidas dão conta; escolha por ergonomia de API e licença.
Dependências
O que go mod graph imprime após um go get fresco:
| Biblioteca | Módulos externos | Deps. arquivadas transitivas |
|---|---|---|
| gpdf (core) | 0 | — |
| signintech/gopdf | 0 | — |
| gofpdf | 0 (mas ela mesma arquivada) | ela mesma |
| go-pdf/fpdf | 0 (mas ela mesma arquivada) | ela mesma |
| johnfercher/maroto v2 | gofpdf (arquivada em 2021) | sim — gofpdf |
| unidoc/unipdf | várias (imagem, crypto, compressão) | nenhuma arquivada |
Para times com regra de lint "sem deps arquivadas em produção", Maroto v2 hoje é barrada pelo transitivo gofpdf. Os mantenedores do Maroto vêm, há mais de um ano, reescrevendo o backend para sair do gofpdf; quando isso chegar, esta linha muda. Vale checar o repo do Maroto antes de decidir sobre essa base.
Licenciamento
| Biblioteca | Licença |
|---|---|
| gpdf (core) | MIT |
| signintech/gopdf | MIT |
| johnfercher/maroto v2 | MIT |
| gofpdf | MIT |
| go-pdf/fpdf | MIT |
| unidoc/unipdf | AGPL-3.0 ou licença comercial |
Os termos AGPL da unidoc são estritos. Se você a usa num servidor com o qual usuários interagem pela rede, o código do servidor também precisa ser publicado sob AGPL — inviável para a maioria de SaaS de código fechado. Normalmente sobra a licença comercial como única opção real, e o preço não é público: conta com uma conversa com o comercial.
É o ponto mais esquecido em comparativos baseados em contagem de stars do GitHub. unidoc tem mais features e mais stars. Também tem uma licença que fecha a porta para a maioria dos casos comerciais sem uma compra. Não é ataque — o modelo é legítimo e o produto é excelente — mas você quer saber antes do go get.
Estado de manutenção, em português claro
- gpdf — mantenedor principal: este time (gpdf-dev). Releases a cada 2–4 semanas; roadmap no repo; CI roda em Go 1.22–1.26; issues do repo principal respondem em poucos dias úteis. Temos skin in the game.
- signintech/gopdf — ativo, cadência de commits menor. Issues recebem atenção; PRs entram em poucas semanas. Caso de uso principal: geração de baixo nível.
- Maroto v2 — ativo. A reescrita v2 chegou em 2023 e está estável. A dependência em gofpdf é conhecida e está sendo substituída; cheque o repo antes de se comprometer.
- gofpdf — arquivado em 2021-09-08. Banner no repo: "This repository has been archived by the owner. It is now read-only." Sem patches de segurança, sem correções.
- go-pdf/fpdf — arquivado em 2025. O README recomenda usar outra biblioteca. Escrevemos um guia dedicado: gofpdf está arquivado. Veja como migrar para gpdf..
- unidoc/unipdf — ativo, time comercial, bem financiado. Suporte enterprise disponível.
Como escolher
Em vez de matriz de features, uma árvore de decisão, porque "mais features" raramente é a pergunta certa:
- "Tenho um código Go que gera notas fiscais, relatórios ou documentos, quero MIT, zero deps, e às vezes os documentos têm CJK." → gpdf.
- "Faço geração PDF de baixo nível com geometria customizada e quero uma biblioteca pequena, estável e com controle manual." → signintech/gopdf.
- "Já tenho código estilo Maroto que funciona hoje." → fique no Maroto v2 até a remoção do gofpdf cair, depois reavalie. A API não é o problema.
- "Preciso de PDF/A, OCR, redação, assinatura digital, e meu empregador paga licença comercial." → unidoc/unipdf, com a conversa de licenciamento primeiro.
- "Ainda estou no gofpdf e funciona." → hoje, ok. Planeje a migração antes que caia o próximo CVE em uma dep não relacionada e você fique preso em código sem manutenção. Guia de migração.
- "Renderização pixel-perfect de HTML/CSS para PDF." → nenhuma das acima. Use go-rod ou chromedp com Chromium headless, assumindo o custo de cold start.
Somos o time do gpdf, então claro que achamos gpdf o default certo para o primeiro caso e a maior parte do quinto. Leia o código do benchmark, rode localmente, não engula a tabela.
Um exemplo gpdf em 30 linhas
Porque "a mais rápida" e "com o menor grafo de deps" só importam se o código for suportável. Esta é uma nota fiscal de uma página completa e executável — sem pseudocódigo, sem imports omitidos:
package main
import (
"log"
"os"
"github.com/gpdf-dev/gpdf"
"github.com/gpdf-dev/gpdf/document"
"github.com/gpdf-dev/gpdf/pdf"
"github.com/gpdf-dev/gpdf/template"
)
func main() {
doc := gpdf.NewDocument(
gpdf.WithPageSize(document.A4),
gpdf.WithMargins(document.UniformEdges(document.Mm(20))),
)
page := doc.AddPage()
page.AutoRow(func(r *template.RowBuilder) {
r.Col(12, func(c *template.ColBuilder) {
c.Text("NOTA FISCAL #2026-0042", template.Bold(), template.FontSize(20))
c.Spacer(document.Mm(6))
c.Table(
[]string{"Descrição", "Qtd.", "Unit.", "Valor"},
[][]string{
{"Desenvolvimento frontend", "40 h", "R$ 750,00", "R$ 30.000,00"},
{"Desenvolvimento backend", "60 h", "R$ 750,00", "R$ 45.000,00"},
{"Design de UI", "20 h", "R$ 600,00", "R$ 12.000,00"},
},
template.ColumnWidths(50, 15, 15, 20),
template.TableHeaderStyle(
template.Bold(),
template.TextColor(pdf.White),
template.BgColor(pdf.RGBHex(0x1A237E)),
),
)
})
})
data, err := doc.Generate()
if err != nil {
log.Fatal(err)
}
if err := os.WriteFile("invoice.pdf", data, 0o644); err != nil {
log.Fatal(err)
}
}
Zero SetXY. Zero conta manual de largura de coluna. Troque "Descrição" por "品目" e adicione gpdf.WithFont("NotoSansJP", ttfBytes) nas opções do documento — renderiza japonês sem nenhuma outra mudança. Útil também para DANFE e layouts de nota fiscal eletrônica que precisam de fontes específicas.
O que ficou de fora
Todo post de comparação tem a seção "omitido por X". A nossa:
- Forks privados de gofpdf. Existem forks em produção dentro de empresas. Não dá para benchmarkear código que não vemos.
pdfcpu. Está em toda lista de "bibliotecas Go PDF", mas é sobretudo um processador (merge, split, encrypt, stamp), não um gerador. Fora de escopo aqui; um artigo separado sobre processamento está planejado.- Qualquer wrapper em volta do
gotenbergou serviço de navegador headless. Não é biblioteca. Não é comparação justa. - Nossos próprios benchmarks do
gpdf. O que importa para a comparação são os números do core.
FAQ
Por que gpdf é 10× mais rápido que gofpdf? Qual é o truque? Nenhum truque único. Três designs se somam: layout em passada única (sem AST entre builder e writer), tipos concretos no hot path e um subsetador TrueType que cacheia a cmap. Qualquer um isolado dá 2×. Empilhados, dá uma ordem de grandeza.
Dá para reproduzir esse benchmark?
Dá. git clone https://github.com/gpdf-dev/gpdf && cd gpdf/_benchmark && go test -bench=. -benchmem. Se os números divergirem do post — mesma arquitetura, mesma versão do Go — abra uma issue. Drift de benchmark acontece; preferimos saber.
Gofpdf volta? Na prática, não. Último commit de 2021. Issue tracker fechado. Mesmo que alguém reabrisse, a arquitetura (cursor único, fontes de um byte, sem grid) é ponto de partida errado para 2026. Melhor tratar como artefato histórico e migrar.
E Java iText / Python ReportLab / Node pdfkit? Benchmark entre linguagens é outro post. Versão curta: Go costuma vencer em throughput e cold start, perde em amplitude de features (sobretudo em fidelidade HTML→PDF). Para times já em Go, gpdf é mais rápido e menor que qualquer dessas opções cross-language; para times em Python ou Node, o custo de migração só se paga em volume alto.
Essa comparação continua justa se os concorrentes melhorarem? Continua. Rodamos todo ano. Se signintech/gopdf publicar uma API de tabela que corta o tempo pela metade, entra no post de 2027. Se Maroto v2 terminar de remover gofpdf, aquela linha muda. O código do benchmark é público justamente para ninguém precisar acreditar em nós pela palavra.
Experimente o gpdf
gpdf é uma biblioteca Go para gerar PDFs. MIT, zero dependências, CJK nativo.
go get github.com/gpdf-dev/gpdf
⭐ Star no GitHub · Leia os docs
Próximas leituras
- gofpdf está arquivado. Veja como migrar para gpdf. — o mapeamento completo de API com cinco pares antes/depois.
- Quickstart — setup de cinco minutos, incluindo
go.mod. - O código do benchmark:
_benchmark/benchmark_test.go.