我先去网上去搜了一波,发现网上很大一部分博客都是将如何导出pdf模板的,就是先制作一张pdf模板,把固定不变的地方先写好,把需要改变的地方留白并设置参数,然后在代码里为参数赋值就行了,这种方式很简单,代码量也很少,但是这种方式只适合导出格式和内容是固定的文件,而需求如果是内容不定,这很显然就不能用模板的方式,只好老老实实把报表的数据从头到尾一一导出来。
使用iText导出pdf表格
iText是一种生成PDF报表的Java组件,先把jar包下下来,maven依赖如下:
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.0.6</version>
</dependency>
按照惯例,先来生一个“Hello World”的文件,代码如下:
public class TestPdf {
public static void main(String[] args) throws Exception {
TestPdf pdf = new TestPdf();
String filename = "D:/Program Files/pdfTest/testTable3.pdf";
pdf.createPDF(filename);
System.out.println("打印完成");
}
public void createPDF(String filename) throws IOException {
Document document = new Document(PageSize.A4);
try {
PdfWriter.getInstance(document, new FileOutputStream(filename));
document.addTitle("example of PDF");
document.open();
document.add(new Paragraph("Hello World!"));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
} finally {
document.close();
}
}
}
这个没什么可说的,照着写就行了,不过我们导出的pdf大多数是以表格的形式,所以我就直接切入主题了,这里要用到一个很关键的类com.itextpdf.text.pdf.PDFPTable,先导出一张两行两列的表格
public static PdfPTable createTable(PdfWriter writer) throws DocumentException, IOException{
PdfPTable table = new PdfPTable(2);//生成一个两列的表格
PdfPCell cell;
int size = 15;
cell = new PdfPCell(new Phrase("one"));
cell.setFixedHeight(size);//设置高度
table.addCell(cell);
cell = new PdfPCell(new Phrase("two"));
cell.setFixedHeight(size);
table.addCell(cell);
cell = new PdfPCell(new Phrase("three"));
cell.setFixedHeight(size);
table.addCell(cell);
cell = new PdfPCell(new Phrase("four"));
cell.setFixedHeight(size);
table.addCell(cell);
return table;
}
public void createPDF(String filename) throws IOException {
Document document = new Document(PageSize.A4);
try {
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(filename));
document.addTitle("example of PDF");
document.open();
PdfPTable table = createTable(writer);
document.add(table);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
} finally {
document.close();
}
}
效果如下图
现在把我们的需求变得更苛刻一点,再增加一行,格子数为1,文字水平和垂直居中对齐,占两行高度,先贴代码再做解释
public static PdfPTable createTable(PdfWriter writer) throws DocumentException, IOException{
PdfPTable table = new PdfPTable(2);//生成一个两列的表格
PdfPCell cell;
int size = 15;
cell = new PdfPCell(new Phrase("one"));
cell.setFixedHeight(size);
table.addCell(cell);
cell = new PdfPCell(new Phrase("two"));
cell.setFixedHeight(size);
table.addCell(cell);
cell = new PdfPCell(new Phrase("three"));
cell.setFixedHeight(size);
table.addCell(cell);
cell = new PdfPCell(new Phrase("four"));
cell.setFixedHeight(size);
table.addCell(cell);
cell = new PdfPCell(new Phrase("five"));
cell.setColspan(2);//设置所占列数
cell.setFixedHeight(size*2);//设置高度
cell.setHorizontalAlignment(Element.ALIGN_CENTER);//设置水平居中
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);//设置垂直居中
table.addCell(cell);
return table;
}
这里有一个很坑的地方,就是每一行的长度要和初始化表格的长度相等,即要把整行给占满,否则后面的都不会打印出来,所以要用到setColspan()方法来合并列,接下来还有一个就是合并行的的方法setRowspan()。我们在导出pdf报表的实际操作中经常会有这样的需求
所以合并行的方法显得尤为重要,那就尝试一下用这个方法来合并行,
public static PdfPTable createTable(PdfWriter writer) throws DocumentException, IOException{
PdfPTable table = new PdfPTable(2);//生成一个两列的表格
PdfPCell cell;
int size = 20;
cell = new PdfPCell(new Phrase("one"));
cell.setFixedHeight(size);
table.addCell(cell);
cell = new PdfPCell(new Phrase("two"));
cell.setFixedHeight(size);
table.addCell(cell);
cell = new PdfPCell(new Phrase("three"));
cell.setFixedHeight(size);
table.addCell(cell);
cell = new PdfPCell(new Phrase("four"));
cell.setFixedHeight(size);
table.addCell(cell);
cell = new PdfPCell(new Phrase("five"));
cell.setColspan(1);//设置所占列数
cell.setRowspan(2);//合并行
cell.setFixedHeight(size*2);//设置高度
cell.setHorizontalAlignment(Element.ALIGN_CENTER);//设置水平居中
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);//设置垂直居中
table.addCell(cell);
cell = new PdfPCell(new Phrase("six"));
cell.setFixedHeight(size);
table.addCell(cell);
cell = new PdfPCell(new Phrase("seven"));
cell.setFixedHeight(size);
table.addCell(cell);
return table;
}
效果如下
事实上很简单,在原来的代码上将“five”这一行长度由2变为1,加上setRowspan(2)方法,再加上两行长度为1的cell就是上面的效果了。
解决中文字体的问题
在之前的例子中我用的都是英文字,但是中文字体是一个不得不解决的问题,根据我去网上搜集到的资料大抵有以下几个方法:
- 使用windows系统自带的字体,这种事最省事,也是最简单的,代码如下
public static PdfPTable createTable(PdfWriter writer) throws DocumentException, IOException{ PdfPTable table = new PdfPTable(2);//生成一个两列的表格 PdfPCell cell; int size = 20; Font font = new Font(BaseFont.createFont("C:/Windows/Fonts/SIMYOU.TTF",BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED)); cell = new PdfPCell(new Phrase("显示中文",font)); cell.setFixedHeight(size); cell.setColspan(2); table.addCell(cell); return table; }
- 使用itext-asian.jar中的中文字体,代码如下:
Font font = new Font(BaseFont.createFont( "STSongStd-Light" ,"UniGB-UCS2-H",BaseFont.NOT_EMBEDDED));
在这里可能会报Font 'STSongStd-Light' with 'UniGB-UCS2-H' is not recognized.的错,网上都说是语言包中的包名有问题,但是我把itexitextpdf包的版本从5.0.6改成5.4.3就解决了,我把语言包解压后发现路径是对的,应该是后面的版本已经把语言包中的包名解决了,那为什么5.0.6的版本不行呢,反正我到现在还没找到原因,如果有谁知道的话欢迎留言告知。
3.使用自己下载的资源字体。这种方法可以说是最麻烦的,但是也是最有效的。麻烦是因为自己要去下载字体文件,但是效果确是最好的,能够应对各种系统,各种状况,下面会提到,先看一下我的文件路径
然后是代码
Font font = new Font(BaseFont.createFont( "/simsun.ttf",BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED));
其实跟第一个是差不多的,只是一个是读系统的文件,一个是读项目自己的文件。