Word裁剪图片转HTML走样,该在渲染层修还是入口修

我最近在给一条文档入库的流程定规矩。收进来的是 Word 文档,往下游送的有两样东西。一样是给人看的网页,一样是列表里用的缩略图。讨论到图片时有人问 Word 里裁过的图转出去还是不是裁过的。要是不对,该让做网页的和做缩略图的各自去修还是在入口统一处理?前一个问题我答不上来,后一个更没底。为了答它我照着 OOXML 的文件结构手写了一份 docx 样本,并没有用 Word 存。本机也没装 Word。样本里放了 7 个图片用例,有只缩放的、四边裁的、只裁下边的、旋转的和不等比拉扁的。图只有两张。一张是网上找的风景照,另一张是我自己写的订单后台演示页截下来的。收货人、手机号和地址全是占位。下面讲 Word 本身的地方我只引微软的支持文档,其余都是从文件结构和各家导出反推的。

两个下游读错的层不一样

先把文件拆开看。docx 改成 zip 解开以后图片在 word/media/ 里,位置和裁剪写在 document.xml 里。一张裁过的图在这里分三层记。wp:extent 管图在页面上占多大的框,用例 B 是 6×4.4 英寸。a:srcRect 管四边各裁掉多少,单位是千分之一个百分点。B 写的是 l=30000 t=20000 r=20000 b=25000。a:xfrm 上的 rot 管旋转,用例 C 的 900000 就是 15°。media 里的图本身一个像素都没动。风景照还是 2736×1824 共 1,388,716 B,截图还是 1800×1240 共 143,480 B。

红框里是 Word 显示的范围。框外的雪地和芦苇也一起存在文件里,面积比框里还大。风景照出自 Dietmar Rabich 之手,发在 Wikimedia Commons 上,协议是 CC BY-SA 4.0。红框是我描上去的。改过的这张也走同一个协议。

样本是我造的,media 里的整张原图也是我放进去的,单靠它证明不了 Word 会这样存。能说明问题的是微软自己的支持页。它讲减小图片体积时在 macOS 部分提到裁过图以后可以勾选「删除图片的剪裁区域」。要专门勾一下才删,说明不勾的时候被裁部分还留在文件里。Word 保存时会不会自动删、各版本默认怎么设我没有 Word 测不了。我顺手扫了本机 14 份 docx 里的 53 张图。带非零 srcRect 的只有样本自己那 3 张,真实文件这一块暂时没有对照。

