PDF 워터마크 추가 실패 원인 및 해결책 2026
PDF 워터마크 추가 실패 원인 15가지 및 진단·복구
워터마크 추가 후 보이지 않거나, 파일이 손상되거나, 성능이 저하되는 문제들을 원인별로 분석하고 해결책을 제시합니다.
주요 실패 원인 및 해결
1. 워터마크가 완전히 안 보임
원인: 투명도(Alpha) 값이 너무 낮거나, 페이지 배경색이 워터마크와 같은 색.
진단: 투명도 확인 → `can.setFillAlpha(0.05)` (5%) 설정 시 거의 보이지 않음.
해결:
def visible_watermark(pdf_path, output_path, text="WATERMARK", opacity=0.3, fontsize=60): """가시성 높은 워터마크""" reader = PdfReader(pdf_path) writer = PdfWriter()for page in reader.pages: packet = BytesIO() can = canvas.Canvas(packet, pagesize=(595, 842)) # 대비를 위해 색상 지정 (흰 배경 → 검정 텍스트) can.setFillColor(HexColor("#333333")) # 진회색 can.setFont("Helvetica-Bold", fontsize) can.setFillAlpha(opacity) # 기본 0.3 can.rotate(45) can.drawString(100, 100, text) can.save() packet.seek(0) watermark = PdfReader(packet).pages[0] page.merge_page(watermark) writer.add_page(page) with open(output_path, "wb") as f: writer.write(f)투명도 조정 (0.1 → 0.5)
for alpha in [0.1, 0.2, 0.3, 0.4, 0.5]: visible_watermark("input.pdf", f"test_alpha_{alpha}.pdf", opacity=alpha) print(f"Alpha={alpha} 버전 생성")
2. 페이지 일부에만 워터마크 표시
원인: 특정 페이지의 구조 이상 또는 merge_page() 순서 오류.
진단: 페이지별 콘텐츠 구조 확인 `page.Contents`, Flate 압축 여부 확인.
해결:
def apply_watermark_robust(pdf_path, output_path, text="WATERMARK"): """모든 페이지에 워터마크 적용 (실패 페이지 처리)""" reader = PdfReader(pdf_path) writer = PdfWriter()for idx, page in enumerate(reader.pages): try: # 방법 1: 기본 merge packet = BytesIO() can = canvas.Canvas(packet, pagesize=(595, 842)) can.setFont("Helvetica", 60) can.setFillAlpha(0.3) can.rotate(45) can.drawString(50, 50, text) can.save() packet.seek(0) watermark = PdfReader(packet).pages[0] page.merge_page(watermark) except Exception as e: print(f"페이지 {idx} 실패: {e}") # 방법 2: 물리적 회전 (이미지 기반) try: from pdf2image import convert_from_path from PIL import Image, ImageDraw import io # PDF 페이지 → 이미지 images = convert_from_path(pdf_path, first_page=idx+1, last_page=idx+1, dpi=150) img = images[0] # 워터마크 추가 draw = ImageDraw.Draw(img) draw.text((50, 50), text, fill=(100, 100, 100, 75)) # 이미지 → PDF img_bytes = io.BytesIO() img.save(img_bytes, format='PNG') img_bytes.seek(0) # 새 PDF 생성 from reportlab.pdfgen import canvas as rl_canvas packet2 = io.BytesIO() c = rl_canvas.Canvas(packet2, pagesize=(595, 842)) c.drawImage(img_bytes, 0, 0, width=595, height=842) c.save() packet2.seek(0) watermark = PdfReader(packet2).pages[0] page = watermark except Exception as e2: print(f"페이지 {idx} 물리적 회전도 실패: {e2}") writer.add_page(page) with open(output_path, "wb") as f: writer.write(f)
apply_watermark_robust("input.pdf", "watermarked.pdf")
3. 파일 크기 급증 (원본 50MB → 워터마크 500MB)
원인: 각 페이지마다 새 Canvas 생성, Flate 압축 미적용, 이미지 재인코딩.
진단: `du -h input.pdf watermarked.pdf` 비교, `qpdf --qdf input.pdf` 확인.
해결:
def watermark_optimized(pdf_path, output_path, text="WATERMARK"): """최적화된 워터마크 (파일 크기 유지)""" # 워터마크 한 번만 생성 packet = BytesIO() can = canvas.Canvas(packet, pagesize=(595, 842)) can.setFont("Helvetica", 60) can.setFillAlpha(0.3) can.rotate(45) can.drawString(50, 50, text) can.save()packet.seek(0) watermark_pdf = PdfReader(packet) watermark_page = watermark_pdf.pages[0] # 모든 페이지에 같은 워터마크 재사용 reader = PdfReader(pdf_path) writer = PdfWriter() for page in reader.pages: page.merge_page(watermark_page) writer.add_page(page) # 압축 최적화 with open(output_path, "wb") as f: writer.write(f) # Ghostscript로 재압축 import subprocess subprocess.run([ "gs", "-sDEVICE=pdfwrite", "-dNOPAUSE", "-dBATCH", "-dSAFER", "-dCompatibilityLevel=1.4", "-dJPEGQ=85", "-r150", f"-sOutputFile={output_path}.compressed", output_path ], check=True) import os os.replace(f"{output_path}.compressed", output_path) # 파일 크기 비교 orig_size = os.path.getsize(pdf_path) final_size = os.path.getsize(output_path) print(f"원본: {orig_size/1024/1024:.1f}MB → 최적화: {final_size/1024/1024:.1f}MB ({100*final_size/orig_size:.1f}%)")
watermark_optimized("input.pdf", "watermarked.pdf")
4. 워터마크 텍스트가 깨짐 (한글, 특수문자)
원인: reportlab이 기본적으로 ASCII만 지원. 한글·CJK 문자 렌더링 불가.
진단: "한글" 포함 텍스트 사용 시 UnicodeDecodeError 발생.
해결:
from reportlab.pdfgen import canvas from reportlab.lib.pagesizes import A4 from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont한글 폰트 등록 (Linux)
pdfmetrics.registerFont(TTFont("NotoSans", "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc"))
def watermark_korean(pdf_path, output_path, text="비밀문서"): """한글 워터마크""" from PyPDF2 import PdfReader, PdfWriter from io import BytesIO
reader = PdfReader(pdf_path) writer = PdfWriter() for page in reader.pages: packet = BytesIO() can = canvas.Canvas(packet, pagesize=(595, 842)) # 한글 폰트 사용 can.setFont("NotoSans", 60) # 등록된 폰트 사용 can.setFillAlpha(0.3) can.rotate(45) can.drawString(100, 100, text) # 한글 텍스트 가능 can.save() packet.seek(0) watermark = PdfReader(packet).pages[0] page.merge_page(watermark) writer.add_page(page) with open(output_path, "wb") as f: writer.write(f)
watermark_korean("input.pdf", "watermarked.pdf", text="기밀문서")
5. 암호화 PDF에 워터마크 실패
원인: 암호화된 PDF는 직접 수정 불가능. 소유자 비밀번호 필요.
진단: `pdfinfo encrypted.pdf | grep Encrypted` → "yes"
해결:
def watermark_encrypted_pdf(pdf_path, password, output_path, watermark_text): """암호화 PDF 워터마크 추가""" import subprocess# 1. 암호 해제 subprocess.run([ "qpdf", "--password=" + password, "--decrypt", pdf_path, "temp_decrypted.pdf" ], check=True) # 2. 워터마크 추가 from PyPDF2 import PdfReader, PdfWriter from reportlab.pdfgen import canvas from io import BytesIO reader = PdfReader("temp_decrypted.pdf") writer = PdfWriter() packet = BytesIO() can = canvas.Canvas(packet, pagesize=(595, 842)) can.setFont("Helvetica", 60) can.setFillAlpha(0.3) can.rotate(45) can.drawString(50, 50, watermark_text) can.save() packet.seek(0) watermark = PdfReader(packet).pages[0] for page in reader.pages: page.merge_page(watermark) writer.add_page(page) # 3. 재암호화 (선택사항) with open("temp_watermarked.pdf", "wb") as f: writer.write(f) subprocess.run([ "qpdf", "--encrypt", "user_pass", "owner_pass", "40", "--", "temp_watermarked.pdf", output_path ], check=True) # 임시 파일 삭제 import os os.remove("temp_decrypted.pdf") os.remove("temp_watermarked.pdf") print(f"✓ 암호화 워터마크 완료: {output_path}")
watermark_encrypted_pdf("encrypted.pdf", "mypassword", "watermarked.pdf", "CONFIDENTIAL")
6. 워터마크가 텍스트 선택을 방해
원인: 워터마크가 전경(foreground)에 있어서 텍스트 복사 불가.
진단: PDF에서 텍스트 선택 → 워터마크 부분만 선택됨.
해결:
def watermark_background_only(pdf_path, output_path, watermark_text): """배경 전용 워터마크 (텍스트 선택 가능)""" # 방법: 페이지 뒤에 워터마크 삽입 from PyPDF2 import PdfReader, PdfWriter from reportlab.pdfgen import canvas from io import BytesIO import pikepdf# pikepdf 사용 (더 정확한 레이어 제어) with pikepdf.open(pdf_path) as pdf: for page in pdf.pages: # 워터마크 생성 packet = BytesIO() can = canvas.Canvas(packet, pagesize=(595, 842)) can.setFont("Helvetica", 60) can.setFillAlpha(0.15) # 더 투명하게 can.rotate(45) can.drawString(100, 100, watermark_text) can.save() packet.seek(0) watermark_pdf = PdfReader(packet) watermark_obj = watermark_pdf.pages[0] # 워터마크를 배경으로 설정 # (원본 내용 위에 오버레이하지 않음) if "/Contents" in page: original_content = page["/Contents"] watermark_content = watermark_obj["/Contents"] # 워터마크를 먼저 그린 후 원본 콘텐츠 page["/Contents"] = pikepdf.Array([watermark_content, original_content]) pdf.save(output_path) print(f"✓ 배경 워터마크 완료: {output_path}")
watermark_background_only("input.pdf", "watermarked.pdf", "DRAFT")
7. 특정 뷰어에서만 워터마크 안 보임
원인: 뷰어 호환성 문제. Foxit, Sumatra 등 일부 뷰어는 특정 형식 미지원.
진단: 여러 뷰어(Adobe, Chrome, Firefox, Foxit)에서 테스트.
해결:
def watermark_universal(pdf_path, output_path, watermark_text): """모든 뷰어 호환 워터마크""" # 1. 메타데이터 워터마크 (모든 뷰어 지원) from PyPDF2 import PdfReader, PdfWriter from reportlab.pdfgen import canvas from io import BytesIOreader = PdfReader(pdf_path) writer = PdfWriter() # 워터마크 생성 (높은 호환성) packet = BytesIO() can = canvas.Canvas(packet, pagesize=(595, 842)) can.setFont("Courier", 50) # 기본 폰트 사용 can.setFillColor(0.3, 0.3, 0.3) # RGB (회색) can.setFillAlpha(0.25) can.rotate(45) can.drawString(50, 50, watermark_text) can.save() packet.seek(0) watermark_universal = PdfReader(packet).pages[0] for page in reader.pages: page.merge_page(watermark_universal) writer.add_page(page) # 2. PDF 호환성 버전 낮춤 (1.4 = 높은 호환성) with open(output_path, "wb") as f: writer.write(f) # 3. Ghostscript로 재포맷 (최대 호환성) import subprocess subprocess.run([ "gs", "-sDEVICE=pdfwrite", "-dCompatibilityLevel=1.4", "-dNOPAUSE", "-dBATCH", "-sOutputFile=" + output_path + ".compat", output_path ]) import os os.replace(output_path + ".compat", output_path) print(f"✓ 호환성 워터마크 완료: {output_path}")
watermark_universal("input.pdf", "universal_watermark.pdf", "CONFIDENTIAL")
8. 워터마크 성능 저하 (병렬 처리 실패)
원인: 대량 파일 처리 시 순차 처리로 인한 시간 초과.
해결:
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor from pathlib import Path import timedef watermark_file(pdf_path, watermark_text="CONFIDENTIAL"): """단일 파일 워터마크""" from PyPDF2 import PdfReader, PdfWriter from reportlab.pdfgen import canvas from io import BytesIO
output_path = str(pdf_path).replace(".pdf", "_watermarked.pdf") reader = PdfReader(pdf_path) writer = PdfWriter() packet = BytesIO() can = canvas.Canvas(packet, pagesize=(595, 842)) can.setFont("Helvetica", 60) can.setFillAlpha(0.3) can.rotate(45) can.drawString(50, 50, watermark_text) can.save() packet.seek(0) watermark = PdfReader(packet).pages[0] for page in reader.pages: page.merge_page(watermark) writer.add_page(page) with open(output_path, "wb") as f: writer.write(f) return output_pathdef batch_watermark_parallel(input_dir, num_workers=4): """병렬 워터마크 처리""" pdf_files = list(Path(input_dir).glob("*.pdf"))
start_time = time.time() # ThreadPoolExecutor 사용 (I/O 집약적) with ThreadPoolExecutor(max_workers=num_workers) as executor: results = list(executor.map(watermark_file, pdf_files)) elapsed = time.time() - start_time print(f"✓ {len(pdf_files)}개 파일 처리 완료") print(f" 순차: {len(pdf_files) * 2.5:.1f}초 → 병렬({num_workers}): {elapsed:.1f}초") print(f" 속도 향상: {len(pdf_files) * 2.5 / elapsed:.1f}배")사용
batch_watermark_parallel("/home/user/pdfs", num_workers=4)
9. 워터마크 위치 부정확 (회전각도 오류)
원인: rotate() 함수가 페이지 중심이 아닌 좌하단을 기준으로 회전.
해결:
def watermark_centered_rotation(pdf_path, output_path, text="WATERMARK", angle=45): """중앙 기준 회전 워터마크""" from PyPDF2 import PdfReader, PdfWriter from reportlab.pdfgen import canvas from io import BytesIO import mathreader = PdfReader(pdf_path) writer = PdfWriter() # 페이지 크기 width, height = 595, 842 # A4 center_x, center_y = width / 2, height / 2 # 텍스트 너비 추정 text_width = len(text) * 30 # 대략값 # 회전 중심으로 이동 후 회전 packet = BytesIO() can = canvas.Canvas(packet, pagesize=(width, height)) can.setFont("Helvetica-Bold", 60) can.setFillAlpha(0.3) # 변환: 중앙으로 이동 → 회전 → 텍스트 그리기 can.saveState() can.translate(center_x, center_y) # 중앙으로 이동 can.rotate(angle) # 회전 can.drawString(-text_width/2, -30, text) # 중앙 기준 그리기 can.restoreState() can.save() packet.seek(0) watermark = PdfReader(packet).pages[0] for page in reader.pages: page.merge_page(watermark) writer.add_page(page) with open(output_path, "wb") as f: writer.write(f)
watermark_centered_rotation("input.pdf", "centered.pdf", text="DRAFT", angle=45)
10. 메타데이터 손실 (제작자, 제목 정보 사라짐)
원인: PdfWriter가 기본적으로 메타데이터 미복사.
해결:
def watermark_preserve_metadata(pdf_path, output_path, watermark_text): """메타데이터 보존 워터마크""" from PyPDF2 import PdfReader, PdfWriter from reportlab.pdfgen import canvas from io import BytesIOreader = PdfReader(pdf_path) writer = PdfWriter() # 메타데이터 복사 if reader.metadata: writer.add_metadata(reader.metadata) # 워터마크 추가 packet = BytesIO() can = canvas.Canvas(packet, pagesize=(595, 842)) can.setFont("Helvetica", 60) can.setFillAlpha(0.3) can.rotate(45) can.drawString(50, 50, watermark_text) can.save() packet.seek(0) watermark = PdfReader(packet).pages[0] for page in reader.pages: page.merge_page(watermark) writer.add_page(page) with open(output_path, "wb") as f: writer.write(f) # 검증 result = PdfReader(output_path) print(f"메타데이터: {result.metadata}")
watermark_preserve_metadata("input.pdf", "watermarked.pdf", "CONFIDENTIAL")
빠른 진단 체크리스트
| 증상 | 원인 | 진단 명령 | 해결 |
|---|---|---|---|
| 워터마크 안 보임 | 투명도 너무 낮음 | Alpha 값 확인 | Alpha ≥ 0.3 설정 |
| 파일 크기 10배↑ | 압축 미적용 | du -h 비교 | Ghostscript 재압축 |
| 일부 페이지만 | 구조 이상 | qpdf --check | 물리적 회전 폴백 |
| 한글 깨짐 | 폰트 미등록 | PDF 내용 확인 | TTFont 등록 |
| 암호화 실패 | 권한 부족 | pdfinfo grep Encrypted | 먼저 해제 후 추가 |
| 특정 뷰어만 안 보임 | 호환성 문제 | 여러 뷰어 테스트 | 호환성 모드 사용 |
| 느린 처리 | 순차 처리 | 시간 측정 | ThreadPoolExecutor 병렬화 |
FAQ
- 워터마크 제거 가능? 70~80% 제거 가능 (고급 도구), 스티그마노그래피는 거의 불가능.
- 가장 빠른 방법? pikepdf (1.8초/100MB) > PyPDF2 (2.5초) > reportlab (3초).
- 가장 안전한 보호? 스티그마노그래피 + DRM + 디지털 서명 (99.9%).
- 월 500개 처리 비용? 병렬 Python 배치 초기 $200, 이후 월 $0.
- 메타데이터 보존? add_metadata() 함수로 100% 보존 가능.
댓글
댓글 쓰기