Java-使用 iText 给 PDF 添加水印【有版权问题】

2023年09月11日 18:29 · 阅读(1036) ·

开发环境

名称 版本
操作系统 Windows 10 X64
JDK JDK1.8(jdk-8u151-windows-x64)
IntelliJ IDEA IntelliJ IDEA 2022.3
Maven Maven 3.9.4

参考

Java PDF加水印

咖啡汪日志——JAVA导出pdf文件加水印 文字+图片、文字

C#-使用 iText 给 PDF 添加水印

重要说明

因为 iText 有版权问题导致不能继续使用,所以使用 OpenPDF 来替换 iText 相关功能。

Java-使用 OpenPDF 给 PDF 添加水印

只添加水印

pom.xml

  1. <!-- PDF文件依赖包 -->
  2. <dependency>
  3. <groupId>com.itextpdf</groupId>
  4. <artifactId>itextpdf</artifactId>
  5. <version>5.5.13.1</version>
  6. </dependency>
  7. <!-- PDF文件字体 防止中文乱码 -->
  8. <dependency>
  9. <groupId>com.itextpdf</groupId>
  10. <artifactId>itext-asian</artifactId>
  11. <version>5.2.0</version>
  12. </dependency>

PDF 参数类-PDFParamsDTO

  1. package com.luoma.finance.dto;
  2. import lombok.Data;
  3. import java.io.InputStream;
  4. /**
  5. * PDF 参数类
  6. * -luoma - 2023年9月4日22:15:25
  7. */
  8. @Data
  9. public class PDFParamsDTO {
  10. /**
  11. * 是否生成到本地
  12. */
  13. private Boolean generateToLocal;
  14. /**
  15. * PDF 模板路径,generateToLocal 为 true,必填
  16. */
  17. private String templatePath;
  18. /**
  19. * PDF 存放的路径,generateToLocal 为 true,必填
  20. */
  21. private String fileDirectory;
  22. /**
  23. * PDF 模版字节,generateToLocal 为 false,必填
  24. */
  25. private InputStream inputStream;
  26. /**
  27. * 水印文字
  28. */
  29. private String watermark;
  30. /**
  31. * 默认字体大小
  32. */
  33. private int DefaultFontSize = 13;
  34. /**
  35. * 需要添加水印的 X 坐标
  36. */
  37. private float x;
  38. /**
  39. * 需要添加水印的 Y 坐标
  40. */
  41. private float y;
  42. /**
  43. * 应用于文本的旋转角度,以弧度为单位
  44. * 为 0 则不旋转
  45. */
  46. private float angle;
  47. /**
  48. * 水印文字-RBG 值-R 值
  49. */
  50. private int r = 255;
  51. /**
  52. * 水印文字-RBG 值-G 值
  53. */
  54. private int g = 0;
  55. /**
  56. * 水印文字-RBG 值-B 值
  57. */
  58. private int b = 0;
  59. }

