結合 Jsoup 解析/標準化 HTML 與 Jacob (Java-COM Bridge) 驅動 Microsoft Word,實現 HTML 轉 Word 並解決圖片外部連結失效問題。
Jsoup.parse(html) 修正不合法標籤<img> 標籤,提取 src 存入 Map<Integer, String>,HTML 中替換為 ${image_1} 等標記FileWriter 輸出處理後 HTML 為 template.doctemplate.doc,調用 replaceText2Image 將佔位符替換為實際圖片selectAll → copy → createNewDocument → paste → saveAsDocument doc = Jsoup.parse(html);
Map<Integer, String> imgMap = new HashMap<>();
int idx = 1;
for (Element img : doc.select("img")) {
String src = img.attr("src");
imgMap.put(idx, src);
img.replaceWith(new TextNode("${image_" + idx + "}", ""));
idx++;
}
String processedHtml = doc.outerHtml();
public class MSOfficeGeneratorUtils {
private ActiveXComponent word;
private Dispatch document, selection;
public MSOfficeGeneratorUtils(boolean visible) {
word = new ActiveXComponent("Word.Application");
word.setProperty("Visible", new Variant(visible));
word.setProperty("AutomationSecurity", new Variant(3)); // 禁用巨集
}
public void openDocument(String path) {
document = Dispatch.call(word.getProperty("Documents"), "Open", path).toDispatch();
selection = word.getProperty("Selection").toDispatch();
}
public void replaceText2Image(String placeholder, String imgPath) {
moveStart();
while (find(placeholder)) {
insertImage(imgPath);
moveEnd(); enterDown(1);
}
}
private void insertImage(String path) {
Dispatch.call(Dispatch.get(selection, "InLineShapes").toDispatch(),
"AddPicture", path);
}
public void copyAll() {
Dispatch.call(document, "Select");
Dispatch.call(selection, "Copy");
}
public void paste() { Dispatch.call(selection, "Paste"); }
public void saveAs(String path) {
Dispatch.call(document, "SaveAs", path);
}
public void quit() {
word.invoke("Quit", new Variant[0]);
ComThread.Release();
}
}
public void generateWord(String html, String outputPath) throws IOException {
// 1. Jsoup 處理
Document doc = Jsoup.parse(html);
Map<Integer, String> imgMap = new LinkedHashMap<>();
int i = 1;
for (Element img : doc.select("img")) {
imgMap.put(i, img.attr("abs:src")); // 確保絕對路徑
img.replaceWith(new TextNode("${img_" + i + "}", ""));
i++;
}
File temp = File.createTempFile("template", ".doc");
Files.write(temp.toPath(), doc.outerHtml().getBytes(StandardCharsets.UTF_8));
// 2. Jacob 處理
MSOfficeGeneratorUtils word = new MSOfficeGeneratorUtils(false);
try {
word.openDocument(temp.getAbsolutePath());
for (Map.Entry<Integer, String> e : imgMap.entrySet()) {
word.replaceText2Image("${img_" + e.getKey() + "}", e.getValue());
}
word.copyAll();
word.createNewDocument();
word.paste();
word.saveAs(outputPath);
} finally {
word.quit();
temp.delete();
}
}
org.jsoup:jsoup:1.17.2com.jacob:jacob:1.20 + jacob.dll 放入 java.library.path (系統路徑或 -Djava.library.path)ComThread.InitSTA() / Release()<meta charset="UTF-8">,Jacob 端確保編碼一致