word文档转pdf开源免费,可自定义水印

在 Spring Boot 中实现 Word 转 PDF 并保留格式、添加水印,主流方案有POI+ITextAspose.WordsOpenOffice/LibreOffice 三种,其中Aspose.Words在格式保留和水印功能上综合效果最优,其次是 OpenOffice/LibreOffice,POI+IText 适合轻量且开源优先的场景。

word文档转pdf,有几种方案。

1、Aspose.Words,免费的有水印,商业,处理能力最强,格式最好

2、**LibreOffice,开源,java调用,中高格式,**依赖外部服务部署,多实例并发需额外配置,水印需二次处理

3、POI+IText(开源,轻量场景)通过 Apache POI 读取 Word 内容,再用 IText 生成 PDF,格式需手动映射(如字体、段落、表格),水印通过 IText 添加,非常复杂。

采用第二种方案,开源免费,效果还可以。

1、首先下载:LibreOffice_25.8.2_Win_x86-64.msi 下载地址:https://zh-cn.libreoffice.org/download/libreoffice/

2、安装,默认安装即可

linux安装:

shell 复制代码
# Ubuntu/Debian
sudo apt-get update && sudo apt-get install libreoffice libreoffice-headless

# CentOS
sudo yum install libreoffice libreoffice-headless

linux安装中文字体:

shell 复制代码
# Ubuntu/Debian
sudo apt-get install fonts-wqy-microhei fonts-wqy-zenhei

# CentOS
sudo yum install fontconfig
sudo yum install wqy-microhei-fonts # 或手动上传 Windows 字体到 /usr/share/fonts/chinese/

3、启动libreoffice服务,cmd切换到安装目录下,服务端口8100

windows启动:

shell 复制代码
C:\Program Files\LibreOffice\program>soffice.exe  --headless --invisible --nologo --nodefault --nofirststartwizard --accept="socket,host=0.0.0.0,port=8100;urp

linux启动:

shell 复制代码
# 启动服务并监听 8100 端口
soffice --headless --accept="socket,host=127.0.0.1,port=8100;urp;" --nofirststartwizard &

# 验证服务是否启动
netstat -tlnp | grep 8100  # 应显示 soffice.bin 进程

4、maven依赖

xml 复制代码
<dependencies>
    <!-- JODConverter:连接 LibreOffice 服务 -->
    <dependency>
        <groupId>org.jodconverter</groupId>
        <artifactId>jodconverter-core</artifactId>
        <version>4.4.6</version>
    </dependency>
    <dependency>
        <groupId>org.jodconverter</groupId>
        <artifactId>jodconverter-local</artifactId>
        <version>4.4.6</version> <!-- 本地服务用这个 -->
        <!-- 远程服务需替换为:jodconverter-remote -->
    </dependency>
    
    <!-- iText:PDF 加水印 -->
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>itextpdf</artifactId>
        <version>5.5.13.3</version>
    </dependency>
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>itext-asian</artifactId>
        <version>5.2.0</version> <!-- 支持中文 -->
    </dependency>
</dependencies>

5、java代码

java 复制代码
package cn.myproject.watermark;

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import org.jodconverter.core.DocumentConverter;
import org.jodconverter.local.LocalConverter;
import org.jodconverter.local.office.LocalOfficeManager;

import java.io.*;
import java.nio.file.Files;

public class WordToPdfMain {

    // LibreOffice 服务地址和端口(根据实际部署修改)
    private static final String OFFICE_HOST = "127.0.0.1"; // 本地服务用127.0.0.1,远程用服务器IP
    private static final int OFFICE_PORT = 8100;

