This commit is contained in:
2026-04-24 16:02:16 +08:00
commit 1d6f0cc370
66 changed files with 9990 additions and 0 deletions

View File

@@ -0,0 +1,166 @@
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