需求来源 需要在文件上用输入仪签字/需要pdf上添加图片
注:输入仪签字返回的是 base64格式的图片
流程:
1.通过使用 itextpdf
2.结合 TestRenderListener 获取pdf里的文字坐标 ,增加偏移量,并控制图片大小,使签名图片落在合适位置,保存文件
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.4.3</version>
</dependency>
<dependency>
<groupId>org.jodd</groupId>
<artifactId>jodd-core</artifactId>
<version>5.1.4</version>
</dependency>
注:jodd使用来处理base64 图片的
代码示例:
package com.mickyss.pdf;
import com.itextpdf.awt.geom.Rectangle2D;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.parser.PdfReaderContentParser;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author mickyss
* @description
* @since 2020/10/30 0030
*/
public class PDFTest {
//
public static void main(String[] args) throws IOException {
File file = new File("D:\\sign.pdf");
if(file.exists()){
System.out.println("123");
}
String signBase64 = "这里放base64图片字符串,注意不需要data:image/jpg;base64,这串前缀";
// 读取模板文件
InputStream input = new FileInputStream(file);
PdfReader reader = new PdfReader(input);
PdfStamper stamper = null;
//获取受理签名坐标
List<Map> rectText = new ArrayList();
try {
stamper = new PdfStamper(reader, new FileOutputStream(
file.getPath()));
} catch (DocumentException e) {
e.printStackTrace();
}
try {
//新建一个PDF解析对象
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
for(int i = 1;i <= reader.getNumberOfPages();i++) {
//新建一个ImageRenderListener对象,该对象实现了RenderListener接口,作为处理PDF的主要类
TestRenderListener listener = new TestRenderListener();
//解析PDF,并处理里面的文字
parser.processContent(i, listener);
List<Map<String, Rectangle2D.Float>> list_text = listener.rows_text_rect;
for (int k = 0; k < list_text.size(); k++) {
Map<String, Rectangle2D.Float> map = list_text.get(k);
for (Map.Entry<String, Rectangle2D.Float> entry : map.entrySet()) {
//根据text信息 获取文字相对于地址,一般来说签名应该是合在一起的,如果用模板文件如果ireport生成的pdf 则text这里的数据都会是完整的而不是单个字体一个
if(entry.getKey().contains("名")){
Map mapEntry = new HashMap();
mapEntry.put("x",entry.getValue().x);
mapEntry.put("y",entry.getValue().y);
rectText.add(mapEntry);
}
}
}
}
for(int i = 0; i<rectText.size();i++ ){
// 读图片
Image image = Image.getInstance(Base64Util.decodeToByte(signBase64));
// 根据域的大小缩放图片
image.scaleToFit(100,100);
// 添加图片 这里可以自己加入偏移量
image.setAbsolutePosition(Float.parseFloat(rectText.get(i).get("x").toString()), Float.parseFloat(rectText.get(i).get("y").toString()));
PdfContentByte under = stamper.getOverContent(i+1);
under.addImage(image);
}
try {
stamper.close();
} catch (IOException e) {
e.printStackTrace();
}
reader.close();
} catch (DocumentException e) {
e.printStackTrace();
}
}
}
TestRenderListener 工具类
package com.mickyss.pdf;
import com.itextpdf.awt.geom.Rectangle2D;
import com.itextpdf.awt.geom.RectangularShape;
import com.itextpdf.text.pdf.parser.ImageRenderInfo;
import com.itextpdf.text.pdf.parser.RenderListener;
import com.itextpdf.text.pdf.parser.TextRenderInfo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author mickyss
* @description
* @since 2020/10/27 0027
*/
public class TestRenderListener implements RenderListener {
//用来存放文字的矩形
List<Rectangle2D.Float> rectText = new ArrayList<Rectangle2D.Float>();
//用来存放文字
List<String> textList = new ArrayList<String>();
//用来存放文字的y坐标
List<Float> listY = new ArrayList<Float>();
//用来存放每一行文字的坐标位置
List<Map<String,Rectangle2D.Float>> rows_text_rect = new ArrayList();
//PDF文件的路径
protected String filepath = null;
public TestRenderListener() {
}
//step 2,遇到"BT"执行
@Override
public void beginTextBlock() {
// TODO Auto-generated method stub
}
//step 3
/**
* 文字主要处理方法
*/
@Override
public void renderText(TextRenderInfo renderInfo) {
//获取文字的下面的矩形
//Rectangle2D.Float rectBase = renderInfo.getBaseline().getBoundingRectange();
String text = renderInfo.getText();
if(text.length() > 0){
RectangularShape rectBase = renderInfo.getBaseline().getBoundingRectange();
//获取文字下面的矩形
Rectangle2D.Float rectAscen = renderInfo.getAscentLine().getBoundingRectange();
//计算出文字的边框矩形
float leftX = (float) rectBase.getMinX();
float leftY = (float) rectBase.getMinY()-1;
float rightX = (float) rectAscen.getMaxX();
float rightY = (float) rectAscen.getMaxY()+1;
Rectangle2D.Float rect = new Rectangle2D.Float(leftX, leftY, rightX - leftX, rightY - leftY);
if(listY.contains(rect.y)){
int index = listY.indexOf(rect.y);
float tempx = rect.x > rectText.get(index).x ? rectText.get(index).x : rect.x;
rectText.set(index,new Rectangle2D.Float(tempx,rect.y,rect.width + rectText.get(index).width,rect.height));
textList.set(index,textList.get(index) + text);
}else{
rectText.add(rect);
textList.add(text);
listY.add(rect.y);
}
Map<String,Rectangle2D.Float> map = new HashMap();
map.put(text,rect);
rows_text_rect.add(map);
}
}
//step 4(最后执行的,只执行一次),遇到“ET”执行
@Override
public void endTextBlock() {
// TODO Auto-generated method stub
}
//step 1(图片处理方法)
@Override
public void renderImage(ImageRenderInfo renderInfo) {
}
}
Base64Util工具类
package com.mickyss.pdf;
import jodd.util.Base64;
/**
* <p>
* 类说明:base64编码、解码工具类
* </p>
*
*/
public class Base64Util {
/**
* 对base64编码的字符串进行解码,返回字节数据
*
* @param str
* @return
*/
static public byte[] decodeToByte(String str) {
return Base64.decode(str);
}
}
参考: https://blog.youkuaiyun.com/sinat_29957455/article/details/78759654?utm_source=blogxgwz14