PDF 페이지 번호 삽입: 기초부터 고급까지
간단한 숫자 표시부터 복잡한 형식(로마자, 부분 번호 지정, 위치 커스터마이징)까지 모든 페이지 번호 삽입 방법을 상세히 설명합니다.
1. 온라인 도구 (무료, 설치 불필요)
2. 데스크톱 소프트웨어
- Adobe Acrobat Pro ($9.99/월) – 완벽한 페이지 번호, 배치 처리 가능
- Preview (macOS) (무료) – 기본 기능만 지원
- PDF-XChange Editor ($75) – 좌표 지정 가능
- Foxit PhantomPDF (월 $9.99) – 고급 포맷팅
3. Python 라이브러리 (개발자용)
3.1 PyPDF2 + reportlab (기본)
from PyPDF2 import PdfReader, PdfWriter
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
from io import BytesIO
def add_page_numbers_basic(input_pdf, output_pdf):
"""기본 페이지 번호 삽입 (하단 중앙)"""
reader = PdfReader(input_pdf)
writer = PdfWriter()
for page_num, page in enumerate(reader.pages, start=1):
# 페이지 번호 생성
packet = BytesIO()
can = canvas.Canvas(packet, pagesize=letter)
can.setFont("Helvetica", 12)
can.drawString(280, 30, str(page_num)) # 하단 중앙 (x=280, y=30)
can.save()
packet.seek(0)
page_number_pdf = PdfReader(packet)
page.merge_page(page_number_pdf.pages[0])
writer.add_page(page)
with open(output_pdf, "wb") as f:
writer.write(f)
print(f"✓ 페이지 번호 추가 완료: {output_pdf}")
# 사용
add_page_numbers_basic("input.pdf", "numbered.pdf")
3.2 pikepdf (고급)
import pikepdf
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from io import BytesIO
def add_page_numbers_advanced(input_pdf, output_pdf,
position="bottom-center",
format="numeric",
start_page=1):
"""고급 페이지 번호 삽입"""
with pikepdf.open(input_pdf) as pdf:
width = float(pdf.pages[0].MediaBox[2])
height = float(pdf.pages[0].MediaBox[3])
for page_num, page in enumerate(pdf.pages, start=start_page):
# 페이지 번호 형식 결정
if format == "numeric":
text = str(page_num)
elif format == "roman_lower":
text = to_roman(page_num).lower()
elif format == "roman_upper":
text = to_roman(page_num)
elif format == "custom":
text = f"Page {page_num}"
# 위치 결정
if position == "bottom-center":
x, y = width / 2, 30
elif position == "bottom-right":
x, y = width - 50, 30
elif position == "bottom-left":
x, y = 50, 30
elif position == "top-right":
x, y = width - 50, height - 40
else:
x, y = width / 2, 30
# 페이지 번호 생성
packet = BytesIO()
can = canvas.Canvas(packet, pagesize=(width, height))
can.setFont("Helvetica", 11)
can.drawString(x, y, text)
can.save()
packet.seek(0)
number_pdf = PdfReader(packet)
number_page = number_pdf.pages[0]
# 병합
page.Contents = pikepdf.Array([page.Contents, number_page.Contents])
pdf.save(output_pdf)
print(f"✓ 고급 페이지 번호 추가: {output_pdf}")
def to_roman(num):
"""숫자를 로마자로 변환"""
val = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
syms = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"]
roman_num = ""
i = 0
while num > 0:
for _ in range(num // val[i]):
roman_num += syms[i]
num -= val[i]
i += 1
return roman_num
# 사용
add_page_numbers_advanced(
"input.pdf",
"numbered.pdf",
position="bottom-right",
format="roman_lower",
start_page=1
)
3.3 reportlab으로 새 PDF 생성 (가장 유연)
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
from reportlab.lib import colors
from PyPDF2 import PdfReader, PdfWriter
def add_page_numbers_flexible(input_pdf, output_pdf):
"""완전 커스터마이징 페이지 번호"""
reader = PdfReader(input_pdf)
writer = PdfWriter()
for page_num, page in enumerate(reader.pages, start=1):
width = float(page.MediaBox[2])
height = float(page.MediaBox[3])
# 페이지 번호 생성 (고급 포맷팅)
packet = BytesIO()
can = canvas.Canvas(packet, pagesize=(width, height))
# 배경 (선택사항)
can.setFillColor(colors.white)
can.rect(width - 80, 20, 60, 20, fill=1)
# 테두리
can.setStrokeColor(colors.grey)
can.rect(width - 80, 20, 60, 20)
# 페이지 번호 텍스트
can.setFont("Courier", 10)
can.setFillColor(colors.black)
can.drawString(width - 70, 28, f"p. {page_num}")
# 페이지 번호와 총 페이지 수
can.setFont("Helvetica", 9)
can.drawString(50, height - 30, f"© 2024 | Page {page_num} of {len(reader.pages)}")
can.save()
packet.seek(0)
page_number_pdf = PdfReader(packet)
page.merge_page(page_number_pdf.pages[0])
writer.add_page(page)
with open(output_pdf, "wb") as f:
writer.write(f)
add_page_numbers_flexible("input.pdf", "numbered.pdf")
4. 부분 페이지 번호 (특정 범위만)
def add_page_numbers_selective(input_pdf, output_pdf,
start_numbering=1,
skip_first_n=0,
skip_last_n=0):
"""특정 페이지만 번호 삽입"""
reader = PdfReader(input_pdf)
writer = PdfWriter()
total_pages = len(reader.pages)
for idx, page in enumerate(reader.pages):
# 처음 skip_first_n 페이지 건너뛰기
if idx < skip_first_n:
writer.add_page(page)
continue
# 마지막 skip_last_n 페이지 건너뛰기
if idx >= total_pages - skip_last_n:
writer.add_page(page)
continue
# 페이지 번호 지정
page_num = start_numbering + (idx - skip_first_n)
# 페이지 번호 추가
packet = BytesIO()
can = canvas.Canvas(packet, pagesize=(595, 842))
can.setFont("Helvetica", 12)
can.drawString(280, 30, str(page_num))
can.save()
packet.seek(0)
page_number_pdf = PdfReader(packet)
page.merge_page(page_number_pdf.pages[0])
writer.add_page(page)
with open(output_pdf, "wb") as f:
writer.write(f)
print(f"✓ 부분 페이지 번호 완료: {output_pdf}")
# 사용: 첫 3페이지 제외, 마지막 2페이지 제외
add_page_numbers_selective(
"input.pdf",
"numbered.pdf",
start_numbering=1,
skip_first_n=3, # 커버 페이지 등
skip_last_n=2 # 부록 등
)
5. 페이지 번호 형식 옵션
6. 배치 처리 (대량 파일)
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
def batch_add_page_numbers(input_dir, output_dir):
"""폴더 내 모든 PDF에 페이지 번호 추가"""
Path(output_dir).mkdir(exist_ok=True)
pdf_files = list(Path(input_dir).glob("*.pdf"))
def process_file(pdf_file):
output_file = Path(output_dir) / pdf_file.name
add_page_numbers_basic(str(pdf_file), str(output_file))
return pdf_file.name
# 병렬 처리 (4 스레드)
with ThreadPoolExecutor(max_workers=4) as executor:
results = list(executor.map(process_file, pdf_files))
print(f"✓ {len(results)}개 파일 처리 완료")
# 사용
batch_add_page_numbers("/home/user/pdfs/input", "/home/user/pdfs/output")
7. Ghostscript를 이용한 페이지 번호 삽입
import subprocess
import os
def add_page_numbers_ghostscript(input_pdf, output_pdf):
"""Ghostscript를 이용한 페이지 번호 추가 (매우 빠름)"""
# 임시 PostScript 파일 생성
ps_code = """
%!PS-Adobe-3.0
/PageNumbers {
/Helvetica findfont 12 scalefont setfont
0.2 setgray
10 20 moveto
(Page: ) show
/PageNumber pdfmark
} def
/EndPage {
PageNumbers
} def
"""
temp_ps = "/tmp/page_numbers.ps"
with open(temp_ps, "w") as f:
f.write(ps_code)
# Ghostscript 실행
subprocess.run([
"gs",
"-sDEVICE=pdfwrite",
"-dNOPAUSE",
"-dBATCH",
f"-sOutputFile={output_pdf}",
temp_ps,
input_pdf
], check=True)
os.remove(temp_ps)
print(f"✓ Ghostscript로 페이지 번호 추가: {output_pdf}")
# 주의: Ghostscript 필요 (apt-get install ghostscript)
# add_page_numbers_ghostscript("input.pdf", "numbered.pdf")
8. 클라우드 자동화 (AWS Lambda)
import boto3
import json
from io import BytesIO
from PyPDF2 import PdfReader, PdfWriter
s3 = boto3.client('s3')
def lambda_handler(event, context):
"""Lambda에서 페이지 번호 삽입"""
try:
# S3에서 PDF 다운로드
bucket = event['Records'][0]['s3']['bucket']['name']
key = event['Records'][0]['s3']['object']['key']
obj = s3.get_object(Bucket=bucket, Key=key)
pdf_content = obj['Body'].read()
# 페이지 번호 추가
reader = PdfReader(BytesIO(pdf_content))
writer = PdfWriter()
from reportlab.pdfgen import canvas
for page_num, page in enumerate(reader.pages, start=1):
packet = BytesIO()
can = canvas.Canvas(packet, pagesize=(595, 842))
can.setFont("Helvetica", 12)
can.drawString(280, 30, str(page_num))
can.save()
packet.seek(0)
page_number_pdf = PdfReader(packet)
page.merge_page(page_number_pdf.pages[0])
writer.add_page(page)
# S3에 저장
output = BytesIO()
writer.write(output)
output.seek(0)
output_key = f"numbered/{key}"
s3.put_object(
Bucket=bucket,
Key=output_key,
Body=output.getvalue()
)
return {
"statusCode": 200,
"body": json.dumps(f"Success: {output_key}")
}
except Exception as e:
return {
"statusCode": 500,
"body": json.dumps(f"Error: {str(e)}")
}
9. 성능 비교
10. 추천 선택
- 개인 (1~5개/월): PDFKit 온라인
- 중소기업 (50~200개/월): PyPDF2 배치 + Cron
- 대기업 (>500개/월): AWS Lambda
- 높은 품질 필요: Adobe Acrobat
- 매우 빠른 처리: Ghostscript CLI
11. FAQ
- 가장 빠른 방법? Ghostscript (2초) > pikepdf (1.8초) > AWS Lambda (1.5초)
- 가장 저렴한 방법? 무료 오픈소스: PyPDF2, pikepdf, Ghostscript
- 가장 유연한 방법? reportlab (완전 커스터마이징)
- 로마자 지원? 모든 Python 라이브러리 가능 (변환 함수 필요)
- 부분 페이지만? skip_first_n, skip_last_n 파라미터 사용
12. 관련 자료
댓글
댓글 쓰기