# -*- coding: utf-8 -*- """ HWPX 표 열 너비 수정기 v2 표 생성 후 HWPX 파일을 직접 수정하여 열 너비 적용 """ import zipfile import re from pathlib import Path import tempfile import shutil # mm → HWPML 단위 변환 (1mm ≈ 283.46 HWPML units) MM_TO_HWPML = 7200 / 25.4 # ≈ 283.46 def inject_table_widths(hwpx_path: str, table_widths_list: list): """ HWPX 파일의 표 열 너비를 수정 Args: hwpx_path: HWPX 파일 경로 table_widths_list: [[w1, w2, w3], [w1, w2], ...] 형태 (mm 단위) """ if not table_widths_list: print(" [INFO] 수정할 표 없음") return print(f"📐 HWPX 표 열 너비 수정 시작... ({len(table_widths_list)}개 표)") # HWPX 압축 해제 temp_dir = Path(tempfile.mkdtemp(prefix="hwpx_table_")) with zipfile.ZipFile(hwpx_path, 'r') as zf: zf.extractall(temp_dir) # section*.xml 파일들에서 표 찾기 contents_dir = temp_dir / "Contents" table_idx = 0 total_modified = 0 for section_file in sorted(contents_dir.glob("section*.xml")): with open(section_file, 'r', encoding='utf-8') as f: content = f.read() original_content = content # 모든 표(...) 찾기 tbl_pattern = re.compile(r'(]*>)(.*?)()', re.DOTALL) def process_table(match): nonlocal table_idx, total_modified if table_idx >= len(table_widths_list): return match.group(0) tbl_open = match.group(1) tbl_content = match.group(2) tbl_close = match.group(3) col_widths_mm = table_widths_list[table_idx] col_widths_hwpml = [int(w * MM_TO_HWPML) for w in col_widths_mm] # 표 전체 너비 수정 (hp:sz width="...") total_width = int(sum(col_widths_mm) * MM_TO_HWPML) tbl_content = re.sub( r'(= len(col_widths_hwpml): return tc_content new_width = col_widths_hwpml[col_idx] # cellSz width 교체 tc_content = re.sub( r'(... 블록 처리 tbl_content = re.sub( r']*>.*?', replace_cell_width, tbl_content, flags=re.DOTALL ) print(f" ✅ 표 #{table_idx + 1}: {col_widths_mm} mm → HWPML 적용") table_idx += 1 total_modified += 1 return tbl_open + tbl_content + tbl_close # 표 처리 new_content = tbl_pattern.sub(process_table, content) # 변경사항 있으면 저장 if new_content != original_content: with open(section_file, 'w', encoding='utf-8') as f: f.write(new_content) print(f" → {section_file.name} 저장됨") # 다시 압축 repack_hwpx(temp_dir, hwpx_path) # 임시 폴더 삭제 shutil.rmtree(temp_dir) print(f" ✅ 총 {total_modified}개 표 열 너비 수정 완료") def repack_hwpx(source_dir: Path, output_path: str): """HWPX 파일 다시 압축""" import os import time temp_output = output_path + ".tmp" with zipfile.ZipFile(temp_output, 'w', zipfile.ZIP_DEFLATED) as zf: # mimetype은 압축 없이 첫 번째로 mimetype_path = source_dir / "mimetype" if mimetype_path.exists(): zf.write(mimetype_path, "mimetype", compress_type=zipfile.ZIP_STORED) # 나머지 파일들 for root, dirs, files in os.walk(source_dir): for file in files: if file == "mimetype": continue file_path = Path(root) / file arcname = file_path.relative_to(source_dir) zf.write(file_path, arcname) # 원본 교체 for attempt in range(3): try: if os.path.exists(output_path): os.remove(output_path) os.rename(temp_output, output_path) break except PermissionError: time.sleep(0.5) # 테스트용 if __name__ == "__main__": test_widths = [ [18.2, 38.9, 42.8, 70.1], [19.9, 79.6, 70.5], [28.7, 81.4, 59.9], [19.2, 61.4, 89.5], ] hwpx_path = r"C:\Users\User\AppData\Local\Temp\geulbeot_output.hwpx" inject_table_widths(hwpx_path, test_widths)