from __future__ import annotations from dataclasses import dataclass, field, asdict from pathlib import Path from typing import Dict, List, Optional, Tuple # (x, y, width, height) in pixels BBox = Tuple[float, float, float, float] @dataclass class TextElement: """문서 내 텍스트 요소 하나를 표현한다.""" id: str page_index: int content: str role: str # "title", "subtitle", "body", "bullet", ... hierarchy_level: Optional[int] = None bbox: Optional[BBox] = None font_family: Optional[str] = None font_size: Optional[float] = None font_weight: Optional[str] = None color: Optional[str] = None @dataclass class LayoutElement: """레이아웃 요소 (텍스트, 이미지, 도형 등).""" id: str page_index: int element_type: str # "text", "image", "shape", ... bbox: BBox z_index: Optional[int] = None role: Optional[str] = None @dataclass class ImageElement: """이미지 요소 + 저장된 파일 경로 + 캡션.""" id: str page_index: int bbox: BBox file_path: Path caption: Optional[str] = None @dataclass class PageSnapshot: """한 페이지(또는 화면)의 스크린샷.""" page_index: int file_path: Path @dataclass class EvalUnit: """ 평가 단위 (주로 페이지/화면 단위). 원본/변환본의 텍스트, 레이아웃, 스냅샷 id를 매핑해 둔다. """ unit_id: str input_page_index: int output_page_index: int input_text_ids: List[str] = field(default_factory=list) output_text_ids: List[str] = field(default_factory=list) input_layout_ids: List[str] = field(default_factory=list) output_layout_ids: List[str] = field(default_factory=list) input_snapshot: Optional[Path] = None output_snapshot: Optional[Path] = None @dataclass class SamplePair: """ 하나의 (input_pptx, output_html) 샘플 쌍. 전처리 결과(T_i/T_o/L_i/L_o/I_i/I_o/S_i/S_o)와 평가 단위(eval_units)를 포함한다. """ sample_id: str input_pptx: Path output_html: Path T_i: List[TextElement] = field(default_factory=list) T_o: List[TextElement] = field(default_factory=list) L_i: List[LayoutElement] = field(default_factory=list) L_o: List[LayoutElement] = field(default_factory=list) I_i: List[ImageElement] = field(default_factory=list) I_o: List[ImageElement] = field(default_factory=list) S_i: List[PageSnapshot] = field(default_factory=list) S_o: List[PageSnapshot] = field(default_factory=list) eval_units: List[EvalUnit] = field(default_factory=list) def to_dict(self) -> Dict: """JSON 직렬화를 위한 dict 변환 (Path → str 처리 포함).""" def _convert_path(value): if isinstance(value, Path): return str(value) return value raw = asdict(self) def _walk(obj): if isinstance(obj, dict): return {k: _walk(v) for k, v in obj.items()} if isinstance(obj, list): return [_walk(v) for v in obj] return _convert_path(obj) return _walk(raw)