PDF 工具类-PDFMakerUtil

  1. package com.luoma.finance.util;
  2. import com.itextpdf.text.BaseColor;
  3. import com.itextpdf.text.Element;
  4. import com.itextpdf.text.pdf.BaseFont;
  5. import com.itextpdf.text.pdf.PdfContentByte;
  6. import com.itextpdf.text.pdf.PdfReader;
  7. import com.itextpdf.text.pdf.PdfStamper;
  8. import com.luoma.finance.dto.PDFParamsDTO;
  9. import lombok.Data;
  10. import lombok.extern.slf4j.Slf4j;
  11. import javax.swing.*;
  12. import java.awt.*;
  13. import java.io.ByteArrayOutputStream;
  14. import java.io.InputStream;
  15. import java.nio.file.Files;
  16. import java.nio.file.Paths;
  17. import java.util.Date;
  18. /**
  19. * PDF 生成类
  20. * - luoma - 2023年9月4日22:18:37
  21. * - 参考:<a href="https://blog.csdn.net/Ying_ph/article/details/131800711">Java PDF加水印</a>
  22. * - 参考:<a href="https://blog.csdn.net/weixin_42994251/article/details/129433070">咖啡汪日志——JAVA导出pdf文件加水印 文字+图片、文字</a>
  23. */
  24. @Data
  25. @Slf4j
  26. public class PDFMakerUtil {
  27. /**
  28. * 给 PDF 文件添加水印
  29. *
  30. * @param pdfParams PDF 参数类
  31. * @return 添加水印后的 PDF 文件字节数组(添加到本地的情况为空)
  32. */
  33. public static byte[] addWatermark(PDFParamsDTO pdfParams) {
  34. String logInfo = "addWatermark >> PDF 添加水印 >> ";
  35. byte[] byteArray = null;
  36. try {
  37. log.info(logInfo + "添加文字:{}", pdfParams.getWatermark());
  38. //坐标
  39. float x = pdfParams.getX();
  40. float y = pdfParams.getY();
  41. float angle = pdfParams.getAngle();
  42. //文字颜色
  43. int r = pdfParams.getR();
  44. int g = pdfParams.getG();
  45. int b = pdfParams.getB();
  46. BaseColor fontColor = new BaseColor(r, g, b);
  47. //文字大小
  48. int fontSize = pdfParams.getDefaultFontSize();
  49. //文字内容
  50. // 使用"||"将内容进行分割
  51. String waterMarkName = pdfParams.getWatermark();
  52. String[] waterMarkContents = waterMarkName.split("\\|\\|");
  53. //获取水印文字的最大高度和宽度
  54. int textH = 0, textW = 0;
  55. for (String waterMarkContent : waterMarkContents) {
  56. JLabel label = new JLabel();
  57. label.setText(waterMarkContent);
  58. FontMetrics metrics = label.getFontMetrics(label.getFont());
  59. if (textH < metrics.getHeight()) {
  60. textH = metrics.getHeight();
  61. }
  62. if (textW < metrics.stringWidth(label.getText())) {
  63. textW = metrics.stringWidth(label.getText());
  64. }
  65. }
  66. //设置输入,输出
  67. PdfReader reader;
  68. PdfStamper stamper;
  69. ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
  70. if (pdfParams.getGenerateToLocal()) {
  71. //输入目录
  72. String inputFile = pdfParams.getTemplatePath();
  73. //输出目录
  74. String outputFile = pdfParams.getFileDirectory();
  75. //设置输入,输出
  76. reader = new PdfReader(inputFile);
  77. stamper = new PdfStamper(reader, Files.newOutputStream(Paths.get(outputFile)));
  78. } else {
  79. //设置输入,输出
  80. InputStream inputStream = pdfParams.getInputStream();
  81. reader = new PdfReader(inputStream);
  82. stamper = new PdfStamper(reader, outputStream);
  83. }
  84. //设置字体
  85. BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
  86. // 获取总页数 +1, 下面从1开始遍历
  87. int total = reader.getNumberOfPages() + 1;
  88. PdfContentByte under;
  89. for (int i = 1; i < total; i++) {
  90. // 在内容上方加水印
  91. under = stamper.getOverContent(i);
  92. // 在内容下方加水印
  93. //under = stamper.getUnderContent(i);
  94. under.saveState();
  95. under.beginText();
  96. //水印字体和大小
  97. under.setFontAndSize(baseFont, fontSize);
  98. //文字加粗
  99. //设置文本描边宽度
  100. //under.setLineWidth(0.3);
  101. //设置文本为描边模式, 会导致 setColorFill 颜色失效
  102. //under.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE);
  103. //字体颜色
  104. under.setColorFill(fontColor);
  105. // 将分段的字段进行输出编写
  106. for (int z = 0; z < waterMarkContents.length; z++) {
  107. //under.showTextAligned(Element.ALIGN_LEFT, waterMarkContents[z], x - textW, y -(textH+10) * (z + 1), angle);
  108. under.showTextAligned(Element.ALIGN_LEFT, waterMarkContents[z], x, y - (textH * z), angle);
  109. }
  110. // 添加水印文字
  111. under.endText();
  112. }
  113. stamper.close();
  114. reader.close();
  115. //outputStream 转换为 byte
  116. ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
  117. outputStream.writeTo(byteArrayOutputStream);
  118. byteArray = byteArrayOutputStream.toByteArray();
  119. log.info(logInfo + "设置完成。");
  120. } catch (Exception e) {
  121. log.error(logInfo + "异常,e:", e);
  122. }
  123. return byteArray;
  124. }
  125. }

测试

