Bibliotecas PDF gratuitas for .NET: custos ocultos e melhores alternativas em C#
Bibliotecas PDF gratuitas for .NET vêm com custos ocultos: armadilhas de licenciamento AGPL, suporte HTML ausente, dependências obsoletas com CVEs não corrigidas, limites de receita e complexidade operacional que muitas vezes excedem os custos de licenças comerciais.
Antes de se comprometer com qualquer uma delas, execute isso em um terminal:
<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe>
<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe>
Se seu aplicativo .NET usarwkhtmltopdfou qualquer um de seus wrappers — DinkToPdf, TuesPechkin, Rotativa, NReco.PdfGenerator — aquele HTML executará uma Server-Side Request Forgery contra o endpoint de metadados do seu provedor de nuvem. Credenciais IAM da AWS, tokens de identidade gerenciados do Azure, chaves de conta de serviço GCP. Tudo exposto. O projeto foi arquivado em janeiro de 2023. Nenhum patch está a caminho.
É isso que "gratuito" custa em produção.
Quickstart: Avalie Bibliotecas PDF para Seu Projeto .NET
// Install: dotnet add package IronPdf
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1><style>h1{font-family:Inter}</style>");
pdf.SaveAs("output.pdf");
// Install: dotnet add package IronPdf
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1><style>h1{font-family:Inter}</style>");
pdf.SaveAs("output.pdf");
var document = new PdfDocument();
var page = document.AddPage();
var gfx = XGraphics.FromPdfPage(page);
gfx.DrawString("Hello World", new XFont("Arial", 20), XBrushes.Black, 72, 72);
document.Save("output.pdf");
var document = new PdfDocument();
var page = document.AddPage();
var gfx = XGraphics.FromPdfPage(page);
gfx.DrawString("Hello World", new XFont("Arial", 20), XBrushes.Black, 72, 72);
document.Save("output.pdf");
A diferença entre esses dois exemplos é a diferença entre o que a maioria dos desenvolvedores .NET precisa e o que a maioria das bibliotecas gratuitas oferece.
O que "Gratuito" Realmente Significa em Bibliotecas PDF .NET?
Procure "PDF" no NuGet e você encontrará bibliotecas em cinco modelos de licenciamento, cada um com diferentes restrições para implantação em .NET:
MIT/Apache (genuinamente permissivo): PdfSharp. Use-o em aplicações comerciais, contêineres Docker, Funções Azure, AWS Lambda — sem restrições, sem limites de receita, sem divulgação de código-fonte. O problema: ele não pode converter HTML para PDF.
AGPL (armadilha copyleft):iText Core(anteriormente iTextSharp). Implante-o em qualquer aplicação acessível por rede — apps web, APIs REST, microsserviços — e você deve liberar todo o seu código-fonte sob AGPL. Empresas SaaS não têm isenção.
Licença baseada em receita:QuestPDFCommunity License. Grátis abaixo de $1M de receita bruta anual. Ultrapasse esse limite e você precisará de uma licença comercial. A transição não é gradual — é um precipício.
Abandonado:wkhtmltopdfe todos os wrappers .NET. Arquivado com CVEs não corrigidos com pontuação 9.8 Crítica. Zero manutenção de segurança. Um passivo em qualquer auditoria de conformidade.
Operacionalmente caro: MarionetistaAfiado e Playwright for .NET. Sem restrições de licenciamento, suporte completo a CSS moderno — mas você está gerenciando processos de navegador externos, downloads de Chromium e ciclo de vida de memória em produção.
Cada categoria cria diferentes riscos para implantações .NET. O restante deste artigo detalha esses riscos com código, números e dados do ecossistema NuGet.
Por que o Preço do iText é a Verdadeira História?
A maioria dos artigos sobre iText foca no ângulo da aplicação AGPL. Isso é abordado em outro lugar. A questão mais relevante para equipes .NET avaliando bibliotecas PDF é o que acontece quando você precisa de uma licença comercial.
Quanto Custa Realmente o iText Comercial?
Em abril de 2020, o iText passou de licenciamento perpétuo para modelos baseados em assinatura. Dados de preços de terceiros do banco de dados de transações da Vendr mostram:
- Contrato anual médio: ~$45,000
- Contratos de alto nível: Até $210,000 dependendo do volume de PDFs
- Modelo de preço: Baseado em volume — os custos escalam com quantos PDFs sua aplicação gera anualmente
Esse modelo baseado em volume cria orçamentos imprevisíveis para aplicações em crescimento. Um microsserviço .NET gerando 10,000 PDFs por mês durante o Q1 que escala para 100,000 durante o Q4 verá uma fatura de licenciamento que escala junto — e os níveis de preços do iText não são públicos.
Compare isso com o preço publicado do IronPDF: $749 por uma licença Lite perpétua. Sem assinatura anual. Sem medição de volume. Sem surpresas quando sua aplicação escala.
Como o Modelo de Assinatura Afeta Equipes .NET?
A mudança de licenciamento perpétuo para assinatura altera o cálculo do TCO:
| Fator | iText Assinatura | IronPDF Perpétua |
|---|---|---|
| Custo do Ano 1 | ~$45,000 | $749 - $2,999 |
| Custo do Ano 3 | ~$135,000 | $749 - $2,999 (uma vez) |
| Escalabilidade de volume | Custos aumentam | Sem medição de volume |
| Previsibilidade de orçamento | Variável | Corrigido |
| Risco de cancelamento | Perder acesso | Propriedade perpétua |
Para uma equipe .NET construindo um produto SaaS, o delta de cinco anos pode exceder $200,000. Essa é a história de precificação que importa mais do que debates sobre a aplicação do AGPL.
Quais são as limitações doPdfSharppara implantações .NET?
PdfSharp é a exceção entre as bibliotecas gratuitas: licença MIT, mais de 34 milhões de downloads no NuGet, genuinamente permissiva para uso comercial. Sem limites de receita. Nenhuma divulgação de código-fonte.
A limitação é arquitetural.PdfSharpopera ao nível de coordenadas de PDF. Não há parser HTML, nenhum motor CSS, sem renderização DOM.
var document = new PdfDocument();
var page = document.AddPage();
var gfx = XGraphics.FromPdfPage(page);
// Every element needs manual coordinates
var titleFont = new XFont("Arial", 18, XFontStyleEx.Bold);
var bodyFont = new XFont("Arial", 10);
gfx.DrawString("INVOICE #2024-0847", titleFont, XBrushes.Black, 72, 72);
gfx.DrawString("Date: 2024-12-15", bodyFont, XBrushes.Gray, 72, 100);
// Table header - manual line drawing
gfx.DrawLine(XPens.Black, 72, 140, 540, 140);
gfx.DrawString("Item", bodyFont, XBrushes.Black, 72, 155);
gfx.DrawString("Qty", bodyFont, XBrushes.Black, 300, 155);
gfx.DrawString("Price", bodyFont, XBrushes.Black, 400, 155);
gfx.DrawLine(XPens.Black, 72, 170, 540, 170);
// Row data - every cell is a manual coordinate
gfx.DrawString("Annual License", bodyFont, XBrushes.Black, 72, 185);
gfx.DrawString("1", bodyFont, XBrushes.Black, 300, 185);
gfx.DrawString("$749.00", bodyFont, XBrushes.Black, 400, 185);
document.Save("invoice.pdf");
var document = new PdfDocument();
var page = document.AddPage();
var gfx = XGraphics.FromPdfPage(page);
// Every element needs manual coordinates
var titleFont = new XFont("Arial", 18, XFontStyleEx.Bold);
var bodyFont = new XFont("Arial", 10);
gfx.DrawString("INVOICE #2024-0847", titleFont, XBrushes.Black, 72, 72);
gfx.DrawString("Date: 2024-12-15", bodyFont, XBrushes.Gray, 72, 100);
// Table header - manual line drawing
gfx.DrawLine(XPens.Black, 72, 140, 540, 140);
gfx.DrawString("Item", bodyFont, XBrushes.Black, 72, 155);
gfx.DrawString("Qty", bodyFont, XBrushes.Black, 300, 155);
gfx.DrawString("Price", bodyFont, XBrushes.Black, 400, 155);
gfx.DrawLine(XPens.Black, 72, 170, 540, 170);
// Row data - every cell is a manual coordinate
gfx.DrawString("Annual License", bodyFont, XBrushes.Black, 72, 185);
gfx.DrawString("1", bodyFont, XBrushes.Black, 300, 185);
gfx.DrawString("$749.00", bodyFont, XBrushes.Black, 400, 185);
document.Save("invoice.pdf");
São 20 linhas para uma fatura de única linha sem estilo, sem layout responsivo, sem CSS. Agora imagine construir um relatório de conformidade de 15 páginas com dados dinâmicos, gráficos, e identidade visual corporativa.
Considerações de Implantação Multiplataforma
PdfSharp funciona bem em cenários multiplataforma .NET 6+ — não possui dependências nativas, binário do Chromium, nem processos externos. Implanta-se perfeitamente no Docker, Azure Functions, e AWS Lambda com tamanho mínimo de contêiner.
Para aplicações que precisam apenas de criação de PDF programática a partir de dados estruturados — etiquetas de envio, recibos simples, diagramas plotados por coordenadas —PdfSharpé uma escolha legítima. Sua saúde no NuGet é forte: commits ativos, mantenedor responsivo, lançamentos regulares.
Para qualquer coisa que envolva conteúdo HTML, modelos web, ou CSS moderno,PdfSharpé a ferramenta errada.
Quando oQuestPDFdeixa de ser gratuito?
QuestPDF adotou uma abordagem de design diferente do PdfSharp: uma API fluente que se lê como uma descrição de layout em vez de matemática de coordenadas. O design da API é realmente bom.
Document.Create(container =>
{
container.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(2, Unit.Centimetre);
page.Content().Column(column =>
{
column.Item().Text("Invoice #2024-0847").FontSize(18).Bold();
column.Item().Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn(3);
columns.RelativeColumn(1);
columns.RelativeColumn(1);
});
table.Cell().Text("Item").Bold();
table.Cell().Text("Qty").Bold();
table.Cell().Text("Price").Bold();
table.Cell().Text("Annual License");
table.Cell().Text("1");
table.Cell().Text("$749.00");
});
});
});
}).GeneratePdf("invoice.pdf");
Document.Create(container =>
{
container.Page(page =>
{
page.Size(PageSizes.A4);
page.Margin(2, Unit.Centimetre);
page.Content().Column(column =>
{
column.Item().Text("Invoice #2024-0847").FontSize(18).Bold();
column.Item().Table(table =>
{
table.ColumnsDefinition(columns =>
{
columns.RelativeColumn(3);
columns.RelativeColumn(1);
columns.RelativeColumn(1);
});
table.Cell().Text("Item").Bold();
table.Cell().Text("Qty").Bold();
table.Cell().Text("Price").Bold();
table.Cell().Text("Annual License");
table.Cell().Text("1");
table.Cell().Text("$749.00");
});
});
});
}).GeneratePdf("invoice.pdf");
Isso é mais expressivo do que o sistema de coordenadas do PdfSharp. Mas compartilha a mesma limitação fundamental: nenhuma renderização HTML.
O Declive de Receita
A licença comunitária doQuestPDFé gratuita para empresas com receita bruta anual abaixo de $1,000,000. Ao ultrapassar esse limite, é necessário uma licença Professional ($699/ano) ou Enterprise ($1,999/ano).
Para uma startup, isso cria um cenário de linha do tempo de crescimento:
- Ano 1 (receita de $200K): Gratuito. A API fluente doQuestPDFacelera o desenvolvimento inicial.
- Ano 2 (receita de $600K): Ainda gratuito. A API está profundamente integrada ao seu código-fonte.
- Ano 3 (receita de $1.1M): Licença necessária. Você está agora preso à API com custos significativos de mudança.
A transição não se trata do custo da licença — é sobre o custo de mudança que você acumulou. Não terceiro ano, sua camada de geração de PDF pode abranger dezenas de arquivos com chamadas de API fluente que não têm equivalente em outras bibliotecas.
A Ilusão do HTML
Desenvolvedores vindos de frameworks web esperam que uma biblioteca moderna .NET de PDF aceite entrada HTML.QuestPDFexplicitamente não suporta a conversão de HTML para PDF. Sua API é somente em código — cada elemento de layout é uma chamada de método, não marcação.
Essa incompatibilidade pega equipes que compram licençasQuestPDF(ou constroem com a edição comunitária) apenas para descobrir no meio do projeto que seus modelos de fatura HTML existentes, fluxos de trabalho de email para PDF, ou geradores de relatórios não podem usar oQuestPDFde forma alguma.
IronPDF aceita HTML, CSS, e JavaScript como entrada porque incorpora um motor de rendering do Chromium. O mesmo HTML que rende no Chrome renderiza identicamente em um PDF.
Por que evitar owkhtmltopdfem qualquer implantação .NET?
Comecei este artigo com a CVE-2022-35583 por um motivo. Essa vulnerabilidade SSRF não é teórica — explorações de prova de conceito estão publicamente disponíveis e sendo ativamente usadas.
O Panorama Completo de Segurança
wkhtmltopdf possui dois CVEs não corrigidos que nunca serão solucionados:
CVE-2022-35583 (CVSS 9.8 Crítico): Forjamento de Solicitação do Servidor via injeção de iframe. Em ambientes de nuvem, isso expõe pontos de extremidade de metadados da instância — credenciais do IAM AWS, tokens de identidade gerenciada do Azure, chaves de contas de serviço do GCP, tokens de contas de serviço do Kubernetes.
CVE-2020-21365 (CVSS 7.5 Alto): Transversal de diretório permitindo que atacantes remotos leiam arquivos locais por meio de entrada HTML criada.
Ambas as vulnerabilidades têm código de exploração pública. Ambos são explorados ativamente. Nenhum receberá uma correção.
Saúde do Ecossistema de Wrappers .NET
Todo wrapper .NET parawkhtmltopdfherda essas vulnerabilidades e adiciona sua própria dívida de manutenção:
| Wrapper | Último Commit Significativo | Problemas Abertos | Suporte a .NET 8 |
|---|---|---|---|
| DinkToPdf | 2018 | 300+ sem resposta | Não |
| Terça-feira Pechkin | 2015 | Abandonado | Não |
| Rotativa | 2019 | Apenas MVC | Não |
| NReco.PdfGenerator | Ativo | Comercial | Limitado |
NReco é o único wrapper mantido ativamente, mas ainda depende do bináriowkhtmltopdf— o que significa que os CVEs acompanham.
Renderização Congelada em 2013
Além da segurança, o motor Qt WebKit dowkhtmltopdfestá congelado nos padrões web de 2013. Sem Flexbox CSS. Sem Grid CSS. Sem Variáveis CSS. Não calc(). JavaScript ES6+ não é executado.
Qualquer aplicativo .NET que use Tailwind CSS, Bootstrap 5 ou frameworks CSS modernos produzirá saída quebrada. Para um aplicativo .NET 8 voltado para implantação em contêiner, um binário não mantido sem suporte a padrões web modernos é a dívida técnica que você está escolhendo carregar.
Qual é o Verdadeiro Custo Operacional do PuppeteerSharp?
Esta é a seção que a maioria dos artigos de 'biblioteca PDF gratuita' erra. MarionetistaAfiado e Playwright for .NET são tecnicamente excelentes — eles renderizam HTML através do Chromium real, suportando todos os recursos CSS e APIs JavaScript. Sem restrições de licença. Sem limites de receita.
O custo é operacional. É assim que a geração de PDF do MarionetistaAfiado em produção realmente é:
using PuppeteerSharp;
public class PuppeteerPdfService : IDisposable
{
private IBrowser _browser;
private readonly SemaphoreSlim _semaphore;
private long _pdfCount = 0;
public PuppeteerPdfService()
{
// Limit concurrent pages to prevent memory exhaustion
_semaphore = new SemaphoreSlim(3, 3);
}
public async Task InitializeAsync()
{
// Step 1: Download Chromium binary (~280MB)
var fetcher = new BrowserFetcher();
await fetcher.DownloadAsync();
// Step 2: Launch with production-hardened flags
_browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true,
Args = new[]
{
"--no-sandbox", // Required in Docker
"--disable-setuid-sandbox", // Required in Docker
"--disable-dev-shm-usage", // Prevent /dev/shm exhaustion
"--disable-gpu",
"--no-zygote",
"--single-process",
"--disable-extensions",
"--max_old_space_size=4096"
},
Timeout = 30000
});
}
public async Task<byte[]> GeneratePdfAsync(string html)
{
await _semaphore.WaitAsync();
IPage page = null;
try
{
Interlocked.Increment(ref _pdfCount);
page = await _browser.NewPageAsync();
await page.SetContentAsync(html, new NavigationOptions
{
WaitUntil = new[] { WaitUntilNavigation.Networkidle0 },
Timeout = 20000
});
var pdfBytes = await page.PdfAsync(new PdfOptions
{
Format = PaperFormat.A4,
PrintBackground = true,
MarginOptions = new MarginOptions
{
Top = "20mm", Bottom = "20mm",
Left = "15mm", Right = "15mm"
}
});
return pdfBytes;
}
finally
{
if (page != null)
{
try { await page.CloseAsync(); }
catch { /* Disposal can hang — GitHub issue #1489 */ }
}
_semaphore.Release();
}
}
// Restart browser periodically to reclaim leaked memory
public async Task RecycleBrowserAsync()
{
var oldBrowser = _browser;
await InitializeAsync();
try { oldBrowser?.Dispose(); } catch { }
}
public void Dispose()
{
_browser?.Dispose();
_semaphore?.Dispose();
}
}
using PuppeteerSharp;
public class PuppeteerPdfService : IDisposable
{
private IBrowser _browser;
private readonly SemaphoreSlim _semaphore;
private long _pdfCount = 0;
public PuppeteerPdfService()
{
// Limit concurrent pages to prevent memory exhaustion
_semaphore = new SemaphoreSlim(3, 3);
}
public async Task InitializeAsync()
{
// Step 1: Download Chromium binary (~280MB)
var fetcher = new BrowserFetcher();
await fetcher.DownloadAsync();
// Step 2: Launch with production-hardened flags
_browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true,
Args = new[]
{
"--no-sandbox", // Required in Docker
"--disable-setuid-sandbox", // Required in Docker
"--disable-dev-shm-usage", // Prevent /dev/shm exhaustion
"--disable-gpu",
"--no-zygote",
"--single-process",
"--disable-extensions",
"--max_old_space_size=4096"
},
Timeout = 30000
});
}
public async Task<byte[]> GeneratePdfAsync(string html)
{
await _semaphore.WaitAsync();
IPage page = null;
try
{
Interlocked.Increment(ref _pdfCount);
page = await _browser.NewPageAsync();
await page.SetContentAsync(html, new NavigationOptions
{
WaitUntil = new[] { WaitUntilNavigation.Networkidle0 },
Timeout = 20000
});
var pdfBytes = await page.PdfAsync(new PdfOptions
{
Format = PaperFormat.A4,
PrintBackground = true,
MarginOptions = new MarginOptions
{
Top = "20mm", Bottom = "20mm",
Left = "15mm", Right = "15mm"
}
});
return pdfBytes;
}
finally
{
if (page != null)
{
try { await page.CloseAsync(); }
catch { /* Disposal can hang — GitHub issue #1489 */ }
}
_semaphore.Release();
}
}
// Restart browser periodically to reclaim leaked memory
public async Task RecycleBrowserAsync()
{
var oldBrowser = _browser;
await InitializeAsync();
try { oldBrowser?.Dispose(); } catch { }
}
public void Dispose()
{
_browser?.Dispose();
_semaphore?.Dispose();
}
}
São mais de 80 linhas antes de gerar um único PDF. E ainda falta recuperação de erro, verificações de saúde, métricas e o temporizador de reciclagem de memória que os sistemas de produção precisam.
Por Que Esta Complexidade Importa para Implantações .NET
O fardo operacional escala com o alvo de implantação:
Docker: Você deve incluir o Chromium na sua imagem de contêiner. Isso adiciona ~280MB à imagem, aumentando os tempos de pull, custos de armazenamento de registro e latência de inicialização a frio. Seu Dockerfile precisa de comandos apt-get install explícitos para as dependências do sistema do Chromium — libgbm-dev, libasound2, libatk-bridge2.0-0, e cerca de 15 outros que variam conforme a imagem base.
Azure Functions / AWS Lambda: Ambientes serverless restringem memória e tempo de execução. A inicialização a frio do Chromium — baixando e iniciando o processo do navegador — pode consumir 5-10 segundos e mais de 500MB de memória. O limite de pacote de implantação de 250MB do Lambda significa que o Chromium mal cabe, e o limite de memória de 1,5GB do plano de Consumo do Azure deixa pouco espaço para a própria geração do PDF.
Kubernetes: Processos de navegador não funcionam bem com orquestração de contêiner. Limites de memória que parecem adequados para o código do seu aplicativo tornam-se insuficientes quando o Chromium gera processos de renderização. OOMKills do Pod se tornam uma ocorrência regular, a menos que você defina pedidos de memória significativamente maiores do que seu aplicativo realmente precisa.
O Código Equivalente do IronPDF
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
renderer.RenderingOptions.MarginTop = 20;
renderer.RenderingOptions.MarginBottom = 20;
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
renderer.RenderingOptions.MarginTop = 20;
renderer.RenderingOptions.MarginBottom = 20;
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
Seis linhas.IronPDFincorpora o Chromium internamente — o ciclo de vida do navegador, gerenciamento de memória e agrupamento de processos são tratados pela biblioteca. Sem SemaphoreSlim. Sem reciclagem de navegador. Sem modificações no Dockerfile. O pacote NuGet inclui tudo.
A troca é real: o pacote NuGet doIronPDFé maior do que o doPdfSharpporque inclui o binário do Chromium. A latência do primeiro PDF é de 2-5 segundos enquanto o motor inicializa, então 100-500ms para gerações subsequentes. Para aplicativos onde o tamanho da implantação é a principal restrição, isso importa. Para aplicativos onde o tempo do desenvolvedor e a confiabilidade operacional importam mais, a abordagem embutida vence.
Saúde do Ecossistema .NET: Comparação de Pacotes NuGet
Antes de escolher uma biblioteca, verifique a saúde do seu ecossistema NuGet. Cadeias de dependências, frequência de lançamento e tempo de resolução de problemas dizem mais do que listas de recursos:
| Biblioteca | Downloads do NuGet | Último Lançamento | Problemas Abertos | .NET 8 TFM | Dependências nativas |
|---|---|---|---|---|---|
| PdfSharp | 34M+ | Ativo | Baixo | ✅ | Nenhum |
| QuestPDF | 8M+ | Ativo | Baixo | ✅ | Nenhum |
| iText Core | 30M+ | Ativo | Moderado | ✅ | Nenhum |
| IronPDF | 10M+ | Ativo | Baixo | ✅ | Chromium (incluído) |
| DinkToPdf | 5M+ | 2018 | 300+ | ❌ | binário wkhtmltopdf |
| MarionetistaAfiado | 15M+ | Ativo | Moderado | ✅ | Chromium (externo) |
Contagens altas de downloads em pacotes abandonados comoDinkToPdfrefletem adoção legada, não saúde atual. As mais de 300 issues abertas sem respostas contam a verdadeira história.
Para aplicações .NET 8 direcionadas ao net8.0 TFM: PdfSharp, QuestPDF,iText CoreeIronPDFoferecem suporte nativo. Wrapperswkhtmltopdfnão oferecem — espere erros de incompatibilidade de framework de destino DllNotFoundException e NU1202.
Matriz de Decisão
| Exigência | PdfSharp | QuestPDF | iText Core | wkhtmltopdf | MarionetistaAfiado | IronPDF |
|---|---|---|---|---|---|---|
| Realmente gratuito (MIT/permissivo) | ✅ | ❌ Porta de receita | ❌ AGPL | ⚠️ Abandonado | ✅ | ❌ Comercial |
| HTML para PDF | ❌ | ❌ | ⚠️ Quantidade limitada | ⚠️ CSS Quebrado | ✅ | ✅ |
| CSS Moderno (Flexbox/Grid) | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
| Execução de JavaScript | ❌ | ❌ | ❌ | ⚠️ Apenas ES5 | ✅ | ✅ |
| Sem gerenciamento de navegador | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
| Atualizações de segurança ativas | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ |
| Sem limite de receita | ✅ | ❌ | N / D | N / D | ✅ | ✅ |
| Custo de licenciamento previsível | Livre | Precipício em $1M | ~$45K/ano em média | N / D | Livre | $749 perpétuo |
| Amigável ao Docker | ✅ Pequeno | ✅ Pequeno | ✅ Pequeno | ⚠️ Dependência binária | ⚠️ +280MB | ✅ Autossuficiente |
| Compatível com serverless | ✅ | ✅ | ✅ | ❌ | ⚠️ Início frio | ✅ |
Se você precisa apenas de criação programática de PDF a partir de dados estruturados:PdfSharp(MIT, sem restrições) ouQuestPDF(melhor API, fique de olho no limite de receita).
Se você precisa de HTML-para-PDF com CSS moderno e não quer gerenciar a infraestrutura do navegador: IronPDF. Chromium incorporado gerencia o ciclo de vida do mecanismo de renderização. Preço publicado é uma fração do modelo de assinatura do iText.
Se você precisa de HTML-para-PDF e está confortável gerenciando processos de navegador: MarionetistaAfiado dá total controle. Orçamento para a sobrecarga operacional.
Se você está atualmente usandowkhtmltopdfou qualquer um de seus envelopes: Migre. A exposição de segurança por si só justifica o esforço — e a cada mês que você adia, a lista de CVEs permanece sem correção.