"""
职来产品开发方案 Word 格式化脚本
将原始 docx 转为规范的 A4 企业级文档:封面、目录、多级标题、正文排版、表格、页眉页脚
"""
import copy
from docx import Document
from docx.shared import Pt, Cm, RGBColor, Emu
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_LINE_SPACING
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.enum.section import WD_ORIENT
from docx.oxml.ns import qn, nsdecls
from docx.oxml import parse_xml
# ⚠️ 历史遗留脚本:原依赖青核项目(collegeCli)目录下的 Word 文档
# 青核项目已独立为 C:\Users\15642\Desktop\青核\collegeCli
# 若输入文件不存在,请手动指定 SRC 路径后运行
SRC = r"C:\Users\15642\Desktop\青核\collegeCli\职来(mydreamjob)产品开发方案-v1.docx"
OUT = r"C:\Users\15642\Desktop\青核\collegeCli\职来(mydreamjob)产品开发方案-v1-格式化.docx"
# ── style mapping ──
HEADING_STYLE_MAP = {"Heading 1": "Heading 1", "Heading 2": "Heading 2", "Heading 3": "Heading 3"}
MONO_FONTS = {"JetBrains Mono", "Consolas", "Courier New", "Fira Code", "Source Code Pro"}
def setup_page(doc):
"""A4, margins"""
for sec in doc.sections:
sec.page_width = Cm(21.0)
sec.page_height = Cm(29.7)
sec.top_margin = Cm(2.54)
sec.bottom_margin = Cm(2.54)
sec.left_margin = Cm(3.18)
sec.right_margin = Cm(3.18)
def setup_styles(doc):
"""Define heading & body styles"""
# ── Normal (body text) ──
style = doc.styles["Normal"]
style.font.name = "宋体"
style.font.size = Pt(12) # 小四
style.font.color.rgb = RGBColor(0x00, 0x00, 0x00)
style.paragraph_format.line_spacing = 1.5
style.paragraph_format.space_before = Pt(0)
style.paragraph_format.space_after = Pt(0)
style.paragraph_format.first_line_indent = Pt(24) # 2 chars ≈ 24pt
# Set east-asian font
rPr = style.element.get_or_add_rPr()
rFonts = rPr.find(qn("w:rFonts"))
if rFonts is None:
rFonts = parse_xml(f'')
rPr.append(rFonts)
else:
rFonts.set(qn("w:eastAsia"), "宋体")
# ── Heading 1 ──
h1 = doc.styles["Heading 1"]
h1.font.name = "黑体"
h1.font.size = Pt(18) # 小二
h1.font.bold = True
h1.font.color.rgb = RGBColor(0x00, 0x00, 0x00)
h1.paragraph_format.space_before = Pt(18)
h1.paragraph_format.space_after = Pt(12)
h1.paragraph_format.line_spacing = 1.5
h1.paragraph_format.first_line_indent = Pt(0)
h1.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT
rPr = h1.element.get_or_add_rPr()
rFonts = rPr.find(qn("w:rFonts"))
if rFonts is None:
rFonts = parse_xml(f'')
rPr.append(rFonts)
else:
rFonts.set(qn("w:eastAsia"), "黑体")
# ── Heading 2 ──
h2 = doc.styles["Heading 2"]
h2.font.name = "黑体"
h2.font.size = Pt(15) # 小三
h2.font.bold = True
h2.font.color.rgb = RGBColor(0x00, 0x00, 0x00)
h2.paragraph_format.space_before = Pt(12)
h2.paragraph_format.space_after = Pt(6)
h2.paragraph_format.line_spacing = 1.5
h2.paragraph_format.first_line_indent = Pt(0)
h2.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT
rPr = h2.element.get_or_add_rPr()
rFonts = rPr.find(qn("w:rFonts"))
if rFonts is None:
rFonts = parse_xml(f'')
rPr.append(rFonts)
else:
rFonts.set(qn("w:eastAsia"), "黑体")
# ── Heading 3 ──
h3 = doc.styles["Heading 3"]
h3.font.name = "黑体"
h3.font.size = Pt(14) # 四号
h3.font.bold = True
h3.font.color.rgb = RGBColor(0x00, 0x00, 0x00)
h3.paragraph_format.space_before = Pt(8)
h3.paragraph_format.space_after = Pt(4)
h3.paragraph_format.line_spacing = 1.5
h3.paragraph_format.first_line_indent = Pt(0)
h3.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT
rPr = h3.element.get_or_add_rPr()
rFonts = rPr.find(qn("w:rFonts"))
if rFonts is None:
rFonts = parse_xml(f'')
rPr.append(rFonts)
else:
rFonts.set(qn("w:eastAsia"), "黑体")
def setup_numbering(doc):
"""Multi-level heading auto-numbering: 1 / 1.1 / 1.1.1"""
# Create a numbering part if it doesn't exist
numbering_part = doc.part.numbering_part
numbering = numbering_part.element
# Abstract numbering
abstract_num_id = 0
abstract_num = parse_xml(
f''
f' '
f' '
f' '
f' '
f' '
f' '
f' '
f' '
f' '
f' '
f' '
f' '
f' '
f' '
f' '
f' '
f' '
f' '
f' '
f' '
f' '
f' '
f''
)
numbering.insert(0, abstract_num)
# Numbering instance
num_id = 1
num = parse_xml(
f''
f' '
f''
)
numbering.append(num)
# Link numbering to heading styles
for level, style_name in [(0, "Heading 1"), (1, "Heading 2"), (2, "Heading 3")]:
style = doc.styles[style_name]
pPr = style.element.get_or_add_pPr()
numPr = pPr.find(qn("w:numPr"))
if numPr is not None:
pPr.remove(numPr)
numPr = parse_xml(
f''
f' '
f' '
f''
)
pPr.append(numPr)
def add_cover_page(doc):
"""Professional cover page"""
for _ in range(6):
doc.add_paragraph("")
title_p = doc.add_paragraph("")
title_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
title_run = title_p.add_run("职来(mydreamjob)产品开发方案")
title_run.font.name = "黑体"
title_run.font.size = Pt(26)
title_run.font.bold = True
title_run.font.color.rgb = RGBColor(0x00, 0x00, 0x00)
rPr = title_run._r.get_or_add_rPr()
rFonts = parse_xml(f'')
rPr.append(rFonts)
doc.add_paragraph("")
subtitle_p = doc.add_paragraph("")
subtitle_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
sub_run = subtitle_p.add_run("面向大学生与应届生的可信求职平台")
sub_run.font.name = "宋体"
sub_run.font.size = Pt(16)
sub_run.font.color.rgb = RGBColor(0x44, 0x44, 0x44)
for _ in range(8):
doc.add_paragraph("")
meta_items = [
("文档版本:", "v1.0"),
("编制日期:", "2026 年 7 月 30 日"),
("密级:", "内部"),
("编制:", "项目组"),
("送阅对象:", "公司负责人 / 投资人"),
]
for label, value in meta_items:
mp = doc.add_paragraph("")
mp.alignment = WD_ALIGN_PARAGRAPH.CENTER
lr = mp.add_run(label)
lr.font.name = "宋体"
lr.font.size = Pt(12)
vr = mp.add_run(value)
vr.font.name = "宋体"
vr.font.size = Pt(12)
vr.font.bold = True
doc.add_page_break()
def add_toc(doc):
"""Insert TOC field (populated when opened in Word)"""
toc_heading = doc.add_paragraph("目 录")
toc_heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
toc_heading.style = doc.styles["Normal"]
toc_heading.paragraph_format.first_line_indent = Pt(0)
toc_heading.paragraph_format.space_after = Pt(12)
for run in toc_heading.runs:
run.font.name = "黑体"
run.font.size = Pt(18)
run.font.bold = True
# Insert TOC field
paragraph = doc.add_paragraph("")
paragraph.paragraph_format.first_line_indent = Pt(0)
run = paragraph.add_run()
fldChar1 = parse_xml(f'')
run._r.append(fldChar1)
run2 = paragraph.add_run()
instrText = parse_xml(
f' TOC \\o "1-3" \\h \\z \\u '
)
run2._r.append(instrText)
run3 = paragraph.add_run()
fldChar2 = parse_xml(f'')
run3._r.append(fldChar2)
run4 = paragraph.add_run('(请在 Word 中右键此处,选择"更新域"以生成目录)')
run4.font.name = "宋体"
run4.font.size = Pt(10)
run4.font.color.rgb = RGBColor(0x99, 0x99, 0x99)
run5 = paragraph.add_run()
fldChar3 = parse_xml(f'')
run5._r.append(fldChar3)
def add_header_footer(doc):
"""Header with doc title, footer with page number"""
for sec in doc.sections:
# Header
header = sec.header
header.is_linked_to_previous = False
hp = header.paragraphs[0] if header.paragraphs else header.add_paragraph()
hp.alignment = WD_ALIGN_PARAGRAPH.CENTER
hp.paragraph_format.first_line_indent = Pt(0)
hr = hp.add_run("职来(mydreamjob)产品开发方案")
hr.font.name = "宋体"
hr.font.size = Pt(9)
hr.font.color.rgb = RGBColor(0x88, 0x88, 0x88)
# Add bottom border
pPr = hp._p.get_or_add_pPr()
pBdr = parse_xml(
f''
f' '
f''
)
pPr.append(pBdr)
# Footer
footer = sec.footer
footer.is_linked_to_previous = False
fp = footer.paragraphs[0] if footer.paragraphs else footer.add_paragraph()
fp.alignment = WD_ALIGN_PARAGRAPH.CENTER
fp.paragraph_format.first_line_indent = Pt(0)
# Page number field
fr1 = fp.add_run()
fldChar1 = parse_xml(f'')
fr1._r.append(fldChar1)
fr2 = fp.add_run()
instrText = parse_xml(f' PAGE ')
fr2._r.append(instrText)
fr3 = fp.add_run()
fldChar2 = parse_xml(f'')
fr3._r.append(fldChar2)
fr4 = fp.add_run("1")
fr4.font.size = Pt(9)
fr5 = fp.add_run()
fldChar3 = parse_xml(f'')
fr5._r.append(fldChar3)
def is_code_paragraph(p):
"""Detect code/monospace paragraphs"""
if not p.runs:
return False
for r in p.runs:
if r.font.name and r.font.name in MONO_FONTS:
return True
return False
def is_separator(text):
"""Detect separator lines"""
s = text.strip()
if not s:
return False
# Check if mostly dashes or special separator chars
dash_chars = {"─", "—", "-", "=", "━", "┅", "―"}
total = len(s)
dash_count = sum(1 for c in s if c in dash_chars)
return dash_count / total > 0.6 and total > 10
def fixup_font_eastasia(run, font_name):
"""Set east-Asian font on a run"""
rPr = run._r.get_or_add_rPr()
rFonts_elem = rPr.find(qn("w:rFonts"))
if rFonts_elem is None:
rFonts_elem = parse_xml(f'')
rPr.append(rFonts_elem)
else:
rFonts_elem.set(qn("w:eastAsia"), font_name)
def copy_paragraph(doc, src_p, heading_counter):
"""Copy a paragraph with style-aware formatting and heading auto-numbering"""
src_style = src_p.style.name if src_p.style else "Normal"
text = src_p.text.strip()
# Determine target style
if src_style in HEADING_STYLE_MAP:
target_style = HEADING_STYLE_MAP[src_style]
# Increment heading counter
if src_style == "Heading 1":
heading_counter[0] += 1
heading_counter[1] = 0
heading_counter[2] = 0
elif src_style == "Heading 2":
heading_counter[1] += 1
heading_counter[2] = 0
elif src_style == "Heading 3":
heading_counter[2] += 1
new_p = doc.add_paragraph(style=target_style)
else:
new_p = doc.add_paragraph(style="Normal")
# Handle separator
if is_separator(text):
new_p.paragraph_format.first_line_indent = Pt(0)
new_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = new_p.add_run("· · ·")
run.font.name = "宋体"
run.font.size = Pt(10)
run.font.color.rgb = RGBColor(0xCC, 0xCC, 0xCC)
fixup_font_eastasia(run, "宋体")
return
# Reset first-line indent for non-body paragraphs
if src_style in HEADING_STYLE_MAP or src_style == "List Paragraph":
new_p.paragraph_format.first_line_indent = Pt(0)
# Handle List Paragraph style
if src_style == "List Paragraph":
new_p.paragraph_format.left_indent = Cm(1.0)
new_p.paragraph_format.first_line_indent = Cm(-0.5)
# Check for code paragraphs
is_code = is_code_paragraph(src_p)
# Copy runs
for src_run in src_p.runs:
new_run = new_p.add_run(src_run.text)
# Determine font
if is_code:
new_run.font.name = "Consolas"
new_run.font.size = Pt(9.5)
elif src_style in HEADING_STYLE_MAP:
# Heading font handled by style
pass
else:
new_run.font.name = "宋体"
new_run.font.size = Pt(12)
fixup_font_eastasia(new_run, "宋体")
# Copy bold
if src_run.bold is not None:
new_run.bold = src_run.bold
# Copy italic
if src_run.italic is not None:
new_run.italic = src_run.italic
return new_p
def format_table(new_tbl):
"""Apply uniform formatting to a table"""
new_tbl.alignment = WD_TABLE_ALIGNMENT.CENTER
# Table borders
tbl = new_tbl._tbl
tblPr = tbl.tblPr if tbl.tblPr is not None else parse_xml(f'')
borders = parse_xml(
f''
f' '
f' '
f' '
f' '
f' '
f' '
f''
)
existing = tblPr.find(qn("w:tblBorders"))
if existing is not None:
tblPr.remove(existing)
tblPr.append(borders)
# Format header row (first row)
for ri, row in enumerate(new_tbl.rows):
for cell in row.cells:
for p in cell.paragraphs:
p.paragraph_format.space_before = Pt(2)
p.paragraph_format.space_after = Pt(2)
p.paragraph_format.line_spacing = 1.15
p.paragraph_format.first_line_indent = Pt(0)
for run in p.runs:
if ri == 0:
run.font.name = "黑体"
run.font.size = Pt(10.5)
run.font.bold = True
fixup_font_eastasia(run, "黑体")
else:
run.font.name = "宋体"
run.font.size = Pt(10.5)
fixup_font_eastasia(run, "宋体")
# Set column widths based on column count
ncols = len(new_tbl.columns)
if ncols > 0:
col_width = Cm(14.0 / ncols) # approx usable width
for col in new_tbl.columns:
for cell in col.cells:
cell.width = col_width
def copy_table(doc, src_tbl, table_index):
"""Copy a table from source to target document with formatting"""
# Add table caption
caption_p = doc.add_paragraph("")
caption_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
caption_p.paragraph_format.first_line_indent = Pt(0)
caption_p.paragraph_format.space_before = Pt(12)
caption_p.paragraph_format.space_after = Pt(4)
cr = caption_p.add_run(f"表 {table_index + 1}")
cr.font.name = "黑体"
cr.font.size = Pt(10.5)
cr.font.bold = True
fixup_font_eastasia(cr, "黑体")
# Create new table
nrows = len(src_tbl.rows)
ncols = len(src_tbl.columns)
new_tbl = doc.add_table(rows=nrows, cols=ncols)
# Copy cell contents
for ri, src_row in enumerate(src_tbl.rows):
for ci, src_cell in enumerate(src_row.cells):
new_cell = new_tbl.rows[ri].cells[ci]
# Clear default paragraph
new_cell.paragraphs[0].clear()
# Copy paragraphs from source
for sp in src_cell.paragraphs:
np = new_cell.add_paragraph(sp.text) if sp.text.strip() else new_cell.paragraphs[0]
if not sp.text.strip() and new_cell.paragraphs[0].text == "":
continue
for sr in sp.runs:
nr = np.add_run(sr.text) if np != new_cell.paragraphs[0] else new_cell.paragraphs[0].add_run(sr.text)
if ri == 0:
nr.font.bold = True
format_table(new_tbl)
return new_tbl
def main():
import os
if not os.path.exists(SRC):
print(f"❌ 输入文件不存在: {SRC}")
print(" 请手动指定 SRC 路径后重试(本脚本为历史遗留脚本,依赖青核项目目录)")
return
print("Reading source document...")
src = Document(SRC)
print(f" {len(src.paragraphs)} paragraphs, {len(src.tables)} tables")
print("Creating new document...")
doc = Document()
setup_page(doc)
setup_styles(doc)
# 原文档已有一、二、三...手工编号,不叠自动编号以避免冲突
# setup_numbering(doc)
print("Adding cover page...")
add_cover_page(doc)
print("Adding TOC...")
add_toc(doc)
print("Copying content...")
heading_counter = [0, 0, 0] # [H1, H2, H3]
# Walk through body elements in original order
body = src.element.body
para_idx = 0
tbl_idx = 0
for child in body:
tag = child.tag.split("}")[-1] if "}" in child.tag else child.tag
if tag == "p":
# Paragraph
if para_idx < len(src.paragraphs):
copy_paragraph(doc, src.paragraphs[para_idx], heading_counter)
para_idx += 1
elif tag == "tbl":
# Table
if tbl_idx < len(src.tables):
copy_table(doc, src.tables[tbl_idx], tbl_idx)
tbl_idx += 1
print(f" Processed {para_idx} paragraphs, {tbl_idx} tables")
print("Adding header/footer...")
add_header_footer(doc)
print(f"Saving to {OUT}...")
doc.save(OUT)
print("Done!")
if __name__ == "__main__":
main()