From b4ebab65c76e726d6a2688e006351e5fc90bbdca Mon Sep 17 00:00:00 2001 From: matevip Date: Mon, 27 Apr 2026 08:42:03 +0800 Subject: [PATCH] feat(tool): docx image embedding + multi-file render MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two follow-up improvements on top of renderDocxFromFile so the docx pipeline can handle real long-form deliverables instead of just prose-only memos. Image embedding (P1). MarkdownDocxRenderer now recognizes single-line ![alt](path) markdown and embeds the referenced file via POI's XWPFRun.addPicture(): - PNG / JPG / GIF / BMP read straight from disk - SVG rasterized via Apache Batik (PNGTranscoder, target width 1400px) before embedding — OOXML stores raster images, so any vector source needs conversion. Batik runs in-JVM, no rsvg-convert / cairo on host. - Pictures are pinned to roughly the printable page width (≈ 5.77 in for A4 minus default 1800-twip margins) and given a 4:3 height fallback. Mixing images inline with other paragraph text is not supported by design — the markdown subset assumes one image per block paragraph. Inline images would require splitting paragraphs across runs with explicit positioning, well beyond what this renderer covers. - Failure modes (missing file, unsupported format, Batik blowing up) emit an italicised "[image: alt — reason]" placeholder so the rest of the document still renders; the agent can read its own log to see why the picture didn't make it. - Adds two transitive deps via pom: batik-transcoder + batik-codec at 1.18, ~10 MB combined. Worth it given the alternative is shelling out to system tooling. Multi-file render (P2-lite). New tool renderDocxFromFiles(List filePaths, filename, pageSize) reads several markdown files in order and renders one combined docx. Lets the agent split a 30-page proposal into cover.md / ch1.md / ch2.md / appendix.md and produce a single deliverable in one tool call. Each path goes through WorkspacePathGuard.validatePath; any empty or unreadable file aborts with a typed error so the agent fixes its file list before retrying. Files are joined with a blank line — no separator markup is injected, headings carry over cleanly. I deliberately did NOT build the heavier mutable-docx state ("appendDocxChapter / finalizeDocx") flavor of P2: the multi-file form covers the same workflow with no per-conversation state to clean up, and the agent can iterate by rewriting the chapter file and re-running the tool. Stateful append can come later if a streaming use case actually shows up. renderDocx and renderDocxFromFile @Tool descriptions updated to point the agent at renderDocxFromFile for >5 KB markdown and to advertise the new image-embedding capability. --- mateclaw-server/pom.xml | 19 +++ .../vip/mate/tool/builtin/DocxRenderTool.java | 117 ++++++++++++++- .../tool/document/MarkdownDocxRenderer.java | 139 +++++++++++++++++- 3 files changed, 270 insertions(+), 5 deletions(-) diff --git a/mateclaw-server/pom.xml b/mateclaw-server/pom.xml index ee3a6b3f..c5e79d37 100644 --- a/mateclaw-server/pom.xml +++ b/mateclaw-server/pom.xml @@ -291,6 +291,25 @@ 5.4.1 + + + + org.apache.xmlgraphics + batik-transcoder + 1.18 + + + org.apache.xmlgraphics + batik-codec + 1.18 + +