测试代码

  1. public static void main(String[] args) {
  2. PDFParamsDTO pdfParams = new PDFParamsDTO();
  3. pdfParams.setWatermark(String.format("适用范围:%s||有效期:%s||再次复印无效", "xxx", "2022年5月9日11:14:36"));
  4. //------------------------------------------------region 生成到本地
  5. pdfParams.setGenerateToLocal(true);
  6. //竖版-正常
  7. pdfParams.setTemplatePath("D:\\data\\105.pdf");
  8. pdfParams.setFileDirectory("D:\\data\\105-1.pdf");
  9. pdfParams.setX(45f);
  10. pdfParams.setY(330.5f);
  11. pdfParams.setAngle(0);
  12. PDFMakerUtil.addWatermark(pdfParams);
  13. //------------------------------------------------endregion
  14. //------------------------------------------------region 生成到 byte[]
  15. try {
  16. //输入输出流
  17. pdfParams.setGenerateToLocal(false);
  18. File file = new File("D:\\\\data\\\\105.pdf");
  19. InputStream inputStream = Files.newInputStream(file.toPath());
  20. pdfParams.setInputStream(inputStream);
  21. pdfParams.setFileDirectory("D:\\data\\105-1.pdf");
  22. pdfParams.setX(280f);
  23. pdfParams.setY(115f);
  24. pdfParams.setAngle(0);
  25. byte[] arrByte = PDFMakerUtil.addWatermark(pdfParams);
  26. // 步骤1:创建FileOutputStream对象
  27. // 参数1:文件路径
  28. // 参数2:是否追加写入(true为追加,false为覆盖)
  29. FileOutputStream fos = new FileOutputStream(pdfParams.getFileDirectory(), false);
  30. // 步骤2:将byte数据写入文件
  31. // 参数:要写入的byte数组
  32. fos.write(arrByte);
  33. // 步骤3:关闭文件
  34. fos.close();
  35. } catch (Exception e) {
  36. log.error("生成失败,e:", e);
  37. }
  38. //------------------------------------------------endregion
  39. }

测试结果

添加水印,旋转,透明度,密码

pom.xml

  1. <!-- PDF文件依赖包 -->
  2. <dependency>
  3. <groupId>com.itextpdf</groupId>
  4. <artifactId>itextpdf</artifactId>
  5. <version>5.5.13.1</version>
  6. </dependency>
  7. <!-- PDF文件字体 防止中文乱码 -->
  8. <dependency>
  9. <groupId>com.itextpdf</groupId>
  10. <artifactId>itext-asian</artifactId>
  11. <version>5.2.0</version>
  12. </dependency>
  13. <!--PDF 加密-->
  14. <dependency>
  15. <groupId>org.bouncycastle</groupId>
  16. <artifactId>bcprov-ext-jdk15on</artifactId>
  17. <version>1.47</version>
  18. </dependency>

PDF参数类-PDFParamsDTO

  1. package com.atguigu.boot3.actuator.dto;
  2. import lombok.Data;
  3. import java.io.InputStream;
  4. @Data
  5. public class PDFParamsDTO {
  6. /**
  7. * 是否生成到本地
  8. */
  9. private Boolean generateToLocal;
  10. /**
  11. * PDF 模板路径,generateToLocal 为 true,必填
  12. */
  13. private String templatePath;
  14. /**
  15. * PDF 存放的路径,generateToLocal 为 true,必填
  16. */
  17. private String fileDirectory;
  18. /**
  19. * PDF 模版字节,generateToLocal 为 false,必填
  20. */
  21. private InputStream inputStream;
  22. /**
  23. * 水印文字
  24. */
  25. private String watermark;
  26. /**
  27. * 默认字体大小
  28. */
  29. private int DefaultFontSize = 13;
  30. /**
  31. * 水印文字透明度
  32. */
  33. private float fillOpacity;
  34. /**
  35. * 需要添加水印的 X 坐标
  36. */
  37. private float x;
  38. /**
  39. * 需要添加水印的 Y 坐标
  40. */
  41. private float y;
  42. /**
  43. * 应用于文本的旋转角度,以弧度为单位
  44. * 为 0 则不旋转
  45. */
  46. private float angle;
  47. /**
  48. * 水印文字-RBG 值-R 值
  49. */
  50. private int r = 255;
  51. /**
  52. * 水印文字-RBG 值-G 值
  53. */
  54. private int g = 0;
  55. /**
  56. * 水印文字-RBG 值-B 值
  57. */
  58. private int b = 0;
  59. /**
  60. * 打开文档时输入的密码
  61. */
  62. private String userPassword;
  63. /**
  64. * 用户编辑 PDF 时需要的密码
  65. */
  66. private String ownerPassword;
  67. }

