import os import sys import docx from docx import Document from docx.shared import Inches, Pt, RGBColor from docx.enum.text import WD_ALIGN_PARAGRAPH from docx.enum.table import WD_TABLE_ALIGNMENT from docx.oxml import parse_xml from docx.oxml.ns import nsdecls def set_cell_background(cell, hex_color): tcPr = cell._tc.get_or_add_tcPr() shd = parse_xml(f'') tcPr.append(shd) def set_cell_margins(cell, top=100, bottom=100, left=150, right=150): tcPr = cell._tc.get_or_add_tcPr() tcMar = parse_xml( f'' f' ' f' ' f' ' f' ' f'' ) tcPr.append(tcMar) def set_table_borders(table, color="D0D7DE", sz="4", val="single"): tblPr = table._tbl.tblPr tblBorders = parse_xml( f'' f' ' f' ' f' ' f' ' f' ' f' ' f'' ) tblPr.append(tblBorders) def add_header_banner(doc, title, subtitle, doc_type="운영 매뉴얼", version="v1.1", date_str="2026년 8월"): p_meta = doc.add_paragraph() p_meta.paragraph_format.space_before = Pt(0) p_meta.paragraph_format.space_after = Pt(4) r_meta = p_meta.add_run(f"SMARTFARM HMI SYSTEM | {doc_type.upper()} | {version}") r_meta.font.name = "Malgun Gothic" r_meta.font.size = Pt(9) r_meta.font.bold = True r_meta.font.color.rgb = RGBColor(0x00, 0x77, 0x91) p_title = doc.add_paragraph() p_title.paragraph_format.space_before = Pt(4) p_title.paragraph_format.space_after = Pt(6) r_title = p_title.add_run(title) r_title.font.name = "Malgun Gothic" r_title.font.size = Pt(22) r_title.font.bold = True r_title.font.color.rgb = RGBColor(0x1B, 0x36, 0x5D) p_sub = doc.add_paragraph() p_sub.paragraph_format.space_before = Pt(0) p_sub.paragraph_format.space_after = Pt(14) r_sub = p_sub.add_run(subtitle) r_sub.font.name = "Malgun Gothic" r_sub.font.size = Pt(11) r_sub.font.color.rgb = RGBColor(0x55, 0x55, 0x55) tbl = doc.add_table(rows=2, cols=4) tbl.alignment = WD_TABLE_ALIGNMENT.CENTER set_table_borders(tbl, color="CCCCCC", sz="4") meta_headers = ["문서명", "시스템명", "버전 / 작성일", "대상 플랫폼"] meta_values = [doc_type, "SmartFarmHMI (FMX Multi-Platform)", f"{version} ({date_str})", "Windows, Android, iOS, macOS, Linux"] for i in range(4): c_h = tbl.cell(0, i) c_h.text = meta_headers[i] set_cell_background(c_h, "F0F4F8") set_cell_margins(c_h, top=80, bottom=80, left=100, right=100) p = c_h.paragraphs[0] p.alignment = WD_ALIGN_PARAGRAPH.CENTER for r in p.runs: r.font.name = "Malgun Gothic" r.font.size = Pt(8.5) r.font.bold = True r.font.color.rgb = RGBColor(0x1B, 0x36, 0x5D) c_v = tbl.cell(1, i) c_v.text = meta_values[i] set_cell_background(c_v, "FFFFFF") set_cell_margins(c_v, top=80, bottom=80, left=100, right=100) p = c_v.paragraphs[0] p.alignment = WD_ALIGN_PARAGRAPH.CENTER for r in p.runs: r.font.name = "Malgun Gothic" r.font.size = Pt(8.5) r.font.color.rgb = RGBColor(0x33, 0x33, 0x33) col_w = [1.5, 2.0, 1.4, 1.6] for row in tbl.rows: for i, w in enumerate(col_w): row.cells[i].width = Inches(w) doc.add_paragraph().paragraph_format.space_after = Pt(12) def add_heading_1(doc, text): p = doc.add_paragraph() p.paragraph_format.space_before = Pt(16) p.paragraph_format.space_after = Pt(6) p.paragraph_format.keep_with_next = True r = p.add_run(text) r.font.name = "Malgun Gothic" r.font.size = Pt(14) r.font.bold = True r.font.color.rgb = RGBColor(0x1B, 0x36, 0x5D) return p def add_heading_2(doc, text): p = doc.add_paragraph() p.paragraph_format.space_before = Pt(12) p.paragraph_format.space_after = Pt(4) p.paragraph_format.keep_with_next = True r = p.add_run(text) r.font.name = "Malgun Gothic" r.font.size = Pt(11.5) r.font.bold = True r.font.color.rgb = RGBColor(0x00, 0x77, 0x91) return p def add_heading_3(doc, text): p = doc.add_paragraph() p.paragraph_format.space_before = Pt(8) p.paragraph_format.space_after = Pt(2) p.paragraph_format.keep_with_next = True r = p.add_run(text) r.font.name = "Malgun Gothic" r.font.size = Pt(10) r.font.bold = True r.font.color.rgb = RGBColor(0x33, 0x33, 0x33) return p def add_body_p(doc, text, bold_prefix=None): p = doc.add_paragraph() p.paragraph_format.space_before = Pt(2) p.paragraph_format.space_after = Pt(4) p.paragraph_format.line_spacing = 1.15 if bold_prefix: r_bp = p.add_run(bold_prefix) r_bp.font.name = "Malgun Gothic" r_bp.font.size = Pt(9.5) r_bp.font.bold = True r_bp.font.color.rgb = RGBColor(0x1B, 0x36, 0x5D) r = p.add_run(text) r.font.name = "Malgun Gothic" r.font.size = Pt(9.5) r.font.color.rgb = RGBColor(0x33, 0x33, 0x33) return p def add_bullet_item(doc, text, bold_prefix=None, level=0): p = doc.add_paragraph(style='List Bullet') p.paragraph_format.space_before = Pt(1) p.paragraph_format.space_after = Pt(2) p.paragraph_format.line_spacing = 1.15 p.paragraph_format.left_indent = Inches(0.25 * (level + 1)) if bold_prefix: r_bp = p.add_run(bold_prefix) r_bp.font.name = "Malgun Gothic" r_bp.font.size = Pt(9.5) r_bp.font.bold = True r_bp.font.color.rgb = RGBColor(0x22, 0x22, 0x22) r = p.add_run(text) r.font.name = "Malgun Gothic" r.font.size = Pt(9.5) r.font.color.rgb = RGBColor(0x33, 0x33, 0x33) return p def add_callout(doc, text, title="참고 / 주의사항", bg_color="F0F4F8", border_color="1B365D"): tbl = doc.add_table(rows=1, cols=1) tbl.alignment = WD_TABLE_ALIGNMENT.CENTER tbl.autofit = False cell = tbl.cell(0, 0) cell.width = Inches(6.5) set_cell_background(cell, bg_color) set_cell_margins(cell, top=100, bottom=100, left=160, right=160) tcPr = cell._tc.get_or_add_tcPr() tcBorders = parse_xml( f'' f' ' f' ' f' ' f' ' f'' ) tcPr.append(tcBorders) p = cell.paragraphs[0] p.paragraph_format.space_before = Pt(2) p.paragraph_format.space_after = Pt(2) p.paragraph_format.line_spacing = 1.15 r_title = p.add_run(f"📌 {title}\n") r_title.font.name = "Malgun Gothic" r_title.font.size = Pt(9.5) r_title.font.bold = True r_title.font.color.rgb = RGBColor(0x1B, 0x36, 0x5D) r_text = p.add_run(text) r_text.font.name = "Malgun Gothic" r_text.font.size = Pt(9) r_text.font.color.rgb = RGBColor(0x33, 0x33, 0x33) doc.add_paragraph().paragraph_format.space_after = Pt(4) def add_code_block(doc, code_text): tbl = doc.add_table(rows=1, cols=1) tbl.alignment = WD_TABLE_ALIGNMENT.CENTER tbl.autofit = False cell = tbl.cell(0, 0) cell.width = Inches(6.5) set_cell_background(cell, "F8F9FA") set_cell_margins(cell, top=100, bottom=100, left=140, right=140) tcPr = cell._tc.get_or_add_tcPr() tcBorders = parse_xml( f'' f' ' f' ' f' ' f' ' f'' ) tcPr.append(tcBorders) p = cell.paragraphs[0] p.paragraph_format.space_before = Pt(2) p.paragraph_format.space_after = Pt(2) p.paragraph_format.line_spacing = 1.1 r = p.add_run(code_text) r.font.name = "Consolas" r.font.size = Pt(8.5) r.font.color.rgb = RGBColor(0x22, 0x22, 0x22) doc.add_paragraph().paragraph_format.space_after = Pt(4) def format_table(table, col_widths, headers, data, align_cols=None): table.alignment = WD_TABLE_ALIGNMENT.CENTER set_table_borders(table, color="D0D7DE", sz="6") # Header Row hdr_cells = table.rows[0].cells for i, title in enumerate(headers): hdr_cells[i].text = title set_cell_background(hdr_cells[i], "1B365D") set_cell_margins(hdr_cells[i], top=100, bottom=100, left=120, right=120) p = hdr_cells[i].paragraphs[0] p.alignment = WD_ALIGN_PARAGRAPH.CENTER for r in p.runs: r.font.name = "Malgun Gothic" r.font.size = Pt(9) r.font.bold = True r.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF) # Data Rows for row_idx, row_data in enumerate(data): row_cells = table.add_row().cells bg_c = "F8F9FB" if row_idx % 2 == 1 else "FFFFFF" for col_idx, text in enumerate(row_data): row_cells[col_idx].text = str(text) set_cell_background(row_cells[col_idx], bg_c) set_cell_margins(row_cells[col_idx], top=80, bottom=80, left=120, right=120) p = row_cells[col_idx].paragraphs[0] if align_cols and col_idx < len(align_cols): p.alignment = align_cols[col_idx] else: p.alignment = WD_ALIGN_PARAGRAPH.LEFT for r in p.runs: r.font.name = "Malgun Gothic" r.font.size = Pt(8.5) r.font.color.rgb = RGBColor(0x33, 0x33, 0x33) for row in table.rows: for i, w in enumerate(col_widths): row.cells[i].width = Inches(w) print("Base helper updated.")