Files
Story-extractor/backend/app/services/docx_generator.py
2026-04-24 16:02:16 +08:00

167 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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