2026년 Python PDF 라이브러리 비교
PDF Oxide를 PyMuPDF(fitz), pypdfium2, pypdf, pdfplumber, pdfminer 등과 비교합니다. 이 문서에서는 성능, 기능 커버리지, 라이선스, API 차이를 정리해 텍스트 추출에 적합한 Python PDF 라이브러리를 고를 수 있도록 돕습니다.
요약
| PDF Oxide | PyMuPDF | pypdfium2 | pypdf | pdfplumber | pdfminer | |
|---|---|---|---|---|---|---|
| 평균 추출 시간 | 0.8ms | 4.6ms | 4.1ms | 12.1ms | 23.2ms | 16.8ms |
| 성공률(3,830개 PDF) | 100% | 99.3% | 99.2% | 98.4% | 98.8% | 98.8% |
| 라이선스 | MIT | AGPL-3.0 | Apache-2.0 | BSD-3 | MIT | MIT |
| 구현 언어 | Rust + PyO3 | C (MuPDF) | C (PDFium) | 순수 Python | 순수 Python | 순수 Python |
| 텍스트 추출 | 지원 | 지원 | 지원 | 지원 | 지원 | 지원 |
| 문자 위치 | 지원 | 지원 | 지원 | 일부 | 지원 | 지원 |
| 이미지 추출 | 지원 | 지원 | 지원 | 지원 | 미지원 | 미지원 |
| 폼 필드 | 읽기+쓰기 | 읽기+쓰기 | 읽기 전용 | 읽기+쓰기 | 읽기 전용 | 미지원 |
| PDF 생성 | 지원 | 지원 | 미지원 | 제한적 | 미지원 | 미지원 |
| PDF 편집 | 지원 | 지원 | 미지원 | 지원 | 미지원 | 미지원 |
| Markdown 출력 | 지원 | 미지원 | 미지원 | 미지원 | 미지원 | 미지원 |
| HTML 출력 | 지원 | 미지원 | 미지원 | 미지원 | 미지원 | 미지원 |
| 암호화 | 읽기+쓰기 | 읽기+쓰기 | 읽기 전용 | 읽기+쓰기 | 미지원 | 미지원 |
| PDF/A 검증 | 지원 | 미지원 | 미지원 | 미지원 | 미지원 | 미지원 |
| 렌더링 | 지원 | 지원 | 지원 | 미지원 | 미지원 | 미지원 |
| 검색 | 정규식 + 공간 검색 | 지원 | 지원 | 미지원 | 미지원 | 미지원 |
| Python 버전 | 3.8–3.14 | 3.8–3.12 | 3.8+ | 3.6+ | 3.8+ | 3.6+ |
| 설치 용량 | ~5 MB wheel | ~20 MB wheel | ~3 MB wheel | ~1 MB | ~1 MB | ~1 MB |
성능 비교
PDF 한 개당 평균 텍스트 추출 시간을 3,830개 PDF로 구성된 전체 코퍼스에서 측정했습니다. 공개된 세 가지 독립 테스트 스위트를 결합했으며, 이를 통해 PDF 사양의 모든 버전(1.0–2.0), 암호화 파일, 손상 문서, CJK 인코딩, 복잡한 레이아웃, 보안 엣지 케이스까지 폭넓게 다룹니다. 각 스위트에서 검증하는 내용과 결과의 재현성은 코퍼스 상세를 참고하세요.
| 라이브러리 | 평균 | 상대 | p99 | 성공률 |
|---|---|---|---|---|
| PDF Oxide | 0.8ms | 1× | 9ms | 100% |
| PyMuPDF | 4.6ms | 5.8× | 28ms | 99.3% |
| pypdfium2 | 4.1ms | 5.1× | 42ms | 99.2% |
| pymupdf4llm | 55.5ms | 69× | 280ms | 99.1% |
| pdftext | 7.3ms | 9.1× | 82ms | 99.0% |
| pdfminer | 16.8ms | 21× | 124ms | 98.8% |
| pdfplumber | 23.2ms | 29× | 189ms | 98.8% |
| markitdown | 108.8ms | 136× | 378ms | 98.6% |
| pypdf | 12.1ms | 15.1× | 97ms | 98.4% |
PDF Oxide는 PyO3를 통해 Python 확장 모듈로 컴파일된 네이티브 Rust 코어로 이러한 속도를 달성합니다. 서브프로세스 오버헤드나 C 라이브러리 브리지가 없으며, Rust 코드는 Python 프로세스 내부에서 직접 실행됩니다.
안정성
PDF Oxide는 유효한 3,823개 PDF를 모두 실패 없이 처리하며 성공률 100%를 기록합니다. 3,830개 코퍼스 중 통과하지 못한 7개 파일은 의도적으로 손상된 테스트 픽스처입니다(PDF 헤더 누락, 퍼징으로 손상된 카탈로그, 잘못된 xref 스트림).
| 라이브러리 | 유효 PDF 통과 수 | 성공률 |
|---|---|---|
| PDF Oxide | 3,823 / 3,823 | 100% |
| PyMuPDF | 3,796 / 3,823 | 99.3% |
| pypdfium2 | 3,792 / 3,823 | 99.2% |
| pymupdf4llm | 3,787 / 3,823 | 99.1% |
| pdftext | 3,784 / 3,823 | 99.0% |
| pdfminer | 3,777 / 3,823 | 98.8% |
| pdfplumber | 3,777 / 3,823 | 98.8% |
| markitdown | 3,771 / 3,823 | 98.6% |
| pypdf | 3,762 / 3,823 | 98.4% |
텍스트 품질
PDF Oxide는 전체 코퍼스에서 PyMuPDF 및 pypdfium2 대비 99.5%의 텍스트 일치율을 보입니다. 품질은 추출 결과를 문자 단위로 비교해 측정했습니다. 남은 0.5% 차이는 공백 정규화와 합자(ligature) 처리에서 발생하며, PDF Oxide 쪽이 더 깔끔한 출력을 만들어 냅니다.
라이선스 비교
| 라이브러리 | 라이선스 | 상업적 이용 | 카피레프트 |
|---|---|---|---|
| PDF Oxide | MIT | 제한 없음 | 없음 |
| pypdfium2 | Apache-2.0 | 제한 없음 | 없음 |
| PyMuPDF | AGPL-3.0 | 상업 라이선스 필요($) | 있음 |
| pypdf | BSD-3 | 제한 없음 | 없음 |
| pdfplumber | MIT | 제한 없음 | 없음 |
| pdfminer | MIT | 제한 없음 | 없음 |
| pdftext | GPL-3.0 | 오픈소스 공개 필요 | 있음 |
PyMuPDF는 AGPL-3.0으로 배포되는 MuPDF를 사용합니다. PyMuPDF를 쓰는 소프트웨어를 배포하려면 해당 소프트웨어도 AGPL-3.0으로 공개하거나 Artifex에서 상업 라이선스를 구입해야 합니다. 이는 SaaS 제품, 웹 애플리케이션, 모든 배포 바이너리에 적용됩니다.
PDF Oxide는 MIT 라이선스로 제약이 없습니다. 독점 제품, SaaS 플랫폼, 비공개 소스 애플리케이션에서 라이선스 부담 없이 사용할 수 있습니다.
| 활용 사례 | PDF Oxide (MIT) | PyMuPDF (AGPL) | pypdfium2 (Apache) | pypdf (BSD) | pdfplumber (MIT) | pdfminer (MIT) |
|---|---|---|---|---|---|---|
| 상업 제품 | 가능 | 라이선스 필요 | 가능 | 가능 | 가능 | 가능 |
| 비공개 소스 | 가능 | 불가(라이선스 없을 때) | 가능 | 가능 | 가능 | 가능 |
| SaaS/클라우드 | 가능 | 라이선스 필요 | 가능 | 가능 | 가능 | 가능 |
| 사내 도구 | 가능 | 가능 | 가능 | 가능 | 가능 | 가능 |
API 비교
텍스트 추출
PDF Oxide:
from pdf_oxide import PdfDocument
doc = PdfDocument("report.pdf")
text = doc.extract_text(0)
print(text)
PyMuPDF:
import fitz
doc = fitz.open("report.pdf")
page = doc[0]
text = page.get_text()
print(text)
pypdf:
from pypdf import PdfReader
reader = PdfReader("report.pdf")
page = reader.pages[0]
text = page.extract_text()
print(text)
pdfplumber:
import pdfplumber
with pdfplumber.open("report.pdf") as pdf:
page = pdf.pages[0]
text = page.extract_text()
print(text)
pdfminer:
from pdfminer.high_level import extract_text
text = extract_text("report.pdf", page_numbers=[0])
print(text)
문자 단위 추출
PDF Oxide:
from pdf_oxide import PdfDocument
doc = PdfDocument("report.pdf")
chars = doc.extract_chars(0)
for ch in chars:
print(f"'{ch.char}' at ({ch.bbox[0]:.1f}, {ch.bbox[1]:.1f}) "
f"size={ch.font_size:.1f}")
PyMuPDF:
import fitz
doc = fitz.open("report.pdf")
page = doc[0]
blocks = page.get_text("dict")["blocks"]
for block in blocks:
if "lines" in block:
for line in block["lines"]:
for span in line["spans"]:
print(f"'{span['text']}' size={span['size']:.1f}")
pdfplumber:
import pdfplumber
with pdfplumber.open("report.pdf") as pdf:
page = pdf.pages[0]
for char in page.chars:
print(f"'{char['text']}' at ({char['x0']:.1f}, {char['top']:.1f}) "
f"size={char['size']:.1f}")
pdfminer:
from pdfminer.high_level import extract_pages
from pdfminer.layout import LTChar
for page_layout in extract_pages("report.pdf"):
for element in page_layout:
if hasattr(element, '__iter__'):
for text_line in element:
if hasattr(text_line, '__iter__'):
for char in text_line:
if isinstance(char, LTChar):
print(f"'{char.get_text()}' at ({char.x0:.1f}, {char.y0:.1f}) "
f"size={char.size:.1f}")
이미지 추출
PDF Oxide:
from pdf_oxide import PdfDocument
doc = PdfDocument("report.pdf")
images = doc.extract_image_bytes(0)
for i, img in enumerate(images):
with open(f"image_{i}.{img['format']}", "wb") as f:
f.write(img["data"])
PyMuPDF:
import fitz
doc = fitz.open("report.pdf")
page = doc[0]
for i, img in enumerate(page.get_images()):
xref = img[0]
base_image = doc.extract_image(xref)
with open(f"image_{i}.{base_image['ext']}", "wb") as f:
f.write(base_image["image"])
pypdf:
from pypdf import PdfReader
reader = PdfReader("report.pdf")
page = reader.pages[0]
for i, image in enumerate(page.images):
with open(f"image_{i}.{image.name.split('.')[-1]}", "wb") as f:
f.write(image.data)
PDF 생성
PDF Oxide:
from pdf_oxide import Pdf
pdf = Pdf.from_markdown("# Hello World\n\nThis is a PDF.")
pdf.save("output.pdf")
# HTML도 지원
pdf = Pdf.from_html("<h1>Hello</h1><p>World</p>")
pdf.save("output.pdf")
PyMuPDF:
import fitz
doc = fitz.open()
page = doc.new_page()
text_point = fitz.Point(72, 72)
page.insert_text(text_point, "Hello World", fontsize=24)
doc.save("output.pdf")
pypdf:
# pypdf는 PDF 병합/수정만 가능하며, 텍스트가 포함된 PDF를 처음부터 만들 수는 없습니다.
# 생성은 reportlab이나 fpdf2로 하고, 이후 pypdf로 병합하세요.
암호화된 PDF
PDF Oxide:
from pdf_oxide import PdfDocument
doc = PdfDocument("encrypted.pdf", password="password")
text = doc.extract_text(0)
PyMuPDF:
import fitz
doc = fitz.open("encrypted.pdf")
doc.authenticate("password")
page = doc[0]
text = page.get_text()
pypdf:
from pypdf import PdfReader
reader = PdfReader("encrypted.pdf")
reader.decrypt("password")
text = reader.pages[0].extract_text()
Markdown·HTML 출력
PDF Oxide(고유 기능):
from pdf_oxide import PdfDocument
doc = PdfDocument("paper.pdf")
# 제목 감지와 함께 Markdown으로 변환
md = doc.to_markdown(0, detect_headings=True)
print(md)
# HTML로 변환
html = doc.to_html(0)
print(html)
Python PDF 라이브러리 중에서 Markdown 또는 HTML 변환을 기본 제공하는 것은 PDF Oxide뿐입니다.
라이브러리별 특징
PDF Oxide
장점:
- Rust 코어 기반으로 벤치마크에서 가장 빠른 텍스트 추출 속도 — PyMuPDF 대비 5.8×
- 3,830개 PDF 코퍼스에서 100% 성공률로 테스트 라이브러리 중 최고 안정성 확보
- 단일 라이브러리에서 추출·생성·편집을 통합 API로 제공
- 제목 감지 기능과 함께 Markdown·HTML 내보내기 기본 제공
- MIT 라이선스로 카피레프트 제약 없음
- 규격 준수 검증(PDF/A, PDF/UA, PDF/X) 내장
- 주요 플랫폼과 Python 3.8–3.14용 사전 빌드 wheel 제공
- 시스템 의존성 없음 — wheel에 모든 것이 포함됨
한계:
- 등장한 지 얼마 되지 않아 커뮤니티 규모가 작음
- 표 추출은 pdfplumber의 알고리즘에 비해 단순함
- 렌더링 엔진은 MuPDF 수준까지 성숙하지 않음
PyMuPDF (fitz)
장점:
- 실사용 환경에서 검증된 성숙한 라이브러리(MuPDF 기반, 2005년부터 개발)
- 복잡한 PDF에서도 뛰어난 렌더링 품질
- OCR 연동(Tesseract) 내장
- SVG 출력, 페이지 조작, 표 감지 등 풍부한 기능
한계:
- AGPL-3.0 라이선스로 애플리케이션의 소스를 공개하거나 상업 라이선스를 구입해야 함
- MuPDF를 함께 담기 때문에 wheel 크기가 큼(~20 MB)
- Markdown 내보내기 내장 기능 없음
- 규격 준수 검증 없음
pypdfium2
장점:
- 빠름(Google의 PDFium 엔진 기반)
- Apache-2.0 라이선스로 상업 이용에 관대함
- 우수한 렌더링 품질
한계:
- PDF Oxide나 PyMuPDF에 비해 텍스트 추출 API가 제한적
- PDF 생성·편집 불가
- 폼 필드는 읽기 전용만 지원
pypdf
장점:
- 순수 Python으로 어디에나 설치 가능, 컴파일 의존성 없음
- 가볍고 꾸준히 관리됨
- PDF 조작(병합, 분할, 회전, 암호화)에 적합
- 큰 커뮤니티와 풍부한 문서
한계:
- 텍스트 추출에서 PDF Oxide보다 15× 느림
- 복잡한 레이아웃에서는 추출 품질이 떨어짐
- 렌더링, Markdown/HTML 출력, 표 추출 모두 미지원
pdfplumber
장점:
- Python PDF 라이브러리 중 가장 우수한 표 추출
- 뛰어난 문자 단위 위치 정보
- 시각적 디버깅 도구(주석이 달린 페이지 이미지)
- MIT 라이선스
한계:
- 순수 Python으로 PDF Oxide보다 29× 느림
- 읽기 전용 — PDF 생성·편집 불가
- 암호화·렌더링 미지원
pdfminer
장점:
- 문자·레이아웃에 대한 상세한 분석 제공
- CJK 텍스트 지원 양호
- pdfplumber 등 여러 도구의 기반
- MIT 라이선스
한계:
- PDF Oxide보다 21× 느림(최적화되지 않은 순수 Python)
- 읽기 전용, 생성·편집 불가
- 기본 작업에 비해 API가 장황함
- 유지 관리가 활발하지 않음
어떤 라이브러리를 쓸까
| 활용 사례 | 추천 라이브러리 |
|---|---|
| 빠른 텍스트 추출 | PDF Oxide |
| 상업 / 독점 제품 | PDF Oxide, pypdfium2, pypdf, pdfplumber, pdfminer |
| PyMuPDF 대체(MIT 라이선스) | PDF Oxide |
| Markdown/HTML로 PDF 생성 | PDF Oxide |
| 규격 준수 검증(PDF/A, PDF/X) | PDF Oxide |
| 인보이스 표 추출 | pdfplumber |
| 추출 결과 시각적 디버깅 | pdfplumber |
| 기존 MuPDF 자산 활용 | PyMuPDF (AGPL 허용 시) |
| 최소 의존성 | pypdf (순수 Python) |
| 세밀한 레이아웃 분석 | pdfminer |
| 스캔 문서 OCR | PyMuPDF |
설치
# PDF Oxide
pip install pdf_oxide
# PyMuPDF
pip install pymupdf
# pypdfium2
pip install pypdfium2
# pypdf
pip install pypdf
# pdfplumber
pip install pdfplumber
# pdfminer
pip install pdfminer.six
PDF Oxide는 Linux(x86_64, aarch64), macOS(x86_64, arm64), Windows(x86_64)용 사전 빌드 wheel을 제공합니다. 컴파일러나 시스템 라이브러리는 필요하지 않습니다.
관련 페이지
- 성능 벤치마크 – 전체 코퍼스 벤치마크 결과
- Python 시작하기 – 설치와 첫 추출
- Python API 레퍼런스 – 전체 Python API
- vs Rust PDF 라이브러리 – Rust 생태계 비교