Skip to content

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")

다음 단계