PDF 工具类-PDFMakerUtil

  1. package com.atguigu.boot3.actuator.util;
  2. import com.itextpdf.text.BaseColor;
  3. import com.itextpdf.text.Element;
  4. import com.itextpdf.text.Rectangle;
  5. import com.itextpdf.text.pdf.*;
  6. import com.atguigu.boot3.actuator.dto.PDFParamsDTO;
  7. import io.micrometer.common.util.StringUtils;
  8. import lombok.Data;
  9. import lombok.extern.slf4j.Slf4j;
  10. import javax.swing.*;
  11. import java.awt.*;
  12. import java.io.ByteArrayOutputStream;
  13. import java.io.File;
  14. import java.io.FileOutputStream;
  15. import java.io.InputStream;
  16. import java.nio.file.Files;
  17. import java.nio.file.Paths;
  18. /**
  19. * PDF 生成类
  20. * - luoma - 2023年9月4日22:18:37
  21. * - 参考:<a href="https://blog.csdn.net/Ying_ph/article/details/131800711">Java PDF加水印</a>
  22. * - 参考:<a href="https://blog.csdn.net/weixin_42994251/article/details/129433070">咖啡汪日志——JAVA导出pdf文件加水印 文字+图片、文字</a>
  23. */
  24. @Data
  25. @Slf4j
  26. public class PDFMakerUtil {
  27. /**
  28. * 给 PDF 文件添加水印,旋转,透明度,密码
  29. *
  30. * @param pdfParams PDF 参数类
  31. * @return 添加水印后的 PDF 文件字节数组(添加到本地的情况为空)
  32. */
  33. public static byte[] addWatermarkAuto(PDFParamsDTO pdfParams) {
  34. String logInfo = "addWatermarkAuto >> PDF 添加水印 >> ";
  35. byte[] byteArray = null;
  36. try {
  37. log.info(logInfo + "添加文字:{}", pdfParams.getWatermark());
  38. //坐标
  39. float x = pdfParams.getX();
  40. float y = pdfParams.getY();
  41. float angle = pdfParams.getAngle();
  42. //文字颜色
  43. int r = pdfParams.getR();
  44. int g = pdfParams.getG();
  45. int b = pdfParams.getB();
  46. BaseColor fontColor = new BaseColor(r, g, b);
  47. //文字大小
  48. int fontSize = pdfParams.getDefaultFontSize();
  49. //文字内容
  50. // 使用"||"将内容进行分割
  51. String waterMarkName = pdfParams.getWatermark();
  52. String[] waterMarkContents = waterMarkName.split("\\|\\|");
  53. //获取水印文字的最大高度和宽度
  54. int textH = 0, textW = 0;
  55. for (String waterMarkContent : waterMarkContents) {
  56. JLabel label = new JLabel();
  57. label.setText(waterMarkContent);
  58. FontMetrics metrics = label.getFontMetrics(label.getFont());
  59. if (textH < metrics.getHeight()) {
  60. textH = metrics.getHeight();
  61. }
  62. if (textW < metrics.stringWidth(label.getText())) {
  63. textW = metrics.stringWidth(label.getText());
  64. }
  65. }
  66. //设置输入,输出
  67. PdfReader reader;
  68. PdfStamper stamper;
  69. ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
  70. if (pdfParams.getGenerateToLocal()) {
  71. //输入目录
  72. String inputFile = pdfParams.getTemplatePath();
  73. //输出目录
  74. String outputFile = pdfParams.getFileDirectory();
  75. //设置输入,输出
  76. reader = new PdfReader(inputFile);
  77. stamper = new PdfStamper(reader, Files.newOutputStream(Paths.get(outputFile)));
  78. } else {
  79. //设置输入,输出
  80. InputStream inputStream = pdfParams.getInputStream();
  81. reader = new PdfReader(inputStream);
  82. stamper = new PdfStamper(reader, outputStream);
  83. }
  84. // 设置密码文件打开密码文件编辑密码
  85. byte[] arrUserPassword = null;
  86. byte[] arrOwnerPassword = null;
  87. if(StringUtils.isNotBlank(pdfParams.getUserPassword())) {
  88. arrUserPassword = pdfParams.getUserPassword().getBytes();
  89. }
  90. if(StringUtils.isNotBlank(pdfParams.getOwnerPassword())) {
  91. arrOwnerPassword = pdfParams.getOwnerPassword().getBytes();
  92. }
  93. if(StringUtils.isNotBlank(pdfParams.getUserPassword()) || StringUtils.isNotBlank(pdfParams.getOwnerPassword())) {
  94. stamper.setEncryption(arrUserPassword, arrOwnerPassword, PdfWriter.ALLOW_PRINTING, false);
  95. }
  96. //设置字体
  97. BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
  98. // 获取总页数 +1, 下面从1开始遍历
  99. int total = reader.getNumberOfPages() + 1;
  100. PdfContentByte under;
  101. for (int i = 1; i < total; i++) {
  102. // 在内容上方加水印
  103. under = stamper.getOverContent(i);
  104. // 在内容下方加水印
  105. //under = stamper.getUnderContent(i);
  106. under.saveState();
  107. under.beginText();
  108. under.setFontAndSize(baseFont, fontSize);
  109. //文字加粗
  110. //设置文本描边宽度
  111. //under.setLineWidth(0.3);
  112. //设置文本为描边模式, 会导致 setColorFill 颜色失效
  113. //under.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE);
  114. //字体颜色
  115. under.setColorFill(fontColor);
  116. // 设置透明度
  117. PdfGState gs = new PdfGState();
  118. gs.setFillOpacity(pdfParams.getFillOpacity());
  119. under.setGState(gs);
  120. //水印字体和大小
  121. Rectangle pageRect = reader.getPageSizeWithRotation(i);
  122. int width1 = (int) pageRect.getWidth();
  123. int height1 = (int) pageRect.getHeight();
  124. //fontSize = (width1 + height1) / pdfParams.getDefaultFontSize();
  125. x = (float) (width1 ) / 2;
  126. y = (float) height1 / 2;
  127. // 将分段的字段进行输出编写
  128. for (int z = 0; z < waterMarkContents.length; z++) {
  129. //文字
  130. String itemWaterMark = waterMarkContents[z];
  131. // 间隔距离(参数可调节)
  132. int interval = fontSize * z;
  133. under.showTextAligned(Element.ALIGN_CENTER, itemWaterMark, x, y - (interval + textH * z), angle);
  134. }
  135. // 添加水印文字
  136. under.endText();
  137. }
  138. stamper.close();
  139. reader.close();
  140. //outputStream 转换为 byte
  141. ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
  142. outputStream.writeTo(byteArrayOutputStream);
  143. byteArray = byteArrayOutputStream.toByteArray();
  144. log.info(logInfo + "设置完成。");
  145. } catch (Exception e) {
  146. log.error(logInfo + "异常,e:", e);
  147. }
  148. return byteArray;
  149. }
  150. }

