<div id="content_views" class="markdown_views prism-atom-one-dark">
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
</svg>
<blockquote>
由于在工作中遇到了合同套打的需求,于是在之前 pdfBox获取关键字坐标 的基础上实现了,根据 pdf 模版来套打合同。
PDF合同表单生成
首先,需要确定合同是否可以套打,如果合同格式固定,需要添加数据内容一般不发生变化,则可以通过 生成表单域 来创建合同模板。
如何生成表单域
- 使用 pdf 工具打开 合同模板.pdf,此处使用的是 Adobe Acrobat,当然也可以使用其他pdf阅读器。
- 点击菜单 准备表单 ,然后如下图所示,会自动将下划线变为 文本域,如果没有也可以手动添加。
- 同时也会有其他如 单选按钮、复选框等表单控件。
生成模版
具体实现的代码如下所示:
工具方法
/**
* 填充pdf模板的表单域生成新的pdf文件
*
* @param template
* @param font
* @param data
* @param target
* @param formFlattening
* @return
* @throws IOException
* @throws DocumentException
*/
public static File generatePDF(String template, String font, JSONObject data, File target, boolean formFlattening)
throws IOException, DocumentException {
PdfReader reader = new PdfReader(template);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
BaseFont bf = BaseFont.createFont(font, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
PdfStamper ps = new PdfStamper(reader, bos);
AcroFields fields = ps.getAcroFields();
ArrayList<BaseFont> fontList = new ArrayList<BaseFont>();
fontList.add(bf);
fields.setSubstitutionFonts(fontList);
for (String key : data.keySet()) {
String value = data.getString(key);
// 设置复选框以及单选框
if (AcroFields.FIELD_TYPE_CHECKBOX == fields.getFieldType(key)
|| AcroFields.FIELD_TYPE_RADIOBUTTON == fields.getFieldType(key)) {
// 为单选框赋值,需要设置
if("On".equals(value))
fields.setField(key, value, true);
else
fields.setField(key, value, false);
} else {
// 为字段赋值,注意字段名称是区分大小写的
fields.setField(key, value);
}
}
// 表单域是否能再次赋值
ps.setFormFlattening(formFlattening);
ps.close();
reader.close();
FileUtils.writeByteArrayToFile(target, bos.toByteArray());
return target;
}
// 为单选框赋值,需要设置
if("On".equals(value))
fields.setField(key, value, true);
else
fields.setField(key, value, false);
此处设置在模板中 如下图所示:
水印实现
有些时候,需要为生成的pdf文件增加水印,具体的方法如下:
/* * 水印生成 * @return * @throws * @author TomatoYan */ public void waterMark(PdfReader pdfReader, PdfStamper stamper, String waterMark) throws IOException, DocumentException { int interval = 50; BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED); Rectangle pageRect = null; PdfGState gs = new PdfGState(); //设置透明度 gs.setFillOpacity(0.3f); gs.setStrokeOpacity(0.4f); int total = pdfReader.getNumberOfPages() + 1; JLabel label = new JLabel(); FontMetrics metrics; int textH = 0; int textW = 0; label.setText(waterMark); metrics = label.getFontMetrics(label.getFont()); textH = metrics.getHeight(); textW = metrics.stringWidth(label.getText());
PdfContentByte under; for (int i = 1; i < total; i++) { pageRect = pdfReader.getPageSizeWithRotation(i); under = stamper.getOverContent(i); under.saveState(); under.setGState(gs); under.beginText(); under.setFontAndSize(base, 20); //设置水印颜色 under.setColorFill(BaseColor.BLACK); // 水印文字成45度角倾斜 for (int height = interval + textH; height < pageRect.getHeight(); height = height + textH * 3) { for (int width = interval + textW; width < pageRect.getWidth() + textW; width = width + textW * 2) { under.showTextAligned(Element.ALIGN_LEFT, waterMark, width - textW, height - textH, 45); break; } break; } // 添加水印文字 under.endText(); }
}
水印生成可以放在工具方法中。
主函数
/**
参数:
模板路径:templateDir
模版文件名称:合同模板.pdf
字体文件名称:simkai.ttf
生产文件路径:outDir
模版填充数据(表单域内容):键值对 data()
*/
public static void main(String[] args) throws IOException, DocumentException {
String templateDir = "D:/testpdf/";
String outDir = "D:/testpdf/";
generatePDF(templateDir + "合同模板.pdf", templateDir + "simkai.ttf", data(),
new File(outDir + "result_" + System.currentTimeMillis() + ".pdf"), true);
}
表单域内容
public static JSONObject data() {
JSONObject data = new JSONObject();
data.put("name", "张三");
data.put("bank_name", "番茄银行");
data.put("amt", "1000.00");
return data;
}
表单域的填充内容为 Json格式的键值对,键值代表的是在模板PDF中表单的名称。
至此pdf的合同模板就完成了。可以结合之前的 pdfBox获取关键字坐标 文章为pdf合同增加电子签章。