Files
deep_research/.opencode/skills/pdf-reportlab/SKILL.md
T
2026-04-21 12:31:58 +08:00

256 lines
8.2 KiB
Markdown
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.
---
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", ...]`,未来版本会处理。