最近在调试合同模板,发现原实现方式生成的pdf校验后差别很大,当反过来去修改html模板,却又很大程度上不能完全相同的pdf文档结构,这样就浪费很多开发时间去调N套html页面样式。决定直接用程序在标准pdf上操作,一劳永逸。
原实现方式:html模板+${参数}(手动加入) => 读入并生成pdf => 与业务方提供的标准pdf校验;
现实现方式:业务提供标准pdf+参数域(手动加入)=> 读入并生成pdf => 与业务方提供的标准pdf校验;
一、Adobe Acrobat X Pro为pdf设置域
我使用的是Adobe Acrobat X Pro 10.1.0版本(之前误下过Reader版本,只读版本不能在pdf上操作)
二、实现PDFUtil.java
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.imageio.ImageIO;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfImportedPage;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.PdfWriter;
/**
* @Title: PDFUtil.java
*/
public class PDFUtil {
private static BaseFont bfChinese;
private Document document;
private PdfReader reader;
private FileOutputStream out;
private ByteArrayOutputStream bos;
private PdfStamper stamper;
private AcroFields acroFields;
private int pageCount;
static {
try {
bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", true);
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @param filePath 生成的PDF的保存路径
* @throws FileNotFoundException
* @throws DocumentException
*/
public PDFUtil(String filePath) throws FileNotFoundException, DocumentException {
this.document = new Document(PageSize.A4, 50, 50, 50, 50);
PdfWriter.getInstance(document, new FileOutputStream(filePath));
document.open();
}
/**
* @param templatePath PDF文件模板路径
* @param targetPath 生成的PDF的路径
* @throws IOException
* @throws DocumentException
* @throws Exception
*/
public PDFUtil(String templatePath, String targetPath) throws IOException, DocumentException {
this.reader = new PdfReader(templatePath);// 读取pdf模板
this.pageCount = this.reader.getNumberOfPages();// 获取pdf文件总页数
this.bos = new ByteArrayOutputStream();// 存储pdf文件每页的内容
this.stamper = new PdfStamper(this.reader, this.bos);
this.acroFields = stamper.getAcroFields();// 获取表单域
this.out = new FileOutputStream(targetPath);
}
//~~~~~~~~~~~~~生成PDF文件~~~~~~~~~~~~~~~~
/**
* 创建table表格并添加到pdf中
* @param tableHeader 表头信息
* @param contents 表数据信息
* @param colsPercent 每一列所占宽度比例
* @param fontSize 表格字体大写
* @param hasBorder 是否有边框
* @throws DocumentException
* @throws IOException
*/
public void createTable(String[] tableHeader, String[][] contents, int[] colsPercent, float fontSize, boolean hasBorder) throws DocumentException, IOException {
Font fontsize = new Font(bfChinese, fontSize, Font.BOLD);
PdfPTable table = new PdfPTable(tableHeader.length);
table.setWidthPercentage(523 / 5.23f);
table.setWidths(colsPercent);
for (String header : tableHeader) {
PdfPCell cell = new PdfPCell(new Phrase(header, fontsize));
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
if (!hasBorder) {
cell.setHorizontalAlignment(Element.ALIGN_LEFT);
cell.setBorder(PdfPCell.NO_BORDER);
} else {
cell.setBackgroundColor(BaseColor.LIGHT_GRAY);
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
}
table.addCell(cell);
}
for (String[] content : contents) {
for (String header : content) {
PdfPCell cell = new PdfPCell(new Phrase(header, fontsize));
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
if (!hasBorder) {
cell.setHorizontalAlignment(Element.ALIGN_LEFT);
cell.setBorder(PdfPCell.NO_BORDER);
} else {
cell.setHorizontalAlignment(Element.ALIGN_CENTER);
}
table.addCell(cell);
}
}
this.document.add(table);
}
/**
* 创建段落并添加到表格
* @param paragraphContent 段落内容
* @param isIndentation 段落首行是否缩进
* @param fontSize 段落字体大小
* @param baseColor 段落字体颜色
* @version 1.0
* @throws DocumentException
*/
public void createParagraph(String paragraphContent, boolean isIndentation, float fontSize, BaseColor baseColor) throws DocumentException {
if (isIndentation) {
paragraphContent = " " + paragraphContent;
}
Font font = new Font(bfChinese, fontSize, Font.BOLD);
font.setColor(baseColor);
Paragraph paragraph = new Paragraph();
paragraph.setSpacingAfter(10f);
Chunk chunk = new Chunk(paragraphContent, font);
paragraph.add(chunk);
this.document.add(paragraph);
}
/**
* 创建段落
* @param paragraphContent 主题内容
* @param fontSize 字体大小
* @version 1.0
* @throws DocumentException
*/
public void createSubject(String paragraphContent, float fontSize) throws DocumentException {
Font font = new Font(bfChinese, fontSize, Font.BOLD);
Paragraph paragraph = new Paragraph();
paragraph.setAlignment(Element.ALIGN_CENTER);
paragraph.setSpacingAfter(10f);
Chunk chunk = new Chunk(paragraphContent, font);
paragraph.add(chunk);
this.document.add(paragraph);
}
/**
* 创建图片
* @Title createPicture
* @Description TODO
* @param picturePath
* @throws MalformedURLException
* @throws IOException
* @throws DocumentException
* void
*/
public void createPicture(String picturePath) throws MalformedURLException, IOException, DocumentException {
Image image = Image.getInstance(picturePath);
BufferedImage sourceImg = ImageIO.read(new FileInputStream(new File(picturePath)));
int imgWidth = sourceImg.getWidth();//
System.out.println("图片宽度像素 : " + imgWidth);
int imgHeight = sourceImg.getHeight();
System.out.println("图片高度像素 : " + imgWidth);
float heightPercent = 0.0f;
float widthPercent = 0.0f;
if (imgWidth > 500) {
widthPercent = 500.0f / imgWidth * 100;
}
if (imgHeight > 700) {
heightPercent = 700.0f / imgHeight * 100;
}
if (widthPercent > 0) {
if (heightPercent == 0.0) {
image.scalePercent(widthPercent);
} else if (heightPercent > 0) {
if (widthPercent < heightPercent) {
image.scalePercent(widthPercent);
} else {
image.scalePercent(heightPercent);
}
}
} else if (heightPercent != 0.0) {
image.scalePercent(heightPercent);
}
document.add(image);
}
/**
* 保存
*/
public void save() {
this.document.close();
}
// ~~~~~~~~~~~~~~套打/合并PDF~~~~~~~~~~~~~~~~~~~
/**
* 利用模板生成pdf
* @param values PDF填充域数据
* @throws Exception
*/
public void generatePDFByTemplate(Map<String, Object> values) throws Exception {
Document doc = new Document();
try {
setValues(values, acroFields);
stamper.setFormFlattening(true);// 如果为false那么生成的PDF文件还能编辑,一定要设为true
stamper.close();
PdfCopy copy = new PdfCopy(doc, out);
doc.open();
for (int i = 1; i <= pageCount; i++) {
PdfImportedPage page = copy.getImportedPage(new PdfReader(bos.toByteArray()), i);
copy.addPage(page);
}
} catch (IOException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
} finally {
doc.close();
this.close();
}
}
/**
* 填充数据
* @param values 填充域对应的填充数据
* @param acroFields pdf填充域
* @throws IOException
* @throws DocumentException
*/
private void setValues(Map<String, Object> values, AcroFields acroFields)
throws IOException, DocumentException {
BaseFont bf = BaseFont.createFont("STSong-Light,Bold", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
Set<String> keys = acroFields.getFields().keySet();
Iterator<String> it = keys.iterator();
while (it.hasNext()) {
String name = it.next();
acroFields.setFieldProperty(name, "textfont", bf, null);
acroFields.setField(name, String.valueOf(values.get(name)));
}
}
/**
* 关闭流
*/
private void close() {
if (this.reader != null) {
this.reader.close();
}
if (this.out != null) {
try {
this.out.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (this.bos != null) {
try {
this.bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
/**
* 多个PDF合并功能
* @param pdfPaths 要进行合并的多个PDF路径
* @param savepath 生成的新PDF路径
* @throws Exception
*/
public static boolean mergePDF(String[] pdfPaths, String savepath) throws Exception {
if (pdfPaths.length > 1) {
try {
Document document = new Document(new PdfReader(pdfPaths[0]).getPageSize(1));
PdfCopy copy = new PdfCopy(document, new FileOutputStream(savepath));
document.open();
for (int i = 0; i < pdfPaths.length; i++) {
PdfReader reader = new PdfReader(pdfPaths[i]);
int n = reader.getNumberOfPages();
for (int j = 1; j <= n; j++) {
document.newPage();
PdfImportedPage page = copy.getImportedPage(reader, j);
copy.addPage(page);
}
}
document.close();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
} catch (DocumentException e) {
e.printStackTrace();
return false;
}
} else {
throw new Exception("要进行合成的合同文件数量必须不少于两个");
}
}
}
public static void main(String[] args) {
Map<String, Object> values = new HashMap<String, Object>();
values.put( "A", "1.234%/月");
values.put( "B", "比亚迪" );
values.put( "C", "CF0038803" );
values.put( "D", "29761.60" );
try {
PDFUtil util = new PDFUtil(source, target);
util.generatePDFByTemplate(values);
} catch (Exception e) {
e.printStackTrace();
}
}
注意:关于pdf模板中复选框类型的域,当模板类属性中导出值设置的与程序赋值相同时为选中状态;反之则不被选中。