Word转pdf&水印

2024-11-24

2024-11-24-word转pdf&水印

一、引入相关包

<!-- 集成libreOffice -->
<dependency>
	<groupId>org.jodconverter</groupId>
	<artifactId>jodconverter-local</artifactId>
	<version>4.4.2</version>
</dependency>

<dependency>
	<groupId>org.jodconverter</groupId>
	<artifactId>jodconverter-core</artifactId>
	<version>4.4.2</version>
</dependency>

<dependency>
	<groupId>org.jodconverter</groupId>
	<artifactId>jodconverter-spring-boot-starter</artifactId>
	<version>4.4.2</version>
</dependency>

<dependency>
	<groupId>org.apache.pdfbox</groupId>
	<artifactId>pdfbox</artifactId>
	<version>2.0.24</version>
</dependency>

二、配置文件

  • 提供libreOffice相关配置
  • 参照以下配置,启动springboot服务的同时会自动启动系统中已安装的libreOffice,运行端口为2002
# libreOffice配置
jodconverter:
  # 本地方式连接
  local:
    enabled: true
    #安装路径
    office-home: C:\Program Files (x86)\LibreOffice
    #端口
    portNumbers: 2002
    #最大处理数量
    maxTasksPerProcess: 100
    # 任务执行的超时时间
    task-execution-timeout: 360000
    # 任务队列的超时时间
    task-queue-timeout: 360000
    # 一个进程的超时时间
    process-timeout: 360000

三、编写工具类

  • word2Pdf工具类
import com.hymake.framework.core.utils.SpringContextUtils;
import lombok.extern.slf4j.Slf4j;
import org.jodconverter.core.DocumentConverter;
import org.jodconverter.core.document.DefaultDocumentFormatRegistry;
import org.jodconverter.local.LocalConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.UUID;


/**
 * @Author: huanghwh
 * @Date: 2024/11/01 下午8:46
 * <p>
 * word转pdf工具
 */
@Component
@Slf4j
public class WordPdfUtil {

    private static boolean license = false;

    /**
     * 导出根路径
     */
    private static String OUT_FOLDER_PATH;
  

    @Value("${spring.web.upload.folder}")
    public void setOutFolderPath(String outFolderPath) {
        WordPdfUtil.OUT_FOLDER_PATH = outFolderPath;
    }


    /**
     * doc转pdf
     *
     * @param wordPath         word路径
     * @param pdfPath          pdf文件输出路径
     * @return {@link File}
     * @author huanghwh
     * @date 2022/7/7 下午3:28
     */
    public static File doc2Pdf(String wordPath, String pdfPath) {
        return doc2Pdf(wordPath, pdfPath, false, null);
    }


    /**
     * doc2 pdf doc转pdf
     *
     * @param wordPath         word路径
     * @param pdfPath          pdf文件输出路径
     * @param addWatermark     是否添加水印
     * @param watermarkImgPath 水印img路径
     * @return {@link File}
     * @author huanghwh
     * @date 2022/7/7 下午3:28
     */
    public static File doc2Pdf(String wordPath, String pdfPath, boolean addWatermark, String watermarkImgPath) {
        return doc2Pdf(wordPath, pdfPath, addWatermark, watermarkImgPath, false);
    }
  

