最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java實現Word轉PDF的兩種常見方案和性能實測

 更新時間:2026年01月13日 09:03:36   作者:Knight_AL  
這篇文章對比了Java后端開發(fā)中Word轉PDF的兩種常見方案,即docx4j(純Java實現)和LibreOffice(調用Office原生引擎),下面小編就和大家詳細介紹一下具體實現方法吧

本文對比了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 排版
 → PDF

  • 排版在 Java 層完成
  • 表格、分頁、字體極其耗 CPU
  • GC 壓力大

LibreOffice 執(zhí)行路徑

docx
 → Office 原生排版引擎
 → PDF

  • 原生排版
  • 無 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文件不識別的解決

    這篇文章主要介紹了IDEA導入Springboot項目,注解和pom文件不識別的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • Spring?MVC策略模式之MethodArgumentResolver源碼解析

    Spring?MVC策略模式之MethodArgumentResolver源碼解析

    這篇文章主要為大家介紹了Spring?MVC策略模式之MethodArgumentResolver源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-03-03
  • Spring Data JPA 簡單查詢--方法定義規(guī)則(詳解)

    Spring Data JPA 簡單查詢--方法定義規(guī)則(詳解)

    下面小編就為大家?guī)硪黄猄pring Data JPA 簡單查詢--方法定義規(guī)則(詳解)。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • Java比較兩個對象大小的三種方法詳解

    Java比較兩個對象大小的三種方法詳解

    在優(yōu)先級隊列中插入的元素必須能比較大小,如果不能比較大小,如插入兩個學生類型的元素,會報ClassCastException異常。本文就為大家總結了Java比較兩個對象大小的三種方法,需要的可以參考一下
    2022-07-07
  • Spring Boot Java循環(huán)依賴的解決方案

    Spring Boot Java循環(huán)依賴的解決方案

    本文詳細介紹了循環(huán)依賴的概念、常見場景以及多種解決方法,包括SpringApplicationEvent事件驅動、@Lazy延遲加載、AOP切面和ObjectProvider等,同時,還提供了解決循環(huán)依賴的標準回答技巧和預防措施,感興趣的朋跟隨小編一起看看吧
    2025-12-12
  • 詳解java中的四種代碼塊

    詳解java中的四種代碼塊

    這篇文章主要介紹了詳解java中的四種代碼塊,具有一定借鑒價值,需要的朋友可以參考下。
    2017-12-12
  • java中兩個字符串的拼接、整數相加和浮點數相加實現代碼

    java中兩個字符串的拼接、整數相加和浮點數相加實現代碼

    這篇文章主要為大家介紹java中從鍵盤讀取用戶輸入兩個字符串,實現這兩個字符串的拼接、整數相加和浮點數相加,并輸出結果,需要的朋友可以參考下
    2021-05-05
  • Java基礎入門級教程之從零開始掌握核心概念

    Java基礎入門級教程之從零開始掌握核心概念

    Java是一種強大的面向對象編程語言,廣泛應用于Web開發(fā)、移動應用(如Android)和企業(yè)級系統,這篇文章是Java編程語言的入門教程,從基本概念到實際應用,涵蓋了Java的核心知識,感興趣的朋友跟隨小編一起看看吧
    2026-02-02
  • springcloud安裝rabbitmq并配置延遲隊列插件的過程詳解

    springcloud安裝rabbitmq并配置延遲隊列插件的過程詳解

    本期主要講解如何利用docker快速安裝rabbitmq并且配置延遲隊列插件,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-05-05
  • 基于ComponentScan注解的掃描范圍及源碼解析

    基于ComponentScan注解的掃描范圍及源碼解析

    這篇文章主要介紹了基于ComponentScan注解的掃描范圍及源碼解析,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09

最新評論

昌江| 六安市| 无为县| 日土县| 武汉市| 承德县| 巴东县| 石嘴山市| 济南市| 贵定县| 扎赉特旗| 阜南县| 遂溪县| 冕宁县| 象山县| 兴安县| 梁河县| 鄯善县| 屏山县| 松溪县| 英山县| 隆安县| 栾川县| 宣城市| 靖江市| 永丰县| 唐河县| 霍林郭勒市| 奉新县| 广安市| 通化市| 阿瓦提县| 武隆县| 松阳县| 大同市| 澄迈县| 孝昌县| 麻栗坡县| 喀喇沁旗| 东平县| 红安县|