# -*- coding: utf-8 -*- """ HWP ㅽ 紐 v2.0 HTML (Role) HWP ㅽ v2.0 蹂寃쎌ы: - pyhwpx API apply_to_hwp() - CharShape/ParaShape 吏 ㅼ 諛⑹ 媛 ㅽ from dataclasses import dataclass from typing import Dict, Optional from enum import Enum class HwpStyleType(Enum): """HWP ㅽ """ PARAGRAPH = "paragraph" CHARACTER = "character" @dataclass class HwpStyle: """HWP ㅽ # ============================================================================= # 湲곕낯 ㅽ # ============================================================================= DEFAULT_STYLES: Dict[str, HwpStyle] = { # 吏 "COVER_TITLE": HwpStyle( id=100, name="吏紐", type=HwpStyleType.PARAGRAPH, font_size=32, font_bold=True, align="center", space_before=20, space_after=10, font_color="1a365d" ), "COVER_SUBTITLE": HwpStyle( id=101, name="吏遺", type=HwpStyleType.PARAGRAPH, font_size=18, font_bold=False, align="center", font_color="555555" ), "COVER_INFO": HwpStyle( id=102, name="吏 蹂", type=HwpStyleType.PARAGRAPH, font_size=12, align="center", font_color="666666" ), # 紐⑹감 "TOC_H1": HwpStyle( id=110, name="紐⑹감1", type=HwpStyleType.PARAGRAPH, font_size=12, font_bold=True, indent_left=0 ), "TOC_H2": HwpStyle( id=111, name="紐⑹감2", type=HwpStyleType.PARAGRAPH, font_size=11, indent_left=20 ), "TOC_H3": HwpStyle( id=112, name="紐⑹감3", type=HwpStyleType.PARAGRAPH, font_size=10, indent_left=40, font_color="666666" ), # 紐 痢 (媛 1~7 留ㅽ) "H1": HwpStyle( id=1, name="媛 1", type=HwpStyleType.PARAGRAPH, font_size=20, font_bold=True, align="left", space_before=30, space_after=15, font_color="1a365d" ), "H2": HwpStyle( id=2, name="媛 2", type=HwpStyleType.PARAGRAPH, font_size=16, font_bold=True, align="left", space_before=20, space_after=10, font_color="2c5282" ), "H3": HwpStyle( id=3, name="媛 3", type=HwpStyleType.PARAGRAPH, font_size=14, font_bold=True, align="left", space_before=15, space_after=8, font_color="2b6cb0" ), "H4": HwpStyle( id=4, name="媛 4", type=HwpStyleType.PARAGRAPH, font_size=12, font_bold=True, align="left", space_before=10, space_after=5, indent_left=10 ), "H5": HwpStyle( id=5, name="媛 5", type=HwpStyleType.PARAGRAPH, font_size=11, font_bold=True, align="left", space_before=8, space_after=4, indent_left=20 ), "H6": HwpStyle( id=6, name="媛 6", type=HwpStyleType.PARAGRAPH, font_size=11, font_bold=False, align="left", indent_left=30 ), "H7": HwpStyle( id=7, name="媛 7", type=HwpStyleType.PARAGRAPH, font_size=10.5, font_bold=False, align="left", indent_left=40 ), # 蹂몃Ц "BODY": HwpStyle( id=20, name="諛湲", type=HwpStyleType.PARAGRAPH, font_size=11, align="justify", line_spacing=180, indent_first=10 ), "LIST_ITEM": HwpStyle( id=8, name="媛 8", type=HwpStyleType.PARAGRAPH, font_size=11, align="left", indent_left=15, line_spacing=160 ), "HIGHLIGHT_BOX": HwpStyle( id=21, name="媛議ꕕ", type=HwpStyleType.PARAGRAPH, font_size=10.5, align="left", bg_color="f7fafc", indent_left=10, indent_first=0 ), # "TABLE": HwpStyle( id=30, name=" ", type=HwpStyleType.PARAGRAPH, font_size=10, align="center" ), "TH": HwpStyle( id=11, name="紐", type=HwpStyleType.PARAGRAPH, font_size=10, font_bold=True, align="center", bg_color="e2e8f0" ), "TD": HwpStyle( id=31, name=" 댁 ", type=HwpStyleType.PARAGRAPH, font_size=10, align="left" ), "TABLE_CAPTION": HwpStyle( id=19, name=" 罹 몃┝ "FIGURE": HwpStyle( id=32, name="洹몃┝", type=HwpStyleType.PARAGRAPH, font_size=10, align="center" ), "FIGURE_CAPTION": HwpStyle( id=18, name="洹몃┝罹 고 "UNKNOWN": HwpStyle( id=0, name="諛湲", type=HwpStyleType.PARAGRAPH, font_size=10, align="left" ), } # 媛 踰 留㼼 (StyleShortcut ) ROLE_TO_OUTLINE_NUM = { "H1": 1, "H2": 2, "H3": 3, "H4": 4, "H5": 5, "H6": 6, "H7": 7, "LIST_ITEM": 8, "BODY": 0, # 諛湲 "COVER_TITLE": 0, "COVER_SUBTITLE": 0, "COVER_INFO": 0, } # HWP ㅽ 留㼼 ROLE_TO_STYLE_NAME = { "H1": "媛 1", "H2": "媛 2", "H3": "媛 3", "H4": "媛 4", "H5": "媛 5", "H6": "媛 6", "H7": "媛 7", "LIST_ITEM": "媛 8", "BODY": "諛湲", "COVER_TITLE": "吏紐", "COVER_SUBTITLE": "吏遺", "TH": "紐", "TD": " 댁 ", "TABLE_CAPTION": " 罹 몃┝罹 湲", } class HwpStyleMapper: """HTML HWP ㅽ """ def __init__(self, custom_styles: Optional[Dict[str, HwpStyle]] = None): self.styles = DEFAULT_STYLES.copy() if custom_styles: self.styles.update(custom_styles) def get_style(self, role: str) -> HwpStyle: return self.styles.get(role, self.styles["UNKNOWN"]) def get_style_id(self, role: str) -> int: return self.get_style(role).id def get_all_styles(self) -> Dict[str, HwpStyle]: return self.styles class HwpStyGenerator: """ HTML ㅽ HWP ㅽ⑷린 pyhwpx API瑜 ъ : 1. 蹂 ㅽ蹂 2. ㅽ 쎌 CharShape/ParaShape 吏 3. 媛 ㅽ 留㼼 諛 異異 ㅽ 곗 """ for role, style_dict in html_styles.items(): if role in DEFAULT_STYLES: base = DEFAULT_STYLES[role] # color 泥 - # 嫄 color = style_dict.get('color', base.font_color) if isinstance(color, str): color = color.lstrip('#') self.styles[role] = HwpStyle( id=base.id, name=base.name, type=base.type, font_size=style_dict.get('font_size', base.font_size), font_bold=style_dict.get('bold', base.font_bold), font_color=color, align=style_dict.get('align', base.align), line_spacing=style_dict.get('line_spacing', base.line_spacing), space_before=style_dict.get('space_before', base.space_before), space_after=style_dict.get('space_after', base.space_after), indent_left=style_dict.get('indent_left', base.indent_left), indent_first=style_dict.get('indent_first', base.indent_first), bg_color=style_dict.get('bg_color', base.bg_color), ) else: # 湲곕낯 ㅽ ъ self.styles[role] = DEFAULT_STYLES.get('UNKNOWN') # 대 湲곕낯媛쇰 梨 for role in DEFAULT_STYLES: if role not in self.styles: self.styles[role] = DEFAULT_STYLES[role] def apply_to_hwp(self, hwp) -> Dict[str, HwpStyle]: """ HwpStyle 留㼼 諛 ㅽ 鍮 깊 (API 臾몄 ) # for role, style in self.styles.items(): # self._create_or_update_style(hwp, role, style) if not self.styles: self.styles = DEFAULT_STYLES.copy() print(f" ㅽ 猷: {len(self.styles)}媛") return self.styles def _create_or_update_style(self, hwp, role: str, style: HwpStyle): """HWP ㅽ """ try: # 1. ㅽ 紐⑤ hwp.HAction.GetDefault("ModifyStyle", hwp.HParameterSet.HStyle.HSet) hwp.HParameterSet.HStyle.StyleName = style.name # 2. 湲 ⑥ color_hex = style.font_color.lstrip('#') if len(color_hex) == 6: r, g, b = int(color_hex[0:2], 16), int(color_hex[2:4], 16), int(color_hex[4:6], 16) text_color = hwp.RGBColor(r, g, b) else: text_color = hwp.RGBColor(0, 0, 0) hwp.HParameterSet.HStyle.CharShape.Height = hwp.PointToHwpUnit(style.font_size) hwp.HParameterSet.HStyle.CharShape.Bold = style.font_bold hwp.HParameterSet.HStyle.CharShape.TextColor = text_color # 3. 臾몃 紐⑥ align_map = {'left': 0, 'center': 1, 'right': 2, 'justify': 3} hwp.HParameterSet.HStyle.ParaShape.Align = align_map.get(style.align, 3) hwp.HParameterSet.HStyle.ParaShape.LineSpacing = int(style.line_spacing) hwp.HParameterSet.HStyle.ParaShape.SpaceBeforePara = hwp.PointToHwpUnit(style.space_before) hwp.HParameterSet.HStyle.ParaShape.SpaceAfterPara = hwp.PointToHwpUnit(style.space_after) # 4. ㅽ hwp.HAction.Execute("ModifyStyle", hwp.HParameterSet.HStyle.HSet) print(f" ㅽ ") except Exception as e: print(f" [寃쎄 ] ㅽ ㅽ : {e}") def get_style(self, role: str) -> HwpStyle: """대 ㅽ ⑥ """ style = self.get_style(role) try: # RGB 蹂 ⑥ ㅼ hwp.HAction.GetDefault("CharShape", hwp.HParameterSet.HCharShape.HSet) hwp.HParameterSet.HCharShape.Height = hwp.PointToHwpUnit(style.font_size) hwp.HParameterSet.HCharShape.Bold = style.font_bold hwp.HParameterSet.HCharShape.TextColor = text_color hwp.HAction.Execute("CharShape", hwp.HParameterSet.HCharShape.HSet) except Exception as e: print(f" [寃쎄 ] 湲 ⑥ ㅽ ({role}): {e}") def apply_para_shape(self, hwp, role: str): """ 臾몃 紐⑥ """ style = self.get_style(role) try: # align_actions = { 'left': "ParagraphShapeAlignLeft", 'center': "ParagraphShapeAlignCenter", 'right': "ParagraphShapeAlignRight", 'justify': "ParagraphShapeAlignJustify" } if style.align in align_actions: hwp.HAction.Run(align_actions[style.align]) # 臾몃 紐⑥ ㅼ hwp.HAction.GetDefault("ParagraphShape", hwp.HParameterSet.HParaShape.HSet) p = hwp.HParameterSet.HParaShape p.LineSpaceType = 0 # 쇱 쇳 p.LineSpacing = int(style.line_spacing) p.LeftMargin = hwp.MiliToHwpUnit(style.indent_left) p.IndentMargin = hwp.MiliToHwpUnit(style.indent_first) p.SpaceBeforePara = hwp.PointToHwpUnit(style.space_before) p.SpaceAfterPara = hwp.PointToHwpUnit(style.space_after) hwp.HAction.Execute("ParagraphShape", p.HSet) except Exception as e: print(f" [寃쎄 ] 臾몃 紐⑥ ㅽ ({role}): {e}") def apply_style(self, hwp, role: str): """ 泥 ㅽ (湲몃 )""" self.apply_char_shape(hwp, role) self.apply_para_shape(hwp, role) def export_sty(self, hwp, output_path: str) -> bool: """ㅽ대낫닿린 ( 誘몄由] .sty 대낫닿린 誘몄 嫄 대━ # ============================================================================= import re NUMBERING_PATTERNS = { 'H1': re.compile(r'^(\d+)\.\s*'), # "1. " 嫄 'H2': re.compile(r'^(\d+)\.(\d+)\s*'), # "1.1 " 嫄 'H3': re.compile(r'^(\d+)\.(\d+)\.(\d+)\s*'), # "1.1.1 " 嫄 'H4': re.compile(r'^[媛- . " 嫄 'H5': re.compile(r'^(\d+)\)\s*'), # "1) " 嫄 'H6': re.compile(r'^\((\d+)\)\s*'), # "(1) " 嫄 'H7': re.compile(r'^[△™bㅲβ╈㎮ⓥ]\s*'), # " " 嫄 'LIST_ITEM': re.compile(r'^[\-]\s*'), # " " 嫄 } def strip_numbering(text: str, role: str) -> str: """ 곕 ㅽ /湲고 嫄 HWP 媛 湲곕μ 깊濡 以蹂 諛⑹ """ if not text: return text pattern = NUMBERING_PATTERNS.get(role) if pattern: return pattern.sub('', text).strip() return text.strip() if __name__ == "__main__": # ㅽ print("=== ㅽ ㅽ ===") gen = HwpStyGenerator() # HTML ㅽ裕щ size={style.font_size}pt, bold={style.font_bold}, color=#{style.font_color}")