중복 파일 삭제 스크립트를 돌렸다가 식은땀 흘린 날
이 블로그의 PDF 관련 글들을 보면 라이브러리가 조금씩 다릅니다. PDF 자동 병합 편과 PDF 텍스트 추출 편은 pypdf, PDF 자동 압축 편은 PyMuPDF(fitz), PDF 도장 삽입 편은 pypdf와 reportlab을 함께 사용했습니다. 왜 매번 다른 도구를 썼는지, 이번 글에서 세 라이브러리의 역할을 명확히 정리해보겠습니다.
pypdf → 기존 PDF를 "자르고 합치고 읽는" 라이브러리. 가볍고 빠름. PyMuPDF → PDF 내부(이미지, 텍스트, 구조)까지 깊이 파고드는 라이브러리. 강력하지만 무거움. reportlab → PDF를 "처음부터 새로 그리는" 라이브러리. 편집이 아닌 생성 전용.
셋 다 "PDF를 다룬다"는 공통점이 있지만, pypdf는 편집, PyMuPDF는 분석·변환, reportlab은 생성이라는 서로 다른 용도로 설계되었습니다. 같은 작업을 각각 어떻게 처리하는지 비교해보면 이 차이가 명확해집니다.
pypdf (가장 간단하고 빠름)
from pypdf import PdfWriter
writer = PdfWriter()
for pdf in ["file1.pdf", "file2.pdf", "file3.pdf"]:
writer.append(pdf)
writer.write("합본.pdf")
# 3줄로 끝남. 페이지 순서만 그대로 이어 붙이는 작업이라 매우 가벼움
PyMuPDF (가능은 하지만 더 무거움)
import fitz
merged = fitz.open()
for path in ["file1.pdf", "file2.pdf", "file3.pdf"]:
with fitz.open(path) as pdf:
merged.insert_pdf(pdf)
merged.save("합본.pdf")
# 결과는 같지만, 페이지 내부 구조까지 분석하는 라이브러리라
# 단순 병합에는 pypdf보다 처리 속도가 느림
💡 여기서 알 수 있는 것: 단순히 파일을 이어붙이는 작업이라면 pypdf가 더 가볍고 빠릅니다. PDF 자동 병합 편에서 pypdf를 선택한 이유가 바로 이것입니다.
pypdf (이미지 내부 조작은 제한적)
from pypdf import PdfReader
reader = PdfReader("문서.pdf")
for page in reader.pages:
for img in page.images:
print(img.name, len(img.data))
# 이미지 목록을 꺼내볼 수는 있지만, 이미지를 재압축해서
# 다시 페이지에 끼워 넣는 기능은 지원하지 않음
PyMuPDF (이미지 추출·재압축·재삽입까지 완벽 지원)
import fitz
from PIL import Image
from io import BytesIO
doc = fitz.open("문서.pdf")
for page in doc:
for img in page.get_images(full=True):
xref = img[0]
base = doc.extract_image(xref)
pil_img = Image.open(BytesIO(base["image"]))
buf = BytesIO()
pil_img.save(buf, format="JPEG", quality=60) # 재압축
doc._updateStream(xref, buf.getvalue()) # 다시 삽입
doc.save("압축본.pdf")
💡 여기서 알 수 있는 것: PDF 내부의 이미지를 꺼내서 가공한 뒤 다시 넣는 작업은 PyMuPDF만 가능합니다. PDF 자동 압축 편에서 PyMuPDF를 쓸 수밖에 없었던 이유입니다.
이 작업은 pypdf나 PyMuPDF 단독으로는 어렵습니다. reportlab으로 새 레이어를 그린 뒤, pypdf로 기존 PDF와 합성하는 조합이 필요합니다.
from reportlab.pdfgen import canvas
from pypdf import PdfReader, PdfWriter
from io import BytesIO
# ① reportlab으로 "도장만 있는" 새 PDF 레이어를 그림
buffer = BytesIO()
c = canvas.Canvas(buffer)
c.drawImage("도장.png", 450, 100, width=80, height=80)
c.save()
buffer.seek(0)
# ② pypdf로 기존 PDF와 방금 그린 레이어를 합성
stamp_page = PdfReader(buffer).pages[0]
reader = PdfReader("계약서.pdf")
writer = PdfWriter()
for page in reader.pages:
page.merge_page(stamp_page) # 기존 페이지 위에 도장 레이어를 겹침
writer.add_page(page)
writer.write("계약서_도장완료.pdf")
💡 여기서 알 수 있는 것:reportlab은 "빈 도화지에 그림을 그리는" 도구이지 "기존 PDF를 여는" 도구가 아닙니다. 그래서 항상pypdf와 짝을 이뤄 사용됩니다. PDF 도장 삽입 편에서 두 라이브러리를 함께 쓴 이유가 이것입니다.
pypdf PyMuPDF reportlab 주요 용도 병합·분할·읽기 이미지 분석·변환 PDF 생성(무에서 유) 설치 용량 가벼움 무거움 가벼움 처리 속도 빠름 보통(정밀한 만큼) 빠름 텍스트 추출 가능 매우 정확 해당 없음(생성 전용) 이미지 재압축 불가능 가능 해당 없음 새 텍스트 추가 제한적(합성 필요) 가능 완벽 지원 기존 PDF 열기 가능 가능 불가능(생성만 가능) 학습 난이도 쉬움 보통 쉬움
pypdf를 선택하세요, 만약:
PDF를 합치거나, 나누거나, 특정 페이지만 뽑아내거나, 텍스트를 단순히 추출하는 작업이라면. 가볍고 빠르며 대부분의 "편집" 작업에 충분합니다.
PyMuPDF를 선택하세요, 만약:
PDF 안의 이미지를 직접 조작(압축, 교체, 추출)해야 하거나, 텍스트 위치·서식까지 정밀하게 분석해야 한다면. 설치 용량이 크고 무거운 대신 가장 강력한 기능을 제공합니다.
reportlab을 선택하세요, 만약:
빈 상태에서 완전히 새로운 PDF(보고서, 인증서, 라벨)를 생성해야 하거나, 기존 PDF 위에 도장·워터마크·서명 같은 새 요소를 겹쳐 그려야 한다면. 단, 기존 PDF를 직접 열 수는 없으므로 반드시 pypdf와 함께 사용해야 합니다.
실무에서는 조합해서 씁니다.
이 블로그의 PDF 도장 삽입 편처럼, reportlab으로 새 요소를 그리고 pypdf로 기존 문서와 합치는 조합이 가장 흔한 패턴입니다. 이미지가 많이 포함된 PDF를 다뤄야 한다면 여기에 PyMuPDF까지 추가로 익혀두는 것을 권장합니다.