文章目录
前言
近期为了知识库文件
导出,文件数据安全处理,增加水印
处理。
方式一 itextpdf
项目依赖引入
xml
<!-- 文件水印添加 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
编写PDF添加水印工具类
java
/**
* pdf文件添加文字水印
*
* @param srcPath 输入的文件路径
* @param destPath 输出的文件路径
* @param word 水印文字
* @throws Exception
*/
public static void addPDFWaterMark(String srcPath, String destPath, String word)
throws Exception {
PdfReader reader = new PdfReader(srcPath);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(destPath));
//使用系统字体
String prefixFont = null;
String os = System.getProperties().getProperty("os.name");
if (os.startsWith("win") || os.startsWith("Win")) {
// 某些机型可能没有,需要调整
prefixFont = "C:\\Windows\\Fonts\\SIMSUN.TTC,1";
} else {
prefixFont = "/usr/share/fonts/chinese/TrueType/uming.ttf";
}
//创建字体,第一个参数是字体路径
BaseFont base = BaseFont.createFont(prefixFont, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
//BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
PdfGState gs = new PdfGState();
gs.setFillOpacity(0.1f);//图片水印透明度(数值越大,水印越深)
//gs.setStrokeOpacity(0.4f);//设置笔触字体不透明度
PdfContentByte content = null;
int total = reader.getNumberOfPages();//pdf文件页数
for (int i = 0; i < total; i++) {
float x = reader.getPageSize(i + 1).getWidth();//页宽度
float y = reader.getPageSize(i + 1).getHeight();//页高度
content = stamper.getOverContent(i + 1);
content.setGState(gs);
content.beginText();//开始写入
content.setFontAndSize(base, 10);//字体大小(数值越大,字体越大)
//每页7行,一行3个
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 7; k++) {
//showTextAligned 方法的参数(文字对齐方式,位置内容,输出水印X轴位置,Y轴位置,旋转角度)
content.showTextAligned(Element.ALIGN_CENTER, word, x / 3 * j + 90, y / 7 * k, 25);
}
}
content.endText();//结束写入
}
//关闭流
stamper.close();
reader.close();
}
测试
java
public static void main(String[] args) {
// 获取指定路径的pdf
File file = new File("F:\\springboot-stydy\\springboot-pdf-waterMark\\pdf\\22.pdf");
try {
System.out.println(file.getName());
PDFUtil.addPDFWaterMark("F:\\springboot-stydy\\springboot-pdf-waterMark\\pdf\\22.pdf",
"F:\\springboot-stydy\\springboot-pdf-waterMark\\pdf\\water\\22.pdf" , "香蕉不拿拿(xjbnn)@xiangjiao 2025-04-08");
} catch (Exception e) {
e.printStackTrace();
}
}
效果展示

方式二 pdfbox
依赖引入
xml
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.4</version>
</dependency>
编写实现类
java
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
import org.apache.pdfbox.util.Matrix;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class PdBoxTest {
public static void main(String[] args) throws IOException {
// 水印文字
String waterMark = "香蕉不拿拿@(xjbnn) "+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
// 加载源文件
File file = new File("F:\\springboot-stydy\\springboot-pdf-waterMark\\pdf\\22.pdf");
// 如果是 inputstream 类的流信息,也可以直接使用 PDDocument.load(inputStream)
PDDocument document = PDDocument.load(file);
document.setAllSecurityToBeRemoved(true);
//使用系统字体
String prefixFont = null;
String os = System.getProperties().getProperty("os.name");
if (os.startsWith("win") || os.startsWith("Win")) {
// 仿宋 简体 常规 (其他类型会报错)
prefixFont = "C:\\Windows\\Fonts\\simfang.ttf";
} else {
prefixFont = "/usr/share/fonts/chinese/TrueType/uming.ttf";
}
PDFont font = PDType0Font.load(document, new FileInputStream(prefixFont), true);
// 读取pdf文件,将每一页中都增加水印
for (PDPage page : document.getPages()) {
PDPageContentStream stream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true, true);
PDExtendedGraphicsState r = new PDExtendedGraphicsState();
// 设置透明度
r.setNonStrokingAlphaConstant(0.1f);
r.setAlphaSourceFlag(true);
stream.setGraphicsStateParameters(r);
//开始写入
stream.beginText();
stream.setFont(font, 10);
stream.newLineAtOffset(0, -15);
// 获取PDF页面大小
float pageHeight = page.getMediaBox().getHeight();
float pageWidth = page.getMediaBox().getWidth();
// 根据纸张大小添加水印,30度倾斜
for (int h = 10; h < pageHeight; h = h + 150) {
for (int w = - 10; w < pageWidth; w = w + 150) {
stream.setTextMatrix(Matrix.getRotateInstance(0.3, w, h));
stream.showText(waterMark);
}
}
// 结束渲染,关闭流
stream.endText();
stream.restoreGraphicsState();
stream.close();
}
// 将加工后的文件写入到新的文件中
File outFile = new File("F:\\springboot-stydy\\springboot-pdf-waterMark\\pdf\\water\\pdbox.pdf");
document.save(outFile);
document.close();
}
}
效果展示

