150 lines
5.1 KiB
Python
150 lines
5.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
문서 템플릿 분석기 v5.1 (오케스트레이터)
|
|
|
|
역할: tools/ 모듈을 조합하여 HWPX → 템플릿 정보 추출
|
|
- 직접 파싱 로직 없음 (모두 tools에 위임)
|
|
- 디폴트값 생성 없음 (tools가 None 반환하면 결과에서 제외)
|
|
- 사용자 추가 사항(config.json) → 템플릿에도 반영
|
|
|
|
구조:
|
|
tools/
|
|
page_setup.py §7 용지/여백
|
|
font.py §3 글꼴
|
|
char_style.py §4 글자 모양
|
|
para_style.py §5 문단 모양
|
|
border_fill.py §2 테두리/배경
|
|
table.py §6 표
|
|
header_footer.py §8 머리말/꼬리말
|
|
section.py §9 구역 정의
|
|
style_def.py 스타일 정의
|
|
numbering.py 번호매기기/글머리표
|
|
image.py 이미지
|
|
"""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
from .tools import (
|
|
page_setup,
|
|
font,
|
|
char_style,
|
|
para_style,
|
|
border_fill,
|
|
table,
|
|
header_footer,
|
|
section,
|
|
style_def,
|
|
numbering,
|
|
image,
|
|
content_order,
|
|
)
|
|
|
|
|
|
class DocTemplateAnalyzer:
|
|
"""HWPX → 템플릿 추출 오케스트레이터"""
|
|
|
|
# ================================================================
|
|
# Phase 1: 추출 (모든 tools 호출)
|
|
# ================================================================
|
|
|
|
def analyze(self, parsed: dict) -> dict:
|
|
"""HWPX parsed 결과에서 템플릿 구조 추출.
|
|
|
|
Args:
|
|
parsed: processor.py가 HWPX를 파싱한 결과 dict.
|
|
raw_xml, section_xml, header_xml, footer_xml,
|
|
tables, paragraphs 등 포함.
|
|
|
|
Returns:
|
|
추출된 항목만 포함하는 dict (None인 항목은 제외).
|
|
"""
|
|
raw_xml = parsed.get("raw_xml", {})
|
|
|
|
extractors = {
|
|
"page": lambda: page_setup.extract(raw_xml, parsed),
|
|
"fonts": lambda: font.extract(raw_xml, parsed),
|
|
"char_styles": lambda: char_style.extract(raw_xml, parsed),
|
|
"para_styles": lambda: para_style.extract(raw_xml, parsed),
|
|
"border_fills": lambda: border_fill.extract(raw_xml, parsed),
|
|
"tables": lambda: table.extract(raw_xml, parsed),
|
|
"header": lambda: header_footer.extract_header(raw_xml, parsed),
|
|
"footer": lambda: header_footer.extract_footer(raw_xml, parsed),
|
|
"section": lambda: section.extract(raw_xml, parsed),
|
|
"styles": lambda: style_def.extract(raw_xml, parsed),
|
|
"numbering": lambda: numbering.extract(raw_xml, parsed),
|
|
"images": lambda: image.extract(raw_xml, parsed),
|
|
"content_order":lambda: content_order.extract(raw_xml, parsed),
|
|
}
|
|
|
|
result = {}
|
|
for key, extractor in extractors.items():
|
|
try:
|
|
value = extractor()
|
|
if value is not None:
|
|
result[key] = value
|
|
except Exception as e:
|
|
# 개별 tool 실패 시 로그만, 전체 중단 안 함
|
|
result.setdefault("_errors", []).append(
|
|
f"{key}: {type(e).__name__}: {e}"
|
|
)
|
|
|
|
return result
|
|
|
|
|
|
# ================================================================
|
|
# Phase 2: 사용자 추가 사항 병합
|
|
# ================================================================
|
|
|
|
def merge_user_config(self, template_info: dict,
|
|
config: dict) -> dict:
|
|
"""config.json의 사용자 요구사항을 template_info에 병합.
|
|
|
|
사용자가 문서 유형 추가 시 지정한 커스텀 사항을 반영:
|
|
- 색상 오버라이드
|
|
- 글꼴 오버라이드
|
|
- 제목 크기 오버라이드
|
|
- 기타 레이아웃 커스텀
|
|
|
|
이 병합 결과는 style.json에 저장되고,
|
|
이후 template.html 생성 시에도 반영됨.
|
|
|
|
Args:
|
|
template_info: analyze()의 결과
|
|
config: config.json 내용
|
|
|
|
Returns:
|
|
병합된 template_info (원본 수정됨)
|
|
"""
|
|
user_overrides = config.get("user_overrides", {})
|
|
if not user_overrides:
|
|
return template_info
|
|
|
|
# 모든 사용자 오버라이드를 template_info에 기록
|
|
template_info["user_overrides"] = user_overrides
|
|
|
|
return template_info
|
|
|
|
# ================================================================
|
|
# Phase 3: template_info → style.json 저장
|
|
# ================================================================
|
|
|
|
def save_style(self, template_info: dict,
|
|
save_path: Path) -> Path:
|
|
"""template_info를 style.json으로 저장.
|
|
|
|
Args:
|
|
template_info: analyze() + merge_user_config() 결과
|
|
save_path: 저장 경로 (예: templates/user/{doc_type}/style.json)
|
|
|
|
Returns:
|
|
저장된 파일 경로
|
|
"""
|
|
save_path = Path(save_path)
|
|
save_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
with open(save_path, 'w', encoding='utf-8') as f:
|
|
json.dump(template_info, f, ensure_ascii=False, indent=2)
|
|
|
|
return save_path |