Skip to content

fix(docparser): anydoc 内嵌 EMF/WMF 图片的扩展名与 Rust 序列化器对齐,不再静默丢图 - #3938

Open
linus-liu-web wants to merge 1 commit into
Tencent:mainfrom
linus-liu-web:fix/anydoc-emf-wmf-image-links
Open

linus-liu-web wants to merge 1 commit into
Tencent:mainfrom
linus-liu-web:fix/anydoc-emf-wmf-image-links

Conversation

@linus-liu-web

Copy link
Copy Markdown
Contributor

Description

Fix embedded EMF/WMF images being silently dropped when the anydoc engine parses a document.

anydoc renders an embedded image in place as ![alt](images/image-N<ext>). The <ext> comes from the Rust serializer (third_party/anydoc-go/src/asset_links.rs::extension_for). ImageResolver then looks that exact string up in a map keyed by anydoc.ImageDir + Asset.Name, where Asset.Name is built from internal/infrastructure/docparser/anydoc/backend_cgo.go::extensionFor. The lookup is an exact string match with no normalisation, so the two functions have to agree character for character.

They did not agree on EMF/WMF. anydoc emits image/emf and image/wmf for those parts (hard-coded in shared/assets.rs::media_type_for and shared/officeart.rs), the Rust table fell through to .bin, and the Go side asked the platform MIME registry instead of using a fixed table — so on a Debian image with media-types installed, /etc/mime.types supplied .emf/.wmf. The Markdown link and the ImageRef never matched, saveReferencedImage found nothing, and the image bytes were never stored. Nothing was logged and the document still reported success.

This change:

  • adds image/emf and image/wmf to extension_for in asset_links.rs
  • adds the same two media types to extensionFor in backend_cgo.go, and makes it a pure table

Dropping the mime.ExtensionsByType call is deliberate. Letting the host's /etc/mime.types decide the extension is what caused the divergence, and a table that has to mirror a static Rust table cannot depend on the runtime environment anyway. Unknown types fall back to .bin on both sides, which still satisfies the original goal of never writing an extension-less blob.

Verified reachable divergences before the fix, by reproducing extensionFor against every media type anydoc can produce:

media_type Rust Go
image/png, jpeg, gif, bmp, tiff, svg, webp same same
application/octet-stream, application/vnd.ms-ole-object .bin .bin
image/emf .bin .emf
image/wmf .bin .wmf

The other producers of images/... references derive the link and the map key from the same variable, so they cannot drift this way: builtin_converter.go::imageToResult uses one safeRef for both, and the Python parser writes image_path into the map and the Markdown together. The Rust/Go boundary was the only place where two independently built strings had to agree.

Type of Change

  • 🐛 Bug fix

Related Issue

Fixes #3932

Testing

Added TestEmbeddedVectorImageLinksResolve, which builds a minimal .docx carrying one EMF and one WMF drawing in memory, converts it through the linked converter, and requires every Markdown image reference to resolve against the ImageRef map the resolver builds. It also pins .emf/.wmf, so agreeing on .bin for everything could not pass.

The test runs under the existing anydoc CI job (go test -tags anydoc ./internal/infrastructure/docparser/...).

Before/after on that fixture, with the archive rebuilt from this branch:

unfixed:  markdown "images/image-1.bin"  vs  refMap "images/image-1.emf"  -> no match
fixed:    markdown "images/image-1.emf"  vs  refMap "images/image-1.emf"  -> resolves

The test is not vacuous: pointing either table back at ".bin" turns it red with

markdown reference "images/image-1.emf" matches no ImageRef, so the image would
never be stored (lookup keys: [images/image-1.bin images/image-2.bin])

Also ran go vet -tags anydoc ./internal/infrastructure/docparser/..., go test -tags anydoc ./internal/infrastructure/docparser/... and go build -tags anydoc ./cmd/server.

anydoc 把内嵌图片就地渲染成 `![alt](images/image-N<ext>)`,`<ext>` 来自
asset_links.rs::extension_for;ImageResolver 再拿这个字符串去一张 map 里查,
键是 anydoc.ImageDir + Asset.Name,而 Name 由 backend_cgo.go::extensionFor 拼出。
查表是精确匹配,两边必须逐字符一致。

EMF/WMF 上两边并不一致:anydoc 对这两类 part 硬编码产出 image/emf 与
image/wmf(shared/assets.rs::media_type_for、shared/officeart.rs),Rust 表
落到 `.bin`,而 Go 侧去查平台 MIME 注册表——在装了 media-types 的 Debian
镜像上拿到 `.emf`/`.wmf`。链接与 ref 于是永远对不上,saveReferencedImage
找不到条目,图片字节从不落盘,且没有任何日志。

- asset_links.rs::extension_for 补 image/emf 与 image/wmf
- backend_cgo.go::extensionFor 补同样两项,并改成纯查表

去掉 mime.ExtensionsByType 是有意的:让扩展名取决于宿主的 /etc/mime.types
正是这个 bug 的成因,何况这张表本来就要和 Rust 的静态表逐字符对齐。未知
类型两边都落到 .bin,仍满足「不写出无扩展名 blob」的原意。

新增 TestEmbeddedVectorImageLinksResolve:在内存里构造一个含一张 EMF 与
一张 WMF 的最小 docx,转换后要求每一条 Markdown 图片引用都能在 resolver
建的 ImageRef 表里命中,并固定扩展名为 .emf/.wmf(避免两边一起退化成
.bin 也能通过)。

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: anydoc 引擎下 EMF/WMF 内嵌图片的 Markdown 链接与 ImageRef 对不上,图片被静默丢弃

1 participant