测试

测试代码

  1. public static void main(String[] args) {
  2. try {
  3. File file = new File("D:\\\\data\\\\105.pdf");
  4. InputStream inputStream = Files.newInputStream(file.toPath());
  5. PDFParamsDTO pdfParams = new PDFParamsDTO();
  6. pdfParams.setWatermark(String.format("适用范围:%s||有效期:%s||再次复印无效", "申请页面的原因及用途申请页面的原因及用途", "2022年5月9日11:14:36"));
  7. pdfParams.setGenerateToLocal(false); //输入输出流
  8. pdfParams.setInputStream(inputStream);
  9. pdfParams.setFileDirectory("D:\\data\\105-1.pdf");
  10. pdfParams.setAngle(30);
  11. pdfParams.setR(210);
  12. pdfParams.setG(210);
  13. pdfParams.setB(210);
  14. pdfParams.setDefaultFontSize(25);
  15. pdfParams.setFillOpacity(0.7f);
  16. //pdfParams.setUserPassword("123");
  17. //pdfParams.setOwnerPassword("456");
  18. byte[] arrByte = PDFMakerUtil.addWatermarkAuto(pdfParams);
  19. // 步骤1:创建FileOutputStream对象
  20. // 参数1:文件路径
  21. // 参数2:是否追加写入(true为追加,false为覆盖)
  22. FileOutputStream fos = new FileOutputStream(pdfParams.getFileDirectory(), false);
  23. // 步骤2:将byte数据写入文件
  24. // 参数:要写入的byte数组
  25. fos.write(arrByte);
  26. // 步骤3:关闭文件
  27. fos.close();
  28. } catch (Exception e) {
  29. log.error("生成失败,e:", e);
  30. }
  31. //------------------------------------------------endregion
  32. }

测试结果