昨天下午三点多,我正在跑一份别的活。群里跳出一条消息,来自一个客户:分离式 CSS 的 HTML,能不能转成 pptx。
我当时的第一反应是——这问题问错对象了。转格式这种事,随便找个在线工具,或者让 AI 写个脚本,十分钟的事。
但接下来六个小时证明:这个需求里真正难的部分,跟「转格式」四个字一点关系都没有。
一、客户真正要的是什么
先说说这份课件的性质。
我们给一个客户做了三套课程的 HTML 版——每套 14 页,分离式 CSS,浏览器里翻页,排版、间距、字体、强调色都调过。这个形态很好用:投屏流畅,手机上看也不塌,改一处 CSS 全篇跟着变。
但客户看了一眼之后,提的不是「好不好看」,是「我要能改」。
这个区别很关键。HTML 是给人看的,PPTX 是给人改的。
一份课件在客户手里,不是终点,是起点。他要拿它去讲课,而讲课的人一定会做三件事:
- 删掉今天不讲的页
- 在里面加一个自己的案例
- 改几句措辞,换成自己习惯的说法
这三件事在 HTML 里全都做不了——他得找我们,然后我们改 CSS、重新部署。所以他要的不是「一份 PPT」,是控制权。
需求描述得很轻:「分离式 CSS 的 HTML 能不能转 pptx。」翻译过来其实是:这份东西我要能自己动,但我可不想重新做一遍。
二、第一版:截图版,三十秒就想到了,二十分钟做完了
第一条路最简单:把每页 HTML 用 Playwright 截成图,铺到 PPT 里。
这条路 16:22 就交出去了。3 套 × 14 页 = 42 张图,每张铺满一页幻灯片。视觉效果和 HTML 一模一样——因为它本质上就是 HTML 的照片。
但这条路有个致命的边界:它交出去的是照片,不是文字。
客户拿到手里,双击任意一个文本框——没有文本框。图片就是图片,一行字都点不动。
这条路的定位很清楚:保真,但不可编辑。 适合拿去播、拿去投屏、拿去发给别人看,但客户想改一个字,还得回来找我们。
所以当天晚上六点半,客户回了一句:「好,继续修可编辑版。」

三、第二版才是真正的活:让机器拆解一份排版
可编辑版的思路也不复杂:不截图,把 HTML 的每个元素读出来,用 pptxgenjs 在 PPT 里重画一遍。
每个标题变成 PPT 的原生文本框,每个段落变成文本框,每个色块变成矩形,每张图变成图片对象。最终交付的三套 14 页课件里,各有 240、228、242 个原生文本元素——每一个都能双击、能改字、能挪位置。
听上去是体力活。真正难的地方只有一个,而且只有一处:
换行。
四、中文没有空格,所以中文没有合法的断点
这是整件事里唯一一个让我坐直了的瞬间。
HTML 里做排版,浏览器负责换行。它知道 CSS 的宽度是多少,它知道这段文字会不会溢出,它在合适的字之间断行——我们什么都不用管,只要把 CSS 写对。
换成 PPT,这件事得我们自己算:这一行文字,放得下几个字?
我们第一版的做法很自然:用 Playwright 去量——在浏览器里把这段文字渲染出来,量出它的像素宽度,按比例换算成 PPT 的坐标,然后决定在哪个字后面换行。
逻辑没问题,结果不对。
问题出在字体上。Playwright 量的是苹方,PPTX 打开之后走的是微软雅黑。同一个字,两个字体,不一样的宽度。 你在浏览器里量出「这一行刚好放得下 22 个字」,PPT 里用另一个字体渲染,第 22 个字可能已经挤出去了,或者只放了 21 个字就换行了。
在英文里,这个偏差通常能忍——英文有空格,换行位置本来就有很多选择,错一个词观感差别不大。
中文不行。 中文是一串没有空格的方块,断点只能落在字与字之间,没有任何弹性空间。宽度算错一个字,观感上不是「稍微挤了点」,是这一行的字直接压到了边线外面。
更麻烦的是这件事没有报错。PPT 不会告诉你字体不一样,它只会安安静静地按它的字体渲染,然后溢出。你打开文件,看到的是排版崩了,但没有任何一行日志会告诉你为什么。
所以最后的修法是把判据换掉:不用浏览器量的宽度,用 PPT 实际字体的实测字宽来算换行。 边界条件从「浏览器里的像素」变成「PPT 打开后的真实结果」——判据落到最终容器这一侧,问题才真的关了。
这句话我后来想了很久:我们一开始踩坑,不是因为技术不够,是因为我们拿错误的尺子量对了。

五、两种交付并存的行业含义
活干完了,但我想说的不是「我们解决了换行」。
我想说的是「一次交付,两种形态」这件事本身。
做内容交付的人这两年应该都有体感:客户要的东西越来越像一组双胞胎。一份给眼睛,一份给手。一份保真,一份可改。一份用来消费,一份用来生产。
看视频的人和剪视频的人,要的不是同一个文件。看页面的人和改页面的,要的也不是同一个文件。
而行业里的默认做法,往往是只给一份,然后让客户自己在两者之间妥协——给他 PPT,他忍排版崩;给他 HTML,他忍改不了。我们以前也是这样:选一个形态交出去,剩下的问题归类为「客户那边的事」。
昨天这六个小时给我的教训很实在:
「转换」这个词本身是错的。 转换暗示有一个源、一个目标、一次映射。但当两种形态的消费方式根本不同时,它们不是同一个东西的两个格式——它们是两个交付物。两个交付物就得分别设计,分别验收,各自回答「这个形态的边界在哪」。
截图版和可编辑版不是「同一个东西的两种导出」,是两种承诺:一个承诺你看到的就是我看到的,一个承诺你能改你想改的。
承诺不一样,验收标准就不能共用一套。

六、一条能带走的判断
如果你也在做「一份内容多形态交付」的活,我建议带走这三个问题:
第一,先问清客户要的是保真还是可控。 这两个需求不能用同一份文件满足,而且客户往往说不清自己更要哪个——他只会说「你能不能转成 PPT」。你要替他问出来:你是要拿它播,还是要拿它改?
第二,判据要落到最终容器那一侧。 我们在源容器(浏览器)里量得很精确,但交付物在目标容器(PPT)里渲染。任何「源侧精确、目标侧不准」的测量,都等于没测。换行是这样,字号是这样,颜色也是——测试的判据,必须和真实消费的位置一致。
第三,中文排版没有免费的午餐。 英文排版里能容忍的宽度误差,到中文就是溢出。这不是中文字体的问题,是中文没有空格这个结构事实——没有断点弹性,就没有容错空间。
收尾
今天早上我又把两个版本的 PPT 打开看了一遍。
截图版 42 页,翻起来干脆利落,每一页都跟我们做的 HTML 分毫不差。可编辑版 42 页,每一页有一两百个能被点开的元素,像一份还没组装完的乐高。
同一个客户的同一门课,两个文件,两种安全感。
客户后来说了一句「这个我们后面自己就能改了」。这句话有点像夸奖,其实也挺像压力——他把控制权拿走的那一刻,我们能提供的东西就得换一种了。
从「我们做给你看」,换到「我们做给你改」。
这两种活,还真不是同一门手艺。
本文基于 2026-09-16 的真实交付记录:三套课程 HTML → 双形态 PPTX(截图版 + 可编辑版),工具链沉淀在项目本地 ppt/_tools/ 目录下,含独立依赖,不依赖临时目录。