我拿样本过了五种转法。mammoth.js 1.13.0 和 docx-preview 0.4.1 放在我自己写的复现页里跑,浏览器是 Chromium 149 开源构建。macOS 自带的快速查看缩略图和 textutil -convert html 也算进来。缩略图那一路正好对应我流程里的缩略图下游。网页这一侧我还用的是图映(https://imging.cn/)的 DOCX 转 HTML,喂的是同一份手写样本,内核同样是 Chromium 149 开源构建。图映的 DOCX 这块不归我管,我在里面只做端侧编解码。下文讲它的地方也是看导出文件推出来的。

转法 缩放 extent 裁剪 srcRect 旋转 rot 导出里有没有整张原图
mammoth.js 1.13.0 不认 不认 不认 有
macOS 快速查看 认 不认 认 缩略图是位图不适用
textutil 图全丢了 图全丢了 图全丢了 没有图
docx-preview 0.4.1 认 内容对但不对称时偏位 方向对但不留外接框 有
图映 DOCX 转 HTML 认 内容对但不对称时偏位 同上 有

两个下游错在不同的层。缩略图那一路认了缩放和旋转却偏偏不认裁剪。整张图被压进图框,用例 D 被裁掉的收货信息压扁了照样看得见。网页这一路更散,mammoth 三层都没认就把裁过的图显示成整张。它也不写宽高,图按原图比例铺满 900 px 的容器。textutil 干脆把 7 张图全丢了,输出只剩 1,380 B。docx-preview 和图映三层都认了。裁剪内容、缩放和旋转方向都对,可图会偏出图框。同一份 docx 进两个下游错法没有一样是相同的。

渲染层修要每个下游各写一遍

渲染层修听上去最顺手,每个下游自己把三层参数换算对就行。我先替网页这一路试了外框加负 margin 的写法。外框按 extent 设宽高再 overflow:hidden。框里的图按保留下来的比例放大,再靠负 margin 把要显示的那块推进框内。这种写法在 Chromium 149、Firefox 151 和 WebKit 26.5(不是 Safari 正式版)上都对上了参照。换成一行 object-view-box: inset(t r b l) 就只有 Chromium 认,另外两个内核直接显示整张。网页一路光是挑写法就得把三个内核都过一遍。

docx-preview 和图映用的是第三种写法。它正好说明认了参数离换算对还差一截。照着导出里的样式反推,它先把图设成图框大小再用 clip-path 留下可见区,然后拿 scale 放大回图框。缩放默认绕着元素的中心点放大,四边裁得一样多时中心不动,不一样多时可见区就和图框错开了。水平偏移等于框宽乘左右裁剪比例之差再除以两倍的剩余宽度比例。垂直方向同理。用例 B 算出来右移 57.6 px、上移 19.2 px,实测是 (58, −19)。四边各裁 15 % 的 G 公式给 0,实测也是 (0, 0)。

红框是 Word 的图框。左边四边裁得不一样多,画面往右上方跑出了框。右边四边各裁 15 % 正好落在框里。这张也是拿那张风景照改的,许可跟上面一样是 CC BY-SA 4.0。

最常见的裁法偏偏不对称。截图裁掉下面一截是很多人的习惯。用例 D 只裁了下边 35.484 %,图框高 256 px,算出来该上移 70.4 px。图映导出里实测也是 −70.4。图一往上挪就盖住了上方那行标题,工作台的预览里它只剩开头几个字。7 个用例从导入到转换界面上没出过一条和裁剪或旋转有关的提示。缩略图那一路的修法又是另一套。它出的是位图,CSS 帮不上忙。得在画缩略图之前自己按 srcRect 先裁一刀。以后要是再接一个导出 PDF 的下游,同样的换算还得写第三遍。哪一路漏写或者写偏,用户看到的就是走样的图。

写对了的CSS也只是不显示

就算每一路都写对还有一层问题是看不见的。mammoth 和图映导出的 HTML 里图片都是 data URI 内联的。我把它们一张张解出来比对,7 张全是和 docx 里的 media 逐字节相同的整张原图。图映导出的文件有 9,665,338 B,图片字节在里面占到 99.7 %。用例 D 那张截图解出来是 1800×1240 的整张,收货人、手机号、地址和流水号一项不缺。页面上看不见它们,可右键另存为存下来的就是这份字节。docx-preview 预览用的 blob: 地址背后也是整张。

左边是导出网页里显示的样子,收货信息那一块确实没出来。右边是同一个网页里那张图片的字节,红框里的收货人、手机号和地址都在。订单内容是我自己编的占位数据。

这一层决定了渲染层修的上限。CSS 只决定显示哪一块,发出去哪些字节它管不着。网页那一路只要原图还内联在文件里,位置修得再准也是把整张图连同被裁部分一起送了出去。

入口烘焙一次两头都解决

想到这一步我那条流程就有了答案。入口做一次规范化,收进来的 docx 先按 srcRect 把 media 真裁掉再把 srcRect 清空。后面接网页还是接缩略图拿到的都是所见即所得的图。三层参数里藏着像素的那一层也没了。正确性只用在一处写对,不用指望每个下游都写对。

我写了个小脚本做这件事。PNG 走无损,JPEG 用 q92 重新压一遍。docx 烘焙完从原来的 7,234,550 B 变成 5,714,763 B,少了 21.0 %。用例 D 的 media 从 1800×1240 变成了 1800×800。再拿图映转一遍界面上的导出大小从 9.22 MB 变成 7.28 MB。用例 B 和 D 的偏移分别成了 (0, 0) 和 (0, −0.4),页面上也找不到被裁部分了。原样本、烘焙样本、单用例 D 和它的烘焙版这四次转换里图映都没发过一次非 GET 请求。mammoth 烘焙后显示的同样是裁过的画面,只是它仍不认 extent,尺寸照旧按容器铺。它给调用方的图片回调只有字节和类型,没有 srcRect 这一项。想用它就只能在转换前把 docx 处理好。

代价是放弃了反悔的余地

Word 这样存图有它的道理。原始像素不动,修改记成参数叠在上面。今天裁掉的明天想要回来把数字改回去就行,反复编辑画质也不会往下掉。做过编解码的人对这种非破坏的存法都熟。烘焙等于在入口把这份余地主动扔掉。烘焙后的文件再也恢复不出被裁的部分,JPEG 还多压了一代。我的办法是原件和发布件分开放。作者交来的 docx 原样归档,要再编辑从原件改,往下游送的只有烘焙过的副本。谁要找回被裁部分就去档案里取,网页和缩略图那边已经拿不到了。

这套做法还有几处没覆盖

脚本现在只处理 inline 图。浮动图 wp:anchor 没测,srcRect 写负值往外留白的情况也没管。同一张 media 被几处按不同裁法引用时该拆成几份,这一步也还没做。转换器这边 Word 本身、WPS、LibreOffice 和 pandoc 一个都没跑。图映导出的网页在 Firefox 和 WebKit 下长什么样也没看。旋转那层我暂时留在参数里,要不要也在入口转成真像素还没想好。手上也有收 docx 的流程的话拿一张只裁了下边的截图过一遍最快。每个下游都看一眼图有没有往上挪,再把网页里的图片解出来核对尺寸,两样都过了再往流程里放。

相关推荐
云和数据.ChenGuang36 分钟前
trace 是RAG检索链路的**全链路追踪日志对象
人工智能·深度学习·算法·排序算法
鼓掌MVP37 分钟前
决定全案的智能Agent
大数据·人工智能·机器学习
IT_陈寒39 分钟前
深入理解Redis:核心原理与最佳实践
前端·人工智能·后端
来包番茄沙司43 分钟前
【网盘资源分享】2026新版尚硅谷AI Agent智能体极速版|全套课程资料+面经
人工智能·学习
Zhu7581 小时前
开源项目-Sablewisp-轻量、现代的 Chrome 书签管理扩展(Manifest V3)
前端·chrome
wespaul1 小时前
一条自然语言命令的 49 个节点:可视化编排与每段强校验
前端·数据库
OH_TPC1 小时前
【鸿蒙优选三方库】@ohos/axios:前端转鸿蒙,网络层几乎不用改
前端·华为·harmonyos·鸿蒙
程序员大阳1 小时前
副队长CSS教程(38)–transform 变换
前端·css·变换·transform
拉你进_教堂1 小时前
小龙虾技能之【Contactless Health Risk Screening Tool | 非接触式健康风险检测分析工具】简介
人工智能·目标检测·计算机视觉·多模态大模型·openclaw小龙虾技能