| name | pdf-translate |
| description | PDF 문서를 페이지 단위로 번역하는 스킬. 'PDF 번역', 'pdf translate', 'PDF를 한국어로', '중국어 PDF', '영어 PDF 번역', 'PDF 한글 변환' 등의 키워드가 나오면 이 스킬을 사용할 것. PyMuPDF 기반으로 텍스트 블록과 이미지 블록을 분리 추출하고, Claude가 직접 번역한 후 원본 레이아웃을 유지하며 재구성한다. |
PDF 번역 스킬
개요
PDF 문서의 텍스트를 페이지 단위로 번역하고, 이미지와 레이아웃을 보존하여 새 PDF를 생성한다.
PyMuPDF(fitz) 라이브러리를 사용하며, 번역은 Claude가 직접 수행한다.
설치 요구사항
pip install pymupdf
- 한글 폰트:
C:/Windows/Fonts/malgun.ttf (맑은 고딕) — Windows 기본 내장
워크플로우
1단계: 프로젝트 초기화
사용자가 PDF 파일 경로를 제공하면:
{작업_디렉토리}/
├── source/ # 원본 PDF
├── output_pages/ # 페이지별 번역 PDF
├── extracted_images/ # 추출된 이미지 (선택)
├── translation_dict.json # 번역 딕셔너리
├── build_page.py # 빌드+검증 스크립트
└── issues_and_fixes/ # 문제 기록
2단계: 텍스트 추출
원본 PDF에서 모든 고유 텍스트를 추출한다:
import fitz, json, re
doc = fitz.open('source/원본.pdf')
all_texts = set()
for pg in range(len(doc)):
page = doc[pg]
for block in page.get_text('dict')['blocks']:
if block['type'] == 0:
for line in block['lines']:
for span in line['spans']:
txt = span['text'].strip()
if txt and not txt.startswith('....'):
all_texts.add(txt)
need_translate = [t for t in sorted(all_texts)
if not re.match(r'^[\d\s\.\-\,\:\/\(\)a-zA-Z]+$', t)]
3단계: 번역 딕셔너리 생성
Claude가 추출된 텍스트를 한국어로 번역하여 JSON 딕셔너리를 생성한다:
{
"원문 텍스트1": "번역된 텍스트1",
"원문 텍스트2": "번역된 텍스트2"
}
주의사항:
- 특수 따옴표(
" U+201C, " U+201D)가 포함된 텍스트는 좌우 교환 변형도 추가
- 한자 서수(一二三四)는 아라비아 숫자(1234)로 변환
- JSON 저장 시
ensure_ascii=False로 유니코드 유지
4단계: 페이지별 빌드+검증 루프
scripts/build_page.py를 사용하여 페이지별로:
- 빌드: 원본에서 텍스트/이미지 블록 추출 → 번역 적용 → 새 PDF 생성
- 검증: 4가지 항목 자동 검증
- 재시도: 실패 시 최대 3회 재생성
python scripts/build_page.py all
python scripts/build_page.py 7
python scripts/build_page.py verify
핵심 기술
같은 줄 감지 (y범위 overlap)
텍스트와 이미지가 같은 줄에 있는지 감지:
def same_line(a, b):
a_top, a_bot = a['bbox'][1], a['bbox'][3]
b_top, b_bot = b['bbox'][1], b['bbox'][3]
overlap = min(a_bot, b_bot) - max(a_top, b_top)
min_height = min(a_bot - a_top, b_bot - b_top)
return overlap > min_height * 0.3
텍스트 span 합치기
같은 줄의 연속 텍스트 span을 하나로 합친다. x좌표 간격이 80pt 이상이면 별도 열로 간주하여 합치지 않는다.
인라인 이미지+텍스트 x좌표 재배치
한글이 원문보다 길어서 뒤의 이미지/텍스트를 침범하는 문제를 해결:
cursor_x = line[0]['bbox'][0]
for it in line:
if it['type'] == 'text':
w = font.text_length(it['translated'], fontsize=it['size'])
it['new_x0'] = cursor_x
cursor_x += w + GAP
elif it['type'] == 'image':
it['new_x0'] = cursor_x
cursor_x += img_w + GAP
텍스트 넘침 처리
번역된 텍스트가 페이지 오른쪽 끝을 넘으면 insert_textbox로 자동 줄바꿈:
if text_w > avail:
line_h = size * 1.4
needed = int(text_w / avail) + 1
box_h = line_h * needed + 2
page.insert_textbox(rect, text, fontname='malgun', fontsize=size)
y_offset += box_h - orig_h
검증 항목 (4가지)
| # | 항목 | 기준 | FAIL 조건 |
|---|
| 1 | 텍스트 겹침 | bbox 교차 검사 | x>5pt + y>3pt 겹침 |
| 2 | 미번역 잔존 | CJK 한자 범위 | [\u4e00-\u9fff] 1자라도 남아있으면 |
| 3 | 이미지 수 | 원본 vs 생성 비교 | 불일치 |
| 4 | 가독성 | 텍스트 비어있지 않은지 | 빈 문자열 |
알려진 이슈와 해결
Issue 1: PDF 폰트 인코딩 깨짐
- 원인: CID 폰트 + WinAnsiEncoding 혼재
- 확인:
page.get_text('text')로 중국어가 깨지면 PDF 파일 손상 의심
- 해결: 완전한 원본 PDF 확보 (%%EOF 마커 확인)
Issue 2: 같은 줄 텍스트+이미지 겹침
- 원인: 한글이 중국어보다 1.5~2배 길어서 bbox 넘침
- 해결: same_line 감지 + x좌표 재배치
Issue 3: 특수 따옴표 매칭 실패
- 원인: U+201C와 U+201D가 PDF 추출 시 뒤바뀜
- 해결: 번역 딕셔너리에 좌우 교환 변형 키 추가
Issue 4: 다단 레이아웃 잘못 합쳐짐
- 원인: 왼쪽 열과 오른쪽 열이 같은 줄로 인식
- 해결: x좌표 간격 80pt 이상이면 별도 열로 분리
폰트 설정
기본 폰트: 맑은 고딕 (C:/Windows/Fonts/malgun.ttf)
다른 OS에서 사용 시:
- macOS:
/System/Library/Fonts/AppleSDGothicNeo.ttc
- Linux:
/usr/share/fonts/truetype/nanum/NanumGothic.ttf
build_page.py의 FONT_PATH 변수를 수정한다.