java操作pdf添加骑缝章
依赖
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13.3</version>
</dependency>
代码
/**
* 盖骑缝章
*
* @param infilePath 原PDF路径
* @param outFilePath 输出PDF路径
* @param picPath 章图片路径
* @param keyWord 盖章关键字位置
*/
public static void stamperCheckMarkPdf(String infilePath, String outFilePath, String picPath, String keyWord) {
// 选择需要印章的pdf
PdfReader reader = null;
// 加完印章后的pdf
PdfStamper stamp = null;
try {
reader = new PdfReader(infilePath);
stamp = new PdfStamper(reader, Files.newOutputStream(Paths.get(outFilePath)));
// 获得第一页
Rectangle pageSize = reader.getPageSize(1);
float height = pageSize.getHeight();
float width = pageSize.getWidth();
// 获取pdf文档中的页数
int nums = reader.getNumberOfPages();
// 生成骑缝章切割图片
Image[] nImage = subImages(picPath, nums);
for(int n = 1; n <= nums; n++){
// 设置在第几页打印印章
PdfContentByte over = stamp.getOverContent(n);
// 选择图片
Image img = nImage[n-1];
// 控制图片位置
img.setAbsolutePosition(width-img.getWidth(),height/2-img.getHeight()/2);
over.addImage(img);
// 最后一页,查询盖章处,盖章
// 存在的问题:这边我控制的是当最后一页才去根据关键字查找盖章,如果关键字出现在前两页自然找不到
if (n == nums) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
InputStream is = null;
try {
is = Files.newInputStream(Paths.get(infilePath));
byte[] buffer = new byte[is.available()];
int m;
while ((m = is.read(buffer)) != -1) {
bos.write(buffer, 0, m);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bos.close();
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 获取输出流
byte[] bytes = bos.toByteArray();
// 获取关键字位置
List<Map<String, Object>> wordsCoordinates = getWordsCoordinate(bytes, keyWord);
if (!CollectionUtils.isEmpty(wordsCoordinates)) {
Map<String, Object> map = wordsCoordinates.get(wordsCoordinates.size() - 1);
float x = (float) map.get("x");
float y = (float) map.get("y");
float w = (float) map.get("fontWidth") * 6;
float h = (float) map.get("fontHeight");
Image[] nImage2 = subImages(picPath, 1);
Image img2 = nImage2[0];
float xx = x + w - img2.getWidth() / 2;
float yy = y - img2.getHeight() / 2;
// 控制图片位置
img2.setAbsolutePosition(xx, yy);
over.addImage(img2);
}
}
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
if (null != stamp) {
stamp.close();
}
if (null != reader) {
reader.close();
}
} catch (Exception ignored) {}
}
}
/**
* 切割图片
*
* @param imgPath 原始图片路径
* @param n 切割份数
* @return itextPdf的Image[]
*/
public static Image[] subImages(String imgPath, int n) throws IOException, BadElementException {
Image[] nImage = new Image[n];
ByteArrayOutputStream out = new ByteArrayOutputStream();
// 服务器测试和resource下测试
Resource resource = new ClassPathResource(imgPath);
InputStream inputStream = resource.getInputStream();
// 使用本地图片测试方法
// File resource = new File(imgPath);
// InputStream inputStream = Files.newInputStream(resource.toPath());
BufferedImage img = ImageIO.read(inputStream);
// 图片高
int h = img.getHeight();
// 图片宽
int w = img.getWidth();
int sw = w / n;
for(int i = 0; i < n; i++){
BufferedImage subImg;
if( i == n - 1){
// 最后剩余部分
subImg = img.getSubimage(i * sw, 0, w-i*sw, h);
}else {
// 前n-1块均匀切
subImg = img.getSubimage(i * sw, 0, sw, h);
}
ImageIO.write(subImg, imgPath.substring(imgPath.lastIndexOf('.')+1),out);
nImage[i] = Image.getInstance(out.toByteArray());
out.flush();
out.reset();
}
return nImage;
}
/**
* 获取关键词位置
*
* @param pdfData pdf二进制字节流
* @param keyWord 关键字
* @return List<Map<String, Object>>
*/
public static List<Map<String, Object>> getWordsCoordinate(byte[] pdfData, String keyWord) {
if (StringUtils.isBlank(keyWord)) {
return null;
}
List<Map<String, Object>> result = new ArrayList<>();
PdfReader reader = null;
try {
// pdfData :可以是二进制,也可以是文件路径,两种方式选择一种
reader = new PdfReader(pdfData);
// 获取pdf页数
int pages = reader.getNumberOfPages();
for (int pageNum = 1; pageNum <= pages; pageNum++) {
// 每页的宽度
float width = reader.getPageSize(pageNum).getWidth();
// 每页的高度
float height = reader.getPageSize(pageNum).getHeight();
RenderListenerHelper renderListenerHelper = new RenderListenerHelper(pageNum, width, height);
// 解析pdf,定位位置
PdfContentStreamProcessor processor = new PdfContentStreamProcessor(renderListenerHelper);
PdfDictionary pageDic = reader.getPageN(pageNum);
PdfDictionary resourcesDic = pageDic.getAsDict(PdfName.RESOURCES);
processor.processContent(ContentByteUtils.getContentBytesForPage(reader, pageNum), resourcesDic);
// 文本内容
String content = renderListenerHelper.getContent();
// 文本每个字对应的坐标
List<Map<String, Object>> charPositions = renderListenerHelper.getCharPositions();
for (int i = 0; i < content.length(); i++) {
// 获取关键字所在位置
int keyIndex = content.indexOf(keyWord, i);
if (keyIndex == -1) {
break;
}
result.add(charPositions.get(keyIndex));
i = keyIndex + 1;
}
}
} catch (Exception e) {
log.error("获取pdf关键字坐标失败:", e);
} finally {
reader.close();
}
return result;
}
static class RenderListenerHelper implements RenderListener {
private final int pageNum;
private final float pageWidth;
private final float pageHeight;
private final StringBuilder contentBuilder = new StringBuilder();
private final List<Map<String, Object>> charPositions = new ArrayList<>();
public RenderListenerHelper(int pageNum, float pageWidth, float pageHeight) {
this.pageNum = pageNum;
this.pageWidth = pageWidth;
this.pageHeight = pageHeight;
}
public String getContent() {
return contentBuilder.toString();
}
public List<Map<String, Object>> getCharPositions() {
return charPositions;
}
// step 2 遇到"BT"执行
@Override
public void beginTextBlock() {
}
// step 3 文字主要处理方法
@Override
public void renderText(TextRenderInfo renderInfo) {
//获取文本内容每个字信息集合
List<TextRenderInfo> characterRenderInfos = renderInfo.getCharacterRenderInfos();
for (TextRenderInfo textRenderInfo : characterRenderInfos) {
String word = textRenderInfo.getText();
if (word.length() > 1) {
word = word.substring(word.length() - 1);
}
// 关键字上边缘坐标
// Rectangle2D.Float boundingRectange = textRenderInfo.getAscentLine().getBoundingRectange();
// 关键字标准坐标(中间)
Rectangle2D.Float boundingRectange = textRenderInfo.getBaseline().getBoundingRectange();
// 关键字下边缘坐标
// Rectangle2D.Float boundingRectange = textRenderInfo.getDescentLine().getBoundingRectange();
// 正常坐标
Float x = boundingRectange.x;
Float y = boundingRectange.y;
Map<String, Object> coordinate = new HashMap<>(8);
coordinate.put("x", x);
coordinate.put("y", y);
// 页数
coordinate.put("pageNum", pageNum);
// 字体长度
coordinate.put("fontWidth", boundingRectange.width);
// 字段高度
coordinate.put("fontHeight", boundingRectange.height);
charPositions.add(coordinate);
contentBuilder.append(word);
}
}
// step 4(最后执行的,只执行一次),遇到“ET”执行
@Override
public void endTextBlock() {
}
// step 1(图片处理方法)
@Override
public void renderImage(ImageRenderInfo renderInfo) {
}
}
测试
public static void main(String[] args) throws DocumentException, IOException {
// 输出文件
String outfilePath = "C:/" + UUID.randomUUID() +".pdf";
// 图片路径
String picPath = "C:/章.png";
stamperCheckMarkPdf("C:/合同.pdf", outfilePath, picPath, "");
}
章图片