前言
你好呀,我是小邹。
昨天给大家分享了PDF文件添加水印后保存,今天再来聊一聊如何实现印章、签名。
“Talk is cheap,show me the code.”
效果
实现
① 添加相关依赖
<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>
② 工具类PdfUtil
package top.hqxiaozou.utils;
import top.hqxiaozou.entity.PdfAddContentParam;
import com.itextpdf.awt.geom.Rectangle2D;
import com.itextpdf.text.*;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.parser.ImageRenderInfo;
import com.itextpdf.text.pdf.parser.PdfReaderContentParser;
import com.itextpdf.text.pdf.parser.RenderListener;
import com.itextpdf.text.pdf.parser.TextRenderInfo;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
/**
* @author:邹祥发
* @date:2022/11/24 10:19
* @description:PDF工具类,提供 水印、签名、盖章 功能
*/
@Slf4j
public class PdfUtil {
/**
* 往PDF上添加文字水印
*
* @param inputFile 原文件
* @param outputFile 加水印后的文件
* @param waterMarkName 水印字符
*/
public static void addWaterMark(String inputFile, String outputFile, String waterMarkName) {
PdfReader reader = null;
PdfStamper stamper = null;
try {
reader = new PdfReader(inputFile);
stamper = new PdfStamper(reader, Files.newOutputStream(Paths.get(outputFile)));
//水印字体,放到服务器上对应文件夹下(arial中文不生效)
//BaseFont base = BaseFont.createFont("D:/workspace/springboot/src/main/resources/arial.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
Rectangle pageRect;
PdfGState gs = new PdfGState();
//填充不透明度 为1完全不透明
gs.setFillOpacity(0.1f);
//笔画不透明度, 为1完全不透明
gs.setStrokeOpacity(0.1f);
int total = reader.getNumberOfPages() + 1;
JLabel label = new JLabel();
FontMetrics metrics;
int textH;
int textW;
label.setText(waterMarkName);
metrics = label.getFontMetrics(label.getFont());
textH = metrics.getHeight();
textW = metrics.stringWidth(label.getText());
PdfContentByte under;
int interval = 0;
for (int i = 1; i < total; i++) {
pageRect = reader.getPageSizeWithRotation(i);
//在字体下方加水印
under = stamper.getOverContent(i);
under.beginText();
under.saveState();
under.setGState(gs);
//这里修改水印字体大小
under.setFontAndSize(base, 36);
//这里设置字体上下间隔
for (int height = interval + textH; height < pageRect.getHeight(); height = height + textH * 9) {
//这里设置字体左右间隔
for (int width = interval + textW; width < pageRect.getWidth() + textW; width = width + textW * 4) {
//这里设置倾斜角度
under.showTextAligned(Element.ALIGN_LEFT, waterMarkName, width - textW, height - textH, 45);
}
}
under.endText();
}
stamper.close();
reader.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (stamper != null) {
stamper.close();
}
} catch (IOException | com.itextpdf.text.DocumentException e) {
e.printStackTrace();
}
if (reader != null) {
reader.close();
}
}
}
/**
* 根据指定坐标往PDF上添加文字内容
*
* @param filePath 原文件路径
* @param pdfAddContentParams 实体类
*/
public static byte[] addText(String filePath, List<PdfAddContentParam> pdfAddContentParams) throws Exception {
PdfReader pdfReader = new PdfReader(filePath);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
//设置输入文件以及输出文件地址
PdfStamper stamper = new PdfStamper(pdfReader, byteArrayOutputStream);
//设置字体
BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
for (PdfAddContentParam pdfAddContentParam : pdfAddContentParams) {
Font font = new Font(baseFont, 10);
if (pdfAddContentParam.getPageNum() > pdfReader.getNumberOfPages()) {
log.error("设置的页码数[" + pdfAddContentParam.getPageNum() + "]大于原文件页码数[" + pdfReader.getNumberOfPages() + "]!请重新输入");
throw new RuntimeException("设置的页码数[" + pdfAddContentParam.getPageNum() + "]大于原文件页码数[" + pdfReader.getNumberOfPages() + "]!请重新输入");
}
PdfContentByte overContent = stamper.getOverContent(pdfAddContentParam.getPageNum());
ColumnText columnText = new ColumnText(overContent);
columnText.setSimpleColumn(pdfAddContentParam.getLlx(), pdfAddContentParam.getLly(), pdfAddContentParam.getUrx(), pdfAddContentParam.getUry());
Paragraph elements = new Paragraph(pdfAddContentParam.getContent());
elements.setFont(font);
columnText.addElement(elements);
columnText.go();
}
stamper.close();
return byteArrayOutputStream.toByteArray();
}
/**
* 根据关键字往PDF上添加文字内容
*
* @param filePath 原文件路径
* @param pdfAddContentParams 实体类
*/
public static byte[] addTextByKeyword(String filePath, List<PdfAddContentParam> pdfAddContentParams) throws Exception {
PdfReader pdfReader = new PdfReader(filePath);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
//设置输入文件以及输出文件地址
PdfStamper stamper = new PdfStamper(pdfReader, byteArrayOutputStream);
// 获取PDF文件的总页数
int pageNum = pdfReader.getNumberOfPages();
PdfReaderContentParser pdfReaderContentParser = new PdfReaderContentParser(pdfReader);
for (PdfAddContentParam pdfAddContentParam : pdfAddContentParams) {
StringBuilder stringBuilder = new StringBuilder();
final boolean[] hasKeyword = {false, true};
String keyword = pdfAddContentParam.getKeyword();
int length = keyword.length();
if (length == 0) {
throw new RuntimeException("请输入关键字!");
}
for (int page = 1; page <= pageNum; page++) {
if (hasKeyword[0]) {
break;
}
int finalPage = page;
pdfReaderContentParser.processContent(page, new RenderListener() {
@Override
public void beginTextBlock() {
}
@SneakyThrows
@Override
public void renderText(TextRenderInfo renderInfo) {
// 读取PDF文件的内容
String text = renderInfo.getText().trim();
stringBuilder.append(text);
if (stringBuilder.toString().contains(keyword)) {
Rectangle2D.Float boundingRectangle = renderInfo.getBaseline().getBoundingRectange();
if (hasKeyword[1]) {
// 关键字的坐标
double x = boundingRectangle.getX();
double y = boundingRectangle.getY();
//设置字体
BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
Font font = new Font(baseFont, 10);
PdfContentByte overContent = stamper.getOverContent(finalPage);
ColumnText columnText = new ColumnText(overContent);
// 根据关键字的坐标计算+偏移量得到批注字体的坐标
columnText.setSimpleColumn((float) x + pdfAddContentParam.getLlx(), (float) y + pdfAddContentParam.getLly(), (float) x + 100 + pdfAddContentParam.getUrx(), (float) y + pdfAddContentParam.getUry());
Paragraph elements = new Paragraph(pdfAddContentParam.getContent());
elements.setFont(font);
columnText.addElement(elements);
columnText.go();
hasKeyword[1] = false;
}
hasKeyword[0] = true;
}
if (stringBuilder.toString().length() > length * 3) {
stringBuilder.setLength(0);
}
}
@Override
public void endTextBlock() {
}
@Override
public void renderImage(ImageRenderInfo renderInfo) {
}
});
}
}
stamper.close();
pdfReader.close();
return byteArrayOutputStream.toByteArray();
}
/**
* pdf插入图片
*
* @param oldPath 插入图片前的路径
* @param newPath 插入图片后的路径
* @param imgPath 图片路径
* @throws IOException
* @throws DocumentException
*/
public static void addImage(String oldPath, String newPath, String imgPath) throws IOException, DocumentException {
InputStream inputStream = Files.newInputStream(Paths.get(oldPath));
FileOutputStream out = new FileOutputStream(newPath);
PdfReader reader = new PdfReader(inputStream);
//pdf页数
int pdfPages = reader.getNumberOfPages();
PdfStamper stamper = new PdfStamper(reader, out);
//图片
BufferedImage bufferedImage = ImageIO.read(Files.newInputStream(Paths.get(imgPath)));
//x轴坐标
int x = 350;
//y轴坐标
int y = 20;
//图片放置的页码
for (int i = pdfPages; i <= pdfPages; i++) {
//图片处理
Image img = Image.getInstance(ImageUtil.imageToBytes((bufferedImage), "png"));
//设置图片大小
img.scaleAbsolute(192, 100);
img.setTransparency(new int[0]);
//设置图片位置
img.setAbsolutePosition(x, y);
stamper.getOverContent(i).addImage(img);
}
//关闭资源
stamper.close();
out.close();
reader.close();
}
}
③ 工具类ImageUtil
package top.hqxiaozou.utils;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* @author:邹祥发
* @date:2022/11/25 14:14
* @description:转换BufferedImage数据为byte数组
*/
public class ImageUtil {
/**
* 转换BufferedImage 数据为byte数组
*
* @param image Image对象
* @param format image格式字符串.如"gif","png"
* @return byte数组
*/
public static byte[] imageToBytes(BufferedImage image, String format) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
ImageIO.write(image, format, out);
} catch (IOException e) {
e.printStackTrace();
}
return out.toByteArray();
}
/**
* 转换byte数组为Image
*
* @param bytes
* @return Image
*/
public static Image bytesToImage(byte[] bytes) {
Image image = Toolkit.getDefaultToolkit().createImage(bytes);
try {
MediaTracker mt = new MediaTracker(new Label());
mt.addImage(image, 0);
mt.waitForAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
return image;
}
}
④ 实体类
package top.hqxiaozou.entity;
import lombok.Data;
/**
* @author:邹祥发
* @date:2022/11/25 10:36
* @description:pdf添加签名实体类
*/
@Data
public class PdfAddContentParam {
/**
* 要添加的文字
*/
private String content;
/**
* 页码
*/
private Integer pageNum;
/**
* 关键字
*/
private String keyword;
/**
* 文本框坐标(左下角x,y,右上角x,y)
*/
private Float llx;
private Float lly;
private Float urx;
private Float ury;
public PdfAddContentParam() {
}
public PdfAddContentParam(String content, Integer pageNum, Float llx, Float lly, Float urx, Float ury) {
this.content = content;
this.pageNum = pageNum;
this.llx = llx;
this.lly = lly;
this.urx = urx;
this.ury = ury;
}
public PdfAddContentParam(String content, String keyword, Float llx, Float lly, Float urx, Float ury) {
this.content = content;
this.keyword = keyword;
this.llx = llx;
this.lly = lly;
this.urx = urx;
this.ury = ury;
}
}
⑤ 测试类
- 添加水印
package top.hqxiaozou.test;
import top.hqxiaozou.utils.PDFUtils;
/**
* @author:邹祥发
* @date:2022/11/24 10:41
* @description:PDF水印测试类
*/
public class AddWaterMarkTest {
public static void main(String[] args) {
PDFUtils.addWaterMark("C:/Users/zou/Desktop/阿里巴巴Java开发手册(终极版).pdf", "C:/Users/zou/Desktop/new.pdf", "https://www.hqxiaozou.top");
}
}
- 插入图片
package top.hqxiaozou.pdf;
import top.hqxiaozou.utils.PdfUtil;
import com.itextpdf.text.DocumentException;
import java.io.IOException;
/**
* @author:邹祥发
* @date:2022/11/25 14:22
* @description:pdf插入图片测试类
*/
public class AddImageTest {
public static void main(String[] args) {
try {
PdfUtil.addImage("C:/Users/zou/Desktop/watermark.pdf", "C:/Users/zou/Desktop/image.pdf", "C:/Users/zou/Desktop/sign.bmp");
} catch (IOException | DocumentException e) {
throw new RuntimeException(e);
}
}
}
- 根据指定坐标往PDF添加文字
package top.hqxiaozou.pdf;
import top.hqxiaozou.entity.PdfAddContentParam;
import top.hqxiaozou.utils.PdfUtil;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author:邹祥发
* @date:2022/11/24 20:30
* @description:根据指定坐标往PDF添加文字内容测试类
*/
public class AddTextTest {
public static void main(String[] args) {
// 按坐标添加批注
PdfAddContentParam name = new PdfAddContentParam("召田最帅boy", 3, 415F, 36F, 610F, 83F);
List<PdfAddContentParam> pdfAddContentParams = new ArrayList<>();
Collections.addAll(pdfAddContentParams, name);
byte[] bytes;
try {
bytes = PdfUtil.addText("C:/Users/zou/Desktop/image.pdf", pdfAddContentParams);
Files.newOutputStream(Paths.get("C:/Users/zou/Desktop/sign.pdf")).write(bytes);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
- 根据关键字往PDF添加文字
package top.hqxiaozou.pdf;
import top.hqxiaozou.entity.PdfAddContentParam;
import top.hqxiaozou.utils.PdfUtil;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author:邹祥发
* @date:2022/11/25 11:16
* @description:根据关键字往PDF添加文字内容测试类
*/
public class AddTextByKeywordTest {
public static void main(String[] args) {
// 按关键字批注
PdfAddContentParam keyword = new PdfAddContentParam("test", "召田最帅boy", 1F, 2F, 3F, 40F);
List<PdfAddContentParam> pdfAddContentParams = new ArrayList<>();
Collections.addAll(pdfAddContentParams, keyword);
byte[] bytes;
try {
bytes = PdfUtil.addTextByKeyword("C:/Users/zou/Desktop/target.pdf", pdfAddContentParams);
Files.newOutputStream(Paths.get("C:/Users/zou/Desktop/new.pdf")).write(bytes);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}