167 lines
5.3 KiB
Python
167 lines
5.3 KiB
Python
import os
|
||
import re
|
||
from docx import Document
|
||
from docx.shared import Pt, Inches, Cm, RGBColor
|
||
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
||
from docx.oxml.ns import qn
|
||
|
||
|
||
def generate_person_doc(person_name: str, person_info: str, photo_path: str,
|
||
stories: list[dict], output_path: str):
|
||
"""
|
||
为一位讲述者生成最终 DOCX 文档
|
||
|
||
person_info: 个人信息文本
|
||
photo_path: 照片路径(可为空)
|
||
stories: [{title, content}]
|
||
"""
|
||
doc = Document()
|
||
|
||
# 设置默认字体
|
||
style = doc.styles['Normal']
|
||
font = style.font
|
||
font.name = '宋体'
|
||
font.size = Pt(12)
|
||
style.element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
|
||
|
||
# 照片
|
||
if photo_path and os.path.exists(photo_path):
|
||
try:
|
||
doc.add_picture(photo_path, width=Inches(1.5))
|
||
last_paragraph = doc.paragraphs[-1]
|
||
last_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||
except Exception:
|
||
pass
|
||
|
||
# 姓名
|
||
name_para = doc.add_paragraph()
|
||
name_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||
name_run = name_para.add_run(person_name)
|
||
name_run.font.size = Pt(18)
|
||
name_run.font.bold = True
|
||
name_run.font.name = '黑体'
|
||
name_run.element.rPr.rFonts.set(qn('w:eastAsia'), '黑体')
|
||
|
||
# 个人信息
|
||
if person_info:
|
||
info_para = doc.add_paragraph()
|
||
info_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||
info_run = info_para.add_run(person_info.strip())
|
||
info_run.font.size = Pt(11)
|
||
info_run.font.color.rgb = RGBColor(0x66, 0x66, 0x66)
|
||
|
||
# 分隔线
|
||
doc.add_paragraph("─" * 30).alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||
|
||
# 故事
|
||
for i, story in enumerate(stories):
|
||
# 故事标题
|
||
title_para = doc.add_paragraph()
|
||
title_run = title_para.add_run(story.get("title", f"故事 {i + 1}"))
|
||
title_run.font.size = Pt(14)
|
||
title_run.font.bold = True
|
||
title_run.font.name = '黑体'
|
||
title_run.element.rPr.rFonts.set(qn('w:eastAsia'), '黑体')
|
||
|
||
# 故事梗概
|
||
summary = story.get("summary", "")
|
||
if summary:
|
||
p = doc.add_paragraph()
|
||
run = p.add_run("【故事梗概】")
|
||
run.font.size = Pt(11)
|
||
run.font.bold = True
|
||
run.font.color.rgb = RGBColor(0x33, 0x33, 0x99)
|
||
p = doc.add_paragraph(summary.strip())
|
||
p.paragraph_format.first_line_indent = Cm(0.74)
|
||
p.paragraph_format.line_spacing = 1.5
|
||
for run in p.runs:
|
||
run.font.size = Pt(11)
|
||
|
||
# 关键词标签
|
||
tags = story.get("tags", [])
|
||
if tags:
|
||
p = doc.add_paragraph()
|
||
run = p.add_run("【关键词标签】")
|
||
run.font.size = Pt(11)
|
||
run.font.bold = True
|
||
run.font.color.rgb = RGBColor(0x33, 0x33, 0x99)
|
||
tag_text = "、".join(tags)
|
||
p = doc.add_paragraph(tag_text)
|
||
p.paragraph_format.line_spacing = 1.5
|
||
for run in p.runs:
|
||
run.font.size = Pt(10)
|
||
run.font.color.rgb = RGBColor(0x66, 0x66, 0x66)
|
||
|
||
# 故事内容(支持 Markdown 格式)
|
||
content = story.get("content", "")
|
||
if content:
|
||
_add_markdown_content(doc, content)
|
||
|
||
# 多个故事之间加分隔
|
||
if i < len(stories) - 1:
|
||
doc.add_paragraph("─" * 30).alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||
|
||
# 保存
|
||
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||
doc.save(output_path)
|
||
return output_path
|
||
|
||
|
||
def _add_markdown_content(doc, content: str):
|
||
"""将 Markdown 格式的内容添加到 DOCX,支持标题、引用、列表等"""
|
||
lines = content.split("\n")
|
||
i = 0
|
||
while i < len(lines):
|
||
line = lines[i].strip()
|
||
|
||
if not line:
|
||
i += 1
|
||
continue
|
||
|
||
# ## 二级标题
|
||
if line.startswith("## "):
|
||
title = line[3:].strip()
|
||
p = doc.add_paragraph()
|
||
run = p.add_run(title)
|
||
run.font.size = Pt(13)
|
||
run.font.bold = True
|
||
run.font.name = '黑体'
|
||
run.element.rPr.rFonts.set(qn('w:eastAsia'), '黑体')
|
||
p.paragraph_format.space_before = Pt(12)
|
||
|
||
# ### 三级标题
|
||
elif line.startswith("### "):
|
||
title = line[4:].strip()
|
||
p = doc.add_paragraph()
|
||
run = p.add_run(title)
|
||
run.font.size = Pt(12)
|
||
run.font.bold = True
|
||
run.font.name = '黑体'
|
||
run.element.rPr.rFonts.set(qn('w:eastAsia'), '黑体')
|
||
p.paragraph_format.space_before = Pt(6)
|
||
|
||
# > 引用
|
||
elif line.startswith("> "):
|
||
quote_text = line[2:].strip()
|
||
p = doc.add_paragraph()
|
||
p.paragraph_format.left_indent = Cm(1)
|
||
run = p.add_run(quote_text)
|
||
run.font.italic = True
|
||
run.font.color.rgb = RGBColor(0x55, 0x55, 0x55)
|
||
run.font.size = Pt(11)
|
||
|
||
# - 列表项
|
||
elif line.startswith("- "):
|
||
item_text = line[2:].strip()
|
||
p = doc.add_paragraph(style='List Bullet')
|
||
run = p.add_run(item_text)
|
||
run.font.size = Pt(12)
|
||
|
||
# 普通段落
|
||
else:
|
||
p = doc.add_paragraph(line)
|
||
p.paragraph_format.first_line_indent = Cm(0.74)
|
||
p.paragraph_format.line_spacing = 1.5
|
||
|
||
i += 1
|