PDF-бібліотека для C# / .NET — PDF Oxide
PDF Oxide — найшвидша PDF-бібліотека для .NET: 0,8 мс у середньому на видобування тексту, у 5× швидше за PyMuPDF, у 15× швидше за pypdf, 100 % успіху на 3 830 PDF. Один пакет для видобування, створення й редагування — з підтримкою NativeAOT, trim-safe, з ідіоматичним using, async Task<T>, CancellationToken і LINQ-колекціями. Ліцензія MIT / Apache-2.0.
Встановлення
dotnet add package PdfOxide
Цільові фреймворки: net8.0 і net10.0. Увімкнено IsAotCompatible=true та IsTrimmable=true.
У пакеті NuGet постачаються готові нативні бібліотеки для Windows, macOS (Intel + Apple Silicon) та Linux (x64 + ARM64). Системні залежності та Rust-тулчейн не потрібні.
Відкриття PDF
using PdfOxide.Core;
using var doc = PdfDocument.Open("research-paper.pdf");
Console.WriteLine($"Сторінок: {doc.PageCount}");
Console.WriteLine($"Версія PDF: {doc.Version.Major}.{doc.Version.Minor}");
З потоку:
using var stream = File.OpenRead("report.pdf");
using var doc = PdfDocument.Open(stream);
З паролем:
using var doc = PdfDocument.OpenWithPassword("secure.pdf", "user-password");
PDF із шифруванням AES-256 (V=5, R=6) підтримуються повністю.
API сторінок
З версії v0.3.34 PdfDocument надає властивість Pages (IReadOnlyList<PdfPage>) та int-індексатор, тож можна ітерувати foreach і використовувати LINQ.
using PdfOxide.Core;
using var doc = PdfDocument.Open("paper.pdf");
foreach (var page in doc.Pages)
{
Console.WriteLine($"--- Сторінка {page.Index + 1} ---");
Console.WriteLine(page.ExtractText());
}
// Або прямий доступ за індексом
PdfPage first = doc[0];
string md = await first.ToMarkdownAsync();
Кожна PdfPage має повний синхронний та асинхронний набір: ExtractText() / ExtractTextAsync(), ToMarkdown(), ToHtml(), ToPlainText(), ExtractWords(), ExtractTextLines(), ExtractTables(), ExtractChars(), ExtractImages(), Search().
Видобування тексту
Одна сторінка
using var doc = PdfDocument.Open("report.pdf");
string text = doc.ExtractText(0);
Console.WriteLine(text);
Усі сторінки
string allText = doc.ExtractAllText();
Ручний обхід сторінок
for (int i = 0; i < doc.PageCount; i++)
{
Console.WriteLine($"--- Сторінка {i + 1} ---");
Console.WriteLine(doc.ExtractText(i));
}
Асинхронне видобування
Для кожного методу видобування є *Async-варіант, який повертає Task<T> і приймає необов’язковий CancellationToken.
using PdfOxide.Core;
using var doc = PdfDocument.Open("large.pdf");
string text = await doc.ExtractTextAsync(0);
// Fan-out зі скасуванням
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);
Повні шаблони — у посібнику з async.
Структуроване видобування
var words = doc.ExtractWords(0);
foreach (var (text, x, y, w, h) in words)
{
Console.WriteLine($"\"{text}\" у ({x:F1}, {y:F1})");
}
// За областю
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($"таблиця {rows}x{cols}");
}
Конвертація в Markdown
string markdown = doc.ToMarkdown(0);
string allMarkdown = doc.ToMarkdownAll();
Конвертація в HTML
string html = doc.ToHtml(0);
string allHtml = doc.ToHtmlAll();
Видобування зображень
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} байт)");
File.WriteAllBytes($"image_{Array.IndexOf(images.ToArray(), img)}.{img.Format}", img.Data);
}
PDF з індексованим кольором автоматично розгортаються в RGB (1/2/4/8 bpc з базовими колірними просторами RGB, градації сірого або CMYK).
Пошук
var results = doc.SearchAll("квартальний дохід");
foreach (var (page, text, x, y, w, h) in results)
{
Console.WriteLine($"Сторінка {page}: \"{text}\" у ({x}, {y})");
}
// Одна сторінка з урахуванням регістру
var pageResults = doc.SearchPage(0, "точна фраза", caseSensitive: true);
LINQ інтегрується природно:
var hitsByPage = doc.SearchAll("keyword")
.GroupBy(r => r.Page)
.OrderBy(g => g.Key);
foreach (var group in hitsByPage)
{
Console.WriteLine($"Сторінка {group.Key}: збігів — {group.Count()}");
}
Створення PDF
using PdfOxide.Core;
// З Markdown
using (var pdf = Pdf.FromMarkdown("# Рахунок\n\nРазом: **$42,00**"))
{
pdf.Save("invoice.pdf");
}
// З HTML
using (var pdf = Pdf.FromHtml("<h1>Звіт</h1><p>Сформовано 2026-04-09</p>"))
{
pdf.Save("report.pdf");
}
// Із простого тексту
using (var pdf = Pdf.FromText("Простий текстовий документ.\n\nДругий абзац."))
{
pdf.Save("notes.pdf");
}
// Із зображення
using (var pdf = Pdf.FromImage("scan.jpg"))
{
pdf.Save("scan.pdf");
}
Редагування — метадані та форми
using PdfOxide.Core;
using var editor = DocumentEditor.Open("form.pdf");
// Читання метаданих
Console.WriteLine($"Заголовок: {editor.Title}");
Console.WriteLine($"Сторінок: {editor.PageCount}");
// Оновлення метаданих
editor.Title = "Квартальний звіт";
editor.Author = "Фінансовий відділ";
editor.Subject = "Результати Q1 2026";
// Заповнення та флаттенінг полів форми
editor.SetFormFieldValue("employee.name", "Jane Doe");
editor.SetFormFieldValue("employee.email", "jane@example.com");
editor.FlattenForms();
editor.Save("edited.pdf");
// або: await editor.SaveAsync("edited.pdf");
Читання полів форми без редагування:
using var doc = PdfDocument.Open("form.pdf");
foreach (var f in doc.GetFormFields())
{
Console.WriteLine($"{f.Name} ({f.FieldType}) = \"{f.Value}\"");
}
Примітка. Поточний .NET-біндинг надає відкриття, читання, конвертацію та створення документів, видобування зображень, читання, заповнення та флаттенінг полів форми, а також редагування метаданих. Операції зі сторінками, анотації, рендеринг та підписи доступні в Rust-ядрі та інших біндингах; відповідну .NET-поверхню буде додано у майбутніх релізах.
Публікація з NativeAOT
.NET-біндинг PDF Oxide готовий до публікації через NativeAOT:
dotnet publish -c Release -r linux-x64 --self-contained -p:PublishAot=true
Усі 881 оголошення P/Invoke використовують LibraryImport (P/Invoke із генерацією джерел), IsAotCompatible=true, IsTrimmable=true. AOT-бінарник лінкує лише те, що справді використовується, а нативне Rust-ядро статично прив’язане до постачуваної платформо-специфічної бібліотеки.
Плагіни та розширення
Пакет PdfOxide.Plugins (постачається разом із PdfOxide) надає точки розширення для процесорів, що перетворюють видобутий контент: класифікатори, постпроцесори, валідатори. Як писати розширення — у посібнику з плагінів.
Обробка помилок
Усі методи в разі збою кидають типізовані винятки:
using PdfOxide.Core;
try
{
using var doc = PdfDocument.Open("document.pdf");
string text = doc.ExtractText(0);
}
catch (PdfOxideException ex)
{
Console.Error.WriteLine($"Помилка PDF Oxide: {ex.Message}");
}
catch (FileNotFoundException)
{
Console.Error.WriteLine("Файл не знайдено");
}
Наступні кроки
- Швидкий старт Python — PDF Oxide з Python
- Довідник C# API — повна документація API
- Посібник з async — шаблони з
Task<T>+CancellationToken - Посібник з паралелізму — шаблони з
ReaderWriterLockSlim - Видобування тексту — докладні параметри видобування
- Пакет на NuGet — примітки до релізів і статистика завантажень