"""PDF에서 해안조사 사진을 추출하여 scat-photos 폴더에 저장. Type A (해안사전평가정보집): 1페이지 = 1구간 → 가장 큰 RGB 사진 Type B (방제정보집): 1페이지 = 2구간 → RGB 사진 2장, 순서대로 매칭 저장 네이밍: {sect_cd}-1.png (ScatPopup에서 참조하는 형식) """ from __future__ import annotations import re from pathlib import Path from typing import List, Tuple import fitz # PyMuPDF from pdf_parser import is_data_page, _CODE_RE from pdf_parser_b import is_data_page_b import config def _get_page_photos( doc: fitz.Document, page: fitz.Page, ) -> List[fitz.Pixmap]: """페이지에서 RGB 사진만 추출 (배경 제외). 크기 내림차순.""" photos: List[Tuple[fitz.Pixmap, int]] = [] for img_info in page.get_images(full=True): xref = img_info[0] pix = fitz.Pixmap(doc, xref) # CMYK → RGB if pix.n > 4: pix = fitz.Pixmap(fitz.csRGB, pix) elif pix.n == 4: pix = fitz.Pixmap(fitz.csRGB, pix) # 흑백(n=1) 또는 배경(2000px 이상) 스킵 if pix.n < 3 or pix.width >= 2000: continue photos.append((pix, pix.width * pix.height)) photos.sort(key=lambda x: x[1], reverse=True) return photos def _extract_codes_type_a(page: fitz.Page) -> List[str]: """Type A 페이지에서 sect_cd 추출.""" text = page.get_text('text') return _CODE_RE.findall(text) def _extract_codes_type_b(page: fitz.Page) -> List[str]: """Type B 페이지에서 sect_cd 추출 (순서 유지).""" text = page.get_text('text') codes = re.findall(r'([A-Z]{4,}-\d+(?:-[A-Z]){3,})', text) seen = set() unique = [] for c in codes: if c not in seen: seen.add(c) unique.append(c) return unique def _save_pixmap(pix: fitz.Pixmap, output_path: Path): """Pixmap을 PNG로 저장.""" if pix.alpha: pix = fitz.Pixmap(fitz.csRGB, pix) pix.save(str(output_path)) def extract_images_from_pdf( pdf_path: str | Path, output_dir: str | Path | None = None, pdf_type: str = 'A', ) -> int: """PDF에서 데이터 페이지별 대표 사진을 추출. Args: pdf_path: PDF 파일 경로 output_dir: 이미지 저장 경로 (기본: config.SCAT_PHOTOS_DIR) pdf_type: 'A' 또는 'B' Returns: 추출된 이미지 수 """ pdf_path = Path(pdf_path) out_dir = Path(output_dir) if output_dir else config.SCAT_PHOTOS_DIR out_dir.mkdir(parents=True, exist_ok=True) doc = fitz.open(str(pdf_path)) saved = 0 for i in range(doc.page_count): page = doc[i] if pdf_type == 'A': if not is_data_page(page): continue codes = _extract_codes_type_a(page) photos = _get_page_photos(doc, page) if not codes or not photos: continue # 가장 작은 RGB 이미지 = 실제 현장 사진 pix = photos[-1][0] out_path = out_dir / f'{codes[0]}-1.png' _save_pixmap(pix, out_path) saved += 1 elif pdf_type == 'B': if not is_data_page_b(page): continue codes = _extract_codes_type_b(page) photos = _get_page_photos(doc, page) if not codes or not photos: continue # Type B: 사진 크기 동일, 순서대로 매칭 for idx, code in enumerate(codes): if idx < len(photos): pix = photos[idx][0] out_path = out_dir / f'{code}-1.png' _save_pixmap(pix, out_path) saved += 1 doc.close() return saved