1.pom添加
<!-- pdf start --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.9</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext-asian</artifactId> <version>5.2.0</version> </dependency> <!-- pdf end -->
2.PDF样例图
3.PDF CLASS
一共包含三个CLASS
PDFDemo.java(Demo可直接使用)、PDFExport.java(导出封装)、PDFPageEvent.java(导出水印)
PDFDemo.java
package com.dhproject.utils.pdf;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
public class PDFDemo {
public static void main(HttpServletResponse response) {
demoDownload(response,"这个是水印");
}
/**
* 流式下载
* @param response
* @param aligned
*/
public static void demoDownload(HttpServletResponse response,String aligned){
try {
//设置请求返回类型
response.setHeader("Content-Disposition", "attachment; filename=pdffile.pdf");
OutputStream outputStream = response.getOutputStream();
InputStream inputStream = PDFExport.getTpl().getInputStream();
PdfReader reader = new PdfReader(inputStream);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PdfStamper ps = new PdfStamper(reader, bos);
//设置字体
final BaseFont font = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
ArrayList<BaseFont> fontList = new ArrayList<>();
fontList.add(font);
//提取表单
AcroFields s = ps.getAcroFields();
s.setSubstitutionFonts(fontList);
s.setFieldProperty("type", "textfont", font, null);
s.setFieldProperty("createTime", "textfont", font, null);
s.setFieldProperty("title", "textfont", font, null);
s.setField("type", "日报");
s.setField("createTime", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
s.setField("title", "这是title,模板画的位置框");
ps.setFormFlattening(true);
ps.close();
//*******************填充编辑好后的pdf**************
reader = new PdfReader(bos.toByteArray());
Rectangle pageSize = reader.getPageSize(1);
Document document = new Document(pageSize);
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
writer.setPageEvent(new PDFPageEvent(aligned));
// 打开文档
document.open();
PdfContentByte cbUnder = writer.getDirectContentUnder();
PdfImportedPage pageTemplate = writer.getImportedPage(reader, 1);
cbUnder.addTemplate(pageTemplate, 0, 0);
//这里进行数据填充
demoData(writer, document);
document.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
}
/**
*
* @param writer
* @param document
*/
public static void demoData(PdfWriter writer, Document document) throws IOException, DocumentException {
//新创建一个7列的表格
PdfPTable table = PDFExport.getTable(writer,new float[]{25,30,30,30,30,30,30});
//表头信息
//有7列数据将行全部合并 并且隐藏边框
PDFExport.setRowData(table,1,7,"XXXX表",13);
//有12行数据将第一列全部合并
PDFExport.setRowData(table,12,1,"合并列明");
PDFExport.setRowData(table,1,2,"XXXX表");
PDFExport.setRowData(table,1,4,"-");
PDFExport.setRowData(table,1,1,"时 间");
PDFExport.setRowData(table,1,1,"-");
PDFExport.setRowData(table,1,1,"时 间");
PDFExport.setRowData(table,1,1,"-");
PDFExport.setRowData(table,1,1,"时 间");
PDFExport.setRowData(table,1,1,"-");
PDFExport.setRowData(table,1,1,"时 间");
PDFExport.setRowData(table,1,1,"-");
PDFExport.setRowData(table,1,1,"时 间");
PDFExport.setRowData(table,1,1, "-");
PDFExport.setRowData(table,1,1,"时 间");
PDFExport.setRowData(table,1,1,"-");
PDFExport.setRowData(table,1,2,"时 间");
PDFExport.setRowData(table,1,4,"-");
PDFExport.setRowData(table,1,2,"时 间");
PDFExport.setRowData(table,1,4,"-");
PDFExport.setRowData(table,1,2,"时 间");
PDFExport.setRowData(table,1,1,"-");
PDFExport.setRowData(table,1,2,"时 间");
PDFExport.setRowData(table,1,1,"-");
PDFExport.setRowData(table,1,2,"时 间");
PDFExport.setRowData(table,1,4,"-");
PDFExport.setRowData(table,1,2,"时 间");
PDFExport.setRowData(table,1,4,"-");
PDFExport.setRowData(table,4,1,"时 间");
PDFExport.setRowData(table,1,2,"时 间");
PDFExport.setRowData(table,1,3,"-");
PDFExport.setRowData(table,1,2,"时 间");
PDFExport.setRowData(table,1,3,"-");
PDFExport.setRowData(table,1,2,"时 间");
PDFExport.setRowData(table,1,3,"-");
PDFExport.setRowData(table,1,2,"时 间");
PDFExport.setRowData(table,1,3,"-");
PDFExport.setRowData(table,8,1,"时 间");
PDFExport.setRowData(table,1,1,"时 间");
PDFExport.setRowData(table,1,1,"时 间");
PDFExport.setRowData(table,1,1,"时 间");
PDFExport.setRowData(table,1,1,"时 间");
PDFExport.setRowData(table,1,1,"时 间");
PDFExport.setRowData(table,1,1,"时 间");
PDFExport.setRowData(table,1,1,"时 间");
PDFExport.setRowData(table,1,1,"-");
PDFExport.setRowData(table,1,1,"-");
PDFExport.setRowData(table,1,1,"-");
PDFExport.setRowData(table,1,1,"-");
PDFExport.setRowData(table,1,1, "-");
PDFExport.setRowData(table,1,1,"时 间");
PDFExport.setRowData(table,1,1,"-");
PDFExport.setRowData(table,1,1,"-");
PDFExport.setRowData(table,1,1,"-");
PDFExport.setRowData(table,1,1,"-");
PDFExport.setRowData(table,1,1,"-");
PDFExport.setRowData(table,1,3,"时 间");
PDFExport.setRowData(table,1,3,"-");
PDFExport.setRowData(table,1,3,"时 间");
PDFExport.setRowData(table,1,3,"-");
PDFExport.setRowData(table,1,3,"时 间");
PDFExport.setRowData(table,1,3,"-");
PDFExport.setRowData(table,1,3,"时 间");
PDFExport.setRowData(table,1,3,"-");
PDFExport.setRowData(table,1,3,"时 间");
PDFExport.setRowData(table,1,3,"-");
document.add(table);
}
}
PDFExport.java(导出封装)
package com.dhproject.utils.pdf;
import com.dhproject.common.config.ProjectConfig;
import com.dhproject.common.utils.DateUtils;
import com.dhproject.common.utils.StringUtils;
import com.dhproject.common.utils.file.FileUploadUtils;
import com.dhproject.common.utils.uuid.Seq;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import org.springframework.core.io.ClassPathResource;
import java.io.*;
/**
* 模板第一页不占满,生成表格
*/
public class PDFExport {
/**
* 获取上传根目录
* @return
*/
public static final String getBaseDir(){
return ProjectConfig.getDownloadPath();
}
/**
* 获取模板
* @return
*/
public static final ClassPathResource getTpl(){
return new ClassPathResource("static/public/pdf/tpl.pdf");
}
/**
* 编码文件名
* @return
*/
public static final String extractFilename()
{
return StringUtils.format("pdf/{}/{}.{}", DateUtils.datePath(), Seq.getId(Seq.uploadSeqType),"pdf");
}
/**
* 获取URL
* @param fileName
* @return
*/
public static final String getPathFileName(String fileName) throws IOException {
return FileUploadUtils.getPathFileName(getBaseDir(), fileName);
}
/**
* 获取下载绝对路径
* @param fileName
* @return
*/
public static final java.io.File getAbsoluteFile(String fileName)
{
java.io.File desc = new java.io.File(getBaseDir() + java.io.File.separator + fileName);
if (!desc.exists())
{
if (!desc.getParentFile().exists())
{
desc.getParentFile().mkdirs();
}
try {
desc.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
return desc;
}
/**
* 单行数据写入
* //隐藏上边框
* // cell.disableBorderSide(1);
* //隐藏下边框
* // cell.disableBorderSide(2);
* //隐藏上、下边框
* //cell.disableBorderSide(3);
* //隐藏左边框
* //cell.disableBorderSide(4);
* //隐藏左、上边框
* //cell.disableBorderSide(5);
* //隐藏左、下边框
* //cell.disableBorderSide(6);
* //隐藏左、上、下边框
* //cell.disableBorderSide(7);
* //隐藏右边框
* //cell.disableBorderSide(8);
* //隐藏右、上边框
* // cell.disableBorderSide(9);
* //隐藏右、下边框
* //cell.disableBorderSide(10);
* //隐藏右、上、下边框
* //cell.disableBorderSide(11);
* //隐藏左、右边框
* //cell.disableBorderSide(12);//左右没了
* //隐藏上、左、右边框
* //cell.disableBorderSide(13);//只剩下
* //隐藏下、左、右边框
* //cell.disableBorderSide(14);//只剩上
* //隐藏全部
* //cell.disableBorderSide(15);//全没了
* @param table
* @param setRowspan
* @param setColspan
* @param text
* @param disableBorderSide
* @throws IOException
* @throws DocumentException
*/
public static void setRowData(PdfPTable table,int setRowspan,int setColspan,String text,int disableBorderSide) throws IOException, DocumentException {
//定义数据的字体
BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
Font textFont = new Font(baseFont, 10, Font.NORMAL);
PdfPCell heandCell = new PdfPCell();
heandCell.setRowspan(setRowspan);
heandCell.setColspan(setColspan);
heandCell.setMinimumHeight(30);
heandCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
heandCell.setHorizontalAlignment(Element.ALIGN_CENTER);
heandCell.setPhrase(new Phrase(text, textFont));
if(disableBorderSide>0){
heandCell.disableBorderSide(disableBorderSide);//隐藏边框
}
table.addCell(heandCell);
}
/**
* 单行数据写入
* @param table
* @param setRowspan
* @param setColspan
* @param text
* @throws IOException
* @throws DocumentException
*/
public static void setRowData(PdfPTable table,int setRowspan,int setColspan,String text) throws IOException, DocumentException {
setRowData(table,setRowspan,setColspan,text,-1);
}
/**
* 生成表格
* @param writer
* @param colls
* @return
*/
public static PdfPTable getTable(PdfWriter writer,float[] colls){
PdfPTable table = new PdfPTable(colls);
table.setTotalWidth(520);
table.setPaddingTop(0);
table.setLockedWidth(true);
table.setHorizontalAlignment(Element.ALIGN_CENTER);//居中
table.writeSelectedRows(0, -1, 500, 800, writer.getDirectContentUnder());
return table;
}
}
PDFPageEvent.java(导出水印)
package com.dhproject.utils.pdf;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import java.io.IOException;
public class PDFPageEvent extends PdfPageEventHelper {
//页眉
public String header = "";
//文档字体大小,页脚页眉最好和文本大小一致
public int presentFontSize = 10;
//文档页面大小,最好前面传入,否则默认为A4纸张
public Rectangle pageSize = PageSize.A4;
// 模板
public PdfTemplate total;
// 基础字体对象
public BaseFont bf = null;
// 利用基础字体生成的字体对象,一般用于生成中文文字
public Font fontDetail = null;
//水印文字
public String aligned = "";
/**
*Creates a new instance of PdfReportM1HeaderFooter 无参构造方法.
*/
public PDFPageEvent() {
}
/**
* Creates a new instance of PdfReportM1HeaderFooter 无参构造方法.
* @param aligned
*/
public PDFPageEvent(String aligned) {
this.aligned = aligned;
}
/**
*
* Creates a new instance of PdfReportM1HeaderFooter 构造方法.
* @param yeMei
* @param presentFontSize 数据体字体大小
* @param pageSize 页面文档大小,A4,A5,A6横转翻转等Rectangle对象
*/
public PDFPageEvent(String yeMei, int presentFontSize, Rectangle pageSize) {
this.header = yeMei;
this.presentFontSize = presentFontSize;
this.pageSize = pageSize;
}
/**
* Creates a new instance of PdfReportM1HeaderFooter 构造方法.
* @param presentFontSize 数据体字体大小
* @param pageSize 页面文档大小,A4,A5,A6横转翻转等Rectangle对象
*/
public PDFPageEvent( int presentFontSize, Rectangle pageSize) {
this.presentFontSize = presentFontSize;
this.pageSize = pageSize;
}
/**
* TODO 文档打开时创建模板
* @see com.itextpdf.text.pdf.PdfPageEventHelper#onOpenDocument(com.itextpdf.text.pdf.PdfWriter,com.itextpdf.text.Document)
* @param writer
* @param document
*/
public void onOpenDocument(PdfWriter writer, Document document) {
total = writer.getDirectContent().createTemplate(50, 50);// 共 页 的矩形的长宽高
}
/**
* TODO 关闭每页的时候,写入页眉,写入'第几页共'这几个字。
* @see com.itextpdf.text.pdf.PdfPageEventHelper#onEndPage(com.itextpdf.text.pdf.PdfWriter,com.itextpdf.text.Document)
* @param writer
* @param document
*/
public void onEndPage(PdfWriter writer, Document document) {
this.addPage(writer, document);
//加水印
this.addWatermark(writer);
}
/**
* 加分页
* @param writer
* @param document
*/
public void addPage(PdfWriter writer, Document document){
//设置分页页眉页脚字体
try {
if (bf == null) {
bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", false);
}
if (fontDetail == null) {
fontDetail = new Font(bf, presentFontSize, Font.NORMAL);// 数据体字体
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 1.写入页眉
// ColumnText.showTextAligned(writer.getDirectContent(),Element.ALIGN_LEFT, new Phrase(header, fontDetail),document.left(), document.top() + 20, 0);
// 2.写入前半部分的 第 X页/共
int pageS = writer.getPageNumber();
String foot1 = "第 " + pageS + " 页 /共";
//String foot1 = pageS +"/";
Phrase footer = new Phrase(foot1, fontDetail);
// 3.计算前半部分的foot1的长度,后面好定位最后一部分的'Y页'这俩字的x轴坐标,字体长度也要计算进去 = len
float len = bf.getWidthPoint(foot1, presentFontSize);
// 4.拿到当前的PdfContentByte
PdfContentByte cb = writer.getDirectContent();
// 5.写入页脚1,x轴就是(右margin+左margin + right() -left()- len)/2.0F
// 再给偏移20F适合人类视觉感受,否则肉眼看上去就太偏左了
// ,y轴就是底边界-20,否则就贴边重叠到数据体里了就不是页脚了;注意Y轴是从下往上累加的,最上方的Top值是大于Bottom好几百开外的。
ColumnText.showTextAligned(cb, Element.ALIGN_CENTER, footer, (document.rightMargin() + document.right() + document.leftMargin() - document.left() - len) / 2.0F , document.bottom() - 20, 0);
// 6.写入页脚2的模板(就是页脚的Y页这俩字)添加到文档中,计算模板的和Y轴,X=(右边界-左边界 - 前半部分的len值)/2.0F +
// len , y 轴和之前的保持一致,底边界-20
cb.addTemplate(total, (document.rightMargin() + document.right() + document.leftMargin() - document.left()) / 2.0F , document.bottom() - 20); // 调节模版显示的位置
}
/**
* 加水印
* @param writer
*/
public void addWatermark(PdfWriter writer) {
// 水印图片
/*
Image image;
try {
image = Image.getInstance("./web/images/001.jpg");
PdfContentByte content = writer.getDirectContentUnder();
content.beginText();
// 开始写入水印
for(int k=0;k<5;k++){
for (int j = 0; j <4; j++) {
image.setAbsolutePosition(150*j,170*k);
content.addImage(image);
}
}
content.endText();
} catch (IOException | DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
*/
BaseFont font = null;
try {
font = BaseFont.createFont("STSong-Light","UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
PdfGState gs = new PdfGState();
//添加透明度
gs.setFillOpacity(0.4f);
PdfContentByte content = writer.getDirectContentUnder();
content.beginText();
//水印颜色
content.setColorFill(new BaseColor(100, 100, 100));
content.setGState(gs);
//水印字体样式和大小
content.setFontAndSize(font, 26);
//插入水印 循环每页插入的条数
for (int j = 0; j < 1; j++) {
content.showTextAligned(Element.ALIGN_CENTER, aligned, 300, 600 * (j + 1), 30);
}
content.endText();
}
/**
* TODO 关闭文档时,替换模板,完成整个页眉页脚组件
* @see com.itextpdf.text.pdf.PdfPageEventHelper#onCloseDocument(com.itextpdf.text.pdf.PdfWriter,com.itextpdf.text.Document)
* @param writer
* @param document
*/
public void onCloseDocument(PdfWriter writer, Document document) {
// 7.最后一步了,就是关闭文档的时候,将模板替换成实际的 Y 值,至此,page x of y 制作完毕,完美兼容各种文档size。
total.beginText();
total.setFontAndSize(bf, presentFontSize);// 生成的模版的字体、颜色
String foot2 = " " + (writer.getPageNumber()) + " 页"; //页脚内容拼接 如 第1页/共2页
//String foot2 = String.valueOf(writer.getPageNumber()); //页脚内容拼接 如 1/2
total.showText(foot2);// 模版显示的内容
total.endText();
total.closePath();
}
}