SmartFarm/SOURCE/SmartFarmHMI/Docs/docx_builder_base.py
2026-09-04 10:53:44 +09:00

291 lines
10 KiB
Python

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'<w:shd {nsdecls("w")} w:fill="{hex_color}"/>')
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'<w:tcMar {nsdecls("w")}>'
f' <w:top w:w="{top}" w:type="dxa"/>'
f' <w:bottom w:w="{bottom}" w:type="dxa"/>'
f' <w:left w:w="{left}" w:type="dxa"/>'
f' <w:right w:w="{right}" w:type="dxa"/>'
f'</w:tcMar>'
)
tcPr.append(tcMar)
def set_table_borders(table, color="D0D7DE", sz="4", val="single"):
tblPr = table._tbl.tblPr
tblBorders = parse_xml(
f'<w:tblBorders {nsdecls("w")}>'
f' <w:top w:val="{val}" w:sz="{sz}" w:space="0" w:color="{color}"/>'
f' <w:bottom w:val="{val}" w:sz="{sz}" w:space="0" w:color="{color}"/>'
f' <w:left w:val="none"/>'
f' <w:right w:val="none"/>'
f' <w:insideH w:val="{val}" w:sz="{sz}" w:space="0" w:color="{color}"/>'
f' <w:insideV w:val="none"/>'
f'</w:tblBorders>'
)
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'<w:tcBorders {nsdecls("w")}>'
f' <w:top w:val="none"/>'
f' <w:left w:val="single" w:sz="24" w:space="0" w:color="{border_color}"/>'
f' <w:bottom w:val="none"/>'
f' <w:right w:val="none"/>'
f'</w:tcBorders>'
)
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'<w:tcBorders {nsdecls("w")}>'
f' <w:top w:val="single" w:sz="4" w:space="0" w:color="E0E0E0"/>'
f' <w:left w:val="single" w:sz="16" w:space="0" w:color="4A90E2"/>'
f' <w:bottom w:val="single" w:sz="4" w:space="0" w:color="E0E0E0"/>'
f' <w:right w:val="single" w:sz="4" w:space="0" w:color="E0E0E0"/>'
f'</w:tcBorders>'
)
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.")