开发环境
名称 | 版本 |
---|---|
操作系统 | Windows 10 X64 |
JDK | JDK1.8(jdk-8u151-windows-x64) |
IntelliJ IDEA | IntelliJ IDEA 2022.3 |
Maven | Maven 3.9.4 |
参考
重要说明
因为 iText 有版权问题导致不能继续使用,所以使用 OpenPDF 来替换 iText 相关功能。
只添加水印
pom.xml
<!-- PDF文件依赖包 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13.1</version>
</dependency>
<!-- PDF文件字体 防止中文乱码 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
PDF 参数类-PDFParamsDTO
package com.luoma.finance.dto;
import lombok.Data;
import java.io.InputStream;
/**
* PDF 参数类
* -luoma - 2023年9月4日22:15:25
*/
@Data
public class PDFParamsDTO {
/**
* 是否生成到本地
*/
private Boolean generateToLocal;
/**
* PDF 模板路径,generateToLocal 为 true,必填
*/
private String templatePath;
/**
* PDF 存放的路径,generateToLocal 为 true,必填
*/
private String fileDirectory;
/**
* PDF 模版字节,generateToLocal 为 false,必填
*/
private InputStream inputStream;
/**
* 水印文字
*/
private String watermark;
/**
* 默认字体大小
*/
private int DefaultFontSize = 13;
/**
* 需要添加水印的 X 坐标
*/
private float x;
/**
* 需要添加水印的 Y 坐标
*/
private float y;
/**
* 应用于文本的旋转角度,以弧度为单位
* 为 0 则不旋转
*/
private float angle;
/**
* 水印文字-RBG 值-R 值
*/
private int r = 255;
/**
* 水印文字-RBG 值-G 值
*/
private int g = 0;
/**
* 水印文字-RBG 值-B 值
*/
private int b = 0;
}
PDF 工具类-PDFMakerUtil
package com.luoma.finance.util;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Element;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.luoma.finance.dto.PDFParamsDTO;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import javax.swing.*;
import java.awt.*;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Date;
/**
* PDF 生成类
* - luoma - 2023年9月4日22:18:37
* - 参考:<a href="https://blog.csdn.net/Ying_ph/article/details/131800711">Java PDF加水印</a>
* - 参考:<a href="https://blog.csdn.net/weixin_42994251/article/details/129433070">咖啡汪日志——JAVA导出pdf文件加水印 文字+图片、文字</a>
*/
@Data
@Slf4j
public class PDFMakerUtil {
/**
* 给 PDF 文件添加水印
*
* @param pdfParams PDF 参数类
* @return 添加水印后的 PDF 文件字节数组(添加到本地的情况为空)
*/
public static byte[] addWatermark(PDFParamsDTO pdfParams) {
String logInfo = "addWatermark >> PDF 添加水印 >> ";
byte[] byteArray = null;
try {
log.info(logInfo + "添加文字:{}", pdfParams.getWatermark());
//坐标
float x = pdfParams.getX();
float y = pdfParams.getY();
float angle = pdfParams.getAngle();
//文字颜色
int r = pdfParams.getR();
int g = pdfParams.getG();
int b = pdfParams.getB();
BaseColor fontColor = new BaseColor(r, g, b);
//文字大小
int fontSize = pdfParams.getDefaultFontSize();
//文字内容
// 使用"||"将内容进行分割
String waterMarkName = pdfParams.getWatermark();
String[] waterMarkContents = waterMarkName.split("\\|\\|");
//获取水印文字的最大高度和宽度
int textH = 0, textW = 0;
for (String waterMarkContent : waterMarkContents) {
JLabel label = new JLabel();
label.setText(waterMarkContent);
FontMetrics metrics = label.getFontMetrics(label.getFont());
if (textH < metrics.getHeight()) {
textH = metrics.getHeight();
}
if (textW < metrics.stringWidth(label.getText())) {
textW = metrics.stringWidth(label.getText());
}
}
//设置输入,输出
PdfReader reader;
PdfStamper stamper;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
if (pdfParams.getGenerateToLocal()) {
//输入目录
String inputFile = pdfParams.getTemplatePath();
//输出目录
String outputFile = pdfParams.getFileDirectory();
//设置输入,输出
reader = new PdfReader(inputFile);
stamper = new PdfStamper(reader, Files.newOutputStream(Paths.get(outputFile)));
} else {
//设置输入,输出
InputStream inputStream = pdfParams.getInputStream();
reader = new PdfReader(inputStream);
stamper = new PdfStamper(reader, outputStream);
}
//设置字体
BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
// 获取总页数 +1, 下面从1开始遍历
int total = reader.getNumberOfPages() + 1;
PdfContentByte under;
for (int i = 1; i < total; i++) {
// 在内容上方加水印
under = stamper.getOverContent(i);
// 在内容下方加水印
//under = stamper.getUnderContent(i);
under.saveState();
under.beginText();
//水印字体和大小
under.setFontAndSize(baseFont, fontSize);
//文字加粗
//设置文本描边宽度
//under.setLineWidth(0.3);
//设置文本为描边模式, 会导致 setColorFill 颜色失效
//under.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE);
//字体颜色
under.setColorFill(fontColor);
// 将分段的字段进行输出编写
for (int z = 0; z < waterMarkContents.length; z++) {
//under.showTextAligned(Element.ALIGN_LEFT, waterMarkContents[z], x - textW, y -(textH+10) * (z + 1), angle);
under.showTextAligned(Element.ALIGN_LEFT, waterMarkContents[z], x, y - (textH * z), angle);
}
// 添加水印文字
under.endText();
}
stamper.close();
reader.close();
//outputStream 转换为 byte
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
outputStream.writeTo(byteArrayOutputStream);
byteArray = byteArrayOutputStream.toByteArray();
log.info(logInfo + "设置完成。");
} catch (Exception e) {
log.error(logInfo + "异常,e:", e);
}
return byteArray;
}
}
测试
测试代码
public static void main(String[] args) {
PDFParamsDTO pdfParams = new PDFParamsDTO();
pdfParams.setWatermark(String.format("适用范围:%s||有效期:%s||再次复印无效", "xxx", "2022年5月9日11:14:36"));
//------------------------------------------------region 生成到本地
pdfParams.setGenerateToLocal(true);
//竖版-正常
pdfParams.setTemplatePath("D:\\data\\105.pdf");
pdfParams.setFileDirectory("D:\\data\\105-1.pdf");
pdfParams.setX(45f);
pdfParams.setY(330.5f);
pdfParams.setAngle(0);
PDFMakerUtil.addWatermark(pdfParams);
//------------------------------------------------endregion
//------------------------------------------------region 生成到 byte[]
try {
//输入输出流
pdfParams.setGenerateToLocal(false);
File file = new File("D:\\\\data\\\\105.pdf");
InputStream inputStream = Files.newInputStream(file.toPath());
pdfParams.setInputStream(inputStream);
pdfParams.setFileDirectory("D:\\data\\105-1.pdf");
pdfParams.setX(280f);
pdfParams.setY(115f);
pdfParams.setAngle(0);
byte[] arrByte = PDFMakerUtil.addWatermark(pdfParams);
// 步骤1:创建FileOutputStream对象
// 参数1:文件路径
// 参数2:是否追加写入(true为追加,false为覆盖)
FileOutputStream fos = new FileOutputStream(pdfParams.getFileDirectory(), false);
// 步骤2:将byte数据写入文件
// 参数:要写入的byte数组
fos.write(arrByte);
// 步骤3:关闭文件
fos.close();
} catch (Exception e) {
log.error("生成失败,e:", e);
}
//------------------------------------------------endregion
}
测试结果
添加水印,旋转,透明度,密码
pom.xml
<!-- PDF文件依赖包 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13.1</version>
</dependency>
<!-- PDF文件字体 防止中文乱码 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<!--PDF 加密-->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-ext-jdk15on</artifactId>
<version>1.47</version>
</dependency>
PDF参数类-PDFParamsDTO
package com.atguigu.boot3.actuator.dto;
import lombok.Data;
import java.io.InputStream;
@Data
public class PDFParamsDTO {
/**
* 是否生成到本地
*/
private Boolean generateToLocal;
/**
* PDF 模板路径,generateToLocal 为 true,必填
*/
private String templatePath;
/**
* PDF 存放的路径,generateToLocal 为 true,必填
*/
private String fileDirectory;
/**
* PDF 模版字节,generateToLocal 为 false,必填
*/
private InputStream inputStream;
/**
* 水印文字
*/
private String watermark;
/**
* 默认字体大小
*/
private int DefaultFontSize = 13;
/**
* 水印文字透明度
*/
private float fillOpacity;
/**
* 需要添加水印的 X 坐标
*/
private float x;
/**
* 需要添加水印的 Y 坐标
*/
private float y;
/**
* 应用于文本的旋转角度,以弧度为单位
* 为 0 则不旋转
*/
private float angle;
/**
* 水印文字-RBG 值-R 值
*/
private int r = 255;
/**
* 水印文字-RBG 值-G 值
*/
private int g = 0;
/**
* 水印文字-RBG 值-B 值
*/
private int b = 0;
/**
* 打开文档时输入的密码
*/
private String userPassword;
/**
* 用户编辑 PDF 时需要的密码
*/
private String ownerPassword;
}
PDF 工具类-PDFMakerUtil
package com.atguigu.boot3.actuator.util;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Element;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.atguigu.boot3.actuator.dto.PDFParamsDTO;
import io.micrometer.common.util.StringUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import javax.swing.*;
import java.awt.*;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
/**
* PDF 生成类
* - luoma - 2023年9月4日22:18:37
* - 参考:<a href="https://blog.csdn.net/Ying_ph/article/details/131800711">Java PDF加水印</a>
* - 参考:<a href="https://blog.csdn.net/weixin_42994251/article/details/129433070">咖啡汪日志——JAVA导出pdf文件加水印 文字+图片、文字</a>
*/
@Data
@Slf4j
public class PDFMakerUtil {
/**
* 给 PDF 文件添加水印,旋转,透明度,密码
*
* @param pdfParams PDF 参数类
* @return 添加水印后的 PDF 文件字节数组(添加到本地的情况为空)
*/
public static byte[] addWatermarkAuto(PDFParamsDTO pdfParams) {
String logInfo = "addWatermarkAuto >> PDF 添加水印 >> ";
byte[] byteArray = null;
try {
log.info(logInfo + "添加文字:{}", pdfParams.getWatermark());
//坐标
float x = pdfParams.getX();
float y = pdfParams.getY();
float angle = pdfParams.getAngle();
//文字颜色
int r = pdfParams.getR();
int g = pdfParams.getG();
int b = pdfParams.getB();
BaseColor fontColor = new BaseColor(r, g, b);
//文字大小
int fontSize = pdfParams.getDefaultFontSize();
//文字内容
// 使用"||"将内容进行分割
String waterMarkName = pdfParams.getWatermark();
String[] waterMarkContents = waterMarkName.split("\\|\\|");
//获取水印文字的最大高度和宽度
int textH = 0, textW = 0;
for (String waterMarkContent : waterMarkContents) {
JLabel label = new JLabel();
label.setText(waterMarkContent);
FontMetrics metrics = label.getFontMetrics(label.getFont());
if (textH < metrics.getHeight()) {
textH = metrics.getHeight();
}
if (textW < metrics.stringWidth(label.getText())) {
textW = metrics.stringWidth(label.getText());
}
}
//设置输入,输出
PdfReader reader;
PdfStamper stamper;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
if (pdfParams.getGenerateToLocal()) {
//输入目录
String inputFile = pdfParams.getTemplatePath();
//输出目录
String outputFile = pdfParams.getFileDirectory();
//设置输入,输出
reader = new PdfReader(inputFile);
stamper = new PdfStamper(reader, Files.newOutputStream(Paths.get(outputFile)));
} else {
//设置输入,输出
InputStream inputStream = pdfParams.getInputStream();
reader = new PdfReader(inputStream);
stamper = new PdfStamper(reader, outputStream);
}
// 设置密码文件打开密码文件编辑密码
byte[] arrUserPassword = null;
byte[] arrOwnerPassword = null;
if(StringUtils.isNotBlank(pdfParams.getUserPassword())) {
arrUserPassword = pdfParams.getUserPassword().getBytes();
}
if(StringUtils.isNotBlank(pdfParams.getOwnerPassword())) {
arrOwnerPassword = pdfParams.getOwnerPassword().getBytes();
}
if(StringUtils.isNotBlank(pdfParams.getUserPassword()) || StringUtils.isNotBlank(pdfParams.getOwnerPassword())) {
stamper.setEncryption(arrUserPassword, arrOwnerPassword, PdfWriter.ALLOW_PRINTING, false);
}
//设置字体
BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
// 获取总页数 +1, 下面从1开始遍历
int total = reader.getNumberOfPages() + 1;
PdfContentByte under;
for (int i = 1; i < total; i++) {
// 在内容上方加水印
under = stamper.getOverContent(i);
// 在内容下方加水印
//under = stamper.getUnderContent(i);
under.saveState();
under.beginText();
under.setFontAndSize(baseFont, fontSize);
//文字加粗
//设置文本描边宽度
//under.setLineWidth(0.3);
//设置文本为描边模式, 会导致 setColorFill 颜色失效
//under.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE);
//字体颜色
under.setColorFill(fontColor);
// 设置透明度
PdfGState gs = new PdfGState();
gs.setFillOpacity(pdfParams.getFillOpacity());
under.setGState(gs);
//水印字体和大小
Rectangle pageRect = reader.getPageSizeWithRotation(i);
int width1 = (int) pageRect.getWidth();
int height1 = (int) pageRect.getHeight();
//fontSize = (width1 + height1) / pdfParams.getDefaultFontSize();
x = (float) (width1 ) / 2;
y = (float) height1 / 2;
// 将分段的字段进行输出编写
for (int z = 0; z < waterMarkContents.length; z++) {
//文字
String itemWaterMark = waterMarkContents[z];
// 间隔距离(参数可调节)
int interval = fontSize * z;
under.showTextAligned(Element.ALIGN_CENTER, itemWaterMark, x, y - (interval + textH * z), angle);
}
// 添加水印文字
under.endText();
}
stamper.close();
reader.close();
//outputStream 转换为 byte
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
outputStream.writeTo(byteArrayOutputStream);
byteArray = byteArrayOutputStream.toByteArray();
log.info(logInfo + "设置完成。");
} catch (Exception e) {
log.error(logInfo + "异常,e:", e);
}
return byteArray;
}
}
测试
测试代码
public static void main(String[] args) {
try {
File file = new File("D:\\\\data\\\\105.pdf");
InputStream inputStream = Files.newInputStream(file.toPath());
PDFParamsDTO pdfParams = new PDFParamsDTO();
pdfParams.setWatermark(String.format("适用范围:%s||有效期:%s||再次复印无效", "申请页面的原因及用途申请页面的原因及用途", "2022年5月9日11:14:36"));
pdfParams.setGenerateToLocal(false); //输入输出流
pdfParams.setInputStream(inputStream);
pdfParams.setFileDirectory("D:\\data\\105-1.pdf");
pdfParams.setAngle(30);
pdfParams.setR(210);
pdfParams.setG(210);
pdfParams.setB(210);
pdfParams.setDefaultFontSize(25);
pdfParams.setFillOpacity(0.7f);
//pdfParams.setUserPassword("123");
//pdfParams.setOwnerPassword("456");
byte[] arrByte = PDFMakerUtil.addWatermarkAuto(pdfParams);
// 步骤1:创建FileOutputStream对象
// 参数1:文件路径
// 参数2:是否追加写入(true为追加,false为覆盖)
FileOutputStream fos = new FileOutputStream(pdfParams.getFileDirectory(), false);
// 步骤2:将byte数据写入文件
// 参数:要写入的byte数组
fos.write(arrByte);
// 步骤3:关闭文件
fos.close();
} catch (Exception e) {
log.error("生成失败,e:", e);
}
//------------------------------------------------endregion
}
测试结果