8.2 KiB
8.2 KiB
name, description
| name | description |
|---|---|
| pdf-reportlab | 用 ReportLab 生成专业中文 PDF 研究报告。包含思源宋体/黑体+霞鹜文楷的字体注册、集中样式管理、封面/目录/正文/参考文献多页模板、matplotlib 图表嵌入。dr-reporter 用于 Phase 4 出 PDF 稿;也可被用户直接调用渲染单章。 |
ReportLab 中文 PDF 模板使用指南
一、为什么是 ReportLab
- 完全可控:每个字号、行距、缩进都是代码说了算,不像 CSS/LaTeX 会被引擎意外改变
- 中文字体一次搞定:
pdfmetrics.registerFont注册后全局可用,子集嵌入 PDF,分发无忧 - 速度快:纯 Python,30,000 字报告 3-5 秒出稿(matplotlib 图表预渲染后)
- 图表质量高:matplotlib 生成 300 DPI PNG 嵌入,比 LaTeX 的 pgfplots 快得多
- 样式集中:用
StyleSheet管理,避免你之前碰到的"中文字号不一"问题
二、项目模板入口
模板脚本:.opencode/templates/report-template.py
调用方式:
python3 .opencode/templates/report-template.py \
--input projects/<slug>/phase4/final.md \
--manifest projects/<slug>/manifest.json \
--output projects/<slug>/phase4/final.pdf \
--fonts-dir .opencode/templates/fonts
首次运行前必须:
bash .opencode/templates/fonts/download-fonts.sh
三、字体注册(模板已封装,此处仅说明原理)
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
# 思源宋体 = 正文
pdfmetrics.registerFont(TTFont('SrcSerif', 'fonts/SourceHanSerifSC-Regular.otf'))
pdfmetrics.registerFont(TTFont('SrcSerif-Bold', 'fonts/SourceHanSerifSC-Bold.otf'))
pdfmetrics.registerFontFamily('SrcSerif', normal='SrcSerif', bold='SrcSerif-Bold')
# 思源黑体 = 标题/UI
pdfmetrics.registerFont(TTFont('SrcSans-Light', 'fonts/SourceHanSansSC-Light.otf'))
pdfmetrics.registerFont(TTFont('SrcSans-Medium', 'fonts/SourceHanSansSC-Medium.otf'))
pdfmetrics.registerFont(TTFont('SrcSans-Bold', 'fonts/SourceHanSansSC-Bold.otf'))
pdfmetrics.registerFont(TTFont('SrcSans-Heavy', 'fonts/SourceHanSansSC-Heavy.otf'))
# 霞鹜文楷 = 引文/摘要
pdfmetrics.registerFont(TTFont('Kai', 'fonts/LXGWWenKai-Regular.ttf'))
关键:TTFont 虽然类名含 "TT",但也接受 .otf(OpenType),别犹豫。
四、样式表(集中管理,避免字号不一)
所有样式集中在模板的 build_styles() 函数:
| 样式名 | 字体 | 字号 | 行高 | 用途 |
|---|---|---|---|---|
body |
SrcSerif | 10.5 | 18 | 正文 |
body-bold |
SrcSerif-Bold | 10.5 | 18 | 术语 |
h1 |
SrcSans-Bold | 18 | 28 | 章标题 |
h2 |
SrcSans-Bold | 14 | 22 | section 标题 |
h3 |
SrcSans-Medium | 12 | 18 | sub-section |
quote |
Kai | 10.5 | 18 | 引文、摘要 |
caption |
SrcSans-Medium | 9 | 13 | 图表标题 |
footnote |
SrcSerif | 9 | 13 | 脚注/参考文献 |
header-footer |
SrcSans-Light | 8 | 12 | 页眉页脚 |
cover-title |
SrcSans-Heavy | 32 | 42 | 封面大标题 |
行高 = 字号 × 1.5~1.7,不要用默认值。
五、报告 11 件套结构
模板会按以下顺序生成页面:
-
封面页(
PageTemplate: cover)- 主标题:
cover-title - 副标题:
h2 - 作者、日期:
body - 单独版心,无页眉页脚
- 主标题:
-
免责声明(
PageTemplate: normal)- 固定模板,来源 manifest.json 的
disclaimer字段
- 固定模板,来源 manifest.json 的
-
执行摘要(Executive Summary)
quote样式,1-2 页- 来源 final.md 的
## 摘要段
-
术语表
- 两列表格,术语+解释
- 来源 final.md 的
## 术语表段
-
目录
- 自动从 h1/h2 生成,支持超链接
-
主体正文
- 来源 final.md 的各
## 第 N 章 ...段 - 页眉:左=主题缩写 / 右=章节名
- 页脚:居中页码
- 来源 final.md 的各
-
结论与建议
- final.md 的最后一章
-
参考文献
- 来源
projects/<slug>/phase4/citations.bib或sources.jsonl - 按引用顺序编号,GB/T 7714 格式
footnote样式
- 来源
-
附录 A:数据表(可选)
-
附录 B:方法论说明(可选)
-
版本信息
- 生成时间、版本号、生成者(dr-reporter)、字数统计
六、图表嵌入规范
不要用 ReportLab 原生绘图,全部预渲染为 PNG:
# 在 dr-analyst / dr-reporter 阶段,用 matplotlib 出图
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
# 注册中文字体给 matplotlib
font_path = '.opencode/templates/fonts/SourceHanSansSC-Medium.otf'
fm.fontManager.addfont(font_path)
plt.rcParams['font.family'] = 'Source Han Sans SC'
plt.rcParams['axes.unicode_minus'] = False
fig, ax = plt.subplots(figsize=(6, 4), dpi=150)
# ...绘图代码
plt.savefig('projects/<slug>/phase4/figures/fig_01_market_size.png', dpi=300, bbox_inches='tight')
然后在 final.md 里用标准 Markdown 引用:

