一、背景
由于业务需求,我需要将程序的部分数据生成PDF报告以供下载浏览
二、实现方式
- 设计一个pdf模板,可以通过pdf编辑工具编辑模板、市面上支持编辑表单的pdf编辑都可以,如Adobe Acrobat DC、万兴PDF、迅捷PDF等等
- 通过表单编辑设置表单单元格对应名,后续程序赋值用
- 可以设置文本水平居中(但目前的程序都不支持设置垂直居中,后续想要实现的话需要从程序下手)
- 设置好文本则可以开始编写程序赋值(核心代码)
//创建A4大小的文档
Document document = new Document(PageSize.A4);
document.open();
//读取现有模板内容(templateFile为模板路径)
PdfReader reader = new PdfReader(FileUtil.path(templateFile));
//创建输出流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
//实例化PdfStamper准备编辑pdf内容
PdfStamper ps = new PdfStamper(reader, bos);
//获取表单所有元素
AcroFields fields = ps.getAcroFields();
//设置具体字体格式编码,不设置的话中文可能会不显示
BaseFont baseFont = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
/添加替换元素
fields.addSubstitutionFont(baseFont);
//data是一个Map<String,String> 主要存储 key 表单模板中的单元格名 value为想要赋的值,遍历
for (String key : data.keySet()) {
String value = data.get(key);
//个人逻辑,参考即可,对于特定的表单元素设置不同的字体格式大小
if("r_game".equals(key)) {
//设置格式
fields.setFieldProperty(key, "textsize", 35f, null);
}else if("i_title".equals(key)) {
fields.setFieldProperty(key, "textsize", 20f, null);
}else if ("r_image".equals(key)){
if (!StringUtils.isEmpty(value)){
//设置添加图片
addImageToPdf(fields, ps,value);
continue;
}
}else if ("r_project_name".equals(key)||"r_header".equals(key)||"r_appraisal".equals(key)){
//将文本垂直居中添加
addTextToPdfCenter(fields,ps,value,key,baseFont);
continue;
}
else {
fields.setFieldProperty(key, "textsize", defaultSize, null);
}
fields.setField(key, value);
}
//生成的文档可编辑
ps.setFormFlattening(true);
//关闭操作
ps.close();
//将编辑后的文件输出到outputFile路径下
OutputStream fos = new FileOutputStream(FileUtil.path(outputFile));
fos.write(bos.toByteArray());
bos.close();
fos.close();
document.close();
- 图片填充到表模板
private static void addImageToPdf(AcroFields form, PdfStamper stamper, String filePath) throws DocumentException, IOException {
// 通过域名获取所在页和坐标,左下角为起点
int pageNo = form.getFieldPositions("r_image").get(0).page;
Rectangle signRect = form.getFieldPositions("r_image").get(0).position;
float x = signRect.getLeft()+signRect.getRight();
float y = signRect.getTop();
// 读图片
Image image = Image.getInstance(filePath);
// 获取操作的页面
PdfContentByte under = stamper.getOverContent(pageNo);
// 根据域的大小缩放图片
image.scaleToFit(signRect.getWidth()/4, signRect.getHeight()/4);
// 添加图片并设置位置(个人通过此设置使得图片垂直水平居中,可参考,具体情况已实际为准)
image.setAbsolutePosition(x/2-image.getWidth()/2, y/2+image.getHeight());
under.addImage(image);
}
- 文本未垂直居中效果如下
将文本设置为垂直居中
通过获取模板位置,然后重构表单单元格实现垂直居中
private static void addTextToPdfCenter(AcroFields form, PdfStamper stamper, String text,String fieldName,BaseFont baseFont){
// 通过模板表单单元格名获取所在页和坐标,左下角为起点
int pageNo = form.getFieldPositions(fieldName).get(0).page;
Rectangle signRect = form.getFieldPositions(fieldName).get(0).position;
// 获取操作的页面
PdfContentByte contentByte = stamper.getOverContent(pageNo);
//创建表单
PdfPTable table = new PdfPTable(1);
//获取当前模板表单宽度
float totalWidth = signRect.getRight() - signRect.getLeft() - 1;
//设置新表单宽度
table.setTotalWidth(totalWidth);
//设置中文格式
Font font = new Font(baseFont);
//设置单元格格式
PdfPCell cell = new PdfPCell(new Phrase(text,font));
//设置单元格高度
cell.setFixedHeight(signRect.getTop()-signRect.getBottom()-1);
cell.setBorderWidth(0);
//设置垂直居中
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
//设置水平居中
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
//添加到表单中
table.addCell(cell);
//写入pdf
table.writeSelectedRows(0, -1, signRect.getLeft(), signRect.getTop(), contentByte);
}
效果如下