Convert DOCX to PDF in Spring Boot with docx4j: A Lightweight Open‑Source Solution

This article walks through building a pure‑Java DOCX‑to‑PDF converter in Spring Boot using docx4j, compares it with alternatives, shows Maven dependencies, provides a utility class and controller example, and explains how to resolve Chinese font garbling on both Windows and Linux.

java1234
java1234
java1234
Convert DOCX to PDF in Spring Boot with docx4j: A Lightweight Open‑Source Solution

1. Solution Comparison

Apache POI + iText : Open source, no external dependencies, medium style fidelity, simple deployment (★), but poor support for complex formats.

docx4j : Open source, no external dependencies, high style fidelity, moderate deployment effort (★★), recommended for pure Java projects.

LibreOffice + JODConverter : Open source, requires LibreOffice installation, very high style fidelity, complex deployment (★★★).

Aspose.Words : Not open source (commercial license), no external dependencies, highest style fidelity, simple deployment (★), but requires a paid license.

2. Add Maven Dependencies

<dependencies>
    <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>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>2.0.9</version>
    </dependency>
</dependencies>

3. Core Utility Class: DocxToPdfUtil

package com.donglin.utils;

import org.docx4j.Docx4J;
import org.docx4j.fonts.IdentityPlusMapper;
import org.docx4j.fonts.Mapper;
import org.docx4j.fonts.PhysicalFont;
import org.docx4j.fonts.PhysicalFonts;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;

import java.io.File;
import java.io.FileOutputStream;

public class DocxToPdfUtil {

    /**
     * Convert a DOCX file to PDF.
     * @param docxPath input file path
     * @param pdfPath  output file path
     */
    public static void convert(String docxPath, String pdfPath) {
        try {
            // 1. Load the Word document
            WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new File(docxPath));

            // 2. Configure font mapping to avoid Chinese garbling
            Mapper fontMapper = new IdentityPlusMapper();
            PhysicalFonts.discoverPhysicalFonts();

            PhysicalFont simsun = PhysicalFonts.get("SimSun");
            if (simsun != null) {
                fontMapper.put("SimSun", simsun);
                // Common Chinese font mappings
                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"));

                // Fix "宋体(正文)/宋体(标题)" garbling
                PhysicalFonts.put("PMingLiU", PhysicalFonts.get("SimSun"));
                PhysicalFonts.put("新細明體", PhysicalFonts.get("SimSun"));
                wordMLPackage.setFontMapper(fontMapper);
            }

            // 3. Create output stream and perform conversion
            try (FileOutputStream os = new FileOutputStream(pdfPath)) {
                Docx4J.toPDF(wordMLPackage, os);
            }

            System.out.println("✅ PDF generated successfully: " + pdfPath);
        } catch (Exception e) {
            System.err.println("❌ Conversion failed: " + e.getMessage());
        }
    }
}

4. Controller Example

import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import javax.servlet.http.HttpServletResponse;

@RestController
@RequestMapping("/convert")
public class FileController {

    @GetMapping("/convertToPdf")
    public void convertToPdf(@RequestParam String filePath, HttpServletResponse response) throws Exception {
        // 1. Verify file existence
        File inputFile = new File(filePath);
        if (!inputFile.exists()) {
            throw new RuntimeException("File not found: " + filePath);
        }

        // 2. Define temporary PDF output path
        String pdfPath = filePath.replace(".docx", ".pdf");

        // 3. Invoke conversion utility
        DocxToPdfUtil.convert(filePath, pdfPath);

        // 4. Set response headers and stream PDF back to client
        response.setContentType("application/pdf");
        response.setHeader("Content-Disposition", "attachment; filename=" + new File(pdfPath).getName());
        try (FileInputStream fis = new FileInputStream(pdfPath);
             OutputStream os = response.getOutputStream()) {
            fis.transferTo(os);
            os.flush();
        }

        // Optional: delete temporary PDF file
        new File(pdfPath).delete();
    }
}

Send a request with Postman, e.g.:

GET http://localhost:8080/convertToPdf?filePath=E:/ai/report.docx

5. Windows: Fix Chinese Garbling

Windows font mapping example
Windows font mapping example

Add the required Chinese fonts to the fontMapper as shown in the utility class.

6. Linux: Fix Chinese Garbling

Install Windows fonts on the Linux host: sudo mkdir -p /usr/share/fonts/win_font Copy the fonts from a Windows 10 installation ( C:\Windows\Fonts) to /usr/share/fonts/win_font.

cd /usr/share/fonts/win_font
sudo mkfontscale   # generate font scale file
sudo mkfontdir      # generate font directory index
sudo fc-cache -fv  # refresh font cache
Linux font installation result
Linux font installation result

Verify the fonts are loaded with:

fc-list :lang=zh

7. Summary

Use the open‑source library docx4j.

No need to install Office or LibreOffice.

Preserves common styles, images, and tables.

High performance and lightweight deployment.

JavamavenSpring BootPDF conversiondocx4j
java1234
Written by

java1234

Former senior programmer at a Fortune Global 500 company, dedicated to sharing Java expertise. Visit Feng's site: Java Knowledge Sharing, www.java1234.com

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.