最近公司要求后端实现PDF导出功能,并且还要要求能够加盖水印。网上搜寻了一下,大多帖子比较老旧,并且随着开发包的版本更新,都不能用,所以有了这篇博客。
生成PDF所用到的jar包主要是itext,现在这个包在中央仓库的最新版是5.5.13.1
不过官网主打的确是7版本:点击进入官网地址,好像是要收费的,所以还是用5版本吧
下面是用的jar包和版本,解释一下itextpdf是核心包,itext-asian是为了中文支持,如果不引用该包将无法显示中文
com.itextpdf
itextpdf
5.5.13
com.itextpdf
itext-asian
5.2.0
上代码:
public static final String BASE_PDF_PATH = "C:\\Users\\xxx\\Desktop\\";
public static void createDPFTable(String fileName, Map fieldMap, List dataList) {
Document document = new Document(PageSize.A4);
document.setMargins(10, 10, 30, 30);
FileOutputStream outputStream = null;
try {
outputStream = new FileOutputStream(BASE_PDF_PATH + fileName + ".pdf");
PdfWriter pdfWriter = PdfWriter.getInstance(document, outputStream);
pdfWriter.setPdfVersion(PdfWriter.VERSION_1_7);
//自定义字体
BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
Font font = new Font(bf, 8);
pdfWriter.setPageEvent(new PageEvent());
//编辑文档
document.open();
Set> entries = fieldMap.entrySet();
PdfPTable pdfTable = createTable(fieldMap, font);
if (dataList != null && dataList.size() > 0) {
//创建数据
for (T t : dataList) {
for (Map.Entry entry : entries) {
String value = entry.getValue();
Object methodVal = getMethodVal(t.getClass(), t, value);
if (methodVal == null) {
methodVal = "";
}
PdfPCell pdfPCell = new PdfPCell(new Phrase(methodVal.toString(), font));
pdfPCell.setHorizontalAlignment(Paragraph.ALIGN_CENTER);
pdfPCell.setVerticalAlignment(Paragraph.ALIGN_CENTER);
pdfTable.addCell(pdfPCell);
}
}
}
document.add(pdfTable);
document.close();
System.out.println("创建成功!");
} catch (DocumentException | IOException e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static PdfPTable createTable(Map fieldMap, Font font) {
PdfPTable pdfTable = new PdfPTable(fieldMap.size());
pdfTable.setSpacingBefore(10);
pdfTable.setHorizontalAlignment(Element.ALIGN_CENTER);
pdfTable.setSplitRows(false);
Set> entries = fieldMap.entrySet();
for (Map.Entry entry : entries) {
PdfPCell pdfPCell = new PdfPCell(new Phrase(entry.getKey(), font));
pdfPCell.setHorizontalAlignment(Paragraph.ALIGN_CENTER);
pdfPCell.setVerticalAlignment(Paragraph.ALIGN_CENTER);
pdfTable.addCell(pdfPCell);
}
return pdfTable;
}
然后就是添加页码和水印了,因为itext有个pageEvent的概念,意思是在我们生成一个pdf文档的一些时机,我们可以在这些点做一些自己的额外操作。具体做法是建一个类实现PdfPageEventHelper类,并重写里面的方法即可,上代码:
public class PageEvent extends PdfPageEventHelper {
private PdfTemplate tpl;
private BaseFont bf;
@Override
public void onOpenDocument(PdfWriter writer, Document document) {
tpl = writer.getDirectContent().createTemplate(100, 100);
try {
bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
} catch (DocumentException | IOException e) {
e.printStackTrace();
}
}
@Override
public void onStartPage(PdfWriter writer, Document document) {
super.onStartPage(writer, document);
addTextWaterMark(writer, bf, 50, 0.1f, "传说中的水印");
//addImageWaterMark(writer, 0.5f, "C:\\Users\\xxx\\Desktop\\waterMark.png");
}
@Override
public void onEndPage(PdfWriter writer, Document document) {
//每一次在页面写完结束后,计算一下页码,
PdfContentByte cb = writer.getDirectContent();
String text = "第" + writer.getPageNumber() + " 页,共 ";
float textSize = bf.getWidthPoint(text, 9);
float textBase = document.bottom();
cb.beginText();
cb.setFontAndSize(bf, 9);
cb.setTextMatrix(document.left(), textBase);
cb.showText(text);
cb.endText();
cb.addTemplate(tpl, document.left() + textSize, textBase);
}
@Override
public void onCloseDocument(PdfWriter writer, Document document) {
//文档关闭后,计算一下总页数
tpl.beginText();
tpl.setFontAndSize(bf, 9);
tpl.setTextMatrix(0, 0);
tpl.showText((writer.getPageNumber() - 1) + " 页");
tpl.endText();
}
//文字水印
private static void addTextWaterMark(PdfWriter pdfWriter, BaseFont bf, int fontSize, float fillOpacity, String text) {
//添加水印
PdfContentByte contentUnder = pdfWriter.getDirectContentUnder();
contentUnder.beginText();
contentUnder.setColorFill(BaseColor.BLUE);
contentUnder.setFontAndSize(bf, fontSize);
//设置水印透明度
PdfGState pdfGState = new PdfGState();
pdfGState.setFillOpacity(fillOpacity);
contentUnder.setGState(pdfGState);
Random random = new Random();
for (float initX = random.nextInt(200) - 150; initX <= PageSize.A4.getWidth() + 30; initX += 300) {
for (float initY = random.nextInt(200) - 150; initY <= PageSize.A4.getHeight() + 30; initY += 250) {
contentUnder.showTextAligned(Element.ALIGN_CENTER, text, initX, initY, 30);
}
}
contentUnder.endText();
}
//图像水印
private static void addImageWaterMark(PdfWriter pdfWriter, float fillOpacity, String imgUrl) {
PdfContentByte contentUnder = pdfWriter.getDirectContentUnder();
try {
Image image = Image.getInstance(imgUrl);
image.scaleToFit(200, 150);
PdfGState pdfGState = new PdfGState();
pdfGState.setFillOpacity(fillOpacity);
contentUnder.setGState(pdfGState);
Random random = new Random();
for (float initX = random.nextInt(200) - 150; initX <= PageSize.A4.getWidth() + 40; initX += 300) {
for (float initY = random.nextInt(200) - 150; initY <= PageSize.A4.getHeight() + 40; initY += 300) {
image.setAbsolutePosition(initX, initY);
image.setRotationDegrees(30);
contentUnder.addImage(image);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
下面是运行效果: