PDF Oxide vs lopdf
lopdf는 PDF 오브젝트를 직접 조작하는 저수준 Rust 크레이트입니다 — 텍스트 추출이나 렌더링은 포함되어 있지 않습니다. PDF Oxide는 추출·생성·편집을 기본 제공하는 고수준 라이브러리입니다. 두 크레이트는 근본적으로 다른 용도를 노립니다.
주요 차이점
추상화 수준. lopdf는 원시 PDF 오브젝트 — 딕셔너리, 스트림, 크로스 레퍼런스 테이블을 제공합니다. 텍스트 추출, 폰트 디코딩, 이미지 내보내기가 없습니다. PDF Oxide는 extract_text(), extract_images(), to_markdown() 같은 전용 메서드를 제공합니다.
신뢰성. lopdf는 3,830개 PDF 테스트 코퍼스의 20%에서 파싱에 실패합니다. 파싱에 성공한 PDF 중 57%는 lopdf에 텍스트 추출 기능이 없어 빈 출력을 생성합니다 — 오브젝트는 얻지만 텍스트는 얻지 못합니다. PDF Oxide는 100% 통과합니다.
파싱 가능한 PDF에서의 속도. lopdf는 원시 오브젝트 파싱이 더 빠릅니다: 평균 0.3ms vs PDF Oxide의 0.8ms. 하지만 lopdf는 텍스트 추출 작업을 수행하지 않습니다 — 폰트 디코딩, CMap 해석, 간격 분석, 읽기 순서를 직접 구축해야 합니다.
빠른 비교
| PDF Oxide | lopdf | |
|---|---|---|
| API 수준 | 고수준 | 저수준 |
| 텍스트 추출 | 내장 (프로덕션급) | 없음 |
| 통과율 (3,830개 PDF) | 100% | 80.2% |
| 평균 파싱 시간 | 0.8ms | 0.3ms |
| 이미지 추출 | 내장 | 수동 (원시 스트림) |
| 폼 필드 | 읽기 + 쓰기 | 수동 (원시 딕셔너리) |
| PDF 생성 | 예 (Markdown/HTML) | 예 (원시 오브젝트) |
| Markdown/HTML 출력 | 예 | 아니오 |
| 암호화 | 읽기 + 쓰기 | 아니오 |
| 렌더링 | 예 | 아니오 |
| PDF/A 검증 | 예 | 아니오 |
| 라이선스 | MIT | MIT |
lopdf가 할 수 없는 것
lopdf는 PDF 오브젝트에 대한 접근을 제공하지만, 텍스트 추출에는 PDF 사양에 따라 해당 오브젝트를 해석해야 합니다. 직접 구축해야 하는 것들은 다음과 같습니다:
- 콘텐츠 스트림 파싱 — PostScript 유사 연산자(Tj, TJ, Tm, Tf 등) 파싱
- 폰트 해석 — /Font 리소스 조회, 간접 참조 해석
- CMap/ToUnicode 디코딩 — 글리프 ID를 유니코드 문자로 변환
- 폰트 메트릭 간격 — 폰트 디스크립터에서 문자 너비 계산
- 텍스트 매트릭스 변환 — Tm, Td, T* 연산자를 적용하여 텍스트 위치 지정
- 읽기 순서 — 다단 레이아웃의 올바른 순서 결정
- 합자 재구성 — fi, fl, ffi 합자 처리
- CJK 인코딩 — 중국어, 일본어, 한국어 텍스트 인코딩 디코딩
이는 수천 줄의 코드와 ISO 32000에 대한 깊은 지식이 필요합니다. PDF Oxide는 이 모든 것을 내부적으로 처리합니다.
코드 비교
텍스트 추출
PDF Oxide:
use pdf_oxide::PdfDocument;
let mut doc = PdfDocument::open("report.pdf")?;
let text = doc.extract_text(0)?;
println!("{}", text);
lopdf:
use lopdf::Document;
let doc = Document::load("report.pdf")?;
// lopdf는 텍스트 추출을 제공하지 않습니다.
// PDF 오브젝트에만 접근할 수 있습니다:
let page_id = doc.page_iter().next().unwrap();
let page = doc.get_dictionary(page_id)?;
let contents = page.get("Contents")?;
let stream = doc.get_object(contents.as_reference()?)?;
// 실제 텍스트를 얻으려면 다음을 해야 합니다:
// 1. 콘텐츠 스트림 연산자 파싱
// 2. /Resources에서 폰트 참조 해석
// 3. CMap/ToUnicode 매핑 디코딩
// 4. 텍스트 매트릭스 변환 적용
// 5. 인코딩 차이 처리
// ... (수백에서 수천 줄의 코드)
PDF 생성
PDF Oxide:
use pdf_oxide::api::Pdf;
let pdf = Pdf::from_markdown("# Report\n\n| Q1 | Q2 |\n|---|---|\n| $1M | $2M |")?;
pdf.save("report.pdf")?;
lopdf:
use lopdf::{Document, Object, Stream, dictionary};
let mut doc = Document::with_version("1.5");
// 폰트 딕셔너리 생성
let font_id = doc.add_object(dictionary! {
"Type" => "Font",
"Subtype" => "Type1",
"BaseFont" => "Helvetica",
});
// 리소스 생성
let resources_id = doc.add_object(dictionary! {
"Font" => dictionary! { "F1" => font_id },
});
// 콘텐츠 스트림 생성 (원시 PostScript 연산자)
let content = Stream::new(
dictionary! {},
b"BT /F1 12 Tf 72 720 Td (Hello World) Tj ET".to_vec(),
);
let content_id = doc.add_object(content);
// 페이지 생성
let page_id = doc.add_object(dictionary! {
"Type" => "Page",
"MediaBox" => vec![0.into(), 0.into(), 612.into(), 792.into()],
"Contents" => content_id,
"Resources" => resources_id,
});
// 페이지 트리 연결
let pages_id = doc.add_object(dictionary! {
"Type" => "Pages",
"Kids" => vec![page_id.into()],
"Count" => 1,
});
doc.add_object(dictionary! {
"Type" => "Catalog",
"Pages" => pages_id,
});
doc.save("report.pdf")?;
암호화된 PDF
PDF Oxide:
use pdf_oxide::PdfDocument;
let doc = PdfDocument::open_with_password("encrypted.pdf", "secret")?;
let text = doc.extract_text(0)?;
println!("{}", text);
lopdf:
// lopdf는 암호화된 PDF를 지원하지 않습니다.
// 암호화된 PDF를 로드하면 실패하거나 복호화되지 않은 스트림이 생성됩니다.
신뢰성 비교
| 지표 | PDF Oxide | lopdf |
|---|---|---|
| 성공적으로 파싱된 PDF | 3,823 / 3,823 (100%) | 3,071 / 3,823 (80.2%) |
| 텍스트 출력이 있는 PDF | 3,823 / 3,823 | ~1,320 / 3,823 (추정) |
| 암호화된 PDF 지원 | 예 | 아니오 |
| 비정상 PDF 복구 | 예 | 아니오 |
lopdf의 80.2% 통과율은 대략 5개 중 1개의 PDF에서 실패한다는 것을 의미합니다. 실패는 암호화된 문서, 비표준 xref 테이블, 크로스 레퍼런스 스트림을 사용하는 문서에서 발생합니다. PDF Oxide는 관대한 파싱과 폴백 전략으로 이 모든 것을 처리합니다.
언제 어떤 것을 사용할까
PDF Oxide를 선택하세요:
- 텍스트 추출, 이미지 추출, 또는 콘텐츠 수준 작업이 필요한 경우
- 읽기 + 쓰기 + 생성을 위한 단일 크레이트를 원하는 경우
- 모든 PDF를 안정적으로 처리해야 하는 경우 (암호화, 비정상, 복잡한 것 포함)
- Markdown/HTML 출력, 렌더링, 또는 OCR이 필요한 경우
- 적합성 검증이 필요한 경우 (PDF/A, PDF/X, PDF/UA)
lopdf를 선택하세요:
- 커스텀 처리를 위해 PDF 오브젝트에 직접 접근이 필요한 경우
- 오브젝트 수준에서 작동하는 특수 PDF 도구를 구축하는 경우
- 오브젝트 트리를 직접 조작하여 문서를 병합해야 하는 경우
- PDF가 간단하고 잘 형성된 경우 (암호화되지 않은, 표준 xref 테이블)
둘 다 조합:
PDF Oxide를 고수준 작업에 사용하고 lopdf를 원시 오브젝트 접근이 필요한 엣지 케이스에 사용하세요:
[dependencies]
pdf_oxide = "0.3"
lopdf = "0.32"
관련 페이지
- 성능 벤치마크 — 전체 코퍼스 결과
- Rust PDF 라이브러리 비교 — 모든 Rust 크레이트 비교
- Rust 시작하기 — 설치 및 첫 번째 추출