Java實現Word轉PDF的兩種常見方案和性能實測
本文對比了Java后端開發(fā)中Word轉PDF的兩種常見方案:docx4j(純Java實現)和LibreOffice(調用Office原生引擎)。通過可運行的Java代碼測試,分析了單次轉換耗時、冷熱啟動時間以及穩(wěn)定性等指標。測試環(huán)境采用JDK17、8核CPU、16GB內存,針對20頁含圖文表格的中文Word文檔進行測試。項目提供了完整的Maven依賴配置和兩種轉換方案的實現代碼,其中docx4j采用XSL-FO+Apache FOP技術路線,并詳細處理了中文字體映射問題。測試結果將揭示兩種方案在真實場景下的
- docx4j(純 Java 實現)
- LibreOffice(調用 Office 原生引擎)
很多討論停留在“聽說 LibreOffice 更快”,但沒有代碼、沒有數據、無法復現。
用可運行的 Java 代碼,驗證 docx4j 和 LibreOffice 的真實性能差距。
一、測試目標與原則
測試目標
- 對比 單次轉換耗時
- 對比 冷啟動 / 熱啟動
- 對比 穩(wěn)定性(失敗率)
測試原則
- 同一批
.docx文件 - 串行執(zhí)行(對比單次成本)
- 不引入 Spring / Web 干擾
- 用
System.nanoTime()直接計時
二、測試環(huán)境
- JDK:17
- CPU:8 Core
- 內存:16 GB
- OS:Windows
- LibreOffice:25.x
- docx4j:11.x
- 文檔:20 頁 Word(含圖片、表格、頁眉頁腳、中文)
三、項目結構
pdf-bench/
├─ pom.xml
├─ samples/
│ └─ sample.docx
└─ src/main/java/com/example/bench/
├─ BenchMain.java
├─ LibreOfficeConverter.java
├─ Docx4jConverter.java
└─ Stats.java
四、Maven 依賴(pom.xml)
<dependencies>
<!-- docx4j -->
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j-core</artifactId>
<version>11.4.8</version>
</dependency>
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j-JAXB-ReferenceImpl</artifactId>
<version>11.4.8</version>
</dependency>
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j-export-fo</artifactId>
<version>11.4.8</version>
</dependency>
<!-- Apache FOP -->
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>fop</artifactId>
<version>2.9</version>
</dependency>
</dependencies>
五、LibreOffice 轉 PDF 實現
public class LibreOfficeConverter {
private final String soffice;
public LibreOfficeConverter(String soffice) {
this.soffice = soffice;
}
public void convert(Path docx, Path outDir) throws Exception {
ProcessBuilder pb = new ProcessBuilder(
soffice,
"--headless",
"--nologo",
"--nolockcheck",
"--nodefault",
"--nofirststartwizard",
"--convert-to", "pdf",
"--outdir", outDir.toString(),
docx.toString()
);
pb.redirectErrorStream(true);
Process p = pb.start();
int code = p.waitFor();
if (code != 0) {
throw new RuntimeException("LibreOffice failed, exit=" + code);
}
}
}
Windows 記得把 soffice 改成C:\Program Files\LibreOffice\program\soffice.exe
六、docx4j 轉 PDF 實現
public class Docx4jConverter {
public void convert(Path docx, Path pdf) throws Exception {
// 1?? 加載 Word
WordprocessingMLPackage wordMLPackage =
WordprocessingMLPackage.load(docx.toFile());
// 2?? 字體映射(核心)
IdentityPlusMapper fontMapper = new IdentityPlusMapper();
// 宋體(核心兜底)
PhysicalFont simsun = PhysicalFonts.get("SimSun");
if (simsun == null) {
throw new RuntimeException("未找到 SimSun 字體,請確認系統已安裝宋體");
}
fontMapper.put("SimSun", simsun);
// ===== 常用中文字體映射 =====
fontMapper.put("隸書", PhysicalFonts.get("LiSu"));
fontMapper.put("宋體", PhysicalFonts.get("SimSun"));
fontMapper.put("微軟雅黑", PhysicalFonts.get("Microsoft YaHei"));
fontMapper.put("黑體", PhysicalFonts.get("SimHei"));
fontMapper.put("楷體", PhysicalFonts.get("KaiTi"));
fontMapper.put("新宋體", PhysicalFonts.get("NSimSun"));
fontMapper.put("華文行楷", PhysicalFonts.get("STXingkai"));
fontMapper.put("華文仿宋", PhysicalFonts.get("STFangsong"));
fontMapper.put("仿宋", PhysicalFonts.get("FangSong"));
fontMapper.put("幼圓", PhysicalFonts.get("YouYuan"));
fontMapper.put("華文宋體", PhysicalFonts.get("STSong"));
fontMapper.put("華文中宋", PhysicalFonts.get("STZhongsong"));
fontMapper.put("等線", PhysicalFonts.get("SimSun"));
fontMapper.put("等線 Light", PhysicalFonts.get("SimSun"));
fontMapper.put("華文琥珀", PhysicalFonts.get("STHupo"));
fontMapper.put("華文隸書", PhysicalFonts.get("STLiti"));
fontMapper.put("華文新魏", PhysicalFonts.get("STXinwei"));
fontMapper.put("華文彩云", PhysicalFonts.get("STCaiyun"));
fontMapper.put("方正姚體", PhysicalFonts.get("FZYaoti"));
fontMapper.put("方正舒體", PhysicalFonts.get("FZShuTi"));
fontMapper.put("華文細黑", PhysicalFonts.get("STXihei"));
fontMapper.put("宋體擴展", PhysicalFonts.get("simsun-extB"));
fontMapper.put("仿宋_GB2312", PhysicalFonts.get("FangSong_GB2312"));
fontMapper.put("新細明體", PhysicalFonts.get("SimSun"));
// ===== 修復 “宋體(正文)/ 宋體(標題)/ 臺灣字體” 亂碼 =====
PhysicalFonts.put("PMingLiU", simsun);
PhysicalFonts.put("新細明體", simsun);
// 3?? 設置到 Word 包
wordMLPackage.setFontMapper(fontMapper);
// 4?? 轉 PDF
try (OutputStream os = Files.newOutputStream(pdf)) {
Docx4J.toPDF(wordMLPackage, os);
}
}
}
docx4j 走的是 XSL-FO + Apache FOP 路線。
七、性能統計工具(P50 / P95)
public class Stats {
private final List<Long> times = new ArrayList<>();
public void add(long nanos) {
times.add(nanos);
}
public void print(String name) {
Collections.sort(times);
double avg = times.stream().mapToLong(x -> x).average().orElse(0) / 1e6;
double p50 = times.get(times.size() / 2) / 1e6;
double p95 = times.get((int)(times.size() * 0.95)) / 1e6;
System.out.printf(
"%s -> avg=%.2fms, p50=%.2fms, p95=%.2fms%n",
name, avg, p50, p95
);
}
}
八、壓測主程序(BenchMain)
public class BenchMain {
public static void main(String[] args) throws Exception {
Path docx = Path.of("samples/sample.docx");
Path out = Path.of("out");
Files.createDirectories(out);
LibreOfficeConverter lo =
new LibreOfficeConverter("soffice");
Docx4jConverter d4j =
new Docx4jConverter();
int rounds = 5;
// LibreOffice
Stats loStats = new Stats();
for (int i = 0; i < rounds; i++) {
long t0 = System.nanoTime();
lo.convert(docx, out);
loStats.add(System.nanoTime() - t0);
}
// docx4j
Stats d4jStats = new Stats();
for (int i = 0; i < rounds; i++) {
long t0 = System.nanoTime();
d4j.convert(docx, out.resolve("d4j_" + i + ".pdf"));
d4jStats.add(System.nanoTime() - t0);
}
loStats.print("LibreOffice");
d4jStats.print("docx4j");
}
}
九、實測結果

