版本:v15-fix
手术日期:2026年6月26日
主刀医生:ally.ren 技术团队
麻醉方式:咖啡 + 红牛
一、Bug 报告:"我写了37页,导出来是37张白纸"
6月25号深夜,群里弹出一条消息:
"我写了一篇37页的公众号长文,点导出PDF,下载下来……全是空白。"
紧接着又一条:
"而且文字被切成了上下两半,上一页剩半行,下一页又半行,跟腰斩似的。"
我们盯着屏幕,陷入了沉思。
37页啊,相当于一本薄薄的小册子了。用户辛辛苦苦写了几天,我们给人家导出一叠白纸?
这要是古代,我们得被拉出去砍头。好在现在是法治社会,我们只需要——加班。
二、解剖:这引擎到底哪里坏了?
2.1 病因一:浏览器有个"身高歧视"
原来我们用的 html2pdf.js,工作方式非常"直男":
"不管文档多长,我一次性把它拍成一张超级大照片(Canvas),然后塞进PDF。"
听起来很高效对吧?问题是——浏览器对 Canvas 有身高限制,大约 16384 像素。37页文档的渲染高度,早就突破了这个"身高歧视线"。
想象一下:你让一个人把一本百科全书的内容,全部写进一张A4纸。纸不够大,字就全消失了。不是字没写,是纸装不下,直接"吞字"了。
更气人的是,它失败得悄无声息——没有报错,没有弹窗,用户看到的只有一叠白纸。就像一个厨师把菜做糊了,还一脸无辜地端上来说"请慢用"。
2.2 病因二:分页的"一刀切"太粗暴
原来的分页逻辑更离谱:按固定高度(1028像素/页)硬切。如果切割线正好落在某行文字中间——恭喜,这行字被物理腰斩,上半身在第3页,下半身在第4页。
这就好比切蛋糕,刀不管不顾地切下去,把蛋糕上的草莓切成两半。你可以吃,但体验很差。
三、手术方案:给引擎换一颗"会思考的脑子"
我们决定,不再用那个"直男"工具了。我们要自己造一个 "会看路、会绕弯、会省内存"的智能引擎。
3.1 第一刀:化整为零,逐页拍照片
不再一次性拍全景,而是一页一页拍。每页只处理约 1029 像素的内容,远低于浏览器的"身高歧视线"。拍完一页,立刻把照片销毁(释放 Canvas),不给浏览器任何压力。
如果文档超过 20 页,引擎还会自动"降低画质"——把渲染精度从 2 倍降到 1.5 倍。就像一个有经验的摄影师,知道什么时候该省点胶卷。
3.2 第二刀:三级智能切割,让刀口永远落在"舒服"的地方
这是整个手术最精妙的部分。我们设计了一个三级"安全切割点"搜索系统,让分页永远落在排版最舒服的位置:
🥉 第一级:块级边界扫描("找大块头")
先扫描文档里的"大块头":段落、标题、表格、代码块、引用块、图片……
这些元素的顶部和底部,都是天然的"安全区"。就像切蛋糕时,优先在草莓和草莓之间的空白处下刀。
🥈 第二级:行级文字边界扫描("找缝隙")——核心创新!
这是整台手术的灵魂。我们用 Range.getClientRects() 精确扫描每个段落内部每一行文字的像素位置,找到行与行之间的微小缝隙。
切割点永远落在这些缝隙里,绝不切穿任何一行文字。
想象一下:你拿着一把激光刀,面前是一面文字墙。第一级扫描告诉你"这里有一整面墙",第二级扫描告诉你"墙的砖缝在这里"。你的刀,永远沿着砖缝走。
🥇 第三级:容差搜索("就近找缝")
在目标切割点上下 100 像素的范围内,搜索最近的安全切割点。优先找行级缝隙,其次找块级边界。
就像一个细心的裁缝,宁可多调整几毫米,也绝不让布料"断线"。
3.3 第三刀:缓存加速,同一面墙只扫描一次
同一段落的行级边界,只扫描一次,存入 lineCache 缓存。第二次遇到同样的段落,直接复用结果。
长文档的导出速度因此提升了数倍。 就像你第一次去一个小区,要挨家挨户敲门问路;第二次去,直接走记忆里的近道。
四、手术结果:从"全白"到"完美"
37页的长文,从全白到完美导出,每一页的文字都完整、清晰、不被切割。
那个深夜反馈问题的用户,第二天收到了我们的测试版本。他说:"终于能安心存档了。"
这句话,让我们觉得这三天的通宵,值了。

五、术后观察:还顺手发现了一个"安全隐患"
排查过程中,我们发现协作房间的 ROOM_SECRET 没设置,相当于大门没上锁,谁拿到房间 URL 都能进。
我们已经规划了 Token 动态认证 方案:
前端永远不持有密钥 只通过 API 获取有时效的 Token 过期自动失效
核心原则:钥匙只放在服务器保险柜里,绝不揣在用户口袋里。 这是我们对安全的基本态度。
六、术后护理指南:怎么更新?
如果你正在使用 ally.ren 微信编辑器,无需任何操作,更新已自动部署。
如果你需要手动部署,执行以下命令即可:
mkdir -p /root/www.ally.ren/weixinbianji/lib/html2canvas/1.4.1
mkdir -p /root/www.ally.ren/weixinbianji/lib/jspdf/2.5.1
wget -O /root/www.ally.ren/weixinbianji/lib/html2canvas/1.4.1/html2canvas.min.js https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js
wget -O /root/www.ally.ren/weixinbianji/lib/jspdf/2.5.1/jspdf.umd.min.js https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js回滚提示:服务器已自动保留原文件备份:
features/features-export.js.bak
如需回滚,一条命令搞定。
七、写在最后
做工具的人,最幸福的时刻,不是代码跑通的那一刻,而是用户说"终于能用了"的那一刻。
37页空白PDF的故事,到此结束。但我们的优化,不会停止。
如果你在使用中遇到任何问题,欢迎随时反馈。每一个Bug报告,都是让我们变得更好的机会。
让写作回归纯粹,让技术隐于无形。
—— ally.ren 技术团队,2026年6月26日
📌 版本信息:v15-fix(PDF 导出优化)
📌 手术对象:features/features-export.js
📌 新增器官:html2canvas.min.js(195KB)、jspdf.umd.min.js(356KB)
📌 未动刀部位:main.js、config.js、ui.js、upload.js、features-ocr.js等
本文档由 ally.ren 微信编辑器技术团队整理发布,转载请注明出处。
夜雨聆风