基于pdfbox实现的pdf添加文字水印工具

打印 上一主题 下一主题

主题 883|帖子 883|积分 2651

简述

最近有个需求需要给pdf加文字水印,于是开始搜索大法,但是发现网络上的代码基本都是将字体文件直接放在jar包里面。个人强迫症发作(手动狗头),想要像poi一样直接加载系统字体,于是研究了一下午pdfbox的源代码,发现FontFileFinder类可以实现这个功能。废话不多说,直接上代码。
引入依赖
  1. <dependency>
  2.     <groupId>org.apache.pdfbox</groupId>
  3.     <artifactId>pdfbox</artifactId>
  4. </dependency>
  5. <dependency>
  6.     <groupId>org.projectlombok</groupId>
  7.     <artifactId>lombok</artifactId>
  8.     <scope>provided</scope>
  9. </dependency>
  10. <dependency>
  11.     <groupId>jakarta.servlet</groupId>
  12.     <artifactId>jakarta.servlet-api</artifactId>
  13. </dependency>
复制代码
新增水印配置类
  1. @Data
  2. @NoArgsConstructor
  3. public class PdfWatermarkProperties {
  4.     public PdfWatermarkProperties(String content) {
  5.         this.content = content;
  6.     }
  7.     /**
  8.      * 文字水印内容
  9.      */
  10.     private String content = "";
  11.     /**
  12.      * ttf类型字体文件. 为null则使用默认字体
  13.      */
  14.     private File fontFile;
  15.     private float fontSize = 13;
  16.     /**
  17.      * cmyk颜色.参数值范围为 0-255
  18.      */
  19.     private int[] color = {0, 0, 0, 210};
  20.     /**
  21.      * 透明度
  22.      */
  23.     private float transparency = 0.3f;
  24.     /**
  25.      * 倾斜度. 默认30°
  26.      */
  27.     private double rotate = 0.3;
  28.     /**
  29.      * 初始添加水印的点位
  30.      */
  31.     private int x = -10;
  32.     private int y = 10;
  33.     /**
  34.      * 内容区域的宽高.即单个水印范围的大小
  35.      */
  36.     private int width = 200;
  37.     private int height = 200;
  38. }
