2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > Java通过itextpdf生成PDF

Java通过itextpdf生成PDF

时间:2022-12-31 21:29:07

相关推荐

Java通过itextpdf生成PDF

通过Java生成PDF我用的是itextpdf,需要引入的pop坐标如下

<!-- /artifact/com.itextpdf/itextpdf --><dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13</version></dependency><!-- /artifact/com.itextpdf/itext-asian --><dependency><groupId>com.itextpdf</groupId><artifactId>itext-asian</artifactId><version>5.2.0</version></dependency>

我是通过接口的方式在浏览器显示pdf,类继承PdfPageEventHelper 这个类

@RequestMapping@RestControllerpublic class GetPdfByItextPdf extends PdfPageEventHelper {}

重写自己需要的方法,我重谢了三个方法

1:onOpenDocument 我用这个方法初始化PdfTemplate,这个是文件打开的时候就会触发的方法,所以在这个里面初始化一些变量

2:onEndPage 这个是一页PDF完毕的时候会触发的方法,比如我需要再PDF里面加页眉,加当前是第几页的时候,在这个方法里面进行。

3:onCloseDocument 这个是pdf制作完成了的时候,关闭之前触发,这个可以帮我们加上一些内容,比如在每页都弄上这个pdf一共有多少页。目前我用到的是这三个方法,并且感觉比较好用。

先写个接口,再来看是怎么触发上面三个方法的

我是通过接口的HttpServletResponse 输出PDF 的,所以可以浏览器调接口看,也可以直接将pdf文件保存到本地