** LibreOffice 穩(wěn)定快 **
十、為什么差距這么大?
docx4j 執(zhí)行路徑
docx(xml)
→ JAXB
→ Java 對象
→ XSL-FO
→ Apache FOP 排版
- 排版在 Java 層完成
- 表格、分頁、字體極其耗 CPU
- GC 壓力大
LibreOffice 執(zhí)行路徑
docx
→ Office 原生排版引擎
- 原生排版
- 無 XML 重建
- 幾乎等同“另存為 PDF”
十一、結論
如果你是 Java 后端,做真實業(yè)務文檔轉換:
- 性能:LibreOffice
- 穩(wěn)定性:LibreOffice
- 還原度:LibreOffice
docx4j 只適合:
- 簡單模板
- 低 QPS
- 必須純 Java 的場景
docx4j導出來可能有問題

LibreOffice 正常

以上就是Java實現Word轉PDF的兩種常見方案和性能實測的詳細內容,更多關于Java Word轉PDF的資料請關注腳本之家其它相關文章!
相關文章
IDEA導入Springboot項目,注解和pom文件不識別的解決
這篇文章主要介紹了IDEA導入Springboot項目,注解和pom文件不識別的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04
Spring?MVC策略模式之MethodArgumentResolver源碼解析
這篇文章主要為大家介紹了Spring?MVC策略模式之MethodArgumentResolver源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03
Spring Data JPA 簡單查詢--方法定義規(guī)則(詳解)
下面小編就為大家?guī)硪黄猄pring Data JPA 簡單查詢--方法定義規(guī)則(詳解)。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-04-04
Spring Boot Java循環(huán)依賴的解決方案
本文詳細介紹了循環(huán)依賴的概念、常見場景以及多種解決方法,包括SpringApplicationEvent事件驅動、@Lazy延遲加載、AOP切面和ObjectProvider等,同時,還提供了解決循環(huán)依賴的標準回答技巧和預防措施,感興趣的朋跟隨小編一起看看吧2025-12-12
springcloud安裝rabbitmq并配置延遲隊列插件的過程詳解
本期主要講解如何利用docker快速安裝rabbitmq并且配置延遲隊列插件,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-05-05