    public static void main(String[] args) {
        // 输入输出文件路径(根据实际文件修改)
        String inputWordPath = "D://tmp/test.doc";  // 源Word文件
        String outputPdfPath = "D://tmp//result.pdf"; // 目标PDF文件
        String watermarkText = "内部文档-禁止外传"; // 水印文字

        // 1. 启动Office管理器(连接LibreOffice服务)
        LocalOfficeManager officeManager = LocalOfficeManager.builder()
//                .host(OFFICE_HOST)
                .portNumbers(OFFICE_PORT)
                .build();

        try {
            officeManager.start();
            System.out.println("LibreOffice服务连接成功!");

            // 2. 创建转换器
            DocumentConverter converter = LocalConverter.builder()
                    .officeManager(officeManager)
                    .build();

            // 3. 转换并添加水印
            convertWordToPdfWithWatermark(
                    converter,
                    new File(inputWordPath),
                    new File(outputPdfPath),
                    watermarkText
            );
            System.out.println("转换完成!PDF路径:" + outputPdfPath);

        } catch (Exception e) {
            System.err.println("转换失败:" + e.getMessage());
            e.printStackTrace();
        } finally {
            // 4. 停止服务(释放资源)
            try {
                if (officeManager != null) {
                    officeManager.stop();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Word转PDF并添加水印
     */
    private static void convertWordToPdfWithWatermark(
            DocumentConverter converter,
            File inputWord,
            File outputPdf,
            String watermarkText) throws Exception {

        // 创建临时PDF文件(无水印)
        File tempPdf = Files.createTempFile("temp-", ".pdf").toFile();

        try {
            // 第一步:Word转临时PDF
            converter.convert(inputWord).to(tempPdf).execute();

            // 第二步:给临时PDF加水印,输出到目标文件
            addWatermark(tempPdf, outputPdf, watermarkText);

        } finally {
            // 删除临时文件
            if (tempPdf.exists() && !tempPdf.delete()) {
                System.out.println("临时文件删除失败:" + tempPdf.getAbsolutePath());
            }
        }
    }

    /**
     * 给PDF添加文字水印
     */
    private static void addWatermark(File inputPdf, File outputPdf, String watermarkText) throws Exception {
        PdfReader reader = new PdfReader(new FileInputStream(inputPdf));
        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(outputPdf));

        // 设置中文字体
        BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);

        // 遍历所有页添加水印
        int totalPages = reader.getNumberOfPages();
        for (int i = 1; i <= totalPages; i++) {
            PdfContentByte content = stamper.getUnderContent(i); // 水印在文字下方
            content.beginText();
            content.setFontAndSize(baseFont, 36); // 水印大小
            content.setColorFill(BaseColor.LIGHT_GRAY); // 水印颜色
//            content.setAlphaStroke(0.3f); // 透明度

            // 计算居中位置并倾斜45度
            Rectangle pageSize = reader.getPageSize(i);
            float x = pageSize.getWidth() / 2;
            float y = pageSize.getHeight() / 2;
            content.showTextAligned(Element.ALIGN_CENTER, watermarkText, x, y, -45);

            content.endText();
        }

        // 关闭资源
        stamper.close();
        reader.close();
    }
}

6、同时支持远程调用

在配置远程调用前,需确保两个基础条件:

  1. 服务监听外部地址 :LibreOffice 服务启动时,必须将host参数设为0.0.0.0(而非127.0.0.1),允许所有网卡接收请求。
  2. 网络端口开放 :服务所在服务器的防火墙(如 Linux 的iptables、Windows 防火墙)、云服务器安全组需开放监听端口(如 8100)。

windows下:

cmd 复制代码
# 远程可访问的启动命令(端口8100)
"D:\Program Files\LibreOffice\program\soffice.exe" --headless --invisible --nologo --nodefault --nofirststartwizard --accept="socket,host=0.0.0.0,port=8100;urp;"

linux下:将启动命令中的host=127.0.0.1改为host=0.0.0.0,示例:

shell 复制代码
# 远程可访问的启动命令(端口8100)
soffice --headless --accept="socket,host=0.0.0.0,port=8100;urp;" --nofirststartwizard &

# 验证监听地址(应显示 0.0.0.0:8100,而非 127.0.0.1:8100)
netstat -tlnp | grep 8100

四、并发处理与性能优化

1. 多端口扩展(Linux/Windows)

bash

复制代码
# 启动多个服务实例(端口 8100-8102)
soffice --headless --accept="socket,host=127.0.0.1,port=8100;urp;" &
soffice --headless --accept="socket,host=127.0.0.1,port=8101;urp;" &
soffice --headless --accept="socket,host=127.0.0.1,port=8102;urp;" &
2. JODConverter 配置(Spring Boot)

yaml

复制代码
jodconverter:
  local:
    enabled: true
    kill-existing-process: true
    max-tasks-per-process: 100  # 每个进程最大任务数
    office-home: /usr/lib/libreoffice  # Linux 路径
    # office-home: D:\Program Files\LibreOffice  # Windows 路径
    port-numbers: 8100,8101,8102  # 多端口负载均衡
相关推荐
廖松洋(Alina)12 小时前
【收尾以及复盘】flutter开发鸿蒙APP之打卡日历页面
flutter·华为·开源·harmonyos·鸿蒙
廖松洋(Alina)12 小时前
【收尾以及复盘】flutter开发鸿蒙APP之本月数据统计页面
flutter·华为·开源·harmonyos·鸿蒙
晚霞的不甘15 小时前
CANN 支持多模态大模型:Qwen-VL 与 LLaVA 的端侧部署实战
人工智能·神经网络·架构·开源·音视频
拾荒的小海螺1 天前
开源项目:LTX2 高效可控的开源视频生成模型
开源·音视频
wenzhangli71 天前
Ooder A2UI 第一性原理出发 深度解析核心逻辑
人工智能·开源
Java面试题总结1 天前
基于 Java 的 PDF 文本水印实现方案(iText7 示例)
java·python·pdf
徐小夕@趣谈前端1 天前
Web文档的“Office时刻“:jitword共建版2.0发布!让浏览器变成本地生产力
前端·数据结构·vue.js·算法·开源·编辑器·es6
傻啦嘿哟1 天前
Python操作PDF页面详解:删除指定页的完整方案
开发语言·python·pdf
CoderJia程序员甲1 天前
GitHub 热榜项目 - 日榜(2026-02-05)
ai·开源·大模型·github·ai教程
LeoZY_1 天前
开源项目精选:Dear ImGui —— 轻量高效的 C++ 即时模式 GUI 框架
开发语言·c++·ui·开源·开源软件