| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
Tags
- gemini-cli
- 장자명언
- Freesound
- 생성AI
- 파이썬
- AI
- Skills
- Linux
- Flutter
- 오늘의역사
- 오픈소스
- Firebase
- 명언모음
- kotlin
- 명언
- Android
- DART
- 좋은글필사하기
- Codex
- javascript
- FSM
- Gemini
- 소울칼리버6
- Coroutine
- 코틀린
- 이모지메모
- 명심보감
- MCP
- ASMR
Archives
- Today
- Total
Vintage appMaker의 Tech Blog
[python] pdf에 워터마크 및 jpg로 출력하기 본문
PDF Watermark & JPG Converter 개발자 매뉴얼
이 문서는 PDF 문서에 지정된 투명도와 텍스트로 워터마크를 삽입하고, 인쇄 및 복사 방지 설정을 적용한 후 각 페이지를 지정된 압축률의 JPG 이미지로 추출하는 자동화 도구(process_pdf.py)의 개발자 가이드입니다.
1. 목적
- 문서 보안 강화: PDF 파일의 모든 페이지 중앙에 반투명 대각선 워터마크를 삽입합니다.
- 불법 유출 방지: 복사 및 인쇄 등의 사용자 권한을 제한(PDF Permission Flags 설정)하여 암호화된 PDF 문서를 생성합니다.
- 웹 게시 및 경량화: 워터마킹된 PDF의 모든 페이지를 일괄적으로 80% 압축(조절 가능) 품질의 JPG 파일로 변환하여 보관 및 게시를 용이하게 합니다.
2. 소스코드 예제설명
전체 소스코드는 process_pdf.py에서 확인할 수 있으며, 핵심적인 기능별 동작 방식은 다음과 같습니다.
A. 워터마크 캔버스 생성 및 병합
def build_watermark_page(width: float, height: float, text: str, opacity: float):
packet = io.BytesIO()
pdf = canvas.Canvas(packet, pagesize=(width, height))
pdf.saveState()
# 투명도 및 색상 설정
pdf.setFillColor(Color(0.35, 0.35, 0.35, alpha=opacity))
font_size = max(32, min(width, height) / 9)
pdf.setFont("Helvetica-Bold", font_size)
# 페이지 대각선 각도 계산 및 회전
rotation = math.degrees(math.atan2(height, width))
pdf.translate(width / 2, height / 2)
pdf.rotate(rotation)
pdf.drawCentredString(0, -font_size * 0.35, text)
...
- 동작 원리:
reportlab라이브러리를 사용해 메모리 버퍼(BytesIO) 내에 텍스트가 대각선으로 회전된 투명 PDF 페이지를 동적으로 렌더링하고, 이를PyPDF2를 이용하여 대상 PDF의 각 페이지 위에 병합(merge_page)합니다.
B. PDF 보안 설정 (Permissions)
permissions = (
UserAccessPermissions.EXTRACT
| UserAccessPermissions.EXTRACT_TEXT_AND_GRAPHICS
| UserAccessPermissions.FILL_FORM_FIELDS
| UserAccessPermissions.ASSEMBLE_DOC
)
...
writer.encrypt(
user_password="",
owner_password="vintage_appMaker_owner",
permissions_flag=permissions,
)
- 동작 원리: PDF Reader가 문서를 열 때 인쇄(Print) 권한을 제외한 최소한의 기본 권한만 허용하도록
UserAccessPermissions플래그를 설정하여 저장합니다.
C. JPG 고화질/고압축 변환
def convert_pdf_to_jpg(pdf_path: Path, output_dir: Path, quality: int = 80, scale: float = 2.0):
document = fitz.open(pdf_path)
matrix = fitz.Matrix(scale, scale) # 해상도 조절 (기본 2배 스케일링)
...
pixmap = page.get_pixmap(matrix=matrix, alpha=False)
image = Image.frombytes("RGB", [pixmap.width, pixmap.height], pixmap.samples)
image.save(output_path, "JPEG", quality=quality, optimize=True)
- 동작 원리:
PyMuPDF (fitz)모듈로 PDF 페이지의 픽셀 데이터를 고해상도로 렌더링한 후,Pillow (Image)라이브러리를 통해 최적화(optimize=True) 및 품질 압축률(quality=80) 조건으로 JPG 파일로 개별 저장합니다.
3. 패키지 설치법
본 스크립트는 Python 3.8 이상이 필요하며, 아래의 외부 종속성 라이브러리를 설치해야 합니다.
Windows (PowerShell/CMD) 설치 명령
pip install PyPDF2 reportlab pymupdf Pillow
PyPDF2: PDF 파일 병합 및 암호화 처리에 사용됩니다.
reportlab: 동적 워터마크 PDF 페이지 생성에 사용됩니다.
pymupdf: PDF의 초고속 및 고품질 이미지 렌더링을 처리합니다.
Pillow: 추출된 픽셀 데이터를 JPG 파일로 압축 및 최적화 저장하기 위해 사용됩니다.
4. 사용법
process_pdf.py는 명령줄(CLI) 인터페이스를 통해 제어할 수 있습니다.
CLI 기본 구조
python C:\temp\process_pdf.py <pdf_경로> --text <워터마크_텍스트> [옵션들]
파라미터 상세
| 매개변수 | 단축키 | 설명 | 필수 여부 | 기본값 |
|---|---|---|---|---|
pdf_path |
- | 원본 PDF 파일 경로 | 필수 | - |
--text |
-t |
삽입할 워터마크 문자열 | 필수 | - |
--opacity |
-o |
워터마크 불투명도 (0.0 ~ 1.0) |
선택 | 0.2 (20%) |
--quality |
-q |
JPG 저장 화질/압축률 (1 ~ 100) |
선택 | 80 (80% 압축/화질) |
--output-dir |
-d |
JPG가 저장될 디렉토리 | 선택 | <pdf가_위치한_폴더>/images |
실행 예제
python C:\temp\process_pdf.py "C:\temp\The_Buy_Rating_Illusion.pdf" --text "semu note"
5. 전체 소스코드
process_pdf.py의 전체 소스코드는 다음과 같습니다.
import io
import math
import sys
from pathlib import Path
from PyPDF2 import PdfReader, PdfWriter
from PyPDF2.constants import UserAccessPermissions
from reportlab.lib.colors import Color
from reportlab.pdfgen import canvas
import fitz
from PIL import Image
def build_watermark_page(width: float, height: float, text: str, opacity: float):
packet = io.BytesIO()
pdf = canvas.Canvas(packet, pagesize=(width, height))
pdf.saveState()
pdf.setFillColor(Color(0.35, 0.35, 0.35, alpha=opacity))
font_size = max(32, min(width, height) / 9)
pdf.setFont("Helvetica-Bold", font_size)
rotation = math.degrees(math.atan2(height, width))
pdf.translate(width / 2, height / 2)
pdf.rotate(rotation)
pdf.drawCentredString(0, -font_size * 0.35, text)
pdf.restoreState()
pdf.showPage()
pdf.save()
packet.seek(0)
return PdfReader(packet).pages[0]
def apply_watermark(input_pdf: Path, output_pdf: Path, text: str, opacity: float):
reader = PdfReader(str(input_pdf))
writer = PdfWriter()
permissions = (
UserAccessPermissions.EXTRACT
| UserAccessPermissions.EXTRACT_TEXT_AND_GRAPHICS
| UserAccessPermissions.FILL_FORM_FIELDS
| UserAccessPermissions.ASSEMBLE_DOC
)
for page in reader.pages:
width = float(page.mediabox.width)
height = float(page.mediabox.height)
watermark = build_watermark_page(width, height, text, opacity)
page.merge_page(watermark)
writer.add_page(page)
writer.encrypt(
user_password="",
owner_password="vintage_appMaker_owner",
permissions_flag=permissions,
)
with output_pdf.open("wb") as fh:
writer.write(fh)
def convert_pdf_to_jpg(pdf_path: Path, output_dir: Path, quality: int = 80, scale: float = 2.0):
output_dir.mkdir(parents=True, exist_ok=True)
document = fitz.open(pdf_path)
matrix = fitz.Matrix(scale, scale)
saved_files = []
try:
for index, page in enumerate(document, start=1):
pixmap = page.get_pixmap(matrix=matrix, alpha=False)
image = Image.frombytes(
"RGB", [pixmap.width, pixmap.height], pixmap.samples
)
output_path = output_dir / f"{index}.jpg"
image.save(output_path, "JPEG", quality=quality, optimize=True)
saved_files.append(output_path)
finally:
document.close()
return saved_files
def main():
import argparse
parser = argparse.ArgumentParser(description="PDF Watermark & Page to JPG Converter")
parser.add_argument("pdf_path", type=Path, help="Path to the source PDF file.")
parser.add_argument("--text", "-t", required=True, help="Watermark text.")
parser.add_argument("--opacity", "-o", type=float, default=0.2, help="Opacity of the watermark (0.0 to 1.0). Default: 0.2")
parser.add_argument("--quality", "-q", type=int, default=80, help="JPEG quality (1 to 100). Default: 80")
parser.add_argument("--output-dir", "-d", type=Path, help="Directory to save JPG images. Defaults to '<pdf_parent_dir>/images'")
args = parser.parse_args()
input_pdf = args.pdf_path.resolve()
if not input_pdf.is_file():
print(f"Error: PDF file not found at '{input_pdf}'", file=sys.stderr)
sys.exit(1)
watermarked_pdf = input_pdf.parent / f"{input_pdf.stem}_watermarked_no_print.pdf"
output_dir = args.output_dir or input_pdf.parent / "images"
print(f"1. Applying watermark '{args.text}' (opacity: {args.opacity * 100:.0f}%) to {input_pdf}...")
apply_watermark(input_pdf, watermarked_pdf, args.text, args.opacity)
print(f" Saved watermarked PDF: {watermarked_pdf}")
print(f"2. Converting watermarked PDF to JPG (quality: {args.quality}%) in {output_dir}...")
saved_files = convert_pdf_to_jpg(watermarked_pdf, output_dir, quality=args.quality)
print(f" Saved {len(saved_files)} pages to JPG.")
if __name__ == "__main__":
main()'Source code or Tip > python' 카테고리의 다른 글
| pdf, jpg 변환 유틸리티를 TUI로 관리 (0) | 2026.06.18 |
|---|---|
| [이미지 변환] jpg로 압축변환 (1) | 2026.06.11 |
| python에서 case - 딕셔너리와 함수 (0) | 2020.12.30 |
| python에서 간단한 RPC 구현 (Ubuntu <--> Windows) (0) | 2020.12.09 |
| [github] 파이썬 확장모듈(C++) 만들기 (0) | 2020.12.03 |
Comments
