Python PDF 라이브러리 — 10줄로 시작하는 PDF Oxide
PDF Oxide는 Python에서 가장 빠른 PDF 라이브러리입니다. 텍스트 추출 평균 0.8ms, PyMuPDF 대비 5배 빠르며, 3,830개 PDF에서 100% 통과율을 기록했습니다. 추출·생성·편집을 모두 하나의 라이브러리로 처리합니다. MIT 라이선스에 Rust 코어, 시스템 의존성이 없습니다.
설치
pip install pdf_oxide
요구 사항: Python 3.8 이상. Linux, macOS, Windows의 x86_64 및 ARM64용 휠이 미리 빌드되어 제공됩니다. 컴파일러나 시스템 패키지는 필요 없습니다.
PDF 열기
PdfDocument로 어떤 PDF 파일이든 열고 정보를 확인할 수 있습니다.
from pdf_oxide import PdfDocument
doc = PdfDocument("research-paper.pdf")
print(f"Pages: {doc.page_count()}")
print(f"PDF version: {doc.version()}")
Page API
v0.3.34부터 PdfDocument는 반복과 인덱싱이 가능하며, 지연 평가 속성을 가진 PdfPage 객체를 반환합니다.
from pdf_oxide import PdfDocument
with PdfDocument("paper.pdf") as doc:
for page in doc: # len(doc), doc[i], doc[-1] 모두 동작
text = page.text # 지연 평가 — 접근 시 계산
md = page.markdown(detect_headings=True)
for table in page.tables:
for row in table["rows"]:
print([cell["text"] for cell in row["cells"]])
페이지 속성(모두 지연 평가): text, chars, words, lines, spans, tables, images, paths, annotations, width, height, bbox. 메서드: markdown(), plain_text(), html(), render(), search(), region(x, y, w, h).
편집기의 페이지 클래스는 v0.3.34에서
PdfPage와의 충돌을 피하기 위해EditorPage로 이름이 변경되었습니다.
텍스트 추출
단일 페이지
0부터 시작하는 인덱스로 페이지의 일반 텍스트를 추출합니다.
from pdf_oxide import PdfDocument
doc = PdfDocument("report.pdf")
text = doc.extract_text(0)
print(text)
모든 페이지
from pdf_oxide import PdfDocument
doc = PdfDocument("book.pdf")
for i in range(doc.page_count()):
text = doc.extract_text(i)
print(f"--- Page {i + 1} ---")
print(text)
문자 단위 추출
extract_chars()는 페이지의 모든 문자에 대해 정확한 위치와 폰트 메타데이터를 담은 TextChar 목록을 반환합니다.
from pdf_oxide import PdfDocument
doc = PdfDocument("paper.pdf")
chars = doc.extract_chars(0)
for ch in chars[:10]:
print(f"'{ch.char}' at ({ch.x:.1f}, {ch.y:.1f}) "
f"size={ch.font_size:.1f} font={ch.font_name} "
f"bbox={ch.bbox}")
TextChar가 제공하는 필드:
| 필드 | 타입 | 설명 |
|---|---|---|
char |
str |
유니코드 문자 |
x |
float |
가로 위치(포인트) |
y |
float |
세로 위치(포인트) |
font_size |
float |
폰트 크기(포인트) |
font_name |
str |
PostScript 폰트 이름 |
bbox |
tuple[float, 4] |
경계 상자 (x0, y0, x1, y1) |
텍스트 스팬
extract_spans()는 같은 폰트와 크기를 가진 연속 문자들을 스팬으로 묶어, 폰트 메타데이터가 포함된 구조화된 텍스트를 반환합니다.
from pdf_oxide import PdfDocument
doc = PdfDocument("paper.pdf")
spans = doc.extract_spans(0)
for span in spans:
print(f"'{span.text}' font={span.font_name} size={span.font_size}")
Markdown 변환
PDF 페이지를 Markdown으로 변환합니다 (제목 감지 옵션 포함).
from pdf_oxide import PdfDocument
doc = PdfDocument("paper.pdf")
md = doc.to_markdown(0, detect_headings=True)
print(md)
HTML 변환
PDF 페이지를 HTML로 변환합니다.
from pdf_oxide import PdfDocument
doc = PdfDocument("paper.pdf")
html = doc.to_html(0)
print(html)
이미지 추출
extract_images()는 페이지에 포함된 모든 이미지를 ImageInfo 목록으로 반환합니다. 콘텐츠 스트림 내 이미지와 중첩된 Form XObject의 이미지도 포함됩니다.
from pdf_oxide import PdfDocument
doc = PdfDocument("brochure.pdf")
images = doc.extract_image_bytes(0)
for i, img in enumerate(images):
print(f"Image {i}: {img['width']}x{img['height']} "
f"({len(img['data'])} bytes)")
with open(f"image_{i}.{img['format']}", "wb") as f:
f.write(img["data"])
extract_image_bytes()가 반환하는 각 dict의 키는 다음과 같습니다.
| 키 | 타입 | 설명 |
|---|---|---|
width |
int |
이미지 너비(픽셀) |
height |
int |
이미지 높이(픽셀) |
data |
bytes |
원본 이미지 데이터 |
format |
str |
이미지 포맷(예: png, jpeg) |
바이트에서 열기
메모리 상의 바이트로 PDF를 열 수 있습니다. S3, HTTP, 데이터베이스에서 가져온 데이터에 유용합니다.
from pdf_oxide import PdfDocument
doc = PdfDocument.from_bytes(pdf_bytes)
text = doc.extract_text(0)
# 비밀번호도 지원합니다:
doc = PdfDocument.from_bytes(pdf_bytes, password="secret")
빌더 API의 경우:
from pdf_oxide import Pdf
pdf = Pdf.from_bytes(existing_pdf_bytes)
pdf.save("modified.pdf")
비밀번호 보호 PDF
암호화된 문서를 열려면 생성자에 password=를 전달합니다.
from pdf_oxide import PdfDocument
doc = PdfDocument("confidential.pdf", password="secret")
text = doc.extract_text(0)
print(text)
열고 난 뒤 doc.authenticate(password)를 호출하는 방식도 사용할 수 있습니다.
PDF 생성
Pdf 클래스는 다양한 입력 형식으로 PDF를 만드는 팩토리 메서드를 제공합니다.
Markdown에서
from pdf_oxide import Pdf
pdf = Pdf.from_markdown("# Hello World\n\nThis is a PDF.")
pdf.save("output.pdf")
HTML에서
from pdf_oxide import Pdf
pdf = Pdf.from_html("<h1>Invoice</h1><p>Amount due: $42.00</p>")
pdf.save("invoice.pdf")
일반 텍스트에서
from pdf_oxide import Pdf
pdf = Pdf.from_text("Plain text document.\n\nSecond paragraph.")
pdf.save("notes.pdf")
이미지에서
from pdf_oxide import Pdf
pdf = Pdf.from_image("scan.jpg")
pdf.save("scan.pdf")
검색
문서 전체 또는 특정 페이지에서 텍스트를 검색합니다.
from pdf_oxide import PdfDocument
doc = PdfDocument("manual.pdf")
# 모든 페이지 검색
results = doc.search("configuration")
for r in results:
print(f"Page {r.page}: '{r.text}' at ({r.x:.0f}, {r.y:.0f})")
# 단일 페이지 검색
page_results = doc.search_page(0, "configuration")
오류 처리
PDF Oxide는 PDF 관련 오류에 대해 PdfError를 발생시키고, I/O 문제에는 표준 Python 예외를 사용합니다.
from pdf_oxide import PdfDocument, PdfError
try:
doc = PdfDocument("document.pdf")
text = doc.extract_text(0)
except PdfError as e:
print(f"PDF error: {e}")
except FileNotFoundError:
print("File not found")