开发环境
| 名称 | 版本 |
|---|---|
| 操作系统 | 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*/@Datapublic 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@Slf4jpublic 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 转换为 byteByteArrayOutputStream 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;@Datapublic 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@Slf4jpublic 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 转换为 byteByteArrayOutputStream 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}
测试结果