目录

  • 前言
  • 准备环境
  • 代码
    • 添加依赖
    • 工具类
  • 测试
    • 测试加水印与背景
    • 测试pdf合并

前言

本文基于itext7实现pdf加水印和合并的操作。实际上在我们实际项目应用中,对于pdf的操作也是比较常见的,我上一个项目中就有将结果转成pdf导出的需求。本文是基于茶佬的笔记《JAVA 后端给 PDF 添加水印》写成。

准备环境

jdk8,idea2020.1.1,maven3

代码

添加依赖

<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.5.4</version>
</dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>
<dependency><groupId>com.itextpdf</groupId><artifactId>itext7-core</artifactId><version>7.1.16</version>
</dependency>

工具类

图片水印实体

import lombok.Data;
import lombok.ToString;/*** 文字水印** @author mrcode*/
@Data
@ToString
public class ImageWatermark {/*** 图片所在绝对路径*/private String path;/*** 透明度 0-1(完全透明-不透明)*/private float opacity = 0.5F;
}

文字水印实体

import com.itextpdf.io.font.constants.StandardFonts;
import lombok.Data;
import lombok.ToString;/*** 文字水印** @author mrcode*/
@Data
@ToString
public class TextWatermark {/*** 文字水印,多行可使用 \n 换行*/private String text;/*** 透明度 0-1(完全透明-不透明)*/private float opacity = 0.5F;/*** 颜色:只支持 RGB; 为空则默认为黑色;比如 0,0,0;* <pre>*     建议使用 rgba 提供用户选择,后面的 a 的数值用于透明度的设置,展示的颜色和水印效果类似* </pre>*/private String color;/*** 旋转角度*/private float radAngle = 0F;/*** 字体文件路径;如果为空,则使用标准的英文字体 StandardFonts.HELVETICA* <pre>*     支持: afm、pfm、ttf、otf、woff、woff2* </pre>** @see StandardFonts#HELVETICA*/private String fontPath;/*** 字号大小,*/private int fontSize = 30;/*** 文本平铺方式: 1:文本水平垂直居中 2:页面平铺*/private int tileMode = 1;/*** 页面平铺:文字水平间隔;默认为 50*/private Integer pageModeOfHorizontalInterval;/*** 页面平铺:文字垂直间隔; 建议至少为字体大小(默认为字体大小),如果有旋转,则合理的高度是 (文字个数 * 文字高度)*/private Integer pageModeOfVerticalInterval;
}

添加水印和背景的工具类

