让 AI 生成的中文 PPT 不再「字顶穿框」。
根治 python-pptx 中文文字溢出,专为 AI 生成 PPT 设计。
真字体测量 + 精确折行 + 自动缩字 + 交付审计。
已校准:微软雅黑 / 宋体 / 黑体 / 楷体 / 仿宋 / 等线。
用 python-pptx 做中文 PPT 时,文字溢出是最常见的缺陷。根因是 python-pptx 不做文字度量——它把文字放进固定大小的框里,然后默认能放下。一旦中文字多了、换行多了、字体不同了,字就顶穿框、甚至顶穿页面。
现有方案靠「中文字 ≈ 1em」估算,误差可达 14%(因为不同字体的行高/字宽差异很大),正好落在「看着差不多、实际放不下」的坑里。
| 场景 | 旧方案(估算) | 本引擎(真字体测量) |
|---|---|---|
| 微软雅黑 14pt 3 行 | 0.583in ❌ 溢出 | 0.781in ✅ 刚好 |
| 宋体 14pt 3 行 | 0.583in ❌ 溢出 | 0.677in ✅ 刚好 |
| 正文框固定高度,字多时 | 手动试错 / 大概缩 | fit_size 自动缩到放得下 |
校准方法:生成测试 PPTX,每页在引擎预测的文本底部画红色标线,PowerPoint 目视验证。参见
workspace/calibration_v3.pptx。
真字体测量 → 精确折行 → 精确高度 → 自动缩字/分页 → 交付前审计。
┌─ resolve_font:别名 → 真实字体文件
原文本 ─→ 逐字量宽 ──┼─ 折行(尊重 \n) ─→ 精确行数
└─ 读 font metrics + lineGap → 精确行高
│
▼
fit_size / clip_to_box / 分页
│
▼
audit_pptx:交付闸门(BOUNDS + OVERFLOW + 字体替换)
│
▼
美学 lint:对齐 / 假斜体 / 对比度 / 字号节奏 / 重叠
这个工具是给没有视觉的大模型用的。
AI 做 PPT 时最大的短板就是"看不见"。它不知道文字放进去会不会溢出、两个框是不是重叠了——因为它看不到渲染后的画面。
传统方案需要截图 + 视觉模型 review,成本高、速度慢、数据还可能出境。
这个引擎不一样:它用字体文件算,不用眼睛看。 所有测量都是纯数学(逐字量宽、读字体度量表、几何重叠检测),零视觉、零 token、不挑模型。
| AI 做 PPT 的痛点 | 本引擎的解法 |
|---|---|
| "看不见"字溢出 | 用真字体文件量 → 精确到 0.001 英寸 |
| "看不见"框重叠 | 算几何交集 + z-order → 纯逻辑判定 |
| "不知道"字体会不会对不齐 | 读字体文件的 lineGap 表 → PowerPoint 级精度 |
| 每次生成都要视觉模型 review | 一次性数学测量 → 不花 token、不出境 |
经 PowerPoint 实际渲染校准验证(14pt 中文本, 3 行):
| 字体 | 引擎测量(含lineGap) | vs PowerPoint |
|---|---|---|
| 微软雅黑 | 1.34× 字号 | ✅ 匹配 |
| 宋体 / 黑体 / 楷体 / 仿宋 | 1.16× 字号(lineGap 修正) | ✅ 匹配 |
| 等线 | 1.05× 字号 |
校准方法:生成测试 PPTX,每页在预测底部画红色标线,PowerPoint 目视验证。见
workspace/calibration_v3.pptx。
pip install cjk-pptx-engine或从源码安装:
git clone https://github.com/Poy-Coder/cjk-pptx-engine.git
cd cjk-pptx-engine
pip install -e .想让你的 AI(Claude / ChatGPT 等)做中文 PPT 时不溢出?直接把 PROMPT.md 发给它就行。
pip install git+https://github.com/Poy-Coder/cjk-pptx-engine.gitfrom pptx import Presentation
from cjk_measure import safe_textbox
prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[6])
# 自动量字、自动缩到刚好放下、自动钉字体
safe_textbox(slide, left=1, top=1, width=8, height=1.2,
text="违法行为事实清楚,证据确凿充分,应予处罚。",
font_name="仿宋", font_size=14)safe_textbox 跟 add_textbox 用法一样,但它会在后台:
- 用真字体量文字占多高 → 放不下就自动缩字号
- 钉死 ea 字体槽 → 换机打开不对不齐
- 关掉 PowerPoint 自动缩字 → 不会跑偏
from cjk_measure import measure_h, wrap_lines
text = "根据《中华人民共和国行政处罚法》第五十四条的规定,行政机关应当依法作出行政处罚决定。"
# 量一下这段文字在 8 英寸宽的框里占多高
h = measure_h(text, pt=14, box_w_in=8.0, font_name="微软雅黑")
print(f"需要 {h:.3f} 英寸高") # → 0.781 in (3行)
# 看具体怎么折行的
lines = wrap_lines(text, pt=14, box_w_in=8.0, font_name="微软雅黑")
for i, l in enumerate(lines, 1):
print(f" 行{i}: {l}")from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.enum.text import MSO_AUTO_SIZE
from cjk_measure import measure_h, fit_size
prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[6])
text = "违法行为事实清楚,证据确凿充分,且依据有关法律法规应当给予行政处罚的。"
box_w, box_h = 8.0, 1.2 # 英寸
# 先量——用渲染时的真实字体
predicted = measure_h(text, pt=14, box_w_in=box_w, font_name="仿宋")
print(f"文字需要 {predicted:.3f}in,框高 {box_h:.1f}in")
# 如果放不下,自动缩到最大可容字号
if predicted > box_h:
safe_pt = fit_size(text, box_w, box_h, want_pt=14, font_name="仿宋")
print(f"缩到 {safe_pt:.0f}pt")
else:
safe_pt = 14
# 创建文本框(尺寸精确,关掉自动缩字)
txBox = slide.shapes.add_textbox(
Inches(1), Inches(1), Inches(box_w), Inches(box_h)
)
tf = txBox.text_frame
tf.word_wrap = True
tf.auto_size = MSO_AUTO_SIZE.NONE
tf.margin_left = Inches(0)
tf.margin_right = Inches(0)
tf.margin_top = Inches(0)
tf.margin_bottom = Inches(0)
p = tf.paragraphs[0]
p.line_spacing = 1.0
run = p.add_run()
run.text = text
run.font.size = Pt(safe_pt)
run.font.name = "仿宋"
# 交付前审计
import subprocess
subprocess.run(["cjk-audit", "my_deck.pptx"])# 溢出 + 越界 + 字体替换 + 重叠检测 + 美学检查
cjk-audit my_deck.pptx
# 输出示例:
# slide 13.333x7.500 in
# — BLOCKING: bounds/overflow = 0
# — AESTHETIC: P1=0 others=3 (纯逻辑建议)
# RESULT: PASS — 0 越界 · 0 溢出。# 严格模式(美学 P1 也阻断交付)
cjk-audit my_deck.pptx --strict# 纯逻辑检查:对齐、重叠、对比度、假斜体、字号节奏…
python -m cjk_measure.aesthetic_lint my_deck.pptx# 查看本机已安装/待替换的字体
cjk-measure registry
# 量一段具体文字
cjk-measure measure "违法行为事实清楚" --font 宋体 --size 14 --width 8用 txt() 助手函数替换 python-pptx 原生的 add_textbox(),所有文字框自动走量字逻辑:
from pptx.util import Inches, Pt
from pptx.enum.text import MSO_AUTO_SIZE
from cjk_measure import measure_h, fit_size, clip_to_box
def add_textbox(slide, left, top, width, height, text, font_name, font_size,
line_spacing=1.0, fit=False, clip=False):
"""
安全的文本框:自动缩字/裁剪,钉死东亚字体,不溢出。
参数:
fit: True → 文字放不下时自动缩小字号
clip: True → 超过 N 行时裁剪加 "…"
"""
if fit:
font_size = fit_size(text, width, height, font_size,
font_name=font_name, line_spacing=line_spacing)
tb = slide.shapes.add_textbox(Inches(left), Inches(top), Inches(width), Inches(height))
tf = tb.text_frame
tf.word_wrap = True
tf.auto_size = MSO_AUTO_SIZE.NONE
tf.margin_left = Inches(0)
tf.margin_right = Inches(0)
tf.margin_top = Inches(0)
tf.margin_bottom = Inches(0)
p = tf.paragraphs[0]
p.alignment = PP_ALIGN.LEFT
p.line_spacing = line_spacing
run = p.add_run()
run.text = text
run.font.size = Pt(font_size)
run.font.name = font_name
run.font.color.rgb = RGBColor(0x33, 0x33, 0x33)
# 钉死东亚字体槽(CJK 走 ea,换机打开也不会走样)
from pptx.oxml.ns import qn
rPr = run._r.get_or_add_rPr()
ea = rPr.makeelement(qn('a:ea'), {'typeface': font_name})
rPr.append(ea)
return tb
# 使用示例
slide = prs.slides.add_slide(prs.slide_layouts[6])
add_textbox(slide, 1, 1, 8, 1.5,
"违法行为事实清楚,证据确凿……",
font_name="仿宋", font_size=14, fit=True)不管 PPTX 是谁生成的(GordenPPT、ppt-master、Copilot、ChatGPT…),交付前跑一遍审计:
# 检查溢出 + 越界 + 字体替换 + 重叠 + 美学
cjk-audit generated_deck.pptx
# 集成到 CI / 自动化流程
python -c "
from cjk_measure import audit_pptx
blocking, subs, sw, sh = audit_pptx.audit('generated_deck.pptx')
if blocking:
print(f'❌ {len(blocking)} 个阻塞问题,拒绝交付')
exit(1)
print('✅ 通过')
"AI 在写 python-pptx 代码时,按这个模板做就能避免溢出:
1. 规划内容 → 用 measure_h 算每段文字的真实占高
2. 够放 → 直接放;不够 → fit_size 缩字号 或 分页
3. 创建文本框 → 钉死 ea 字体槽
4. 交付前 → cjk-audit 审计闸门
对应到提示词工程,就是让 LLM 在生成代码时默认套用 add_textbox() 助手函数(见方式 A),而不是直接调 python-pptx 的 add_textbox()。
| 工具 | 接入点 | 方式 | 说明 |
|---|---|---|---|
| ppt-master | svg_to_pptx/drawingml_utils.py → estimate_text_width() |
紧集成 | 替换宽度估算函数为 wrap_lines(),并在 convert_text() 中用 measure_h() 算行高 |
| GordenPPT | 模板渲染器的文本框创建步骤 | 紧集成 | 用 add_textbox() 助手替换原生 add_textbox() |
| 任意 python-pptx 脚本 | 审计 | 松集成 | 生成后跑 cjk-audit deck.pptx |
接入遇到问题?提 Issue 或 PR。
不同操作系统、Office 版本、字体文件版本可能导致渲染差异。仓库提供了自校准脚本:
# 在本机跑一遍,验证引擎精度
python scripts/self_calibrate.py这会生成一个 self_calibrate.pptx,每页包含:
- 🔴 红线 = 引擎预测的文本底部
- 🔵 蓝线 = ×1.10 安全系数
打开 PPTX 看文字底部与哪条线对齐,把结果截图贴到 Issue 里,帮更多人校准 🦊
| 函数 | 用途 |
|---|---|
wrap_lines(text, pt, box_w, ...) → list[str] |
逐字量宽,精确折行 |
measure_h(text, pt, box_w, ...) → float |
精确渲染高度(英寸) |
fit_size(text, w, h, want_pt, ...) → float |
最大可容字号 |
clip_to_box(text, pt, w, max_lines, ...) → str |
裁 N 行加"…" |
resolve_font(name, bold) → FontResolution |
字体别名→真实文件路径 |
registry_report() → dict |
字体安装状态报告 |
支持 6 个度量类、30+ 款主流中文字体,覆盖:
Windows 标配:微软雅黑、宋体、黑体、楷体、仿宋、等线
Office 标配:等线(DengXian)
政务公文:方正小标宋、方正仿宋_GBK、方正楷体_GBK、方正黑体_GBK
思源系列:思源黑体/宋体、Noto Sans CJK/SC
macOS 标配:苹方(PingFang SC)
现代 UI:阿里巴巴普惠体、HarmonyOS Sans、MiSans、OPPO Sans
标题/装饰:得意黑、优设标题黑
未安装的字体自动按字宽度量类寻找替身(偏宽 = 保守 = 不漏判溢出)。详见 measure_engine.py 的 _ALIAS_CLASS 表。
无需视觉模型,纯逻辑判定(aesthetic_lint.py):
| 检查项 | 说明 | 级别 |
|---|---|---|
| TEXT_OVERLAP | 两个文本框重叠 | P1 |
| TEXT_COVERED | 填充形状覆盖文字 | P1 |
| ITALIC_CJK | 中文设斜体(假斜) | P1 |
| LOW_CONTRAST | 文字/背景对比度不足 | P1/P2 |
| ALIGN_NEAR_MISS | 近似对齐(差一笔) | P2 |
| SIZE_SOUP | 字号级数过多 | P2 |
| TITLE_INCONSISTENT | 标题跨页不统一 | P2 |
| PALETTE_SPRAWL | 用色过多 | P3 |
| EDGE_HUG | 文字贴安全边 | P3 |
本项目最初是 Poy-Coder 内部工作流的一部分,用于解决 AI 批量生成中文 PPT 时「文字溢出」这一系统性缺陷。经过 PowerPoint 实际校准验证后整理成独立工具开源。
MIT © 2026 Poy-Coder
项目早期,欢迎参与。 校准测试、字体覆盖、新功能……看 CONTRIBUTING.md 找你能帮上忙的方向 🦊