본문 바로가기

파일 형식 백과사전

스캔한 계약서 PDF가 너무 무거울 때, 안에 든 사진을 다시 압축해 줄이기

계약서를 스캔해 받은 PDF가 세 쪽인데 1.4MB입니다. 글자만 있는 문서인데 사진 몇 장보다 무겁습니다. 편집기의 최적화 기능을 눌러도 몇 킬로바이트밖에 안 빠집니다.

스캔본 안에는 글자가 아니라 페이지를 통째로 찍은 사진이 들어 있기 때문입니다. 줄이려면 그 사진을 꺼내 다시 압축해야 합니다. 실제로 3쪽짜리 스캔본을 만들어 놓고 해상도를 단계별로 낮춰 가며 어디까지 줄어드는지, 어디서부터 글자가 상하는지 재 봤습니다.

스캔 PDF 용량 줄이기 실측 표. A4 3쪽 계약서 스캔본 300dpi 원본이 1,465,287바이트였고, 안의 사진을 꺼내 200dpi로 다시 넣으면 1,182,248바이트로 19.3퍼센트, 150dpi면 782,603바이트로 46.6퍼센트, 100dpi면 455,840바이트로 68.9퍼센트 줄었다. 10pt 글자 높이는 각각 41.7, 27.8, 20.8, 13.9픽셀이고 원본과의 차이는 6.5, 7.8, 10.9로 커진다. 측정 환경은 윈도우 11, 파이썬 3.14.6, pypdf 6.13.2, Pillow 12.2.0

 

왜 최적화 버튼이 안 듣는가

일반적인 PDF 최적화는 글꼴 정보를 정리하고 중복된 자원을 합치는 식으로 동작합니다. 글자로 만들어진 문서라면 이것만으로도 꽤 줄어듭니다.

그런데 스캔본에는 정리할 글꼴이 없습니다. 한 쪽이 커다란 JPEG 한 장이고, PDF는 그 사진을 담는 봉투 역할만 합니다. 봉투를 아무리 잘 접어도 안에 든 사진이 그대로면 무게는 그대로입니다.

측정한 원본은 3쪽에 1,465,287바이트였고 한 쪽당 488,429바이트가 사진 몫이었습니다. 여기서 손댈 곳은 사진밖에 없습니다.


해상도를 낮추면 어디까지 줄어드나

300dpi로 만든 스캔본에서 사진을 꺼내 해상도를 낮추고 품질 60으로 다시 넣었습니다. 세 단계를 각각 재 봤습니다.

안에 든 사진 PDF 크기 줄어든 비율 10pt 글자 높이 원본과 차이
300dpi (원본) 1,465,287 B 기준 41.7 픽셀 기준
200dpi 1,182,248 B 19.3 % 27.8 픽셀 6.5
150dpi 782,603 B 46.6 % 20.8 픽셀 7.8
100dpi 455,840 B 68.9 % 13.9 픽셀 10.9

맨 오른쪽 칸은 낮춘 사진을 원래 크기로 되돌린 뒤 원본과 픽셀을 비교해 얻은 값입니다. 숫자가 클수록 원본에서 멀어졌다는 뜻입니다.

150dpi가 가장 이득이 큰 지점이었습니다. 크기가 절반 가까이 빠지는데 원본과의 차이는 7.8로 200dpi의 6.5와 큰 차이가 없습니다. 반면 100dpi로 한 단계 더 내리면 차이가 10.9로 뛰고, 10pt 글자가 세로 13.9픽셀까지 낮아집니다. 획이 가는 한글에서는 이 높이부터 받침이 뭉치기 시작합니다.

직접 줄이는 방법

PDF에서 사진을 꺼내고, 크기를 줄이고, 다시 묶는 세 단계입니다. 아래 코드는 A4 기준으로 150dpi에 맞춰 다시 만듭니다.

import io
from PIL import Image
from pypdf import PdfReader
from reportlab.pdfgen import canvas
from reportlab.lib.utils import ImageReader

TARGET_DPI = 150
W_PT, H_PT = 210 / 25.4 * 72, 297 / 25.4 * 72      # A4 를 포인트로
W_PX = round(210 / 25.4 * TARGET_DPI)
H_PX = round(297 / 25.4 * TARGET_DPI)

reader = PdfReader("scan.pdf")
c = canvas.Canvas("scan_small.pdf", pagesize=(W_PT, H_PT))

for page in reader.pages:
    img = Image.open(io.BytesIO(page.images[0].data)).convert("RGB")
    img = img.resize((W_PX, H_PX), Image.LANCZOS)
    buf = io.BytesIO()
    img.save(buf, "JPEG", quality=60, optimize=True)
    buf.seek(0)
    c.drawImage(ImageReader(Image.open(buf)), 0, 0, width=W_PT, height=H_PT)
    c.showPage()

c.save()

쪽마다 사진이 한 장이라는 전제로 page.images[0]를 씁니다. 한 쪽에 여러 조각으로 나뉘어 들어간 스캔본도 있으니, 먼저 len(page.images)를 찍어 개수를 확인하고 돌리는 편이 안전합니다.

프로그램을 쓰지 않는다면 순서는 같습니다. PDF를 이미지로 내보내고, 이미지 편집기에서 긴 변을 150dpi에 해당하는 픽셀로 줄인 다음, 다시 PDF로 묶으면 됩니다.


줄이기 전에 확인할 것

스캔본을 줄이면 되돌릴 수 없습니다. 버린 화소는 다시 만들어지지 않으니 원본은 따로 보관해 두고 사본을 줄이는 것이 안전합니다.

제출용 문서라면 받는 곳의 기준을 먼저 봅니다. 관공서나 은행 제출 서류는 200dpi 이상을 요구하는 경우가 있고, 그럴 때는 19퍼센트만 줄이고 멈춰야 합니다. 도장이나 서명이 들어간 문서도 획이 가늘어 150dpi 아래로는 내리지 않는 편이 낫습니다.

한 가지 더. 내 PDF가 정말 스캔본인지부터 확인하는 것이 순서입니다. 글자로 만들어진 PDF라면 사진이 없으니 위 방법이 통하지 않고, 대신 일반적인 최적화가 잘 듣습니다. PDF 용량 줄이기를 눌러도 안 줄어드는 이유에서 두 종류의 차이를 다뤘고, 사진을 PDF로 묶을 때 용량이 몇 배가 되는 문제는 사진 여러 장을 PDF로 합칠 때의 용량 쪽에 정리해 두었습니다.

 

무거운 스캔본을 메일로 보내야 한다면 150dpi로 한 부를 따로 만들어 두세요. 원본은 그대로 두고 보내는 용도만 가벼워집니다.

 

위 크기와 차이 값은 윈도우 11에서 파이썬 3.14.6, pypdf 6.13.2, Pillow 12.2.0으로 A4 3쪽 스캔본을 만들어 단계별로 다시 압축해 얻은 값입니다. 원본 스캔의 품질과 문서의 내용에 따라 줄어드는 비율은 달라집니다.