162 lines
5.8 KiB
Python
162 lines
5.8 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, WD_ALIGN_VERTICAL
|
|
from docx.oxml import OxmlElement, parse_xml
|
|
from docx.oxml.ns import nsdecls, qn
|
|
|
|
def set_cell_background(cell, hex_color):
|
|
"""Set background color of a table cell."""
|
|
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):
|
|
"""Set cell padding (in twips: 1 pt = 20 twips)."""
|
|
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="CCCCCC", sz="4", val="single"):
|
|
"""Set light borders on table."""
|
|
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_callout(doc, text, title="참고 / 주의사항", bg_color="F0F4F8", border_color="1B365D"):
|
|
"""Add a styled callout box."""
|
|
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=140, bottom=140, left=200, right=200)
|
|
|
|
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(10)
|
|
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.5)
|
|
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):
|
|
"""Add a styled code block."""
|
|
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=120, bottom=120, left=180, right=180)
|
|
|
|
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(9)
|
|
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):
|
|
"""Helper to populate and format a table nicely."""
|
|
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=120, bottom=120, left=140, right=140)
|
|
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.5)
|
|
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=100, bottom=100, left=140, right=140)
|
|
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(9)
|
|
r.font.color.rgb = RGBColor(0x33, 0x33, 0x33)
|
|
|
|
# Column widths
|
|
for row in table.rows:
|
|
for i, w in enumerate(col_widths):
|
|
row.cells[i].width = Inches(w)
|
|
|
|
print("Base helper defined successfully.")
|