Upload domain/hwpx/hwpx_utils.py
This commit is contained in:
224
03.Code/geulbeot_업로드용/domain/hwpx/hwpx_utils.py
Normal file
224
03.Code/geulbeot_업로드용/domain/hwpx/hwpx_utils.py
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
HWP/HWPX ↔ HTML/CSS 변환 유틸리티
|
||||||
|
|
||||||
|
hwpx_domain_guide.md의 매핑 테이블을 코드화.
|
||||||
|
하드코딩 없이 이 모듈의 함수/상수를 참조하여 정확한 변환을 수행한다.
|
||||||
|
|
||||||
|
참조: 한글과컴퓨터 "글 문서 파일 구조 5.0" (revision 1.3, 2018-11-08)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================================
|
||||||
|
# §1. 단위 변환
|
||||||
|
# ================================================================
|
||||||
|
|
||||||
|
def hwpunit_to_mm(hwpunit):
|
||||||
|
"""HWPUNIT → mm (1 HWPUNIT = 1/720 inch)"""
|
||||||
|
return hwpunit / 7200 * 25.4
|
||||||
|
|
||||||
|
def hwpunit_to_pt(hwpunit):
|
||||||
|
"""HWPUNIT → pt"""
|
||||||
|
return hwpunit / 7200 * 72
|
||||||
|
|
||||||
|
def hwpunit_to_px(hwpunit, dpi=96):
|
||||||
|
"""HWPUNIT → px (기본 96dpi)"""
|
||||||
|
return hwpunit / 7200 * dpi
|
||||||
|
|
||||||
|
def mm_to_hwpunit(mm):
|
||||||
|
"""mm → HWPUNIT"""
|
||||||
|
return mm / 25.4 * 7200
|
||||||
|
|
||||||
|
def pt_to_hwpunit(pt):
|
||||||
|
"""pt → HWPUNIT"""
|
||||||
|
return pt / 72 * 7200
|
||||||
|
|
||||||
|
def px_to_hwpunit(px, dpi=96):
|
||||||
|
"""px → HWPUNIT"""
|
||||||
|
return px / dpi * 7200
|
||||||
|
|
||||||
|
def charsize_to_pt(hwp_size):
|
||||||
|
"""HWP 글자 크기 → pt (100 스케일 제거)
|
||||||
|
예: 1000 → 10pt, 2400 → 24pt
|
||||||
|
"""
|
||||||
|
return hwp_size / 100
|
||||||
|
|
||||||
|
def pt_to_charsize(pt):
|
||||||
|
"""pt → HWP 글자 크기
|
||||||
|
예: 10pt → 1000, 24pt → 2400
|
||||||
|
"""
|
||||||
|
return int(pt * 100)
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================================
|
||||||
|
# §1.3 색상 변환
|
||||||
|
# ================================================================
|
||||||
|
|
||||||
|
def colorref_to_css(colorref):
|
||||||
|
"""HWP COLORREF (0x00BBGGRR) → CSS #RRGGBB
|
||||||
|
|
||||||
|
HWP는 리틀 엔디안 BGR 순서:
|
||||||
|
- 0x00FF0000 → B=255,G=0,R=0 → #0000ff (파랑)
|
||||||
|
- 0x000000FF → B=0,G=0,R=255 → #ff0000 (빨강)
|
||||||
|
"""
|
||||||
|
r = colorref & 0xFF
|
||||||
|
g = (colorref >> 8) & 0xFF
|
||||||
|
b = (colorref >> 16) & 0xFF
|
||||||
|
return f'#{r:02x}{g:02x}{b:02x}'
|
||||||
|
|
||||||
|
def css_to_colorref(css_color):
|
||||||
|
"""CSS #RRGGBB → HWP COLORREF (0x00BBGGRR)"""
|
||||||
|
css_color = css_color.lstrip('#')
|
||||||
|
if len(css_color) == 3: # 단축형 #rgb → #rrggbb
|
||||||
|
css_color = ''.join(c * 2 for c in css_color)
|
||||||
|
r = int(css_color[0:2], 16)
|
||||||
|
g = int(css_color[2:4], 16)
|
||||||
|
b = int(css_color[4:6], 16)
|
||||||
|
return (b << 16) | (g << 8) | r
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================================
|
||||||
|
# §2. 테두리/배경 (BorderFill) 매핑
|
||||||
|
# ================================================================
|
||||||
|
|
||||||
|
# §2.1 테두리선 종류: HWPX type → CSS border-style
|
||||||
|
BORDER_TYPE_TO_CSS = {
|
||||||
|
'NONE': 'none',
|
||||||
|
'SOLID': 'solid',
|
||||||
|
'DASH': 'dashed',
|
||||||
|
'DOT': 'dotted',
|
||||||
|
'DASH_DOT': 'dashed', # CSS 근사
|
||||||
|
'DASH_DOT_DOT': 'dashed', # CSS 근사
|
||||||
|
'LONG_DASH': 'dashed',
|
||||||
|
'CIRCLE': 'dotted', # CSS 근사 (큰 동그라미 → dot)
|
||||||
|
'DOUBLE': 'double',
|
||||||
|
'THIN_THICK': 'double', # CSS 근사
|
||||||
|
'THICK_THIN': 'double', # CSS 근사
|
||||||
|
'THIN_THICK_THIN':'double', # CSS 근사
|
||||||
|
'WAVE': 'solid', # CSS 근사 (물결 → 실선)
|
||||||
|
'DOUBLE_WAVE': 'double', # CSS 근사
|
||||||
|
'THICK_3D': 'ridge',
|
||||||
|
'THICK_3D_REV': 'groove',
|
||||||
|
'3D': 'outset',
|
||||||
|
'3D_REV': 'inset',
|
||||||
|
}
|
||||||
|
|
||||||
|
# CSS border-style → HWPX type (역방향)
|
||||||
|
CSS_TO_BORDER_TYPE = {
|
||||||
|
'none': 'NONE',
|
||||||
|
'solid': 'SOLID',
|
||||||
|
'dashed': 'DASH',
|
||||||
|
'dotted': 'DOT',
|
||||||
|
'double': 'DOUBLE',
|
||||||
|
'ridge': 'THICK_3D',
|
||||||
|
'groove': 'THICK_3D_REV',
|
||||||
|
'outset': '3D',
|
||||||
|
'inset': '3D_REV',
|
||||||
|
}
|
||||||
|
|
||||||
|
# §2.2 HWP 바이너리 테두리 굵기 값 → 실제 mm
|
||||||
|
BORDER_WIDTH_HWP_TO_MM = {
|
||||||
|
0: 0.1, 1: 0.12, 2: 0.15, 3: 0.2, 4: 0.25, 5: 0.3,
|
||||||
|
6: 0.4, 7: 0.5, 8: 0.6, 9: 0.7, 10: 1.0, 11: 1.5,
|
||||||
|
12: 2.0, 13: 3.0, 14: 4.0, 15: 5.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================================
|
||||||
|
# §5. 정렬 매핑
|
||||||
|
# ================================================================
|
||||||
|
|
||||||
|
# §5.1 HWPX align → CSS text-align
|
||||||
|
ALIGN_TO_CSS = {
|
||||||
|
'JUSTIFY': 'justify',
|
||||||
|
'LEFT': 'left',
|
||||||
|
'RIGHT': 'right',
|
||||||
|
'CENTER': 'center',
|
||||||
|
'DISTRIBUTE': 'justify', # CSS 근사
|
||||||
|
'DISTRIBUTE_SPACE': 'justify', # CSS 근사
|
||||||
|
}
|
||||||
|
|
||||||
|
# CSS text-align → HWPX align (역방향)
|
||||||
|
CSS_TO_ALIGN = {
|
||||||
|
'justify': 'JUSTIFY',
|
||||||
|
'left': 'LEFT',
|
||||||
|
'right': 'RIGHT',
|
||||||
|
'center': 'CENTER',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================================
|
||||||
|
# §5.2 줄 간격 매핑
|
||||||
|
# ================================================================
|
||||||
|
|
||||||
|
LINE_SPACING_TYPE_TO_CSS = {
|
||||||
|
'PERCENT': 'percent', # 글자에 따라 (%) → line-height: 160%
|
||||||
|
'FIXED': 'fixed', # 고정값 → line-height: 24pt
|
||||||
|
'BETWEEN_LINES': 'between', # 여백만 지정
|
||||||
|
'AT_LEAST': 'at_least', # 최소
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ================================================================
|
||||||
|
# 종합 변환 함수
|
||||||
|
# ================================================================
|
||||||
|
|
||||||
|
def hwpx_border_to_css(border_attrs):
|
||||||
|
"""HWPX 테두리 속성 dict → CSS border 문자열
|
||||||
|
|
||||||
|
Args:
|
||||||
|
border_attrs: {'type': 'SOLID', 'width': '0.12mm', 'color': '#000000'}
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
'0.12mm solid #000000' 또는 'none'
|
||||||
|
"""
|
||||||
|
btype = border_attrs.get('type', 'NONE')
|
||||||
|
if btype == 'NONE' or btype is None:
|
||||||
|
return 'none'
|
||||||
|
|
||||||
|
width = border_attrs.get('width', '0.12mm')
|
||||||
|
color = border_attrs.get('color', '#000000')
|
||||||
|
css_style = BORDER_TYPE_TO_CSS.get(btype, 'solid')
|
||||||
|
|
||||||
|
return f'{width} {css_style} {color}'
|
||||||
|
|
||||||
|
|
||||||
|
def css_border_to_hwpx(css_border):
|
||||||
|
"""CSS border 문자열 → HWPX 속성 dict
|
||||||
|
|
||||||
|
Args:
|
||||||
|
css_border: '0.12mm solid #000000' 또는 'none'
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
{'type': 'SOLID', 'width': '0.12mm', 'color': '#000000'}
|
||||||
|
"""
|
||||||
|
if not css_border or css_border.strip() == 'none':
|
||||||
|
return {'type': 'NONE', 'width': '0mm', 'color': '#000000'}
|
||||||
|
|
||||||
|
parts = css_border.strip().split()
|
||||||
|
width = parts[0] if len(parts) > 0 else '0.12mm'
|
||||||
|
style = parts[1] if len(parts) > 1 else 'solid'
|
||||||
|
color = parts[2] if len(parts) > 2 else '#000000'
|
||||||
|
|
||||||
|
return {
|
||||||
|
'type': CSS_TO_BORDER_TYPE.get(style, 'SOLID'),
|
||||||
|
'width': width,
|
||||||
|
'color': color,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def hwpx_borderfill_to_css(bf_element_attrs):
|
||||||
|
"""HWPX <hh:borderFill> 전체 속성 → CSS dict
|
||||||
|
|
||||||
|
Args:
|
||||||
|
bf_element_attrs: {
|
||||||
|
'left': {'type': 'SOLID', 'width': '0.12mm', 'color': '#000000'},
|
||||||
|
...
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
'border-left': hwpx_border_to_css(bf_element_attrs.get('left', {})),
|
||||||
|
'border-right': hwpx_border_to_css(bf_element_attrs.get('right', {})),
|
||||||
|
'border-top': hwpx_border_to_css(bf_element_attrs.get('top', {})),
|
||||||
|
'border-bottom': hwpx_border_to_css(bf_element_attrs.get('bottom', {})),
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user