name: cn-pdf-report-typeset slug: cn-pdf-report-typeset displayName: 中文PDF报告排版·手机上也看得清的交付级版式 description: 中文报告做成 PDF,常见翻车三件套:字体乱码、表格错位、字号小到客户在手机上根本看不清。这套是交付级的排版标准加可直接跑的模板。
交付标准(按「客户在手机上看」倒推):
- 大字号:正文 ≥16pt、表格 ≥15pt、H2 ≥17pt、大标题 ≥30pt——不是 12pt 那种印出来都费劲的
- 紧凑不注水:只在封面后分页一次,正文自然流动,禁止每章强制分页撑页数
- 表格智能换行:单元格用 Paragraph 加中文断行,长内容不溢出页面
- 页面均衡:生成后自查每页字符数,避免一页只有两行
- A4 竖排、边距 18-20mm、直接打印可用
附:微软雅黑系统字体直接调用(不用下载字体文件)、封面/页脚页码/核心结论框模板、排版自检脚本(自动查字号是否达标、有没有多余分页)。
适用:可行性研究报告、尽调报告、项目分析、任何要交付给客户的中文正式文档。纯本地 Python,零 API Key。
触发词:做成PDF、生成PDF、中文PDF、报告排版、雅黑、乱码、表格溢出、手机看不清、可研报告、交付文档。 version: 1.1.0 author: 九品锦锂e summary: reportlab+微软雅黑出正式中文PDF:大字号手机可读、表格不溢出、紧凑不硬撑页数。 license: MIT metadata: version: "1.1.0"
中文专业PDF报告生成
🚨 用户两次纠正的血泪教训(2026-08-20,最高优先级,禁止再犯)
- 紧凑排版:严禁"一点点内容就一页"。禁止每个章节加 PageBreak 撑页数——只在封面后加一个 PageBreak,正文让它自然流动。之前6页的内容压到4页才对。
- 大字号(手机可读,客户在手机看):正文≥16pt、表格≥15pt、H2≥17pt、H1≥21pt、标题≥30pt。14pt正文/12pt表格太小,客户手机看不清,用户会当场批评。
- 这两条在 memory 排版标准里也写着,但执行时仍会偷懒用 14pt/12pt + 每章分页——生成脚本前必须自查字号常量和 PageBreak 数量。
⭐ 全军排版标准(2026-08-18 用户钦定,PDF/Word 通用)
这套排版逻辑是标准,以后所有 PDF 和 Word 文档都参照执行:
- A4竖排(210×297mm)——不用横排/其他尺寸
- 大字号,手机可读:正文≥14pt、表格≥12.5pt、H1≥20pt、标题≥32pt
- 表格智能换行不溢出:单元格用 Paragraph + wordWrap="CJK"
- 页面内容均衡:不用强制分页,KeepTogether 智能分块,生成后检查每页字符数(除封面/收尾页外,各页应均衡)
- 排版紧凑:边距 18-20mm、表格 padding 3-4、行距紧凑,直接打印可用
Word 文档同逻辑:A4竖排、正文≥小四/四号(12-14pt)、表格列宽自适应不溢出、页面均衡。
触发条件
- 用户/客户要求"把结论做成PDF"、"发PDF上来"、"可研给我"、"结论做出来发我"
- 需要交付专业排版的中文分析报告(封面 + 表格 + 页脚 + 核心结论框)
- 参考实例:国巡机器人尽调报告(5页,02-项目成果/国巡机器人项目可行性尽调与分析报告.pdf)
环境与安装
- 用 hermes venv 解释器:
python - 首次安装:
$V -m pip install reportlab(本机已装 5.0.0) - 字体:Windows 系统自带微软雅黑,无需下载字体文件
C:/Windows/Fonts/msyh.ttc(常规,subfontIndex=0)C:/Windows/Fonts/msyhbd.ttc(粗体)C:/Windows/Fonts/msyhl.ttc(细体)
标准工作流
- ⚠️ 交付铁律(2026-09-02 用户明示,最高优先级):PDF 生成后必须
MEDIA:发到飞书聊天框,绝不能只存知识库 / 只给路径 / 只发要点。用户原话:"PDF文档你要发到飞书聊天框这里来,要不然我怎么看到呢?就是要形成铁律一样的东西。"- 用户在手机端看,点飞书消息里的文件才看得到;存进
02-项目成果/只是备份,不是交付。 - 交付格式:
<你的输出目录>/<文件名>.pdf(绝对路径)。 - 发完 PDF 附核心结论要点(30秒版)。先发飞书,再存知识库复盘。
- 用户在手机端看,点飞书消息里的文件才看得到;存进
- 取内容:session_search 查历史分析——用户常对附件回"结论做成PDF",先找回之前出过的研判/尽调内容,不要重写
- 写脚本:复制
templates/reportlab_chinese_template.py,只替换内容区(封面文字、章节、表格数据) - 生成:python 运行 → 输出到 Obsidian
Hermes工作成果/02-项目成果/(命名:<主题>项目可行性尽调与分析报告.pdf) - 验证:pymupdf 打开读 get_text,确认页数 / 中文渲染 / 末页结论存在
- 交付:回复
MEDIA:D:\...pdf路径 + 核心结论要点(30秒版),不啰嗦
⭐ 配色铁律(2026-09-02 用户钦定,喜用神金/土——最高优先级)
用户八字喜金喜土,忌水(蓝/黑)忌火(红/紫)。所有 PDF 一律用金土系配色,禁止再用蓝色。颜色变量见下方 palette。
- 章节大标题 H1:金属金底 + 白字(
backColor=#C9A227,textColor=white,borderPadding=(6,8,6,8))——金底白字,不是蓝色文字。 - 表格表头:金属金底 + 白字(
header_bg=#C9A227,FONTNAME=MSYHBD)——金底白字。 - 正文:纯黑
#1f1f1f。 - 副题 / 分隔线:深土金
#B8860B(去红色)。 - 代码块:土金
#8a6d1f(去蓝色)。 - 斑马纹:浅米金
#FBF3DE(暖调,不是灰白)。 - 网格线:浅土金
#D9C9A3。
金色系 palette(reportlab 用)
C_DARK=colors.HexColor("#1f1f1f") # 正文近黑
C_RED=colors.HexColor("#B8860B") # 强调/副题-深土金
C_BLUE=colors.HexColor("#C9A227") # 表头/标题底-金属金
C_LGRAY=colors.HexColor("#FBF3DE") # 斑马纹-浅米金
C_MGRAY=colors.HexColor("#D9C9A3") # 网格线-浅土金
C_WHITE=colors.white
行内强调
S_CODE=st(..., textColor=colors.HexColor("#8a6d1f"));行内<span color='#B8860B'>(不用#c0392b红色)。
排版要点(模板已封装)
- 封面页:大标题 + 金色副题 + 日期/来源 + 核心结论框(深土金底白字表)
- 正文:H1 章节(金属金底白字 MSYHBD 21pt)+ H2 小节(17pt)+ 正文 15pt(2026-08-18 用户指示:手机端可读,字号必须大)
- 表格:
make_table()表头金属金底白字 + 斑马纹(ROWBACKGROUNDS)+ grid + repeatRows=1,表格字号 13pt - 页脚:onPage 回调画报告名 + 页码
- 强调:Paragraph 支持
<b>/<span color='#B8860B'>行内标记 - 长报告分章节:PageBreak 分隔封面与正文
⚠️ 字号铁律(2026-08-18 用户强调):PDF 是给手机看的,正文≥15pt、表格≥13pt、H1≥21pt、标题≥34pt。原模板 10.5pt/9.3pt 太小,手机看不清——所有新报告必须用大字号。字号大导致页数变多是正常的(如5页→8页)。
⭐ 智能排版铁律(2026-08-18 用户强调,打印友好)页面内容必须均衡——不能某页只有200字符、某页挤800字符。方法:
- 少用 PageBreak:只在封面后强制分页,正文让它自然流动
- 用 KeepTogether 包裹"标题+内容":
keep(H2标题, 表格/段落)防止表格被拆到两页、防止标题孤悬页底 - 封面独立一页:封面元素 + 一个 PageBreak
- 每章用小节自然分隔:H2标题 + 表格/要点,不强制每章换页
- 验证均衡度:生成后用 pymupdf 检查每页字符数——正常报告除封面/收尾页外,各页字符数应接近(如600-900字符),差异过大就要调整
- 排版紧凑:边距 20mm/16mm(不要过大),表格 padding 4-5,行距 leading=22(14pt正文),减少空白浪费
坑(PITFALLS)
- ⛔ 禁止两端对齐(TA_JUSTIFY)——字间距被拉宽(2026-09-03 用户最在意的手机可读问题):reportlab 的
TA_JUSTIFY会对中文段落强制把每行拉伸到右边界,某行字少时字间距被撑得极宽、极难看。所有正文/列表/段落一律用TA_LEFT左对齐。- 错误:
b=dict(..., alignment=TA_JUSTIFY, ...) - 正确:
b=dict(..., alignment=TA_LEFT, ...) - 排查:正文/列表样式若继承
st()且alignment=TA_JUSTIFY,必改左对齐。
- 错误:
- ⛔ 封面内容溢出成空白孤页(2026-09-03):封面元素(主标题+副题+说明+核心一句话)必须全部压在第1页内,绝不能让封面内容被推到第2页形成"一小段字+大片空白"。封面内容过多时删掉大段引言、压缩列表间距,让它在1页内放完。每章之间只保留封面后一个 PageBreak,正文自然流动。
- ⛔ 字号/边距要同频调整:放大字号后,边距要同步收窄(本会话 22mm→17mm)才能让每页装更多、不产生孤页。字号 17pt 正文 + 17mm 边距是实测平衡点;表格字号 14.5pt。
- ⛔ KeepTogether 包"自定义对齐小表"会导致页高误判→孤页(2026-09-06 实测):用 KeepTogether 包裹"标题+正文+自定义两列对齐表(field_table)"+价格时,reportlab 对该块的高度估算不准确,常把整块推到下一页,造成上一页只剩"提示句/表尾一行"变成几十字符的孤页(本会话第2页只有76字符)。修复:① 首屏"服务一览表+引言"用 KeepTogether 包整个 head_block 并压缩表格字号/行距确保能在第1页放完;② 自建段渲染后用 pymupdf 逐页看字符数,<120字符的页就是孤页,立即压缩前页表格/删多余提示句。
- 区分(2026-09-10 实测):只包"表格本身"是准且该做的;包"标题+正文+表+价格"大块才容易误判。 让表格工厂函数统一
return KeepTogether(t)(表格整体不跨页),配合只保留封面后一个 PageBreak,实测把 7 页(第5页仅 77 字)修成 6 页、每页 301/451/548/407/551/240 字。表格被分页切断、切出的 1–2 行独占一页,是孤页的头号成因;发现"某页只有表头/末行"先查这个。
- 区分(2026-09-10 实测):只包"表格本身"是准且该做的;包"标题+正文+表+价格"大块才容易误判。 让表格工厂函数统一
- ⛔ 两列"标签+内容"对齐表的标签列不能太窄——中文标签会折行成孤字(2026-09-06):用两列对齐表做"能做什么/做到多深/您能得到什么"时,标签列若太窄(如 32mm),"您能得到什么"会折成"您能得到什"+"么",孤字极难看。修复:标签列宽度要 > 最长标签单行放置(40mm 左右),或把标签改短("您能得到什么"→"带来什么")。
- ⛔ 长句末尾的"。"会单独折行成孤儿标点(2026-09-06):"做到多深/带来什么"这类长字段,整句末尾的"。"在换行时被甩到下一行开头。修复:字段值较长、有折行风险时去掉末尾句号(或用规避短句),宁可不要句号也不留孤点。
- ⛔ "定制技能"要写对口径(2026-09-06 用户纠正):不要把"定制/定做skills"误写成"定时任务"。客户版统一用"专属技能定制"并口语化解释"技能=一件AI能照着干的具体本事"(见 enterprise-ai-service-blueprint)。
.ttc字体必须subfontIndex=0,否则报错或乱码- 加粗必须注册 MSYHBD 并设置 fontName——
<b>标签在无粗体字体注册时静默不加粗 <>的转义要分两种情况(2026-09-10 实测踩坑):正文里的裸尖括号(如"精度<10mm")才转义成</>;而<b>、<span color=...>是内联标记,必须原样传给 Paragraph 才生效。- ⛔ 致命写法:单元格文本一律
.replace('<','<')全转义 → 表格里的加粗会原样显示成<b>字样(本会话第2页整列都印成<b>点名支持…,交付前才发现)。 - ✅ 正确:表格单元格只转义裸
&(v.replace('&','&')),保留内联标签;内容里的裸尖括号由调用方自己写<。 - 自检:交付前抓全文,
'<b>' in text或'</b>' in text为真即说明标签被当文字渲染了,必须回查 make_table 的转义。
- ⛔ 致命写法:单元格文本一律
- 表格列宽总和 ≤ A4 可用宽度(210mm - 左右边距),否则溢出换行难看
- 表格中文溢出(2026-08-18 重要修复):reportlab 的 Table 直接放长中文不会自动换行,会溢出到右边!必须用 Paragraph 包裹单元格 +
wordWrap="CJK"。已在 make_table 内置_cell_style()处理——所有单元格自动换行。验证:用 pymupdf 查get_text('words')的 x2 是否越过内容区右边界。- 阈值必须按实际边距算,不能写死 540pt(2026-09-10 实测):右边界 =
A4[0] - rightMargin(A4 宽 595.3pt;16mm 边距 → 549.9pt,17mm → 547pt)。写死 540 会把正常换行误报成溢出,白折腾。 - 必须排除页脚:onPage 画的"第 N 页"永远超出内容区,判定时要按文本过滤掉(如
w[4] != '页'),否则每次都有假警报。 - 判定示例:
[w for w in page.get_text('words') if w[2] > page.rect.width - rightMargin + 1 and w[4] != '页']
- 阈值必须按实际边距算,不能写死 540pt(2026-09-10 实测):右边界 =
- A4竖排铁律(用户强调):所有PDF必须 A4 竖排(210×297mm),pagesize=A4 默认就是竖排,不要改 landscape
- 交付前必须用 pymupdf 验证一次(页数/中文/乱码/无溢出)——验证通过才发
- ⛔ 写生成脚本时,Python 字符串一律用单引号定界(2026-09-10 实测):工具链会把中文引号 “ ” 规范化成半角
",字符串外层若用双引号定界,正文里的引号会提前闭合字面量 → 报SyntaxError: invalid character '+' (U+FF0B)之类(报错点看着像全角字符非法,真因是引号截断,会误导排查方向)。正文里的中文引号要么改用「」,要么整条字符串用'...'包。 - ⛔ 绝不要"边写边读"同一个文件:
open(f,'w').write(open(f,encoding='utf-8').read() + add)会先把文件截断成 0 字节再读,结果是原文全丢、只剩追加段(本会话知识库笔记从 13KB 缩成 3.8KB,差点交了残档)。追加内容时先txt = open(f).read()存变量,再open(f,'w').write(txt + add);或直接用 patch 工具改。 - 文件 >100KB 通常正常(嵌入字体子集)
验证命令
$V -c "import pymupdf; d=pymupdf.open(r'D:\...\报告.pdf'); print(len(d)); print(d[0].get_text()[:120]); print('末页含结论:', '结论' in d[-1].get_text())"
关联
- 内容框架:
feasibility-study-writing、dual-lens-business-analysis(双光研判出报告前先加载) - 报告审查铁律:严谨报告出稿前执行
report-reflection-review - PPT 场景:
ultimate-ppt-master - 模板:
templates/reportlab_chinese_template.py(旧);templates/reportlab_gold_template.py(推荐):金色系 + 左对齐 + 17pt大字 + 17mm紧凑边距 + 无孤页。已修make_table()内联标签处理(表格单元格内<b>/<span>现在真正生效,不再被转义成字面