我之前写了一篇文章讲了itext的基础操作常用方法(https://my.oschina.net/itazi/blog/1812042),但是有时涉及到比较复杂的PDF生成,手工操作就很复杂,可以使用模板生成方式。有时还要涉及合并等,于是将用到的略作记载。
首先要用到的依赖:
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.10</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.54</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.lowagie/itext -->
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>2.1.7</version>
</dependency>
首先我们来讲讲制作模板,可以查考这个https://www.cnblogs.com/LUA123/p/5108007.html,这里面感觉很详细了,另外特别提到一点,由于要用Adobe制作的话需要购买,所以作为代提方案,你还可以使用福晰PDF软件制作,也可以实现相同效果,亲测可用。制作好模板之后接下来就是根据模板生成PDF,代码如下:
/**
* 根据模版生成pdf
*
* @param templateFile 模板路径
* @param targetFile 将要生成的pdf路径
* @param fieldMap 填充字段
*/
public static Boolean createPdfFromTemplate(String templateFile, String targetFile, Map<String, String> fieldMap) {
Boolean retValue = false;
try {
/* 打开已经定义好字段以后的pdf模板 */
com.itextpdf.text.pdf.PdfReader reader = new com.itextpdf.text.pdf.PdfReader(templateFile);
/* 将要生成的目标PDF文件名称 */
PdfStamper stamp = new PdfStamper(reader, new FileOutputStream(targetFile));
PdfContentByte under = stamp.getUnderContent(1);
/* 使用中文字体 */
BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",BaseFont.NOT_EMBEDDED);
Font fontChinese = new Font(bf, 12, Font.NORMAL);
/* 取出报表模板中的所有字段 */
AcroFields form = stamp.getAcroFields();
form.addSubstitutionFont(bf);//这行必须添加否则中文不显示
/* 为字段赋值,注意字段名称是区分大小写的 */
for (String key : fieldMap.keySet()) {
form.setField(key, fieldMap.get(key));
}
stamp.setFormFlattening(true);
/* 必须要调用这个,否则文档不会生成的 */
stamp.close();
reader.close();
retValue = true;
} catch (DocumentException de) {
System.err.println(de.getMessage());
} catch (IOException ioe) {
System.err.println(ioe.getMessage());
} finally {
return retValue;
}
}
测试代码如下(注意:关于路径你要改成你自己本地的路径,要正确,特别是你制作模板的路径,要填写正确,要不然模板都找不到,谈何制作pdf呢):
Map<String, String> map = new HashMap<String, String>();
map.put("projectname", "map填充");//这里的projectname是你制作PDF时填入的字段
map.put("applyname", "shishui??");//同上
System.out.println(PdfUtils.createPdfFromTemplate("/Users/hxm/Documents/pdf/project_detail_model_wrapper_2.pdf",
"/Users/hxm/Documents/pdf/tempalte.pdf", map));
结果:
填充成功。
接下来我们来谈谈合并PDF,有时候你有多个PDF,正好需要合并成一个,你可能就需要这个方法了(部分内容参考:https://www.cnblogs.com/pocketbook/p/6427579.html)。代码如下:
/**
* 合并pdf
* @param files 要合并的pdf文件
* @param newfile 生成的文件名
* @return
*/
public static Boolean mergePdfFiles(String[] files, String newfile) {
Boolean retValue = false;
Document document = null;
try {
document = new Document(new PdfReader(files[0]).getPageSize(1));
PdfCopy copy = new PdfCopy(document, new FileOutputStream(newfile));
document.open();
for (int i = 0; i < files.length; i++) {
PdfReader reader = new PdfReader(files[i]);
int n = reader.getNumberOfPages();
for (int j = 1; j <= n; j++) {
document.newPage();
PdfImportedPage page = copy.getImportedPage(reader, j);
copy.addPage(page);
}
}
retValue = true;
} catch (Exception e) {
e.printStackTrace();
} finally {
document.close();
}
return retValue;
}
测试代码(路径要能和你自己的pdf地址对上):
String[] files = { "/Users/hxm/Documents/pdf/audit.pdf", "/Users/hxm/Documents/pdf/template.pdf"};
String savepath = "/Users/hxm/Documents/pdf/newmerge"+ System.currentTimeMillis() +".pdf";
PdfTemplateUtil.mergePdfFiles(files, savepath);
结果:
合并成功。
如果已经存在一个PDF,你想添加某些属性再生成一个新的PDF文件,如增加水印,加密,设置权限等,这时你就又需要一个方法了,代码如下(其中部分参考一个外国小哥的代码):
public static Boolean addAttributeToPdfFile(String strFileLocation, String strFileLocationOut) {
Boolean isSuccess = false;
try {
// read existing pdf
com.itextpdf.text.pdf.PdfReader reader = new com.itextpdf.text.pdf.PdfReader(strFileLocation);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(strFileLocationOut));
stamper.setEncryption(null, null, PdfWriter.ALLOW_PRINTING, PdfWriter.ENCRYPTION_AES_128);
// text watermark
Font FONT = new Font(Font.FontFamily.HELVETICA, 34, Font.BOLD, new GrayColor(0.5f));
// Phrase p = new Phrase("Memorynotfound (watermark)", FONT);
BaseFont bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
Font font = new Font(bfChinese, 30, Font.BOLD, new GrayColor(0.90f));
Phrase p = new Phrase("(watermark)", font);
// image watermark
// Image img = Image.getInstance(imgUrl);
// float w = img.getScaledWidth();
// float h = img.getScaledHeight();
// properties
PdfContentByte over;
Rectangle pagesize;
float x, y;
// loop over every page
int n = reader.getNumberOfPages();
for (int i = 1; i <= n; i++) {
// get page size and position
// pagesize = reader.getPageSizeWithRotation(i);
// x = (pagesize.getLeft() + pagesize.getRight()) / 2;
// y = (pagesize.getTop() + pagesize.getBottom()) / 2;
over = stamper.getUnderContent(i);
over.saveState();
// set transparency
PdfGState state = new PdfGState();
state.setFillOpacity(0.2f);
over.setGState(state);
// ColumnText.showTextAligned(over, Element.ALIGN_CENTER, p, x, y, 0);
//控制水印的显示效果,可以自己调整看看
float beginPositionX = 10, beginPositionY = 70, distance = 175;
for (int i2 = 0; i2 < 4; i2++) {
for (int j = 0; j < 4; j++) {
ColumnText.showTextAligned(over, Element.ALIGN_LEFT,
p, beginPositionX + distance*i2, beginPositionY + distance*j, 25);
}
}
// add watermark text and image
// if (i % 2 == 1) {
// ColumnText.showTextAligned(over, Element.ALIGN_CENTER, p, x, y, 0);
// } else {
// over.addImage(img, w, 0, 0, h, x - (w / 2), y - (h / 2));
// }
over.restoreState();
}
stamper.close();
reader.close();
isSuccess = true;
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
return isSuccess;
}
}
测试代码:
PdfUtils.addAttributeToPdfFile("/Users/hxm/Documents/pdf/newmerge1527731906780.pdf",
"/Users/hxm/Documents/pdf/mytemplate.pdf");
效果如下:
另外注意点是我上面的几个方法都放在一个叫PdfUtils的类中。大概结构如下:
package pdf;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.GrayColor;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfGState;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.PdfWriter;
import com.lowagie.text.Document;
import com.lowagie.text.pdf.PdfCopy;
import com.lowagie.text.pdf.PdfImportedPage;
import com.lowagie.text.pdf.PdfReader;
/**
* pdf工具类
*
* @author hxm
*/
public class PdfUtils {
}
上面就是这些常用方法了,因为要用到本人也网上搜索了很多资料,看了很多,最后特地整理一下,也以备自己以后查询之用处。