模板会自动:
- 按 Markdown 解析图片
- 用
caption样式渲染标题 - 图表居中,宽度适配页宽
七、Markdown → ReportLab 的支持范围
模板支持以下 Markdown 元素:
| Markdown | ReportLab 渲染 |
|---|---|
# 标题 |
h1(章标题,自动分页) |
## 标题 |
h2(section,不分页) |
### 标题 |
h3(sub-section) |
**粗体** |
<b> inline |
*斜体* |
<i> inline |
`代码` |
等宽字体 inline |
> 引文 |
quote 样式块 |
- 列表项 / 1. 项 |
项目符号列表 |
表格(| | |) |
ReportLab Table,自动列宽 |
 |
图片 + caption |
[src_001] |
上标引用链接到参考文献 |
--- |
分页符(PageBreak) |
不支持(请在 Markdown 里避免):
- HTML 标签(除少数 inline)
- 数学公式(后续可加 matplotlib 渲染)
- 代码块高亮(只保留等宽显示)
八、manifest.json 的必需字段
{
"slug": "glp1-obesity-2026",
"topic": "GLP-1 减重药物竞争格局与投资机会",
"subtitle": "2026 年产业深度研究",
"author": "Deep Research 系统 v0.1",
"date": "2026-04-20",
"type": "研究类",
"version": "1.0",
"disclaimer": "本报告基于公开信息与 AI 辅助研究生成,仅供参考,不构成投资建议。",
"cover_theme": "blue"
}
九、常见坑与对策
| 坑 | 对策 |
|---|---|
| 中文字号不一 | 集中 StyleSheet,不在 Paragraph 里 inline 改 fontSize |
| 行距太挤 | 行高 = 字号 × 1.5~1.7,不要用默认 |
| 换行断错 | wordWrap='CJK' 必设 |
| 字体子集缺字 | 用完整版思源字体(非 subset 精简版) |
| 图片变形 | 先 matplotlib 出 300 DPI PNG,再 Image(path, width=..., height=...) |
| 页眉页脚重叠 | 用 BaseDocTemplate + PageTemplate,Frame 的 margin 留足 |
| 英文中文混排间距怪 | 思源系列自带 CJK metrics,间距会自适应,一般不用额外处理 |
| 生成慢 | matplotlib 图表预渲染,不要在 PDF 生成阶段现算 |
十、调用流程(dr-reporter 阶段)
1. 检查字体:ls .opencode/templates/fonts/*.otf | wc -l ≥ 6
2. 检查输入:projects/<slug>/phase4/final.md 存在
3. 检查配置:projects/<slug>/manifest.json 有必需字段
4. 执行:
python3 .opencode/templates/report-template.py \
--input projects/<slug>/phase4/final.md \
--manifest projects/<slug>/manifest.json \
--output projects/<slug>/phase4/final.pdf
5. 验证:
- PDF 打得开
- 文件大小 > 500KB(太小说明字体没嵌)
- 页数合理(30,000 字约 60-80 页)
6. 汇报:输出路径、页数、文件大小
十一、MVP 阶段注意
目前(MVP)report-template.py 是基础版,支持:
- 思源字体注册
- 标题 / 正文 / 引文 / 表格 / 图片
- 简单封面 + 目录
- 参考文献自动编号
暂未实现(Phase 4 能力阶段补齐):
- 自动书签/大纲(PDF navigation pane)
- 交叉引用("见第 3 章"自动跳转)
- 复杂页眉(左右对称排版)
- 附录 B 自动生成(方法论模板)
如需上述功能,在 manifest.json 里标 "template_features": ["bookmarks", "xref", ...],未来版本会处理。