Начало работы с PDF Oxide (Dart / Flutter)
PDF Oxide — самый быстрый способ читать PDF из Dart и Flutter: среднее время извлечения текста 0,8 мс и 100% успешных результатов на 3830 PDF. Пакет pdf_oxide представляет собой идиоматичную обёртку dart:ffi поверх ядра на Rust: дескрипторы PDF освобождаются автоматически с помощью NativeFinalizer (а также явным вызовом close()), C-строки и буферы копируются в Dart за вас, а коды ошибок C-ABI преобразуются в исключение PdfOxideError.
Установка
Добавьте pdf_oxide в ваш pubspec.yaml:
dependencies:
pdf_oxide: ^0.3.69
Затем загрузите зависимости:
dart pub get
Привязка загружает нативную библиотеку (libpdf_oxide.{so,dylib,dll}) во время выполнения. Порядок поиска следующий: PDF_OXIDE_LIB_PATH (полный путь) → PDF_OXIDE_LIB_DIR → ../target/release → target/release → системный загрузчик. Для Flutter поставляйте платформенную библиотеку вместе с приложением и указывайте на неё через PDF_OXIDE_LIB_PATH.
Быстрый старт
Откройте PDF и извлеките текст с первой страницы. Всегда вызывайте close() для документа по завершении работы — конструкция try/finally помогает делать это аккуратно.
import 'package:pdf_oxide/pdf_oxide.dart';
void main() {
final doc = PdfDocument.open('research-paper.pdf');
try {
print('Pages: ${doc.pageCount}');
print('PDF version: ${doc.version}'); // e.g. 1.7
print(doc.extractText(0));
} finally {
doc.close();
}
}
Чтобы открыть PDF, уже находящийся в памяти (например, загруженный по HTTP в приложении Flutter), используйте openFromBytes:
import 'dart:typed_data';
import 'package:pdf_oxide/pdf_oxide.dart';
void render(Uint8List bytes) {
final doc = PdfDocument.openFromBytes(bytes);
try {
print(doc.extractText(0));
} finally {
doc.close();
}
}
Текст, Markdown и HTML
Каждую страницу можно вывести как обычный текст, Markdown или HTML. Методы для отдельных страниц принимают индекс страницы, отсчитываемый от 0; варианты с суффиксом …All() обрабатывают весь документ целиком.
import 'package:pdf_oxide/pdf_oxide.dart';
void main() {
final doc = PdfDocument.open('report.pdf');
try {
// Single page (index 0)
print(doc.extractText(0)); // raw extracted text
print(doc.toPlainText(0)); // normalized plain text
print(doc.toMarkdown(0)); // Markdown with headings, lists, tables
print(doc.toHtml(0)); // HTML
// Whole document
print(doc.toMarkdownAll());
print(doc.toHtmlAll());
print(doc.toPlainTextAll());
} finally {
doc.close();
}
}
Также есть лёгкое представление Page, если вам удобнее работать со страницами по одной:
final doc = PdfDocument.open('report.pdf');
final page = doc.page(0);
print(page.text());
print(page.markdown());
doc.close();
Слова и строки с координатами
extractWords возвращает каждое слово вместе с его ограничивающим прямоугольником, шрифтом и насыщенностью; extractTextLines возвращает целые строки. Координаты указываются в точках пользовательского пространства PDF.
import 'package:pdf_oxide/pdf_oxide.dart';
void main() {
final doc = PdfDocument.open('paper.pdf');
try {
for (final word in doc.extractWords(0)) {
final b = word.bbox; // Bbox(x, y, width, height)
print("'${word.text}' at (${b.x}, ${b.y}) "
'font=${word.fontName} size=${word.fontSize} bold=${word.bold}');
}
for (final line in doc.extractTextLines(0)) {
print('${line.wordCount} words: ${line.text}');
}
} finally {
doc.close();
}
}
Для детализации на уровне глифов extractChars возвращает каждый Char с его кодовой точкой Unicode, ограничивающим прямоугольником, именем шрифта и размером:
final doc = PdfDocument.open('paper.pdf');
for (final ch in doc.extractChars(0)) {
print('${String.fromCharCode(ch.character)} @ ${ch.bbox} ${ch.fontSize}pt');
}
doc.close();
Поиск
search ищет в пределах одной страницы; searchAll сканирует весь документ. Оба метода принимают искомую строку и флаг caseSensitive и возвращают записи SearchResult, содержащие найденный текст, индекс его страницы и ограничивающий прямоугольник.
import 'package:pdf_oxide/pdf_oxide.dart';
void main() {
final doc = PdfDocument.open('manual.pdf');
try {
// Single page (page 0), case-insensitive
for (final hit in doc.search(0, 'configuration', false)) {
print("page ${hit.page}: '${hit.text}' at ${hit.bbox}");
}
// Across the whole document
final hits = doc.searchAll('configuration', false);
print('${hits.length} matches');
} finally {
doc.close();
}
}
Создание PDF
Конструктор Pdf превращает Markdown, HTML или обычный текст в PDF. Вызовите toBytes(), чтобы получить байты, или save(), чтобы записать файл, и close() по завершении.
import 'package:pdf_oxide/pdf_oxide.dart';
void main() {
final pdf = Pdf.fromMarkdown('# Hello World\n\nThis is a **PDF**.\n');
try {
pdf.save('output.pdf');
final bytes = pdf.toBytes();
print('Wrote ${bytes.length} bytes');
} finally {
pdf.close();
}
}
Pdf.fromHtml('<h1>Invoice</h1><p>Amount: \$42</p>') и Pdf.fromText('Plain text content.') работают точно так же. Поскольку готовый Pdf — это просто набор байтов, вы можете сразу передать его в PdfDocument.openFromBytes(pdf.toBytes()), чтобы извлечь содержимое обратно, не обращаясь к диску.
Обработка ошибок
Каждый вызов, который может завершиться неудачей, выбрасывает PdfOxideError (который implements Exception), несущий соответствующий код ошибки C-ABI:
import 'package:pdf_oxide/pdf_oxide.dart';
void main() {
try {
final doc = PdfDocument.open('/nonexistent/nope.pdf');
doc.close();
} on PdfOxideError catch (e) {
print('Failed to open PDF: $e');
}
}
Дальнейшие шаги
- Начало работы с Rust — нативное ядро, на котором построена эта привязка
- Начало работы с Python — использование PDF Oxide из Python
- Извлечение текста — подробные параметры извлечения и готовые рецепты
- Создание PDF — продвинутое создание с метаданными и шифрованием