Gerar PDF a partir de HTML em C#
Guia · atualizado em 12/09/2026 · 8 min de leitura
Faturas, relatórios, contratos e comprovantes: em sistemas .NET é comum precisar gerar PDF a partir de HTML em C#. Desenhar o documento em HTML e CSS é mais rápido do que posicionar texto por coordenadas, e o mesmo layout serve para a tela e para o PDF. Este guia compara as opções mais usadas e mostra o código completo, com HttpClient e ASP.NET Core, usando a API do PDF Services.
As opções para converter HTML em PDF no .NET
Existem dois caminhos: rodar um conversor dentro da sua aplicação ou chamar um serviço pela rede. As alternativas mais conhecidas:
| Opção | Como funciona | Pontos de atenção |
|---|---|---|
| iText (pdfHTML) | Biblioteca que converte HTML e CSS direto em PDF, sem navegador. | Licença AGPL ou comercial: usar dentro de um produto fechado exige avaliar a licença. |
| PuppeteerSharp / Playwright | Controlam um Chromium headless e "imprimem" a página em PDF. | Precisam baixar e manter o navegador, consomem bastante memória e complicam containers enxutos e ambientes serverless. |
| wkhtmltopdf (DinkToPdf e similares) | Wrapper de um executável baseado em WebKit. | Projeto arquivado desde 2023, com motor de renderização antigo. Evite em projetos novos. |
| API HTTP (PDF Services) | Você envia o HTML e recebe o PDF na resposta. | Depende de rede e é cobrado por uso, mas não há nada para instalar ou atualizar no servidor. |
O PDF Services usa o próprio iText pdfHTML por baixo. A diferença é que ele roda como serviço: sua aplicação não carrega a biblioteca nem se preocupa com licença, fontes, memória ou atualizações.
Quando faz sentido usar uma API
- Você publica em Azure Functions, AWS Lambda ou containers mínimos, onde instalar um Chromium é caro ou impossível.
- O volume é variável e você prefere pagar por PDF em vez de dimensionar servidores para picos.
- Você não quer lidar com dependências nativas, fontes do sistema operacional e atualizações de segurança do conversor.
Se você precisa executar JavaScript da página, como gráficos montados no navegador, um navegador headless é a ferramenta certa. Para documentos de negócio montados no servidor, HTML e CSS bastam.
Passo a passo: gerar o PDF com HttpClient
1. Crie a conta e a chave de API
Cadastre a empresa, confirme o e-mail e crie uma chave em Chaves de API no painel. A chave aparece uma única vez: guarde-a como segredo, por exemplo em variável de ambiente, User Secrets ou Azure Key Vault, nunca no código-fonte.
2. Envie o HTML e salve o PDF
O exemplo abaixo é um app de console do .NET 8 ou superior. O corpo da requisição é o próprio HTML, com Content-Type: text/html; charset=utf-8:
using System.Text;
var apiKey = Environment.GetEnvironmentVariable("PDFSERVICES_API_KEY")
?? throw new InvalidOperationException("Defina a variável PDFSERVICES_API_KEY.");
var html = """
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<style>
@page { size: A4; margin: 2cm; }
body { font-family: sans-serif; }
</style>
</head>
<body><h1>Olá, PDF!</h1></body>
</html>
""";
using var http = new HttpClient { BaseAddress = new Uri("https://api.pdfservices.sistema.site") };
http.DefaultRequestHeaders.Add("X-Api-Key", apiKey);
using var content = new StringContent(html, Encoding.UTF8, "text/html");
using var response = await http.PostAsync("/v1/html2pdf/html", content);
if (!response.IsSuccessStatusCode)
{
var erro = await response.Content.ReadAsStringAsync();
throw new InvalidOperationException($"Falha {(int)response.StatusCode}: {erro}");
}
await File.WriteAllBytesAsync("documento.pdf", await response.Content.ReadAsByteArrayAsync());
Console.WriteLine($"Cobrado: R$ {response.Headers.GetValues("X-Charged-Amount").First()}");
A resposta traz o PDF em application/pdf e os headers X-Transaction-Id, X-Charged-Amount (valor cobrado) e X-Balance (saldo restante).
3. Se preferir, envie JSON
// O formato json é útil quando o HTML já está em um objeto ou vem de outro serviço.
using var response = await http.PostAsJsonAsync("/v1/html2pdf/json", new { html });
Segurança: chame a API sempre a partir do servidor. Se a chave aparecer em código que roda no navegador ou em um app móvel, qualquer pessoa pode usá-la e consumir seus créditos.
Exemplo em ASP.NET Core: devolvendo o PDF para o usuário
Em uma API web, registre um HttpClient nomeado com IHttpClientFactory e devolva o PDF com Results.File. A chave fica em appsettings ou, melhor, em User Secrets ou variáveis de ambiente (PdfServices__ApiKey):
using System.Net;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient("PdfServices", client =>
{
client.BaseAddress = new Uri("https://api.pdfservices.sistema.site");
client.DefaultRequestHeaders.Add("X-Api-Key", builder.Configuration["PdfServices:ApiKey"]);
});
var app = builder.Build();
app.MapGet("/faturas/{numero}/pdf", async (string numero, IHttpClientFactory factory) =>
{
// Todo dado vindo do usuário deve ser codificado antes de entrar no HTML.
var html = $"""
<!doctype html>
<html lang="pt-BR">
<head><meta charset="utf-8"></head>
<body><h1>Fatura {WebUtility.HtmlEncode(numero)}</h1></body>
</html>
""";
var client = factory.CreateClient("PdfServices");
using var response = await client.PostAsync("/v1/html2pdf/html", new StringContent(html, Encoding.UTF8, "text/html"));
if (!response.IsSuccessStatusCode)
{
return Results.Problem($"Falha ao gerar o PDF ({(int)response.StatusCode}).");
}
var pdf = await response.Content.ReadAsByteArrayAsync();
return Results.File(pdf, "application/pdf", $"fatura-{numero}.pdf");
});
app.Run();
Para documentos maiores, monte o HTML com Razor, por exemplo com uma view renderizada para string ou uma biblioteca de templates. O importante é codificar todo dado vindo do usuário (WebUtility.HtmlEncode) para evitar injeção de HTML no documento.
CSS para PDF: tamanho da página, cabeçalho, rodapé e quebras
O motor de conversão entende CSS de impressão (paged media). É com ele que você controla o documento, sem nada específico de C#:
@page {
size: A4;
margin: 2.5cm 2cm;
@top-center { content: "Relatório mensal"; }
@bottom-right { content: "Página " counter(page) " de " counter(pages); }
}
.nova-pagina { page-break-before: always; }
/* Repete o cabeçalho da tabela em todas as páginas */
thead { display: table-header-group; }
- Tamanho e margens:
@page { size: A4; },A4 landscape,Letterou medidas como21cm 29.7cm. - Cabeçalho e rodapé: as caixas de margem (
@top-center,@bottom-rightetc.) comcounter(page)ecounter(pages)geram o "Página 1 de 3". - Quebras:
page-break-before,page-break-afterepage-break-inside: avoidpara não cortar blocos ao meio. - Imagens: embutidas como
data:URI ou por URL https pública, em JPG ou PNG. - Fontes: as padrão já cobrem acentos; para a sua fonte, use
@font-facecomdata:URI. Fontes por URL, como as do Google Fonts, não são baixadas. - JavaScript não é executado: monte o HTML final no servidor. Para gráficos, gere uma imagem PNG antes.
Tratamento de erros
Quando algo dá errado, a API responde com um JSON {"error": "...", "message": "..."}. Os casos que valem tratar no código:
| HTTP | Código | O que fazer |
|---|---|---|
| 401 | invalid_api_key | Confira a chave configurada; ela pode ter sido excluída no painel. |
| 402 | insufficient_balance | Saldo insuficiente: adicione créditos. Não adianta repetir a chamada. |
| 413 | payload_too_large | O corpo passou de 1 MB. Reduza imagens embutidas ou use URLs https. |
| 422 | conversion_failed | O HTML não pôde ser convertido. Revise o documento; a falha não é cobrada. |
| 503 | busy | Muitas chamadas simultâneas na mesma conta: repita com espera crescente. |
No .NET, o pacote Microsoft.Extensions.Http.Resilience (ou o Polly) resolve as novas tentativas no próprio HttpClient. Limite as tentativas ao status 503 e a falhas de rede: repetir 401, 402 ou 422 não muda o resultado.
Custos e privacidade
O PDF Services cobra R$ 0,001 por conversão bem-sucedida, com créditos pré-pagos e sem mensalidade: mil PDFs custam R$ 1,00. Conversões com erro não são cobradas.
O HTML enviado e o PDF gerado são processados só em memória e nunca são armazenados nem registrados em logs. Ficam registrados apenas metadados da transação, como data, tamanho e valor cobrado. Detalhes na Política de Privacidade. Todo o código do serviço é aberto, sob a licença AGPL v3, e pode ser auditado no GitHub.
Gere seu primeiro PDF agora
Crie a conta da sua empresa, gere uma chave no painel e rode o exemplo acima. Pelo painel também dá para testar a conversão antes de escrever uma linha de código.