# -*- coding: utf-8 -*- """ step_format.py — 형식만 변경 모드 파이프라인 v4 문서 유형 자동 감지 후 전용 핸들러로 분기: fmt_gpd_report.py ← GPD 보고서형 (헤더 2개↑ + 공백 기반 들여쓰기) fmt_outline.py ← 목차/개요형 (1./a./Ⅰ. 번호 패턴 40%↑) fmt_table_report.py ← 표 중심형 (표가 페이지 면적 60%↑) fmt_generic.py ← 범용 fallback (위 유형 해당 없을 때, 페이지별 자동 선택) 판별 기준: 1. 헤더 패턴 2개 이상 → GPD 보고서형 2. 표 면적 비율 60% 이상 → 표 중심형 3. 번호/기호 패턴 비율 40% 이상 → 목차/개요형 4. 해당 없음 → 범용 fallback """ import re import os import sys from pathlib import Path import fitz from fmt_base import ( log, detect_header_footer_patterns, build_html, extract_tables_from_page, ZW_PATTERN ) import fmt_gpd_report import fmt_outline import fmt_table_report import fmt_generic # ============================================================ # 문서 유형 판별 # ============================================================ _NUM_P = re.compile(r'^\d+[\.\uff0e\)]\s*[^\d\.\s]') _ALPHA_P = re.compile(r'^[a-z][\.\uff0e\)]\s*\S') _ROMAN_P = re.compile(r'^[ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅰⅱⅲⅳⅴ]+[\.\s]') _DASH_P = re.compile(r'^-\s*[^\s\(]') def _doc_pattern_ratio(doc, header_patterns, footer_patterns): """전 페이지 평균 번호/기호 패턴 비율""" total_hits, total_lines = 0, 0 for page in doc: ph = page.rect.height tb = ph * 0.85 # 표 bbox 수집 table_rects = [] try: for t in page.find_tables().tables: table_rects.append(t.bbox) except: pass def in_table(x, y): for tx0,ty0,tx1,ty1 in table_rects: if tx0-5<=x<=tx1+5 and ty0-5<=y<=ty1+5: return True return False for b in page.get_text("dict")["blocks"]: if b.get("type") != 0: continue for line in b.get("lines",[]): x, parts = None, [] for s in line.get("spans",[]): t = ZW_PATTERN.sub('', s.get("text","")) if t.strip() and x is None: x = s["bbox"][0] parts.append(t) text = ''.join(parts).strip() y0 = line['bbox'][1] if text and y0= 2: return 'gpd_report' if table_ratio >= 0.6: return 'table_report' if pattern_ratio >= 0.4: return 'outline' return 'generic' # ============================================================ # 메인 진입점 # ============================================================ def run_format_only(session_id: str, input_dir: str) -> dict: gen_dir = Path(f'/tmp/{session_id}/format_out') gen_dir.mkdir(parents=True, exist_ok=True) pdf_files = list(Path(input_dir).rglob('*.pdf')) + \ list(Path(input_dir).rglob('*.PDF')) if not pdf_files: return {'success': False, 'error': '처리할 PDF 파일이 없습니다.'} pdf_path = pdf_files[0] log(f"PDF: {pdf_path.name}") doc = fitz.open(str(pdf_path)) log(f"총 {len(doc)}페이지") header_patterns, footer_patterns, meta = detect_header_footer_patterns(doc) log(f"메타: {meta}") doc_type = detect_doc_type(doc, header_patterns, footer_patterns) log(f"문서 유형: {doc_type}") handler_map = { 'gpd_report': fmt_gpd_report, 'outline': fmt_outline, 'table_report': fmt_table_report, 'generic': fmt_generic, } handler = handler_map[doc_type] pages_html = handler.process(doc, header_patterns, footer_patterns, meta) if not pages_html: return {'success': False, 'error': '생성된 페이지가 없습니다.'} final_html = build_html(pages_html) out_path = gen_dir / 'result.html' out_path.write_text(final_html, encoding='utf-8') log(f"완료: {len(pages_html)}페이지 → {doc_type}") return { 'success': True, 'html': final_html, 'page_count': len(pages_html), 'doc_type': doc_type, 'session_id': session_id }