Skip to content

PDF Oxide 시작하기 (Dart / Flutter)

PDF Oxide는 Dart와 Flutter에서 PDF를 읽는 가장 빠른 방법입니다 — 평균 0.8ms 텍스트 추출, 3,830개 PDF에서 100% 통과율. pdf_oxide 패키지는 Rust 코어를 감싼 관용적인 dart:ffi 래퍼입니다. PDF 핸들은 NativeFinalizer(및 명시적인 close())로 자동 해제되고, C 문자열과 버퍼는 알아서 Dart로 복사되며, C-ABI 에러 코드는 PdfOxideError 예외로 표면화됩니다.

설치

pubspec.yamlpdf_oxide를 추가하세요:

dependencies:
  pdf_oxide: ^0.3.69

그런 다음 의존성을 가져옵니다:

dart pub get

바인딩은 런타임에 네이티브 라이브러리(libpdf_oxide.{so,dylib,dll})를 로드합니다. 탐색 순서는 PDF_OXIDE_LIB_PATH(전체 경로) → PDF_OXIDE_LIB_DIR../target/releasetarget/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(예: Flutter 앱에서 HTTP로 내려받은 PDF)를 열려면 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는 모든 단어를 경계 상자(bounding box), 폰트, 굵기와 함께 반환하고, extractTextLines는 줄 전체를 반환합니다. 좌표는 PDF 사용자 공간(user-space) 포인트 단위입니다.

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를 유니코드 코드포인트, 경계 상자, 폰트 이름, 크기와 함께 반환합니다:

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())로 곧바로 연결하면 디스크를 거치지 않고 다시 추출할 수 있습니다.

에러 처리

실패할 수 있는 모든 호출은 기저의 C-ABI 에러 코드를 담은 PdfOxideError(Exceptionimplements 함)를 던집니다:

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');
  }
}

다음 단계