我那个小工具站一直缺一份像样的帮助文档。我习惯先在 Word 里写好再配上后台截图,最后转成网页挂上去。后台截图难免带着用户的收货信息,我以前在外包公司写交付文档时的做法是在 Word 里把那块裁掉就算打过码了。这次动手写正式文档之前我先拿一份自己造的样本把转网页这一步走了一遍。本来只是想估一下导出的页面会吃掉服务器多少流量。数字一出来我就觉得不对劲。
我的电脑是 Mac 也没装 Word。测试用的 docx 是我按 OOXML 规范里的文件结构自己拼出来的,Word 没碰过它。里面那张截图来自我自己写的一个订单管理后台演示页,收货人、手机号、地址和支付流水号全是占位内容。截图原尺寸是 1800×1240,我在文档里把下面那张收货信息卡片裁掉了只露出上面的退款报错和商品明细。同一份 docx 里还有另外 6 个用到裁剪缩放旋转的图片用例,这篇只说这张截图。

左边是文档里看到的样子。右边是文件里真正存着的那张图,红框里就是以为已经裁掉的收货信息。
导出的网页比我算的大一截
这次我用的是图映 ImgIng(https://imging.cn/)的 DOCX 转 HTML,样本就是上面这份带 7 个图片用例的 docx,浏览器是 Chromium 149 开源构建。整份样本导出来是 9.22 MB,里面有 99.7 % 是图片字节。我又单独拿只放这张截图的文档转了一次。导出是 208.6 KB。页面上的截图只剩上半截也看不见收货信息,可文件大小怎么看都像是整张图。我把网页里那段 data URI 解码出来果然是一张 1800×1240 的 PNG,收货信息、手机号、地址、流水号全都在。从导入到转换界面上都没给过任何跟裁剪有关的提示。截图在页面上还整体往上挪了 70.4 px 盖住了上方那行标题。在工作台的预览里那行标题被盖到只剩「截图:」。网络请求我也看了。几次转换的非 GET 请求都是 0 次。

左边是打开导出网页看到的,截图只剩上半截。右边是把同一个网页里那张图的字节解出来的样子,也就是右键另存为会存下的那张。红框圈住的收货信息全在。
裁掉的部分一直在文件里
那这张整图是从哪来的?docx 本质上是个 zip 包,我把扩展名改成 .zip 直接解开。word/media 目录里那张截图还是 1800×1240 的 143,480 字节,跟我放进去的原图逐字节相同。所谓裁剪只是 document.xml 里这张图的描述多了一行 srcRect,里面写着 b=35484。它的单位是千分之一个百分点,意思是下边裁掉大约三分之一。显示尺寸和旋转角度另外记在 wp:extent 和 rot 里。图片本身一个像素都没动。
这里得先交代一句,样本是我手写的。media 里那张整图是我造样本时自己放进去的,证明不了 Word 会怎么做。Word 那边我只能去看微软自己的支持页。它讲减小图片体积时提到裁过图以后可以勾选「Delete cropped areas of pictures」也就是「删除图片的剪裁区域」。专门给了这么个选项就说明不勾的话裁掉的部分会一直留在文件里。至于 Word 保存时会不会自动删、不同版本默认勾不勾,我手上没有 Word 也测不了。我还翻了一下自己电脑上 14 份 docx 里的 53 张图。带裁剪参数的只有我造的这 3 张,连一个真实的 Word 样本都没找到。
换成mammoth整张图直接露出来
以后要在服务端批量转的话我第一个想到的是轻巧、装上就能跑的 mammoth.js。我写了个预览页用 mammoth.js 1.13.0 转这份样本。结果更直接。页面上显示的就是整张截图,收货信息原样摆在那儿。它既不认裁剪参数也不认显示尺寸,图按原图比例铺满了整个容器。本来打算借它的 convertImage 回调自己把裁剪补回去。翻下去才发现回调只给图片字节和类型,裁剪参数根本拿不到。用 mammoth 的话只能在转换前先把 docx 处理好。顺手试的 macOS 自带 textutil 转出来只有 1,380 字节,7 张图一张不剩。

红框那一块在 Word 的设定里本来是看不见的。转完它就大大方方出现在网页上,手机号和地址都能直接复制。
显示对了也不算安全
这件事我想了挺久。我以前验收文档转换基本就是打开页面看一眼,图对字对就交。裁剪恰好是看一眼查不出问题的那种。页面上看不见并不等于网页里没有。任何人在图上点右键另存为拿到的都是 src 里那份字节,那就是整张原图。公开的帮助页被爬虫抓走的也是整份。靠 CSS 只能做到不显示,删不掉任何东西。这回要不是我抠门先算了一遍流量,大概也会照着页面看一眼就发出去。被裁掉的那三分之一张图谁也看不见,每个打开帮助页的人却都要多下一遍。这份带宽钱还是我出。
说到带宽我就肉疼。写到这儿我家橘猫「服务器」跳上桌把茶杯扫到了地上。好在是喝剩的口粮茶不心疼。
转换之前先把裁剪烘焙进图片
办法其实就是在转换之前把裁剪真正做进图片里。Word 自带的「删除图片的剪裁区域」我没 Word 试不了。我自己写了个小脚本按 srcRect 把 media 里的图片真裁掉再把 srcRect 清空。JPEG 按 q92 重新编码、PNG 无损保存。烘焙后那张截图变成了 1800×800 的 84,507 字节。docx 整体瘦了 21.0 %,从 7,234,550 字节变成 5,714,763 字节。拿去让图映重新转,整份导出由 9.22 MB 缩到 7.28 MB。单用例导出也从 208.6 KB 降到了 131.7 KB。截图的上移从 70.4 px 变成了 0.4 px,被盖住的标题也露出来了。烘焙后的 docx 交给 mammoth 和图映转出来的页面上都再找不到收货信息。mammoth 显示的同样是裁后的画面,只是尺寸照旧铺满容器。
这个脚本还有几处我没处理。同一张图被文档里好几处用不同的裁剪引用时得先拆成几份再裁。srcRect 是负值也就是往外留白的情况我也没管。以后转帮助文档我打算多加一步检查。解包后拿 media 里的图片尺寸跟文档里显示的尺寸比一比,图明显比显示的大就标出来,等我看过再转。其实最省事的办法是重新截图,截的时候就别把收货信息截进来。真要遮就用涂掉像素的打码。
还有哪些情况我没测
Word 本身、WPS、LibreOffice 和 pandoc 这次全都没测,它们转出来是什么样我不知道。图映导出的网页我只在 Chromium 149 里看过,Firefox 和 WebKit 下的显示没验。样本里只有嵌在文字行里的图,浮动图也没试。正式的帮助文档写完以后我会先烘焙再转,转完解一张图出来核对尺寸再上线。