import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.io.font.constants.StandardFonts;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.colors.DeviceRgb;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.extgstate.PdfExtGState;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.layout.property.VerticalAlignment;import java.io.IOException;/*** PDF 水印添加** @author mrcode* @date 2021/10/22 21:27*/
public class PdFWatermarkUtil {/*** 添加文字水印; 默认为居中添加** @param watermark* @param srcPath   原始 PDF 文件绝对路径* @param destPath  添加完水印后的 PDF 存放路径*/public static void addWatermark(TextWatermark watermark, String srcPath, String destPath) throws IOException {PdfDocument pdfDoc = new PdfDocument(new PdfReader(srcPath), new PdfWriter(destPath));Document doc = new Document(pdfDoc);PdfFont font = getPdfFont(watermark.getFontPath());// 设置文字水印样式final String text = watermark.getText();final int fontSize = watermark.getFontSize();Paragraph paragraph = new Paragraph(text).setFont(font)
//                 .setFontColor(new DeviceRgb(0, 0, 0)).setOpacity(watermark.getOpacity()) // 字体透明度 0-1 完全透明~不透明.setFontSize(fontSize); // 字体大小final String color = watermark.getColor();// 设置 RGB 颜色if (color != null) {final String[] rgbs = color.split(",");final DeviceRgb deviceRgb = new DeviceRgb(Integer.parseInt(rgbs[0].trim()),Integer.parseInt(rgbs[1].trim()),Integer.parseInt(rgbs[2].trim()));paragraph.setFontColor(deviceRgb);}// 获取水印文字宽度final float textWidth = font.getWidth(text, fontSize);// 文字高度则是字体大小final float textHeight = fontSize;final int tileMode = watermark.getTileMode();final int pageModeOfHorizontalInterval = watermark.getPageModeOfHorizontalInterval() == null ? 50 : watermark.getPageModeOfHorizontalInterval();final int pageModeOfVerticalInterval = watermark.getPageModeOfVerticalInterval() == null ? (int) textHeight : watermark.getPageModeOfVerticalInterval();for (int i = 1; i <= pdfDoc.getNumberOfPages(); i++) {PdfPage pdfPage = pdfDoc.getPage(i);// 获取页面大小,考虑页面旋转Rectangle pageSize = pdfPage.getPageSizeWithRotation();// 当页面有旋转时,内容自动旋转pdfPage.setIgnorePageRotationForContent(true);// 水印水平垂直居中if (tileMode == 1) {// 计算添加的位置坐标:这里使用居中位置,水平垂直居中float x = (pageSize.getLeft() + pageSize.getRight()) / 2;float y = (pageSize.getTop() + pageSize.getBottom()) / 2;// 参数分别为:文本、x 坐标、y 坐标、添加到底几页、文本水平对齐方式、文本垂直对齐方式、旋转弧度doc.showTextAligned(paragraph,x, // 文本所在 x y 坐标,文字将围绕这个点进行对齐或则旋转y,i, // 添加到 PDF 第几页TextAlignment.CENTER,   // 文本水平对齐方式VerticalAlignment.TOP, // 文本垂直对齐方式// 将 角度 转换为 弧度(float) Math.toRadians(watermark.getRadAngle()));} else {// 水印按照设置平铺页面// 注意这里的坐标点是 文字中心点,所以宽度需要增加文字自己的宽度,否则会重合在一起for (float posX = 0f; posX < pageSize.getWidth(); posX = posX + textWidth + pageModeOfHorizontalInterval) {for (float posY = pageModeOfVerticalInterval; posY < pageSize.getHeight(); posY = posY + pageModeOfVerticalInterval) {// 参数分别为:文本、x 坐标、y 坐标、添加到底几页、文本水平对齐方式、文本垂直对齐方式、旋转弧度doc.showTextAligned(paragraph,posX, // 文本所在 x y 坐标,文字将围绕这个点进行对齐或则旋转posY,i, // 添加到 PDF 第几页TextAlignment.CENTER,   // 文本水平对齐方式VerticalAlignment.TOP, // 文本垂直对齐方式// 将 角度 转换为 弧度(float) Math.toRadians(watermark.getRadAngle()));}}}}doc.close();}/*** 获取字体,支持 afm、pfm、ttf、otf、woff、woff2 字体,ttc 目前直接报错** @param fontPath 字体文件绝对路径,如果为空则返回默认的英文字体* @return*/private static PdfFont getPdfFont(String fontPath) throws IOException {if (fontPath == null) {return PdfFontFactory.createFont(StandardFonts.HELVETICA);}return PdfFontFactory.createFont(fontPath,// 水平书写PdfEncodings.IDENTITY_H,// 是否将字体嵌入到目标文档中: 如果可能,嵌入字体PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);}/*** 添加图片水印; 默认为居中添加** @param watermark* @param srcPath   原始 PDF 文件绝对路径* @param destPath  添加完水印后的 PDF 存放路径*/public static void addWatermark(ImageWatermark watermark, String srcPath, String destPath) throws IOException {PdfDocument pdfDoc = new PdfDocument(new PdfReader(srcPath), new PdfWriter(destPath));Document doc = new Document(pdfDoc);// 创建图片ImageData img = ImageDataFactory.create(watermark.getPath());float w = img.getWidth();float h = img.getHeight();// 设置透明度PdfExtGState gs1 = new PdfExtGState().setFillOpacity(watermark.getOpacity());// 循环添加到每一页的 PDF 中for (int i = 1; i <= pdfDoc.getNumberOfPages(); i++) {PdfPage pdfPage = pdfDoc.getPage(i);// 获取页面大小,考虑页面旋转Rectangle pageSize = pdfPage.getPageSizeWithRotation();// 当页面有旋转时,内容自动旋转pdfPage.setIgnorePageRotationForContent(true);// 计算添加的位置坐标:这里使用居中位置,水平垂直居中float x = (pageSize.getLeft() + pageSize.getRight()) / 2;float y = (pageSize.getTop() + pageSize.getBottom()) / 2;// 添加图片水印使用PdfCanvas over = new PdfCanvas(pdfDoc.getPage(i));over.saveState();over.setExtGState(gs1);// 添加图片水印:位置水平垂直居中over.addImageWithTransformationMatrix(img, w, 0, 0, h, x - (w / 2), y - (h / 2), false);over.restoreState();}doc.close();}
}

文件合并添加页码工具类

import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.io.font.constants.StandardFonts;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.utils.PdfMerger;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.layout.property.VerticalAlignment;import java.io.IOException;
import java.util.List;import cn.hutool.core.util.StrUtil;/*** @author mrcode* @date 2022/1/21 20:27*/
public class PDFUtil {/*** 合并多个 PDF 文件为一个** @param list   要合并的 PDF 文件绝对路径列表* @param toFile 合并后的 PDF 文件绝对路径* @throws IOException*/public static void merge(List<String> list, String toFile) throws IOException {final PdfDocument toPdf = new PdfDocument(new PdfWriter(toFile));PdfMerger merger = new PdfMerger(toPdf);for (String file : list) {final PdfDocument pdfDocument = new PdfDocument(new PdfReader(file));merger.merge(pdfDocument, 1, pdfDocument.getNumberOfPages());pdfDocument.close();}merger.close();toPdf.close();}/*** 添加页码; 在右下角添加** @param fontSize 文字大小,一般为 15 比较合适* @param srcPath  原始 PDF 文件绝对路径* @param destPath 添加完水印后的 PDF 存放路径*/public static void addPageNumber(int fontSize, String srcPath, String destPath) throws IOException {PdfDocument pdfDoc = new PdfDocument(new PdfReader(srcPath), new PdfWriter(destPath));Document doc = new Document(pdfDoc);PdfFont font = getPdfFont(null);// 文字高度则是字体大小final float textHeight = fontSize;final int numberOfPages = pdfDoc.getNumberOfPages();for (int i = 1; i <= numberOfPages; i++) {PdfPage pdfPage = pdfDoc.getPage(i);// 获取页面大小,考虑页面旋转Rectangle pageSize = pdfPage.getPageSizeWithRotation();// 当页面有旋转时,内容自动旋转pdfPage.setIgnorePageRotationForContent(true);// 构建页码final String text = StrUtil.format("{}/{}", i, numberOfPages);Paragraph paragraph = new Paragraph(text).setFont(font).setFontSize(fontSize);// 获取文字宽度final float textWidth = font.getWidth(text, fontSize);// 计算添加的位置坐标// 定位到水平垂直居中// float x = (pageSize.getLeft() + pageSize.getRight()) / 2;// 定位到右侧:根据文字宽度减少宽度,能动态的根据文字宽度调整,让文字不会超出屏幕外面float x = pageSize.getRight() - textWidth;// bottom 是 0,+ 20 就是底部往上 20// 定位到底部:根据文字高度动态往上调整,不会超出屏幕外面;float y = pageSize.getBottom() + textHeight + 10;// 参数分别为:文本、x 坐标、y 坐标、添加到底几页、文本水平对齐方式、文本垂直对齐方式、旋转弧度doc.showTextAligned(paragraph,x, // 文本所在 x y 坐标,文字将围绕这个点进行对齐或则旋转y,i, // 添加到 PDF 第几页TextAlignment.CENTER,   // 文本水平对齐方式VerticalAlignment.TOP, // 文本垂直对齐方式// 将 角度 转换为 弧度(float) Math.toRadians(0));}doc.close();}/*** 获取字体,支持 afm、pfm、ttf、otf、woff、woff2 字体,ttc 目前直接报错** @param fontPath 字体文件绝对路径,如果为空则返回默认的英文字体* @return*/private static PdfFont getPdfFont(String fontPath) throws IOException {if (fontPath == null) {return PdfFontFactory.createFont(StandardFonts.HELVETICA);}return PdfFontFactory.createFont(fontPath,// 水平书写PdfEncodings.IDENTITY_H,// 是否将字体嵌入到目标文档中: 如果可能,嵌入字体PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);}
}

测试

测试加水印与背景

package com.example.pdf;import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.extgstate.PdfExtGState;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.layout.property.VerticalAlignment;public class Demo {public static final String DEST = "C:\\Users\\zhbcm\\Desktop\\readme1.pdf";public static final String IMG = "E:\\图片\\th.jpg";public static final String SRC = "C:\\Users\\zhbcm\\Desktop\\readme.pdf";public static void main(String[] args) throws Exception {new Demo().manipulatePdf(DEST);}protected void manipulatePdf(String dest) throws Exception {PdfDocument pdfDoc = new PdfDocument(new PdfReader(SRC), new PdfWriter(dest));Document doc = new Document(pdfDoc);
//        PdfFont font = PdfFontFactory.createFont(StandardFonts.HELVETICA);// 这个实体是宋体中文字体,只有 4, m// 字体文件源码中只支持 afm、pfm、ttf、otf、woff、woff2,等你提供不支持的格式就会报错,点进去看源码就知道了// ttc 看到源码中也支持,但是提供 ttc 就报错,暂时没深入看源码如何报错// final PdfFont font = PdfFontFactory.createFont("C:\\temp\\songti.ttf",//         // 水平书写//         PdfEncodings.IDENTITY_H,//         // 是否将字体嵌入到目标文档中//         // 该参数被  PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED 代替,true 对应  PREFER_EMBEDDED,如果可能,嵌入字体//         true);// 多行文本使用 \n 换行Paragraph paragraph = new Paragraph("My watermark (中国强text) \n 中国")// .setFont(font).setOpacity(0.1F) // 字体透明度 0-1 完全透明~不透明.setFontSize(30); // 字体大小ImageData img = ImageDataFactory.create(IMG);float w = img.getWidth();float h = img.getHeight();// 用于添加图片水印,设置图片水印透明度PdfExtGState gs1 = new PdfExtGState().setFillOpacity(0.5f);// Implement transformation matrix usage in order to scale imagefor (int i = 1; i <= pdfDoc.getNumberOfPages(); i++) {PdfPage pdfPage = pdfDoc.getPage(i);// 获取页面大小,考虑页面旋转Rectangle pageSize = pdfPage.getPageSizeWithRotation();// 当页面有旋转时,内容自动旋转pdfPage.setIgnorePageRotationForContent(true);// 计算添加的位置坐标float x = (pageSize.getLeft() + pageSize.getRight()) / 2;float y = (pageSize.getTop() + pageSize.getBottom()) / 2;// 添加图片水印使用PdfCanvas over = new PdfCanvas(pdfDoc.getPage(i));over.saveState();over.setExtGState(gs1);if (i % 2 == 1) {// 添加文本水印// 参数分别为:文本、x 坐标、y 坐标、添加到第几页、文本水平对齐方式、文本垂直对齐方式、旋转弧度doc.showTextAligned(paragraph, x, y, i, TextAlignment.CENTER, VerticalAlignment.TOP, 0);} else {// 添加图片水印over.addImageWithTransformationMatrix(img, w, 0, 0, h, x - (w / 2), y - (h / 2), false);}over.restoreState();}doc.close();}
}

水印效果图

背景效果图

测试pdf合并

合并前

合并后

说实话我认为这些功能还是比较实用的,希望对看的人有所帮助,谢谢~~

Java后台给PDF加水印并且合并多个PDF文件相关推荐

  1. PDF转换图片,图片的切割,图片转换PDF以及PDF加水印等记录贴

    PDF转变为图片: 把图片进行切割: 把图片转变回PDF: 为PDF加水印文字: 为PDF加水印图片. 1,PDF转变为图片 /*** @author dalin*将PDF格式的文件转换成png文件* ...

  2. java pdf水印排布问题_java 实现 PDF 加水印功能

    使用java代码实现给PDF加水印的功能 首先导入所需要的依赖 com.itextpdf itextpdf 5.5.11 com.itextpdf itext-asian 5.2.0 java 代码实 ...

  3. Java实现Excel和word转pdf加水印,复制及可用

    Java实现Excel和word转pdf加水印,复制及可用 时隔大半年,之前朋友,今天突然找我说,他们有个需求要做这东西.想起来之前自己照着官方文档写,也走了不少坑.今天就索性写个,也为了方便以后使用 ...

  4. java如何给pdf加水印_java pdf加水印的方法

    本文实例为大家分享了java pdf加水印的具体代码,供大家参考,具体内容如下 引入依赖 com.itextpdf.tool xmlworker 5.5.10 com.itextpdf itextpd ...

  5. java使用2种方法操作liberoffice把word转pdf,pdf加水印,java远程调用Linux执行命令

    文章目录 libreoffice下载地址 安装 (第一种) java调用 (第二种 推荐) java调用Linux命令转pdf java远程连接Linux执行命令(少数情况) linux安装windo ...

  6. (Java)word转pdf(aspose),pdf加水印(itextpdf),并支持POI模板(包括checkbox)导出

    目录 1.引入jar包 2.pdf处理工具类 3.poi模板导出工具类 4.测试类 5.模板 6.最终效果 1.引入jar包 2.pdf处理工具类 import com.aspose.cells.Pd ...

  7. Springboot使用itext及documents4j操作pdf(word转pdf、pdf加水印(文字或图片,可指定位置)、pdf加密(打开密码,编辑密码))

    pom.xml引入 <!-- pdf文档生成 --><dependency><groupId>com.documents4j</groupId>< ...

  8. 看这里,你要的快速、批量PDF加水印工具!

    今天在网上看到有人提问:"求怎么给PDF加水印,要快速批量的那种",好吧,算问对人了.最近在工作中也遇到同事问这个问题,主要是快速和批量,时间宝贵,不能浪费,所以这里大家推荐一个P ...

  9. itextPDF5.5.13 pdf加水印的权限控制

    1.我们的需求需要对pdf加水印,其中有加密的和未加密的, 操作过程中发现 有的虽然加密了 但是符合PDFReader的五种加密算法,就可以成功破解加水印,还有一些就会抛出一些异常 第一种报错:com ...

  10. 如何简简单单批量给PDF加水印

    PDF加水印是我们编辑PDF过程中经常会做的事情,如果只有一个PDF文件的时候,那么PDF加水印也挺简单,但是如果有很多PDF文件要一起加水印,那么如果再一个个打开编辑就显得很麻烦了.那么如何批量给P ...

最新文章

  1. C# 多线程学习总结
  2. The prefix tx for element tx:annotation-driven is not bound.
  3. 剑指offer 算法 (综合)
  4. 像素测量工具_翼眸科技利用无人机屋顶测量光伏行业应用
  5. 编程之美-寻找发帖“水王”方法整理
  6. 监控系统或者网站服务器的报警及复位,机房监控系统标准和常见故障
  7. python multiprocessing 进程间如何共享数据、对象
  8. 一步步编写操作系统 78 intel汇编与ATT汇编语法区别
  9. Rational 中 DataPool 的介绍与实际应用
  10. leetcode python3 简单题101. Symmetric Tree
  11. ESXI5.1 数据备份与恢复
  12. Node.js:中间件——express简单的设置用户表单提交数据的接收中间件
  13. JSON.stringify用法
  14. 金税盘专、普红字发票开具步骤及(税盘注销方法)
  15. 微信扫码支付 java版
  16. LTE中SRB---无线资源承载
  17. python+opencv将图片转换成视频
  18. GraphQL初探:Java服务示例及Yahoo/Elide
  19. 文件上传绕过之00截断
  20. 小米4c+android+6,小米4c有什么接口?小米4c有HDMI接口吗?

热门文章

  1. 小米平板1-专用解账号锁激活设备救砖线刷包 一键刷机
  2. Win7系统怎么截图快捷键
  3. gis插入的文本怎么搞成两行_PPT脱白教程09期手把手带你了解“文本框”(03)...
  4. 2060显卡驱动最新版本_我们平时电脑的驱动需要一直更新吗?教你如何正确的维护你的驱动...
  5. 个人简单版租房合同范本电子版Word模板免费下载样板
  6. idea的设置,导致list长度只有1000
  7. STAMP:扩增子、宏基因组统计分析神器(中文帮助文档)
  8. Git 合并单个文件
  9. 浏览器Browser的WebSQL文件位置
  10. autorunner测试java_AutoRunner自动化测试工具