文章目录
-
- 前言
- 一、先看看外面那些「可编辑PPT」都怎么玩
-
- [1. 三条技术路线](#1. 三条技术路线)
- [2. 路线C的坑,得单独说](#2. 路线C的坑,得单独说)
- [3. 两个必须知道的许可陷阱](#3. 两个必须知道的许可陷阱)
- [4. 调研结论:一个外部仓库都不引](#4. 调研结论:一个外部仓库都不引)
- 二、回头翻自家仓库,发现好东西还真不少
-
- [1. 能直接复用的零件](#1. 能直接复用的零件)
- [2. 不能复用的:现有编辑器压根不是PPT编辑器](#2. 不能复用的:现有编辑器压根不是PPT编辑器)
- 三、七个决策,改起来代价最大的先定死
-
- [1. 不让LLM直接吐坐标](#1. 不让LLM直接吐坐标)
- [2. Layer存「语义角色+token」,不存绝对样式](#2. Layer存「语义角色+token」,不存绝对样式)
- [3. 模板=风格×组件,不做「整页模板」](#3. 模板=风格×组件,不做「整页模板」)
- [4. 字体:生成端锁死+服务器装字体](#4. 字体:生成端锁死+服务器装字体)
- [5. 回渲QA放生成侧,只查结构性错误](#5. 回渲QA放生成侧,只查结构性错误)
- [6. 导出走独立队列,别和渲染共用慢池](#6. 导出走独立队列,别和渲染共用慢池)
- [7. 导出是单向投影](#7. 导出是单向投影)
- 四、能力边界:哪些能吹,哪些只能看看
-
- [1. 能做到的](#1. 能做到的)
- [2. 打折扣的(能做,但别期待满分)](#2. 打折扣的(能做,但别期待满分))
- [3. 做不到的](#3. 做不到的)
- [4. 产品文案的红线](#4. 产品文案的红线)
- 五、实施方案
-
- [1. 模块划分](#1. 模块划分)
- [2. 核心数据模型](#2. 核心数据模型)
- [3. 落库](#3. 落库)
- [4. 接口](#4. 接口)
- [5. 导出映射](#5. 导出映射)
- [6. 里程碑](#6. 里程碑)
- [7. 机器资源](#7. 机器资源)
- 六、风险清单
P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看, 传送门https://blog.csdn.net/qq_34419312
前言
改个PPT里的字有多难,干过的人都知道。你把文件发给客户,客户说"把第二页那个'明天'改成'后天'"。你打开一看,那是个图片。那一刻你终于理解了什么叫薛定谔的PPT------你以为你能改,打开才知道能不能。
今天聊的就是这事儿。在现有「数字人讲PPT」之外,我们准备加一条独立产品线:输入主题/文档 → 生成整套幻灯片 → 网页里自由编辑 → 导出能改字的 .pptx / .pdf。
一句话说清楚跟老产线的区别:老产线是「PPTX进,MP4出」,这条是「内容进,PPTX出」。一个是把菜端给你,一个是把菜谱给你,你自己炒。
本版不做三件事:不做TTS/讲稿配音,不做视频导出,页面上保留notes字段做纯文本存续但不接合成链路。范围就俩字:编辑、导出。
一、先看看外面那些「可编辑PPT」都怎么玩
1. 三条技术路线
| 路线 | 代表方案 | 视觉质量 | 真可编辑 | 稳定性 |
|---|---|---|---|---|
| A. HTML自由排版→DOM量测→原生对象 | Design-Arena/html-to-pptx、Anthropic html2pptx、DeepPresenter自由模式 | 最高 | 中(转换有损) | 低,排版易错位 |
| B. 模板槽位→结构化spec→原生对象 | PPTAgent模板模式、Presenton标准管线 | 中 | 最高 | 高 |
| C. 生图→逆向拆图层 | gpt-image2-ppt-skills editable模式 | 最高 | 假 | 中 |
路线A:视觉最好,但转换有损,排版容易错位。属于「看着能吃,夹起来就散」。路线B:视觉一般,但稳定,真能编辑。属于「丑但实在」。路线C:视觉最好,「可编辑」最假。
2. 路线C的坑,得单独说
这条线听着最香:图好看,还能「编辑」。但它的可编辑性是事后补出来的------先生成整页图,再拆成「干净底图+文本框+形状+图片层」叠回去。字是能选了,但和底图上的原字错位,放大就重影。
这就像美颜相机。远看是个美女,放大全是糊的。拿来骗甲方行,拿来当产品底座不行。它还得配一台LibreOffice/Keynote做回渲QA,伺候成本不低。适合「要好看、能改几个字」,不适合当服务底座。
3. 两个必须知道的许可陷阱
**第一个:Anthropic官方那个pptx skill,不是开源。**人家根目录故意不放LICENSE,许可下沉到子目录。document-skills下那四本是 source-available, not open source,LICENSE里写得清清楚楚:禁止商用、复制、衍生、分发、逆向。
翻译成人话:看着是开源,其实是「只可远观,不可亵玩」。做商用服务碰了就是给自己埋雷。同仓的example-skills才是Apache 2.0,但不含pptx能力------人家把门焊得死死的。
**第二个:Presenton的导出核心闭源。**主仓Apache-2.0、9k+ star,听着很香对吧?但它的scripts/sync-presentation-export.cjs会从GitHub Releases拉一个预编译tarball进node_modules。真正的binary→OOXML转换在@presenton/export-core,源码仓只有一个README。
你看,「可编辑度」恰好是被藏起来的那一环。就像相亲,照片发的是「可编辑版本」,真人不给看。能当对标,不能当底座。
4. 调研结论:一个外部仓库都不引
理由特别朴素:我们自己已经有80%的零件,唯一缺的那一环------矢量页面模型+导出器------外面要么闭源、要么许可受限、要么架构不匹配。我们是绝对定位画布,根本不需要DOM量测那一套。
外部仓库降级成参考清单:
| 仓库 | 许可 | 参考什么 |
|---|---|---|
| presenton/presenton | Apache-2.0(导出核心除外) | PptxPresentationModel的TS/Python双端同构设计 |
| icip-cas/PPTAgent | Apache-2.0 | generate→render→review→fix自省环 |
| Design-Arena/html-to-pptx | MIT | 仅当将来要做「LLM自由排版HTML」时才用 |
二、回头翻自家仓库,发现好东西还真不少
1. 能直接复用的零件
python-pptx在worker依赖里躺着(sse/utils/ppt_utils.py),导出器直接拿来用;LibreOffice headless在做回渲QA和PDF导出;页级派发/账单/OSS的骨架照抄;配额和冻结直接接;拖拽缩放的交互代码搬到新编辑器;逐像素验收方法从shotcraft那条线搬。
总结:家底还行,缺的不是零件,是组装。
2. 不能复用的:现有编辑器压根不是PPT编辑器
这句结论是有证据的,不是拍脑袋。src/views/ppt-detail/index.vue,它不叫PPT编辑器,叫「数字人形象版式标注器」。
(1)底图是栅格PNG。
drawBaseImage把上传的PPTX经 soffice→PDF→PyMuPDF 渲成一整页图,全屏贴上去。isCanvasBaseLayer的判据就是「图层URL==页面底图URL」。这种结构下想导出可编辑pptx?最多得到「一张整页图+几个框」。就好比你在照片上叠了几个便利贴,然后说这是可编辑文档。
(2)Layer没有排版模型。
字段就type/content/renderUrl/x/y/width/height/isAvatar/pageIndex。没有字号、字体、颜色、对齐、行距、项目符号、透明度、旋转、z-index。图层类型只有text|image|video三种。
(3)文字图层是残的。
drawTextLayer写死16px Arial纯黑,无换行、无样式。设计师看到这行代码,血压能直接拉满。好看的排版都在worker的ffmpeg overlay里------也就是说,是「画」上去的,不是「排」上去的。
(4)没有页面CRUD。
全文件搜不到addPage/deletePage/duplicatePage/movePage。页数由上传的pptx决定。你想加一页?对不起,没有这个按钮。
(5)API里零pptx导出。
ppt.ts里19个接口,产物一律是MP4/音频。
结论:新建模块,不改造。现有编辑器和目标模型正交------就像你想把自行车改成电动车,不如直接买辆新的,别折腾那俩轮子了。
三、七个决策,改起来代价最大的先定死
1. 不让LLM直接吐坐标
「网页里自由编辑」不等于「LLM自由排版像素」。你要让模型直接吐每个文本框的绝对坐标,出来必是垃圾:重叠、出界、字号乱。我见过AI画的PPT,字叠字,跟早高峰地铁似的。
正确分工:LLM只干两件事 ------①出内容(标题/要点/数据/配图语义);②选版式意图(「这页是大标题+三卡」还是「左图右文」)。确定性排版器把版式意图算成绝对坐标,按栅格落成Layer\[\]。
这跟shotcraft是同一个DNA:LLM挑组件、填props,坐标由排版器一处定死。出口仍是绝对坐标,用户接着随便拖。确定性只作用于初稿,不限制后续编辑。说白了:AI负责想,排版器负责画,用户负责乱改。
2. Layer存「语义角色+token」,不存绝对样式
js
// 错误示范:存死样式
{ "fontSize": 24, "color": "#1a1a1a", "fontFamily": "PingFang SC" }
// 正确示范:存角色 + token 引用
{ "role": "title", "style": { "colorToken": "fg-primary", "sizeStep": 3 } }
这是「一键换风格」能不能做成的分水岭。存绝对像素的话,用户换风格时你得从像素反推语义角色------推不准就换不干净,换出来跟精神分裂似的。
用户手改过的地方存userOverrides覆盖层,就是CSS的层叠:token基线+用户覆盖。推论:组件里不许硬编码任何颜色和字号,只能引用token。这就好比装修:你先定风格,再配家具,不能先买十个不同风格的沙发摆家里。
3. 模板=风格×组件,不做「整页模板」
分三层:风格 (整体视觉语言,商务蓝/科技暗色/杂志风/手绘风,5-10套);主题token (配色、字体对、圆角、间距刻度、阴影层级,每套风格一套);版式组件(标题、三卡、对比、时间线、数据页、引用、结尾,25-40个)。
用户看到的「模板」=风格×组件。10套风格×30个组件,视觉上像几百套模板,实际维护量是10+30。
不做「模板A的第3页长这样」那种整页模板:每加一套风格要重画所有页面,维护量变成风格数×页数,而且用户改不动。这正是Presenton那条路,长期会变成谁都不敢碰的资产。就像衣柜里塞了三百件搭配好的套装,看着多,其实你只穿最外面那件。
4. 字体:生成端锁死+服务器装字体
先看现状(已核实):worker服务器aiserver0只有1个中文字体------DroidSansFallbackFull;开发机macOS有99个,苹方宋体黑体随便挑。99比1,这差距比我们公司和ChatGPT的用户数差距还大。
而且不同环节对字体的要求完全不同,必须分开处理:
| 环节 | 服务器字体够不够 | 原因 |
|---|---|---|
| 导出.pptx | ✅ 不依赖 | 只写字体名,不渲染,由用户端PowerPoint解析 |
| 生成侧回渲QA | ❌ 不够 | 渲出来是Droid fallback,跟用户看到的不是一个字形 |
| 导出.pdf | ❌ 不够 | PDF在服务器渲染,字体就是Droid |
所以必须给服务器装字体,否则PDF导出不可用、QA没意义。选什么?Noto Sans CJK SC(思源黑体,OFL协议):免费、可自由分发、可嵌入。用户端没装会fallback到微软雅黑/苹方------同为黑体,观感接近,是代价最小的选择。
为什么不做字体嵌入?技术上要手写OOXML,真正的墙是授权------可嵌入的中文字体只有思源系列这类,苹方、微软雅黑不允许嵌入。
代价:牺牲用户的字体自由度,生成端只允许白名单字体,用户不能改。这是主动放弃,要写进产品说明。就像某些餐厅:菜单是固定的,你想加菜?对不起,我们主动放弃了这项服务。
5. 回渲QA放生成侧,只查结构性错误
导出链路里最重的一步是LibreOffice回渲:单页0.5-2秒,40页要1-2分钟。策略是:生成初稿时回渲一次验证布局(低频);导出时只把验证过的IR落成pptx,纯python-pptx,秒级,不需要soffice。
但它有个系统性误差:服务器即使装了Noto Sans CJK,用户端用的也可能是微软雅黑/苹方,字形和度量本就不同。所以QA判据只能是结构性错误:
| QA能查 | QA查不了 |
|---|---|
| 元素重叠 | 文字实际断行位置 |
| 超出页面边界 | 行距的像素级差异 |
| 文本溢出容器 | 用户端的最终观感 |
| 缺图/图片比例失真 |
这里有个扎心的事实:唯一100%保真的交付格式是PDF(服务器渲染,不依赖用户字体)。PPTX的保真度天花板就是「结构正确+字体fallback后观感接近」。PPTX说白了就是:能保证不散架,不保证长一样。
6. 导出走独立队列,别和渲染共用慢池
设想这个场景:一个90分钟的数字人渲染正占着慢池线程,用户点「导出PPTX」就要排在它后面。秒级任务被分钟级任务堵死------这不是概率问题,是必现的产品事故。
就像你去取外卖,前面那位在店里堂食了一顿90分钟的正餐。解决方案:新增导出池,并发硬卡1-2。秒级任务排队无感,但能防住一批人同时导出把soffice进程堆爆。
7. 导出是单向投影
「网页里编辑」和「PowerPoint里编辑」是两件事。前者编辑IR,后者编辑pptx。只做单向导出,不做双向同步------用户在PowerPoint里改完传回来,网页编辑器看不懂,这是无底洞。「用户上传自己的PPTX反解成可编辑图层」也不做,或放到很后面。现有soffice→PDF→PNG只出栅格页,反解要啃pptx逆向解析,工作量是另一个量级。
翻译:分手就是分手,别指望回心转意。双向同步的坑,谁挖谁知道。
四、能力边界:哪些能吹,哪些只能看看
1. 能做到的
生成:
输入主题/大纲/上传文档(doc、pdf、txt)→一次出整套;版式由组件库决定,排版器按内容量自动选;配图支持AI生图/图库/用户上传;风格一键换配色与字体方案。
编辑:
自由画布(拖拽、缩放、旋转、栅格吸附、参考线);图层支持文字、图片、形状、图标、线段;页面新增/删除/复制/排序;文本字号、颜色、粗斜体、对齐、行距、项目符号;图片上传、裁剪、圆角、替换;图层树z-order、锁定、隐藏、编组;撤销重做、对齐分布、备注栏。
导出:
.pptx原生文本框+形状+图片,能改字、能挪框、能换图;.pdf、每页PNG。
2. 打折扣的(能做,但别期待满分)
复杂视觉(渐变、阴影、混合模式、图标字体)→降级成图片层。看着对,但在PowerPoint里改不了。图表:简单图表可做原生chart,定制样式退回图片。表格:基础表格可以,合并单元格、条件格式、排序不行。导出保真:「看着差不多」,不是逐像素相同。字体:只能给白名单。
3. 做不到的
PPTX格式的天花板,再努力也绕不过:动画与切换效果 (python-pptx和PptxGenJS都不支持创建)、SmartArt (同上)、字体嵌入 (授权问题)、公式OMML。这四项只有一条出路:Aspose.Slides,商业授权,按部署收费。要不要为了动画买它?纯商业决定,我这种码农说了不算。
架构上明确不做的:用户上传PPTX反解成可编辑图层;与PowerPoint双向同步;多人实时协作(技术上能做,单机也扛得住,但要加CRDT+长连接,纯工作量问题,可后加)。
4. 产品文案的红线
别写「导出的PPT可以随意编辑」。用户一试形状和动画就会觉得被骗。准确说法:「导出的PPTX里,文字都能直接改。」
这条红线建议裱起来挂墙上。你能想象用户兴冲冲拖一个形状,发现拖不动,然后去应用商店给你一星吗?我能。
五、实施方案
1. 模块划分
编辑器页面:manteng-ai/src/views/deck/(新建),矢量画布编辑器+风格/版式面板;渲染层:manteng-ai/src/views/deck/components/,组件库(25-40个版式组件);主题:manteng-ai/src/.../themes/,token表,5-10套;后端:aigc-backend/src/main/java/org/aigc/deck/(新建),增删改查+生成编排+导出派发;排版器:aigc-backend或worker,LLM内容+版式意图→Layer\[\];导出器:artificial-intelligence-notebook/sse/handlers/deck_handler.py(新建),IR→python-pptx;QA环:worker,生成侧回渲比对。
2. 核心数据模型
设计空间统一1920×1080(不用现有编辑器的768×432)。换算干净:导出时 pt = px / 2。
js
// Deck
{
"id": "...",
"title": "...",
"themeId": "business-blue",
"designW": 1920, "designH": 1080,
"slides": [ /* Slide */ ]
}
// Slide
{
"id": "...",
"index": 0,
"layout": "three-cards", // 版式组件名,用于「换版式」
"background": { "token": "bg-base" }, // 或 { "image": "oss://..." }
"notes": "这一页的讲稿",
"layers": [ /* Layer */ ]
}
// Layer ------ 注意:这里只有 token 引用,没有绝对样式
{
"id": "...",
"kind": "text" | "image" | "shape",
"role": "title" | "body" | "caption" | "card-title",
"box": { "x": 120, "y": 80, "w": 800, "h": 120 },
"rotation": 0,
"z": 3,
"locked": false,
"visible": true,
"style": { // 全部是 token / 刻度引用
"colorToken": "fg-primary",
"fontToken": "font-display",
"sizeStep": 3, // 字号刻度,不是 px
"weight": "bold",
"align": "left",
"lineHeight": 1.4
},
"overrides": {}, // 用户手改,覆盖 token
"text": "...", // kind = text
"src": "oss://...", // kind = image
"shape": "rounded-rect" // kind = shape
}
主题token表(每套风格一份):
js
{
"id": "business-blue",
"colors": {
"bg-base": "#ffffff", "bg-accent": "#f1f5f9",
"fg-primary": "#0f172a", "fg-secondary": "#64748b",
"brand": "#2563eb", "brand-fg": "#ffffff"
},
"fonts": { "font-display": "Source Han Sans", "font-body": "Source Han Sans" },
"scale": { "size-steps": [14, 18, 24, 32, 44, 60], "space-steps": [8, 16, 24, 40, 64] },
"shape": { "radius": 12, "shadow": "sm" }
}
3. 落库
沿用现有「整体保存一页」的模式(同/ppt/update_page存material数组):ppt_deck、ppt_slide两张表,layers存jsonb,整页保存不拆表。编辑器自动保存本来就是一页一提交。
几个坑记一下:写jsonb必须显式转型?::jsonb(连接串无stringtype=unspecified);SimpleJdbcInsert必须指定usingColumns,否则缺列被绑成NULL,DDL默认值不生效;DDL无迁移框架,需手动ssh+psql应用。
4. 接口
js
POST /deck/create 生成整套(异步,返回 taskId)
POST /deck/get 取整套
POST /deck/update_slide 保存单页(整页 layers)
POST /deck/add_slide 新增页
POST /deck/delete_slide 删除页
POST /deck/reorder_slides 页排序
POST /deck/apply_theme 换风格(只换 themeId,前端重渲染)
POST /deck/relayout_slide 单页换版式(重跑排版器)
POST /deck/export { deckId, format: pptx | pdf | png }
GET /deck/themes 风格列表
5. 导出映射
| IR | PPTX |
|---|---|
| box.x/y/w/h(1920设计px) | Inches(px / 144) ------ 1px = 1/144 in,1920px = 13.333in |
| sizeStep → scale.size-stepsstep | Pt(px / 2) |
| kind: text | add_textbox,关闭autofit,word_wrap = True |
| kind: image | add_picture |
| kind: shape | add_shape(圆角矩形/椭圆/直线) |
| colorToken | 查主题表 → RGBColor |
| fontToken | 写字体名(白名单)。同时设 latin + eastasian,主题字体也一并设 |
| 复杂视觉层 | 降级为图片层 |
两个python-pptx的坑必须记下来:
(1)中文字体要写三处。
run.font.name只写latin,中文会退回主题默认字体。必须额外写ea/cs(东亚字体是ea),顺序是latin→ea→cs。
(2)空effectLst挡不住LibreOffice的投影。
add_shape/add_connector会带一个引用主题的p:style,里面有effectRef idx=2(主题投影)。按OOXML规范,显式空effectLst应该覆盖它,PowerPoint也认------但LibreOffice不认,照样渲出投影。而回渲QA和PDF导出都走LibreOffice,所以必须把p:style整个删掉。lnRef/fillRef同理,会让「没设描边的形状」长出一条主题色的边。
这条我要多嘴一句:规范说「应该覆盖」,PowerPoint说「行」,LibreOffice说「我就不」。三个裁判三个口径,你只能把整段删了。跟甲方改需求一样,你以为有个标准,其实没有。
6. 里程碑
| 阶段 | 内容 | 验收 |
|---|---|---|
| M1 | IR schema + 导出器(手工JSON → pptx) | 导出的pptx与预览图逐页对照,肉眼无差异 |
| M2 | 编辑器(矢量画布 + 存/取) | 能建页、拖框、改字、存回、刷新后一致 |
| M3 | 生成端(LLM内容 + 版式意图 → 排版器) | 给主题出一套,无重叠/出界/字号乱 |
| M4 | 主题与组件库(风格 × 组件) | 一键换风格,内容不动,每套都好看 |
| M5 | QA回环 + 验收矩阵(组件 × 风格全量渲染比对) | 改一次token全量重渲无回归 |
| M6 | PDF/PNG导出、配额计费、导出池 | 导出不排队;扣费与报价一致 |
**先做M1。**导出保真度是整条链路风险最高的地方,先用手工JSON把「IR→pptx→回渲→对照」这条路走通,再往上叠东西。地基不打牢,楼盖得再高也是危楼。
7. 机器资源
保存图层:每次拖拽,一次JSON写库。导出pptx(40页):每次点导出,秒级,纯CPU。回渲QA:每次生成初稿,20-80秒,CPU+磁盘。每页PNG(150 DPI):1-3MB/页,走OSS,临时文件要清。
重点:零GPU依赖。「编辑+导出」这条线4核/16GB就够。真正让单机吃紧的是并发导出×soffice进程数,并发卡1-2即可。临时文件清理要单列任务------CosyVoice3那边已经吃过临时分片残留的亏。垃圾不扔,早晚长蟑螂。
六、风险清单
| 风险 | 影响 | 缓解 |
|---|---|---|
| 导出保真度不达标 | 产品核心承诺落空 | M1先做,手工JSON走通再叠功能 |
| 组件 × 风格组合爆炸 | 某些组合丑/撞色 | 自动验收矩阵,M5 |
| 服务器只有1个中文字体 | PDF导出难看、QA无意义 | 给服务器装Noto Sans CJK SC |
| 字体在目标机器缺失 | 排版散架 | 生成端锁字体,写进产品说明;PDF才是保真交付格式 |
| 导出与渲染抢worker | 用户点导出没反应 | 独立导出池 |
| 用户预期「完全可编辑」 | 差评 | 文案红线 |
| Aspose动画需求 | 商业授权成本 | 先不做,观察需求 |
最后总结一下这条线的脾气:能用外部方案的我们全自己写,能降级的复杂效果全降级成图片,能不做的东西全不做,只把「编辑+导出pptx/pdf」这一件事做到位。听起来很怂?不,这叫边界感。
就像我开头说的,你只需要记住一句话:**PPTX里,文字都能直接改。**别的,等用户真的需要了再说。
P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看,传送门https://blog.csdn.net/qq_34419312