Biblioteca PDF para C# / .NET — PDF Oxide
PDF Oxide é a biblioteca PDF mais rápida para .NET: 0,8 ms em média na extração de texto, 5× mais rápida que o PyMuPDF, 15× mais rápida que o pypdf e 100 % de sucesso em 3 830 PDFs. Um único pacote para extrair, criar e editar PDFs — pronto para NativeAOT, trim-safe, com using, async Task<T>, CancellationToken e coleções amigáveis ao LINQ. Licença MIT / Apache-2.0.
Instalação
dotnet add package PdfOxide
Target frameworks: net8.0 e net10.0. IsAotCompatible=true e IsTrimmable=true já vêm habilitados.
O pacote NuGet inclui bibliotecas nativas pré-compiladas para Windows, macOS (Intel + Apple Silicon) e Linux (x64 + ARM64). Sem dependências de sistema e sem precisar da toolchain do Rust.
Abrindo um PDF
using PdfOxide.Core;
using var doc = PdfDocument.Open("research-paper.pdf");
Console.WriteLine($"Páginas: {doc.PageCount}");
Console.WriteLine($"Versão do PDF: {doc.Version.Major}.{doc.Version.Minor}");
A partir de um stream:
using var stream = File.OpenRead("report.pdf");
using var doc = PdfDocument.Open(stream);
Com senha:
using var doc = PdfDocument.OpenWithPassword("secure.pdf", "user-password");
PDFs com AES-256 (V=5, R=6) são totalmente suportados.
API de páginas
Desde a v0.3.34, PdfDocument expõe a propriedade Pages (um IReadOnlyList<PdfPage>) e um indexador int, então você pode iterar com foreach e usar LINQ.
using PdfOxide.Core;
using var doc = PdfDocument.Open("paper.pdf");
foreach (var page in doc.Pages)
{
Console.WriteLine($"--- Página {page.Index + 1} ---");
Console.WriteLine(page.ExtractText());
}
// Ou acesso direto pelo índice
PdfPage first = doc[0];
string md = await first.ToMarkdownAsync();
Cada PdfPage oferece uma superfície completa síncrona e assíncrona: ExtractText() / ExtractTextAsync(), ToMarkdown(), ToHtml(), ToPlainText(), ExtractWords(), ExtractTextLines(), ExtractTables(), ExtractChars(), ExtractImages(), Search().
Extração de texto
Página única
using var doc = PdfDocument.Open("report.pdf");
string text = doc.ExtractText(0);
Console.WriteLine(text);
Todas as páginas
string allText = doc.ExtractAllText();
Percorrer páginas manualmente
for (int i = 0; i < doc.PageCount; i++)
{
Console.WriteLine($"--- Página {i + 1} ---");
Console.WriteLine(doc.ExtractText(i));
}
Extração assíncrona
Todo método de extração tem sua contraparte *Async que retorna Task<T> e aceita um CancellationToken opcional.
using PdfOxide.Core;
using var doc = PdfDocument.Open("large.pdf");
string text = await doc.ExtractTextAsync(0);
// Fan-out com cancelamento
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
var tasks = Enumerable.Range(0, doc.PageCount)
.Select(i => doc.ExtractTextAsync(i, cts.Token));
string[] pages = await Task.WhenAll(tasks);
Veja padrões completos no guia assíncrono.
Extração estruturada
var words = doc.ExtractWords(0);
foreach (var (text, x, y, w, h) in words)
{
Console.WriteLine($"\"{text}\" em ({x:F1}, {y:F1})");
}
// Por região
string regionText = doc.ExtractTextInRect(0, x: 50, y: 700, width: 200, height: 50);
var tables = doc.ExtractTables(0);
foreach (var (rows, cols) in tables)
{
Console.WriteLine($"tabela {rows}x{cols}");
}
Conversão para Markdown
string markdown = doc.ToMarkdown(0);
string allMarkdown = doc.ToMarkdownAll();
Conversão para HTML
string html = doc.ToHtml(0);
string allHtml = doc.ToHtmlAll();
Extração de imagens
using PdfOxide.Core;
using var doc = PdfDocument.Open("brochure.pdf");
var images = doc.ExtractImages(0);
foreach (var img in images)
{
Console.WriteLine($"{img.Width}x{img.Height} {img.Format} ({img.Colorspace}, {img.BitsPerComponent} bpc, {img.Data.Length} bytes)");
File.WriteAllBytes($"image_{Array.IndexOf(images.ToArray(), img)}.{img.Format}", img.Data);
}
PDFs com cor indexada são expandidos automaticamente para RGB (1/2/4/8 bpc com espaços de cor base RGB, escala de cinza ou CMYK).
Busca
var results = doc.SearchAll("receita trimestral");
foreach (var (page, text, x, y, w, h) in results)
{
Console.WriteLine($"Página {page}: \"{text}\" em ({x}, {y})");
}
// Página única, diferenciando maiúsculas
var pageResults = doc.SearchPage(0, "frase exata", caseSensitive: true);
O LINQ se integra naturalmente:
var hitsByPage = doc.SearchAll("keyword")
.GroupBy(r => r.Page)
.OrderBy(g => g.Key);
foreach (var group in hitsByPage)
{
Console.WriteLine($"Página {group.Key}: {group.Count()} ocorrências");
}
Criação de PDFs
using PdfOxide.Core;
// A partir de Markdown
using (var pdf = Pdf.FromMarkdown("# Fatura\n\nTotal: **$42,00**"))
{
pdf.Save("invoice.pdf");
}
// A partir de HTML
using (var pdf = Pdf.FromHtml("<h1>Relatório</h1><p>Gerado em 2026-04-09</p>"))
{
pdf.Save("report.pdf");
}
// A partir de texto puro
using (var pdf = Pdf.FromText("Documento em texto puro.\n\nSegundo parágrafo."))
{
pdf.Save("notes.pdf");
}
// A partir de uma imagem
using (var pdf = Pdf.FromImage("scan.jpg"))
{
pdf.Save("scan.pdf");
}
Edição — metadados e formulários
using PdfOxide.Core;
using var editor = DocumentEditor.Open("form.pdf");
// Leitura de metadados
Console.WriteLine($"Título: {editor.Title}");
Console.WriteLine($"Páginas: {editor.PageCount}");
// Atualização de metadados
editor.Title = "Relatório trimestral";
editor.Author = "Equipe Financeira";
editor.Subject = "Resultados Q1 2026";
// Preencher e achatar campos de formulário
editor.SetFormFieldValue("employee.name", "Jane Doe");
editor.SetFormFieldValue("employee.email", "jane@example.com");
editor.FlattenForms();
editor.Save("edited.pdf");
// ou: await editor.SaveAsync("edited.pdf");
Ler campos de formulário sem editar:
using var doc = PdfDocument.Open("form.pdf");
foreach (var f in doc.GetFormFields())
{
Console.WriteLine($"{f.Name} ({f.FieldType}) = \"{f.Value}\"");
}
Nota: o binding .NET atualmente expõe abertura, leitura, conversão e criação de documentos, extração de imagens, leitura, preenchimento e achatamento de campos de formulário, e edição de metadados. Operações de página, anotações, renderização e assinaturas estão disponíveis no núcleo Rust e em outros bindings; a superfície equivalente para .NET será adicionada em uma versão futura.
Publicação com NativeAOT
O binding .NET do PDF Oxide está pronto para publicação com NativeAOT:
dotnet publish -c Release -r linux-x64 --self-contained -p:PublishAot=true
As 881 declarações P/Invoke usam LibraryImport (P/Invoke gerado pelo compilador), com IsAotCompatible=true e IsTrimmable=true. Seu binário AOT linka apenas o que usa, e o núcleo Rust nativo é linkado estaticamente na biblioteca específica da plataforma incluída.
Plugins e extensões
O pacote PdfOxide.Plugins (distribuído junto com PdfOxide) expõe pontos de extensão para processadores que transformam o conteúdo extraído — classificadores, pós-processadores, validadores. Para escrever extensões, veja o guia de plugins.
Tratamento de erros
Todos os métodos lançam exceções tipadas em caso de falha:
using PdfOxide.Core;
try
{
using var doc = PdfDocument.Open("document.pdf");
string text = doc.ExtractText(0);
}
catch (PdfOxideException ex)
{
Console.Error.WriteLine($"Erro do PDF Oxide: {ex.Message}");
}
catch (FileNotFoundException)
{
Console.Error.WriteLine("Arquivo não encontrado");
}
Próximos passos
- Início com Python — usar o PDF Oxide em Python
- Referência da API C# — documentação completa da API
- Guia assíncrono — padrões com
Task<T>+CancellationToken - Guia de concorrência — padrões com
ReaderWriterLockSlim - Extração de texto — opções detalhadas
- Pacote NuGet — notas de release e estatísticas de download