扩展
1、将inputstream流信息添加水印并导出zip
使用pdfbox
更方便处理。如下工具类:
java
/**
* 添加水印并打包压缩
* @param inputStream 文件输入流信息
* @param path 文件路径
* @param zipOutputStream zip压缩包
* @param waterMark 水印
* @param rowSpace 行间距,大中小分别对应150/100/50
* @param colSpace 列间距,大中小分别对应150/100/50
* @throws Exception
*/
private void addPDFWaterMarkAndZip(InputStream inputStream,String path, ZipOutputStream zipOutputStream, String waterMark,int rowSpace,int colSpace)
throws Exception {
PDDocument document = PDDocument.load(inputStream);
document.setAllSecurityToBeRemoved(true);
//使用系统字体
String prefixFont = null;
String os = System.getProperties().getProperty("os.name");
if (os.startsWith("win") || os.startsWith("Win")) {
prefixFont = "C:\\Windows\\Fonts\\simfang.ttf";
} else {
prefixFont = "/usr/share/fonts/chinese/TrueType/uming.ttf";
}
PDFont font = PDType0Font.load(document, new FileInputStream(prefixFont), true);
for (PDPage page : document.getPages()) {
PDPageContentStream stream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true, true);
PDExtendedGraphicsState r = new PDExtendedGraphicsState();
// 设置透明度
r.setNonStrokingAlphaConstant(0.1f);
r.setAlphaSourceFlag(true);
stream.setGraphicsStateParameters(r);
//开始写入
stream.beginText();
stream.setFont(font, 10);
stream.newLineAtOffset(0, -15);
// 获取PDF页面大小
float pageHeight = page.getMediaBox().getHeight();
float pageWidth = page.getMediaBox().getWidth();
// 根据纸张大小添加水印,30度倾斜
for (int h = 10; h < pageHeight; h = h + rowSpace) {
for (int w = - 10; w < pageWidth; w = w + colSpace) {
stream.setTextMatrix(Matrix.getRotateInstance(0.3, w, h));
stream.showText(waterMark);
}
}
// 结束渲染,关闭流
stream.endText();
stream.restoreGraphicsState();
stream.close();
}
// 创建临时的文件
String snowflakeNextIdStr = IdUtil.getSnowflakeNextIdStr();
File tempFile = File.createTempFile(snowflakeNextIdStr, ".pdf");
// 加了水印的文件暂存临时文件
document.save(tempFile);
document.close();
// 获取临时文件的数据流
InputStream fileInputStream = new FileInputStream(tempFile);
// 加入 zip
ZipEntry ze = new ZipEntry(path);
zipOutputStream.putNextEntry(ze);
byte[] buffer = new byte[1024];
int len;
while ((len = fileInputStream.read(buffer)) > 0) {
zipOutputStream.write(buffer, 0, len);
}
fileInputStream.close();
// 删除临时文件
tempFile.delete();
}
然后再调用处使用:
java
response.setContentType("application/force-download");
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream());
2、部署出现找不到指定字体文件
可以在项目中的src/main/resource/
下放入字体文件。如下
然后修改字体获取方式,如下:
java
String ttfPath = Thread.currentThread().getContextClassLoader().getResource("font/simfang.ttf").getPath();
PDFont font = PDType0Font.load(document, new FileInputStream(ttfPath));