@GetMapping("createPdf")public void createPdf(HttpServletResponse response) throws IOException, DocumentException {// 定义一个pdf 的文件,这个文件包括PDF 的大小,内容所在的位置,这个跟网页的 margin 是一样的,自己设置大小Document document = new Document(PageSize.A4, 80, 40, 90, 50);// 对返回的response 进行设置一下 这样的话浏览器就会识别是PDF文件response.setContentType("application/pdf");// 得到一个PDF的输出流 这里可以换成是电脑上的具体路径// PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("E:\\myTestPdf.pdf"));PdfWriter pdfWriter = PdfWriter.getInstance(document, response.getOutputStream());document.open();document.close();}

在document.open() 和document.close() 中间添加内容就可以了,我这里没加东西,所以会报个错。

-08-17 16:35:37.210 ERROR 11316 --- [io-55555-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is ExceptionConverter: java.io.IOException: The document has no pages.] with root causejava.io.IOException: The document has no pages.at com.itextpdf.text.pdf.PdfPages.writePageTree(PdfPages.java:112) ~[itextpdf-5.5.13.jar:5.5.13]

但是这样的话就不会触发我上面提到的三个方法,要想使onOpenDocument 这个方法生效,需要pdfWriter 上面添加事件

@GetMapping("createPdf")public void createPdf(HttpServletResponse response) throws IOException, DocumentException {// 定义一个pdf 的文件,这个文件包括PDF 的大小,内容所在的位置,这个跟网页的 margin 是一样的,自己设置大小Document document = new Document(PageSize.A4, 80, 40, 90, 50);// 对返回的response 进行设置一下 这样的话浏览器就会识别是PDF文件response.setContentType("application/pdf");// 得到一个PDF的输出流 这里可以换成是电脑上的具体路径// PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("E:\\myTestPdf.pdf"));PdfWriter pdfWriter = PdfWriter.getInstance(document, response.getOutputStream());pdfWriter.setPageEvent(new GetPdfByItextPdf());document.open();document.close();}

只有设置了 pdfWriter.setPageEvent(new GetPdfByItextPdf());这个才能使onOpenDocument onEndPage onCloseDocument 这三个方法生效。

在onOpenDocument 这个里面设置文件打开时要完成的动作,初始化方法

package mybatisplus.google.pdf;import com.itextpdf.text.Document;import com.itextpdf.text.DocumentException;import com.itextpdf.text.ExceptionConverter;import com.itextpdf.text.PageSize;import com.itextpdf.text.pdf.BaseFont;import com.itextpdf.text.pdf.PdfPageEventHelper;import com.itextpdf.text.pdf.PdfTemplate;import com.itextpdf.text.pdf.PdfWriter;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/*** 包: mybatisplus.google.pdf* 作者: climber.luo* 时间:.08.17 15:14*/@RequestMapping@RestControllerpublic class GetPdfByItextPdf extends PdfPageEventHelper {public PdfTemplate tpl;public BaseFont bf;@GetMapping("createPdf")public void createPdf(HttpServletResponse response) throws IOException, DocumentException {// 定义一个pdf 的文件,这个文件包括PDF 的大小,内容所在的位置,这个跟网页的 margin 是一样的,自己设置大小Document document = new Document(PageSize.A4, 80, 40, 90, 50);// 对返回的response 进行设置一下 这样的话浏览器就会识别是PDF文件response.setContentType("application/pdf");// 得到一个PDF的输出流 这里可以换成是电脑上的具体路径// PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("E:\\myTestPdf.pdf"));PdfWriter pdfWriter = PdfWriter.getInstance(document, response.getOutputStream());pdfWriter.setPageEvent(new GetPdfByItextPdf());document.open();// pdf 页面加点东西 加入一行字document.add(new Paragraph("这是一个测试",new com.itextpdf.text.Font(bfChinese, 18, com.itextpdf.text.Font.BOLD)));document.close();}@Overridepublic void onOpenDocument(PdfWriter writer, Document document) {super.onOpenDocument(writer, document);try {// 初始化模板,模板的宽和高自己设定, 初始化一个字体,这两个参数其实可以用代码块实现tpl = writer.getDirectContent().createTemplate(100, 100);bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);} catch (Exception e) {throw new ExceptionConverter(e);}}}

再来设置一下PDF的页眉和页脚,这个我是在onEndPage 这个方法中实现的,可以在这里划线和引入图片logo啥的,我的项目里是需要引入logo和公司的基本信息放在页眉,这里我简单的画两个线。

@Overridepublic void onEndPage(PdfWriter writer, Document document) {super.onEndPage(writer, document);float center = document.getPageSize().getRight() / 2;//页面的水平中点float bottom = document.getPageSize().getBottom() + 20;//在每页结束的时候把“第x页”信息写道模版指定位置PdfContentByte cb = writer.getDirectContent();// 线的宽度cb.setLineWidth(1f);// 线的起点,坐标这个是以左下角为原点的cb.moveTo(70, 760);// 线的终点cb.lineTo(550, 760);// stroke一下cb.stroke();cb.moveTo(70, 40);cb.lineTo(550, 40);cb.stroke();// cb.saveState(); saveState只能一次,不然会报错// 获得当前页String text = writer.getPageNumber() + "/";cb.beginText();cb.setFontAndSize(bf, 8);cb.setTextMatrix(center, bottom);//定位“第x页,共” 在具体的页面调试时候需要更改这xy的坐标cb.showText(text);cb.endText();cb.addTemplate(tpl, center + 8, bottom);//定位“y页” 在具体的页面调试时候需要更改这xy的坐标cb.stroke();cb.saveState();// 这个不能掉cb.restoreState();cb.closePath();}

cb.saveState() 这个只能调用一次,cb.restoreState() 不能缺,不然的话会报错。

-08-17 16:14:43.497 ERROR 12700 --- [io-55555-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.itextpdf.text.exceptions.IllegalPdfSyntaxException: Unbalanced save/restore state operators.] with root causecom.itextpdf.text.exceptions.IllegalPdfSyntaxException: Unbalanced save/restore state operators.at com.itextpdf.text.pdf.PdfContentByte.sanityCheck(PdfContentByte.java:4193) ~[itextpdf-5.5.13.jar:5.5.13]at com.itextpdf.text.pdf.PdfContentByte.reset(PdfContentByte.java:1813) ~[itextpdf-5.5.13.jar:5.5.13]at com.itextpdf.text.pdf.PdfContentByte.reset(PdfContentByte.java:1801) ~[itextpdf-5.5.13.jar:5.5.13]

到了上面的步骤就可以得到一张A4的pdf ,但是不会有总页数,我是在onCloseDocument 这个方法中添加总页数的。

@Overridepublic void onCloseDocument(PdfWriter writer, Document document) {//关闭document的时候获取总页数,并把总页数按模版写道之前预留的位置tpl.beginText();tpl.setFontAndSize(bf, 8);tpl.showText(Integer.toString(writer.getPageNumber()));tpl.endText();tpl.closePath();//sanityCheck();}

好了,最后将完整的demo 代码贴出来,这个类可以改为main() 启动,只是需要将HttpServletResponse 参数去掉。

完整代码

package mybatisplus.google.pdf;import com.itextpdf.text.*;import com.itextpdf.text.pdf.*;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/*** 包: mybatisplus.google.pdf* 作者: climber.luo* 时间:.08.17 15:14*/@RequestMapping@RestControllerpublic class GetPdfByItextPdf extends PdfPageEventHelper {public PdfTemplate tpl;public BaseFont bf;// 这个是代码块 初始化一些参数private static BaseFont bfChinese;static {try {bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);} catch (DocumentException | IOException e) {e.printStackTrace();}}@GetMapping("createPdf")public void createPdf(HttpServletResponse response) throws IOException, DocumentException {// 定义一个pdf 的文件,这个文件包括PDF 的大小,内容所在的位置,这个跟网页的 margin 是一样的,自己设置大小Document document = new Document(PageSize.A4, 80, 40, 90, 50);// 对返回的response 进行设置一下 这样的话浏览器就会识别是PDF文件response.setContentType("application/pdf");// 得到一个PDF的输出流 这里可以换成是电脑上的具体路径// PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("E:\\myTestPdf.pdf"));PdfWriter pdfWriter = PdfWriter.getInstance(document, response.getOutputStream());pdfWriter.setPageEvent(new GetPdfByItextPdf());document.open();document.add(new Paragraph("这是一个测试",new com.itextpdf.text.Font(bfChinese, 18, com.itextpdf.text.Font.BOLD)));document.close();}@Overridepublic void onOpenDocument(PdfWriter writer, Document document) {try {// 初始化模板,模板的宽和高自己设定, 初始化一个字体,这个其实可以用 代码块实现tpl = writer.getDirectContent().createTemplate(100, 100);bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);} catch (Exception e) {throw new ExceptionConverter(e);}}@Overridepublic void onEndPage(PdfWriter writer, Document document) {float center = document.getPageSize().getRight() / 2;//页面的水平中点float bottom = document.getPageSize().getBottom() + 20;//在每页结束的时候把“第x页”信息写道模版指定位置PdfContentByte cb = writer.getDirectContent();// 线的宽度cb.setLineWidth(1f);// 线的起点,坐标这个是以左下角为原点的cb.moveTo(70, 760);// 线的终点cb.lineTo(550, 760);// stroke一下cb.stroke();cb.moveTo(70, 40);cb.lineTo(550, 40);cb.stroke();// cb.saveState(); saveState只能一次,不然会报错// 获得当前页String text = writer.getPageNumber() + "/";cb.beginText();cb.setFontAndSize(bf, 8);cb.setTextMatrix(center, bottom);//定位“第x页,共” 在具体的页面调试时候需要更改这xy的坐标cb.showText(text);cb.endText();cb.addTemplate(tpl, center + 8, bottom);//定位“y页” 在具体的页面调试时候需要更改这xy的坐标cb.stroke();cb.saveState();cb.restoreState();cb.closePath();}@Overridepublic void onCloseDocument(PdfWriter writer, Document document) {//关闭document的时候获取总页数,并把总页数按模版写道之前预留的位置tpl.beginText();tpl.setFontAndSize(bf, 8);tpl.showText(Integer.toString(writer.getPageNumber()));tpl.endText();tpl.closePath();//sanityCheck();}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。