复制代码
工具类
  1. import org.apache.fontbox.ttf.TTFParser;
  2. import org.apache.fontbox.ttf.TrueTypeCollection;
  3. import org.apache.fontbox.ttf.TrueTypeFont;
  4. import org.apache.fontbox.util.autodetect.FontFileFinder;
  5. import org.apache.pdfbox.pdmodel.PDDocument;
  6. import org.apache.pdfbox.pdmodel.PDPage;
  7. import org.apache.pdfbox.pdmodel.PDPageContentStream;
  8. import org.apache.pdfbox.pdmodel.font.PDFont;
  9. import org.apache.pdfbox.pdmodel.font.PDType0Font;
  10. import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState;
  11. import org.apache.pdfbox.util.Matrix;
  12. import javax.servlet.http.HttpServletResponse;
  13. import java.io.*;
  14. import java.net.URI;
  15. import java.net.URLEncoder;
  16. public class PdfUtil {
  17.     private static final String DEFAULT_TTF_FILENAME = "simsun.ttf";
  18.     private static final String DEFAULT_TTC_FILENAME = "simsun.ttc";
  19.     private static final String DEFAULT_FONT_NAME = "SimSun";
  20.     private static final TrueTypeFont DEFAULT_FONT;
  21.     static {
  22.         DEFAULT_FONT = loadSystemFont();
  23.     }
  24.     /**
  25.      * 加载系统字体,提供默认字体
  26.      *
  27.      * @return
  28.      */
  29.     private synchronized static TrueTypeFont loadSystemFont() {
  30.         //load 操作系统的默认字体. 宋体
  31.         FontFileFinder fontFileFinder = new FontFileFinder();
  32.         for (URI uri : fontFileFinder.find()) {
  33.             try {
  34.                 final String filePath = uri.getPath();
  35.                 if (filePath.endsWith(DEFAULT_TTF_FILENAME)) {
  36.                     return new TTFParser(false).parse(filePath);
  37.                 } else if (filePath.endsWith(DEFAULT_TTC_FILENAME)) {
  38.                     TrueTypeCollection trueTypeCollection = new TrueTypeCollection(new FileInputStream(filePath));
  39.                     final TrueTypeFont font = trueTypeCollection.getFontByName(DEFAULT_FONT_NAME);
  40.                     //复制完之后关闭ttc
  41.                     trueTypeCollection.close();
  42.                     return font;
  43.                 }
  44.             } catch (Exception e) {
  45.                 throw new RuntimeException("加载操作系统字体失败", e);
  46.             }
  47.         }
  48.         return null;
  49.     }
  50.     /**
  51.      * 添加文本水印
  52.      * * 使用内嵌字体模式,pdf文件大小会增加1MB左右
  53.      *
  54.      * @param sourceFile 需要加水印的文件
  55.      * @param descFile   目标存储路径
  56.      * @param props      水印配置
  57.      * @throws IOException
  58.      */
  59.     public static void addTextWatermark(File sourceFile, String descFile, PdfWatermarkProperties props) throws IOException {
  60.         // 加载PDF文件
  61.         PDDocument document = PDDocument.load(sourceFile);
  62.         addTextToDocument(document, props);
  63.         document.save(descFile);
  64.         document.close();
  65.     }
  66.     /**
  67.      * 添加文本水印
  68.      *
  69.      * @param inputStream  需要加水印的文件流
  70.      * @param outputStream 加水印之后的流。执行完之后会关闭outputStream, 建议使用{@link BufferedOutputStream}
  71.      * @param props        水印配置
  72.      * @throws IOException
  73.      */
  74.     public static void addTextWatermark(InputStream inputStream, OutputStream outputStream, PdfWatermarkProperties props) throws IOException {
  75.         // 加载PDF文件
  76.         PDDocument document = PDDocument.load(inputStream);
  77.         addTextToDocument(document, props);
  78.         document.save(outputStream);
  79.     }
  80.     /**
  81.      * 处理PDDocument,添加文本水印
  82.      *
  83.      * @param document
  84.      * @param props
  85.      * @throws IOException
  86.      */
  87.     public static void addTextToDocument(PDDocument document, PdfWatermarkProperties props) throws IOException {
  88.         document.setAllSecurityToBeRemoved(true);
  89.         // 遍历PDF文件,在每一页加上水印
  90.         for (PDPage page : document.getPages()) {
  91.             PDPageContentStream stream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true, true);
  92.             // 加载水印字体
  93.             if (DEFAULT_FONT == null) {
  94.                 throw new RuntimeException(String.format("未提供默认字体.请安装字体文件%s或%s", DEFAULT_TTF_FILENAME, DEFAULT_TTC_FILENAME));
  95.             }
  96.             PDFont font;
  97.             if (props.getFontFile() != null) {
  98.                 font = PDType0Font.load(document, props.getFontFile());
  99.             } else {
  100.                 //当TrueTypeFont为字体集合时, embedSubSet 需要设置为true, 嵌入其子集
  101.                 font = PDType0Font.load(document, DEFAULT_FONT, true);
  102.             }
  103.             PDExtendedGraphicsState r = new PDExtendedGraphicsState();
  104.             // 设置透明度
  105.             r.setNonStrokingAlphaConstant(props.getTransparency());
  106.             r.setAlphaSourceFlag(true);
  107.             stream.setGraphicsStateParameters(r);
  108.             // 设置水印字体颜色
  109.             final int[] color = props.getColor();
  110.             stream.setNonStrokingColor(color[0], color[1], color[2], color[3]);
  111.             stream.beginText();
  112.             stream.setFont(font, props.getFontSize());
  113.             // 获取PDF页面大小
  114.             float pageHeight = page.getMediaBox().getHeight();
  115.             float pageWidth = page.getMediaBox().getWidth();
  116.             // 根据纸张大小添加水印,30度倾斜
  117.             for (int h = props.getY(); h < pageHeight; h = h + props.getHeight()) {
  118.                 for (int w = props.getX(); w < pageWidth; w = w + props.getWidth()) {
  119.                     stream.setTextMatrix(Matrix.getRotateInstance(props.getRotate(), w, h));
  120.                     stream.showText(props.getContent());
  121.                 }
  122.             }
  123.             // 结束渲染,关闭流
  124.             stream.endText();
  125.             stream.restoreGraphicsState();
  126.             stream.close();
  127.         }
  128.     }
  129.     /**
  130.      * 设置pdf文件输出的响应头
  131.      *
  132.      * @param response web response
  133.      * @param fileName 文件名(不含扩展名)
  134.      */
  135.     public static void setPdfResponseHeader(HttpServletResponse response, String fileName) throws UnsupportedEncodingException {
  136.         response.setContentType("application/pdf");
  137.         response.setCharacterEncoding("utf-8");
  138.         response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".pdf");
  139.     }
  140. }
复制代码
测试
  1. @GetMapping("/t")
  2. public void getFile(HttpServletResponse response) throws IOException {
  3.     PdfUtil.setPdfResponseHeader(response, "watermark");
  4.     final ServletOutputStream out = response.getOutputStream();
  5.     PdfUtil.addTextWatermark(new FileInputStream("D:/测试文件.pdf"), out, new PdfWatermarkProperties("测试pdf水印"));
  6. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

拉不拉稀肚拉稀

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表