Rust PDF 라이브러리와의 비교
PDF Oxide를 가장 많이 사용되는 Rust PDF 크레이트인 lopdf, printpdf, pdf-rs, pdf_extract와 비교합니다. 각각은 서로 다른 추상화 수준과 서로 다른 사용 사례를 목표로 합니다.
요약
| PDF Oxide | lopdf | printpdf | pdf-rs | pdf_extract | |
|---|---|---|---|---|---|
| API 수준 | 고수준 | 저수준 | 중간 수준(생성) | 저수준(읽기) | 중간 수준(읽기) |
| PDF 읽기 | 예 | 예 | 아니요 | 예 | 예 |
| PDF 쓰기 | 예 | 예 | 예 | 아니요 | 아니요 |
| 텍스트 추출 | 예(고수준) | 수동 | 아니요 | 수동 | 예(기본) |
| 이미지 추출 | 예(고수준) | 수동 | 아니요 | 수동 | 아니요 |
| 양식 필드 | 읽기 + 쓰기 | 수동 | 아니요 | 읽기만 | 아니요 |
| PDF 생성 | 예 | 예 | 예 | 아니요 | 아니요 |
| Markdown/HTML 입력 | 예 | 아니요 | 아니요 | 아니요 | 아니요 |
| 기존 PDF 편집 | 예 | 예(저수준) | 아니요 | 아니요 | 아니요 |
| 주석 | 읽기 + 쓰기 | 수동 | 아니요 | 읽기만 | 아니요 |
| 암호화 | 읽기 + 쓰기 | 아니요 | 아니요 | 아니요 | 아니요 |
| PDF/A 검증 | 예 | 아니요 | 아니요 | 아니요 | 아니요 |
| 렌더링 | 예(tiny-skia) | 아니요 | 아니요 | 부분적 | 아니요 |
| Python 바인딩 | 예 | 아니요 | 아니요 | 아니요 | 아니요 |
| 라이선스 | MIT | MIT | MIT | MIT | Apache-2.0 |
모든 라이브러리는 관대한 라이선스로 제공됩니다. 차이는 범위와 추상화 수준에 있습니다.
성능 비교
전체 코퍼스 벤치마크 (3,830개 PDF)
3,830개 PDF로 구성된 전체 코퍼스에서 테스트했습니다. 이는 독립적이고 공개적으로 이용 가능한 세 가지 테스트 스위트로 구성되며, PDF 사양 준수(veraPDF, 2,907개 파일), 실제 브라우저 렌더링의 경계 사례(Mozilla pdf.js, 897개 파일), 그리고 비정상 구조와 퍼징으로 생성된 손상을 포함한 보안/견고성 스트레스 테스트(DARPA SafeDocs, 26개 파일)를 다룹니다. 전체 코퍼스 세부 정보를 참고하세요.
| 라이브러리 | 평균 | p99 | 통과율 | 텍스트 추출 | 비고 |
|---|---|---|---|---|---|
| PDF Oxide | 0.8ms | 9ms | 100% | 내장, 프로덕션급 | Unicode, CJK, 읽기 순서 |
| oxidize_pdf | 13.5ms | 11ms | 99.1% | 기본 | 최대 48초 이상값 |
| unpdf | 2.8ms | 10ms | 95.1% | 기본 | 전체 코퍼스에서 185건 실패 |
| pdf_extract | 4.08ms | 37ms | 91.5% | 기본 | 복잡한 레이아웃 누락 |
| lopdf | 0.3ms | 2ms | 80.2% | 내장 추출 없음 | PDF의 20%에서 실패 |
lopdf는 파싱할 수 있는 PDF에서는 더 빠릅니다. 하지만 코퍼스의 20%에서 실패하고 텍스트 추출을 제공하지 않습니다. 폰트 디코딩, CMap 해석, 간격 분석을 직접 구현해야 합니다.
pdf_extract는 기본적인 텍스트 추출을 제공하지만 통과율이 91.5%이며 복잡한 레이아웃, CJK 텍스트, 태그된 PDF에서 어려움을 겪습니다. oxidize_pdf는 괜찮은 신뢰성(99.1%)을 갖추고 있지만 평균 추출 시간에서 pdf_oxide보다 17배 느리며, 최악의 경우 48초의 이상값을 보입니다. unpdf는 전체 코퍼스를 처리하지만 185개 PDF에서 실패합니다.
PDF Oxide는 100% 신뢰성과 프로덕션급 텍스트 추출을 결합한 유일한 Rust 크레이트입니다.
API 설계 비교
PDF Oxide: 고수준, 작업 중심
PDF Oxide는 일반적인 작업을 위해 목적에 맞게 설계된 메서드를 제공합니다. PDF 객체나 딕셔너리가 아니라 텍스트, 이미지, 양식 필드를 다룹니다.
use pdf_oxide::PdfDocument;
let mut doc = PdfDocument::open("report.pdf")?;
// Text extraction -- one call
let text = doc.extract_text(0)?;
println!("{}", text);
// Styled spans with font metadata
let spans = doc.extract_spans(0)?;
for span in &spans {
println!("'{}' font={} size={:.1}pt", span.text, span.font_name, span.font_size);
}
// Image extraction
let images = doc.extract_images(0)?;
for img in &images {
println!("{}x{} {:?}", img.width, img.height, img.format);
}
// Form fields
let fields = doc.extract_form_fields()?;
for field in &fields {
println!("{}: {:?}", field.name, field.value);
}
PDF 생성도 마찬가지로 간단합니다.
use pdf_oxide::api::Pdf;
// From Markdown
let pdf = Pdf::from_markdown("# Report\n\n| A | B |\n|---|---|\n| 1 | 2 |")?;
pdf.save("report.pdf")?;
// From HTML
let pdf = Pdf::from_html("<h1>Report</h1><p>Content here.</p>")?;
pdf.save("report.pdf")?;
lopdf: 저수준 객체 조작
lopdf는 PDF 객체, 스트림, 상호 참조 테이블에 대한 직접 접근을 제공합니다. 효과적으로 사용하려면 PDF 사양을 이해해야 합니다. 내장 텍스트 추출이 없으며, 딕셔너리를 직접 탐색하고 스트림을 직접 디코딩해야 합니다.
use lopdf::Document;
let doc = Document::load("report.pdf")?;
// Get page dictionary
let page_id = doc.page_iter().next().unwrap();
let page = doc.get_dictionary(page_id)?;
// Get content stream -- manual work
let contents = page.get("Contents")?;
let stream = doc.get_object(contents.as_reference()?)?;
// To extract text you must:
// 1. Parse the content stream operators
// 2. Resolve font references from /Resources
// 3. Decode CMap/ToUnicode mappings
// 4. Apply text matrix transformations
// 5. Handle encoding differences
//
// lopdf does not provide any of this -- it is raw object access
println!("Page has {} objects", doc.objects.len());
lopdf는 문서 병합, 객체 스트림 재작성, 특화된 PDF 프로세서 구축 등 PDF 구조를 직접 조작해야 할 때 적합한 도구입니다.
printpdf: PDF 생성 전용
printpdf는 생성 전용 라이브러리입니다. 기존 PDF를 읽거나 파싱할 수 없습니다. 텍스트, 이미지, 벡터 그래픽으로 PDF 문서를 처음부터 구축하기 위한 타입 지정 API를 제공합니다.
use printpdf::*;
let (doc, page1, layer1) = PdfDocument::new(
"Report", Mm(210.0), Mm(297.0), "Layer 1"
);
let current_layer = doc.get_page(page1).get_layer(layer1);
// Add text -- requires manual font loading
let font = doc.add_builtin_font(BuiltinFont::Helvetica)?;
current_layer.use_text("Hello World", 24.0, Mm(10.0), Mm(280.0), &font);
// Save
doc.save(&mut std::io::BufWriter::new(
std::fs::File::create("output.pdf")?,
))?;
// Cannot read existing PDFs
// Cannot extract text, images, or form fields
printpdf는 새 PDF를 생성하기만 하면 되고 깔끔하고 집중된 생성 API를 원할 때 적합한 도구입니다.
pdf-rs: 저수준 PDF 읽기
pdf-rs는 PDF 구조를 Rust 타입으로 파싱하지만 고수준 기능은 최소한만 제공합니다. PDF 객체에 대한 타입 지정 접근은 가능하지만 텍스트 디코딩, 폰트 해석, 콘텐츠 스트림 파싱은 여전히 직접 처리해야 합니다.
use pdf::file::FileOptions;
let file = FileOptions::cached().open("report.pdf")?;
// Access page objects
let page = file.get_page(0)?;
let media_box = page.media_box()?;
println!("Page size: {:?}", media_box);
// Content stream access -- low-level
if let Some(ref contents) = page.contents {
// Returns raw operations -- you must interpret them
// No built-in text assembly, font decoding, or layout analysis
}
// Cannot write or modify PDFs
pdf-rs는 분석, 검증 또는 사용자 정의 렌더러 구축을 위해 타입 안전한 PDF 파서가 필요할 때 적합한 도구입니다.
작업별 기능 비교
텍스트 추출
| 라이브러리 | 내장 | 품질 | 필요한 노력 |
|---|---|---|---|
| PDF Oxide | 예 | 프로덕션급(Unicode, CJK, 읽기 순서) | 메서드 호출 한 번 |
| pdf_extract | 예 | 기본(복잡한 레이아웃 누락) | 메서드 호출 한 번 |
| lopdf | 아니요 | 해당 없음 | 수백 줄의 사용자 정의 코드 |
| printpdf | 아니요 | 해당 없음 | 불가능(쓰기 전용) |
| pdf-rs | 아니요 | 해당 없음 | 상당한 사용자 정의 코드 필요 |
PDF Oxide는 CMap/ToUnicode 디코딩, 폰트 메트릭 기반 간격, 구조 트리 읽기 순서, 합자 재구성을 처리합니다. lopdf나 pdf-rs 위에 동등한 기능을 구현하려면 수천 줄의 코드와 깊은 PDF 사양 지식이 필요합니다.
PDF 생성
| 라이브러리 | 방식 | Markdown/HTML 입력 | 표 | 바코드 |
|---|---|---|---|---|
| PDF Oxide | 고수준 + 저수준 | 예 | 예 | 예 |
| lopdf | 원시 객체 구성 | 아니요 | 아니요 | 아니요 |
| printpdf | 타입 지정 레이어 API | 아니요 | 아니요 | 아니요 |
| pdf-rs | 해당 없음(읽기 전용) | 해당 없음 | 해당 없음 | 해당 없음 |
암호화
| 라이브러리 | 암호화 읽기 | 암호화 쓰기 | 알고리즘 |
|---|---|---|---|
| PDF Oxide | 예 | 예 | RC4-40, RC4-128, AES-128, AES-256 |
| lopdf | 아니요 | 아니요 | – |
| printpdf | 아니요 | 아니요 | – |
| pdf-rs | 부분적 | 아니요 | RC4만 |
규격 준수
| 라이브러리 | PDF/A | PDF/X | PDF/UA |
|---|---|---|---|
| PDF Oxide | 검증 + 변환 | 검증 | 검증 |
| lopdf | 아니요 | 아니요 | 아니요 |
| printpdf | 부분적(PDF/A-1b 출력) | 아니요 | 아니요 |
| pdf-rs | 아니요 | 아니요 | 아니요 |
의존성 규모
| 라이브러리 | 의존성 | 컴파일 시간 | 바이너리 크기 |
|---|---|---|---|
| PDF Oxide | ~40(코어) | ~30s | ~4 MB |
| lopdf | ~15 | ~10s | ~1 MB |
| printpdf | ~20 | ~15s | ~2 MB |
| pdf-rs | ~25 | ~20s | ~2 MB |
PDF Oxide는 폰트 파싱, 이미지 디코딩, 콘텐츠 스트림 해석, 암호화를 포함하기 때문에 의존성이 더 많습니다. 이는 다른 라이브러리들이 사용자에게 맡기거나 아예 생략하는 기능입니다. 모든 선택적 기능(rendering, barcodes, office)을 활성화하면 그 수가 ~100으로 늘어납니다.
라이브러리 조합
모두 관대한 라이선스로 제공되므로 하나의 프로젝트에서 조합할 수 있습니다.
[dependencies]
pdf_oxide = "0.3"
lopdf = "0.32" # Optional: raw object access for edge cases
일반적인 패턴:
- PDF Oxide + lopdf: 추출과 생성에 PDF Oxide를 사용하고, 원시 객체 조작이 필요한 경계 사례에서는 lopdf로 폴백합니다.
- PDF Oxide + printpdf: 읽기에 PDF Oxide를 사용하고, 특화된 생성 워크플로에 printpdf를 사용합니다.
사용 사례 매트릭스
“PDF에서 텍스트를 추출해야 한다”
| 크레이트 | 적합한가? | 비고 |
|---|---|---|
| PDF Oxide | 예 | 최고의 추출 품질, 100% 통과율, 읽기 순서, 폰트 메타데이터 |
| pdf_extract | 부분적 | 기본 추출, 91.5% 통과율 |
| lopdf | 아니요 | 텍스트 추출 없음 |
| printpdf | 아니요 | PDF를 읽을 수 없음 |
| pdf-rs | 부분적 | 기본 파싱, 고수준 텍스트 추출 없음 |
“PDF를 생성해야 한다”
| 크레이트 | 적합한가? | 비고 |
|---|---|---|
| PDF Oxide | 예 | 고수준(Markdown/HTML) 및 저수준 API |
| lopdf | 부분적 | 저수준 객체 구성 |
| printpdf | 예 | 깔끔한 생성 API, 읽기 없음 |
| pdf-rs | 아니요 | 읽기 전용 |
“기존 PDF를 편집해야 한다”
| 크레이트 | 적합한가? | 비고 |
|---|---|---|
| PDF Oxide | 예 | DOM 유사 편집, 주석, 양식 |
| lopdf | 부분적 | 저수준 객체 조작 |
| printpdf | 아니요 | PDF를 읽을 수 없음 |
| pdf-rs | 아니요 | 읽기 전용 |
“전체 라이프사이클이 필요하다(추출 + 생성 + 편집)”
| 크레이트 | 적합한가? | 비고 |
|---|---|---|
| PDF Oxide | 예 | 세 가지 모두를 다루는 유일한 크레이트 |
| lopdf + printpdf | 부분적 | 두 개의 크레이트, 텍스트 추출 없음 |
| pdf-rs + printpdf | 부분적 | 두 개의 크레이트, 편집 없음 |
각각을 언제 사용할까
PDF Oxide를 선택하세요 — 둘 이상의 PDF 기능(추출 + 생성, 또는 추출 + 편집)이 필요하고 100% 신뢰성을 갖춘 잘 테스트된 단일 의존성을 원할 때.
lopdf를 선택하세요 — 저수준 PDF 구조 조작이 필요하고 PDF 사양을 직접 다루는 것이 편할 때. 병합, 분할, 일괄 PDF 처리에 적합합니다.
printpdf를 선택하세요 — PDF를 생성하기만 하고 읽을 필요가 전혀 없을 때. 보고서 및 문서 생성을 위한 가장 깔끔한 API입니다.
pdf-rs를 선택하세요 — PDF 분석을 위한 사양 준수 파서가 필요하거나 자체 렌더링 파이프라인을 구축할 때.
pdf_extract를 선택하세요 — 기본적인 텍스트 추출이 필요하고 높은 신뢰성이나 복잡한 레이아웃 지원이 필요하지 않을 때.
관련 페이지
- 성능 벤치마크 – 전체 코퍼스 벤치마크 결과
- Rust 시작하기 – 설치 및 첫 추출
- Rust API 레퍼런스 – 완전한 Rust API
- Python PDF 라이브러리와의 비교 – Python 생태계 비교