    /**
     * doc转pdf (libreOffice + pdfBox)
     *
     * @param wordPath         word路径
     * @param pdfPath          pdf文件输出路径
     * @param addWatermark     是否添加水印
     * @param watermarkImgPath 水印图片路径
     * @param removeBlank      是否删除空白页面 (当前版本不支持)
     * @return {@link File}
     * @author huanghwh
     * @date 2024/11/01 下午3:28
     */
    public static File doc2Pdf(String wordPath, String pdfPath, boolean addWatermark, String watermarkImgPath, boolean removeBlank) {
        pdfPath = OUT_FOLDER_PATH + pdfPath + "/" + UUID.randomUUID() + ".pdf";
        File inputFile = new File(wordPath);
        File outputFile = new File(pdfPath);
        try {
            long old = System.currentTimeMillis();
            DocumentConverter documentConverter = SpringContextUtils.getBean(LocalConverter.class);
            documentConverter.convert(inputFile).to(outputFile).as(DefaultDocumentFormatRegistry.PDF).execute();
            if (addWatermark) {
                WatermarkUtil.insertWatermarkImgByPdfBox(outputFile, watermarkImgPath, 600, 600);
            }

            long now = System.currentTimeMillis();
            log.info("Word 转 Pdf 共耗时:" + ((now - old) / 1000.0) + "秒");
            return outputFile;
        } catch (Exception e) {
            e.printStackTrace();
            ErrorUtils.printErrorLog(e);
            throw new RuntimeException("Word 转 Pdf 失败: " + e.getMessage());
        }
    }  
}
  • 水印工具类
@Slf4j
public class WatermarkUtil {
  
    /**
     * 添加水印(pdfBox)
     * 
     * @param pdfFile
     * @param imgPath
     * @param width
     * @param height
     * @return
     * @author huanghwh
     * @date 2024/11/1 下午4:08
     */
    public static void insertWatermarkImgByPdfBox(File pdfFile, String imgPath, Integer width, Integer height) throws Exception {
        log.info("开始添加水印");

        // 下载并处理水印图像
        File watermarkImage = downloadImage(imgPath);
   
        try (PDDocument document = PDDocument.load(pdfFile)) {
            // 创建 PDFBox 图像对象
            PDImageXObject pdImage = PDImageXObject.createFromFile(watermarkImage.getAbsolutePath(), document);

            // 在每一页添加水印
            for (PDPage page : document.getPages()) {
                try (PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.PREPEND, true, true)) {
                    // 保存当前图形状态
                    contentStream.saveGraphicsState();

                    // 计算水印的位置
                    float x = (page.getMediaBox().getWidth() - width) / 2; // 水平居中
                    float y = (page.getMediaBox().getHeight() - height) / 2; // 垂直居中

                    // 添加水印图像
                    contentStream.drawImage(pdImage, x, y, width, height);

                    // 恢复图形状态
                    contentStream.restoreGraphicsState();
                }
            }
            document.save(pdfFile);
            log.info("水印添加完毕");
        } catch (Exception e) {
            ErrorUtils.printErrorLog(e);
            throw new CommonException("添加水印失败:" + e.toString());
        } finally {
            if (watermarkImage.exists()) {
                boolean deleted = watermarkImage.delete();
                if (!deleted) {
                    log.warn("未能删除水印文件: " + watermarkImage.getAbsolutePath());
                }
            }
        }
    }


    /**
     * 下载水印文件
     * 
     * @param imageUrl
     * @return 
     * @author huanghwh
     * @date 2024/11/1 下午4:09
     */
    private static File downloadImage(String imageUrl) throws IOException {
        URL url = new URL(imageUrl);
        // 临时文件
        File tempFile = File.createTempFile("watermark", ".png");

        try (InputStream in = url.openStream(); FileOutputStream out = new FileOutputStream(tempFile)) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
        }

        return tempFile;
    }

}

三、使用示例

String outPath = "/project/";
String templateName = "demo.docx";
File docFile = PoiTlUtil.exportWord(templateName, dataMap, outPath);
// doc加水印并转成pdf
Params waterMarkParam = paramsService.getByParamCode("WATER_MARK_IMG");
// waterMarkParam.getParamValue() 为水印远程地址,例如:http://192.168.0.1:8085/static/watermark.png
File pdfFile = WordPdfUtil.doc2Pdf(docFile.getPath(), outPath, true, waterMarkParam.getParamValue(), true);

四、使用问题

1.转pdf后排版发生变化

  • 字体不存在时,libreOffice会使用替代字体,造成排版变化,尝试修改字体,或将需要的字体放置在操作系统的字体文件夹下,libreOffice将读取系统字体。