snapshot before v0.5 refactor

This commit is contained in:
kai
2026-04-21 12:31:58 +08:00
commit 4a38f6bed1
82 changed files with 15230 additions and 0 deletions
+255
View File
@@ -0,0 +1,255 @@
---
name: pdf-reportlab
description: 用 ReportLab 生成专业中文 PDF 研究报告。包含思源宋体/黑体+霞鹜文楷的字体注册、集中样式管理、封面/目录/正文/参考文献多页模板、matplotlib 图表嵌入。dr-reporter 用于 Phase 4 出 PDF 稿;也可被用户直接调用渲染单章。
---
# ReportLab 中文 PDF 模板使用指南
## 一、为什么是 ReportLab
- **完全可控**:每个字号、行距、缩进都是代码说了算,不像 CSS/LaTeX 会被引擎意外改变
- **中文字体一次搞定**`pdfmetrics.registerFont` 注册后全局可用,子集嵌入 PDF,分发无忧
- **速度快**:纯 Python30,000 字报告 3-5 秒出稿(matplotlib 图表预渲染后)
- **图表质量高**matplotlib 生成 300 DPI PNG 嵌入,比 LaTeX 的 pgfplots 快得多
- **样式集中**:用 `StyleSheet` 管理,避免你之前碰到的"中文字号不一"问题
---
## 二、项目模板入口
模板脚本:`.opencode/templates/report-template.py`
调用方式:
```bash
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
bash .opencode/templates/fonts/download-fonts.sh
```
---
## 三、字体注册(模板已封装,此处仅说明原理)
```python
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 件套结构
模板会按以下顺序生成页面:
1. **封面页**`PageTemplate: cover`
- 主标题:`cover-title`
- 副标题:`h2`
- 作者、日期:`body`
- 单独版心,无页眉页脚
2. **免责声明**`PageTemplate: normal`
- 固定模板,来源 manifest.json 的 `disclaimer` 字段
3. **执行摘要**Executive Summary
- `quote` 样式,1-2 页
- 来源 final.md 的 `## 摘要`
4. **术语表**
- 两列表格,术语+解释
- 来源 final.md 的 `## 术语表`
5. **目录**
- 自动从 h1/h2 生成,支持超链接
6. **主体正文**
- 来源 final.md 的各 `## 第 N 章 ...`
- 页眉:左=主题缩写 / 右=章节名
- 页脚:居中页码
7. **结论与建议**
- final.md 的最后一章
8. **参考文献**
- 来源 `projects/<slug>/phase4/citations.bib``sources.jsonl`
- 按引用顺序编号,GB/T 7714 格式
- `footnote` 样式
9. **附录 A:数据表**(可选)
10. **附录 B:方法论说明**(可选)
11. **版本信息**
- 生成时间、版本号、生成者(dr-reporter)、字数统计
---
## 六、图表嵌入规范
**不要用 ReportLab 原生绘图**,全部预渲染为 PNG
```python
# 在 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
![图 12020-2025 GLP-1 市场规模](figures/fig_01_market_size.png)
```
模板会自动:
- 按 Markdown 解析图片
-`caption` 样式渲染标题
- 图表居中,宽度适配页宽
---
## 七、Markdown → ReportLab 的支持范围
模板支持以下 Markdown 元素:
| Markdown | ReportLab 渲染 |
|---|---|
| `# 标题` | h1(章标题,自动分页) |
| `## 标题` | h2section,不分页) |
| `### 标题` | h3sub-section |
| `**粗体**` | `<b>` inline |
| `*斜体*` | `<i>` inline |
| `` `代码` `` | 等宽字体 inline |
| `> 引文` | `quote` 样式块 |
| `- 列表项` / `1. 项` | 项目符号列表 |
| `表格`\| \| \| | ReportLab Table,自动列宽 |
| `![caption](path)` | 图片 + caption |
| `[src_001]` | 上标引用链接到参考文献 |
| `---` | 分页符(`PageBreak` |
**不支持**(请在 Markdown 里避免):
- HTML 标签(除少数 inline
- 数学公式(后续可加 matplotlib 渲染)
- 代码块高亮(只保留等宽显示)
---
## 八、manifest.json 的必需字段
```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", ...]`,未来版本会处理。