替换pdf字段主要工具
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import com.owinfo.mpw.cash.service.entity.vo.DeclarationPdfVO;
import com.owinfo.mpw.cash.service.entity.vo.PdfAttributeVO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.util.*;
import java.util.List;
/**
* <p>
*
* @author
* @version v1
* @create 2020-09-30 11:18:11
*
*/
public class PdfUtils {
/**
*
* @param fontPath 字体库的路径 C:\Windows\Fonts\simhei.ttf,simhei.ttf这个名字的来源,找个字体文件,右键属性,就可以看到这中名字了
* @return
* @throws Exception
*/
private static BaseFont getBaseFont(String fontPath) throws Exception{
return BaseFont.createFont(fontPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
}
/**
*
* @param declarationPdfVO 替换信息
* @param fontPathes 字体的文件路径,放在配置中心得
* @param path 模本文件路径
* @param tempFile 生成临时文件的路径
* @return 返回生成文件的名称
* @throws Exception
*/
public static String getPDF(DeclarationPdfVO declarationPdfVO, String[] fontPathes, String path, String tempFile) throws Exception {
//模板文件
String templatePath = path + "cashpdf.pdf";
//生成的新文件
String pdfName = declarationPdfVO.getFormSerialNum() + "-" + declarationPdfVO.getName() + BaseUtils.formatDateYMDHMss(new Date()) + ".pdf";
String targetPath = tempFile + pdfName;
Map<String, PdfAttributeVO> replaceMap = new HashMap<>(10);
getReplaceMap(declarationPdfVO, replaceMap);
getReplaceMap(declarationPdfVO.getBody1(), replaceMap);
getReplaceMap(declarationPdfVO.getBody2(), replaceMap);
getReplaceMap(declarationPdfVO.getBody3(), replaceMap);
PdfReader reader = null;
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
FileOutputStream out = new FileOutputStream(targetPath);
//读取PDF模板
reader = new PdfReader(templatePath);
PdfStamper stamper = new PdfStamper(reader, bos);
//获取所有的表单域
AcroFields form = stamper.getAcroFields();
Iterator<String> it = form.getFields().keySet().iterator();
while (it.hasNext()) {
String name = it.next();
PdfAttributeVO pdfAttribute = replaceMap.get(name);
if (pdfAttribute == null) {
continue;
}
if (name.startsWith("fill")) {
//表示是字段
setFieldAndFont(getBaseFont(fontPathes[pdfAttribute.getFontPathIndex()]), stamper, form, pdfAttribute.getValue(), pdfAttribute.getFontSize(), pdfAttribute.getFontPosition(), name);
} else {
//表示是复选框
form.setField(name, pdfAttribute.getValue(), true);
}
}
//如果为false那么生成的PDF文件还能编辑 但是不能编辑上面赋的值 可以研究一下
//设置为true文档将不可以编辑
stamper.setFormFlattening(true);
stamper.close();
//写一个新的PDF
Document doc = new Document();
PdfCopy copy = new PdfCopy(doc, out);
doc.open();
PdfReader newReader = new PdfReader(bos.toByteArray());
copy.addPage(copy.getImportedPage(newReader, 1));
copy.addPage(copy.getImportedPage(newReader, newReader.getNumberOfPages()));
doc.close();
} catch (IOException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
} finally {
if (reader != null) {
reader.close();
}
}
return pdfName;
}
/**
*
* @param bf 字体
* @param stamp
* @param form
* @param chunkStr
* @param fontSize 字体大小
* @param fontPosition 字体在上下方向的位置
* @param property
*/
public static void setFieldAndFont(BaseFont bf, PdfStamper stamp, AcroFields form, String chunkStr, float fontSize, float fontPosition, String property) {
try {
Font font = new Font(bf, fontSize, -1, new BaseColor(0, 0, 0));
List<AcroFields.FieldPosition> list = form.getFieldPositions(property);
int page = list.get(0).page;
PdfContentByte pdfContentByte = stamp.getOverContent(page);
ColumnText columnText = new ColumnText(pdfContentByte);
Rectangle rectangle = list.get(0).position;
columnText.setSimpleColumn(rectangle);
Chunk chunk = null;
chunk = new Chunk(chunkStr);
Paragraph paragraph = new Paragraph(fontPosition, chunk);
columnText.addText(paragraph);
paragraph.setFont(font);
columnText.addElement(paragraph);
columnText.go();
} catch (DocumentException e) {
e.printStackTrace();
}
}
//映射字段
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Mapping {
//pdf表单字段
String field();
//文字在上下方向的位置
float fontPosition() default 14f;
//文字大小
float fontSize() default 8.52f;
//是否需要做字体大小缩放
boolean flag() default false;
//字符串数量判断范围
int[] range() default {};
//根据字符串数量判断范围来取的字体大小
float[] rangeValue() default {};
//字体路径,这里是配置文件的下标
int fontPathIndex() default 0;
}
/**
* 将表单上的字段和实体值对应上,返回map,做替换
*
* @param obj pdf表单上的字段映射到实体对象
*/
public static void getReplaceMap(Object obj, Map<String, PdfAttributeVO> replaceMap) throws Exception {
if (obj == null) {
return;
}
Class<PdfUtils.Mapping> mapping = PdfUtils.Mapping.class;
Class<?> aClass = obj.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field field : declaredFields) {
field.setAccessible(true);
Object value = field.get(obj);
if (field.isAnnotationPresent(mapping)) {
PdfUtils.Mapping mapping1 = field.getAnnotation(mapping);
if (value != null && (field.getType() == String.class || field.getType() == Integer.class)) {
//判断是否检测字符串长度
replaceMap.put(mapping1.field(), fontZoom(mapping1, value.toString()));
}
}
}
}
/**
* 判断字体是否缩放
*
* @param mapping
*/
public static PdfAttributeVO fontZoom(PdfUtils.Mapping mapping, String value) {
float fontSize = mapping.fontSize();
float fontPosition = mapping.fontPosition();
if (mapping.flag()) {
int length = BaseUtils.strLength(value);
int[] range = mapping.range();
float[] rangeValue = mapping.rangeValue();
if (length > range[0] && length <= range[1]) {
fontSize = rangeValue[0];
//fontPosition = fontPosition + 0.6f;
} else if (length > range[1]) {
fontSize = rangeValue[1];
//fontPosition = fontPosition + 1.3f;
}
}
return new PdfAttributeVO(value.toString(), fontPosition, fontSize, mapping.fontPathIndex());
}
}
/**
* pdf模板的各种属性
*/
@Setter
@Getter
@ToString
public class PdfAttributeVO {
private String value;
/*
* 字体位置
*/
private float fontPosition;
/*
* 字体大小
*/
private float fontSize;
/*
* 字体路径,配置文件中的下标
*/
private int fontPathIndex;
public PdfAttributeVO() {
}
public PdfAttributeVO(String value, float fontPosition, float fontSize, int fontPathIndex) {
this.value = value;
this.fontPosition = fontPosition;
this.fontSize = fontSize;
this.fontPathIndex = fontPathIndex;
}
}
注解的用法
/**
* 永久居住地
* 地址过长,会根据长度缩小字体显示
* fill_12:pdf模板中代表地址的字段
* flag = true表示要做字体缩放
* range = {86, 104} 表示在三个区间缩放,range<=86 或 86<range<=104 或 range >104 ,对应的字体大小分别为:8(默认)/7/6
* fontPathIndex:这个参数还未使用,是防止不同的字段会用到不同的字体,我是吧这个放在配置文件中读取的。
*/
@PdfUtils.Mapping(field = "fill_12", flag = true, range = {86, 104}, rangeValue = {7, 6})
private String address;