利用Java动态生成 PDF 文档,则需要开源的API。首先我们先想象需求,在企业应用中,客户会提出一些复杂的需求,比如会针对具体的业务,构建比较典型的具备文档性质的内容,一般会导出PDF进行存档。那么目前最佳的解决方案,你可能会想到 iText ,对没错。。。 iText+(Velocity / Freemarker)可以实现。不过据我熟悉,iText本身提供的HTML解析器还是不够强大,许多HTML标签和属性无法识别,更悲催的是简单的CSS它不认识,排版调整样式会让你头大的。不要失望,接下来我就来介绍一个比较理想的解决方案 flying-saucer + iText + (Velocity / Freemarker)。 大家都知道 iText 是一个生成PDF文档的开源Java库,能够动态的从XML或者数据库生成PDF,同时还可以对文档进行加密,权限控制,甭且还支持Java/C#等。。。 flying-saucer也是导出PDF的一种解决方案并且是基于iText的开源API,并且实现了CSS解析器,能够很好的支持CSS2.1,以及少量的CSS3 生成PDF解决方案: Flying-Saucer + iText + Velocity 1. 第一步 将jar包放到你的工程里,需要的jar如下: bcprov-jdk15-140.jar core-renderer.jar iText-2.0.8.jar iTextAsian.jar velocity-1.4.jar Jar包下载地址:http://code.google.com/p/flying-saucer/downloads/list 2. 第二步 设计模版,进行排版调整样式,css样式也可以导入@import 等,通过Velocity模版引擎动态替换 页面内容,以下是模版内容: <?xml version="1.0" encoding="UTF-8" ?> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>PDF模版</title> <style type="text/css"> <!-- body { font: 100% Verdana, Arial, Helvetica, sans-serif; margin: 0; padding: 0; text-align: center; color: #000000; } .oneColLiqCtrHdr #container { width: 100%; margin: 0 auto; text-align: left; } div.header-left {display: none} div.header-right {display: none} div.footer-left {display: none} div.footer-right {display: none} @media print { div.header-left { display: block; position: running(header-left); } div.header-right { display: block; position: running(header-right); } div.footer-left { display: block; position: running(footer-left); } div.footer-right { display: block; position: running(footer-right); } } @page { margin: 0.65in; padding: 1em; @top-left{ content:element(header-left); }; @top-right { content: element(header-right) }; @bottom-left { content: element(footer-left) }; @bottom-right { content: element(footer-right) }; } #pagenumber:before { content: counter(page); } #pagecount:before { content: counter(pages); } .tbClass { width:100%;height:100%; border-left:1px #000000 solid; border-bottom:1px #000000 solid } .tbClass td { border-top:1px #000000 solid; border-right:1px #000000 solid } @page landscape{ size:841.9pt 595.3pt; mso-page-orientation:landscape; margin:89.85pt 72.0pt 89.85pt 72.0pt; mso-header-margin:42.55pt; mso-footer-margin:49.6pt; mso-paper-source:0; layout-grid:15.6pt; } div.landscape{ page:landscape; } @page portrait{ size:595.3pt 841.9pt; margin:36.0pt 36.0pt 36.0pt 36.0pt; mso-header-margin:42.55pt; mso-footer-margin:49.6pt; mso-paper-source:0; layout-grid:16.3pt 0pt; mso-layout-grid-char-alt:0; } div.portrait{ page:portrait; } --> </style> </head> <body class="oneColLiqCtrHdr"> <div id="container"> <div id="header"> <!--***************页眉_start*****************--> <div id="header-left" class="header-left" align="left"> 页眉左侧 </div> <div id="header-right" class="header-right"> 页眉右侧 </div> <!--***************页眉_end*****************--> </div> <div id="footer"> <!--***************页脚_start*****************--> <div id="footer-left" class="footer-left" align="left"> 页脚左侧 </div> <div id="footer-right" class="footer-right" align="right"> 页脚右侧 </div> <!--***************页脚_endt*****************--> </div> <div id="mainContent"> <!-- start #mainContent --> <div>内容1</div> <div class="portrait" style="page-break-after:always"><!--分页--> <div>内容2</div> <div class="portrait" style="page-break-after:always"><!--分页--> <div class="landscape">内容3 横向显示</div> <!-- end #mainContent --> </div> </div> </body> </html> 3. 模版与业务数据整合 @SuppressWarnings("unchecked") //打印业务数据对象baseInfo public void getPdf(BASEINFO baseInfo) throws Exception{ String sysurl = PdfBO.class.getProtectionDomain().getCodeSource().getLocation().getPath(); sysurl = sysurl.substring(0,sysurl.indexOf("WEB-INF/",0)); sysurl = java.net.URLDecoder.decode(sysurl, "UTF-8"); //首先创建一个模板引擎的实例 VelocityEngine engine = new VelocityEngine(); //模版路径 String tempath = sysurl+"\pdf\template"; //设置参数 engine.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, tempath); engine.setProperty(Velocity.INPUT_ENCODING, "UTF-8"); engine.setProperty(Velocity.OUTPUT_ENCODING, "UTF-8"); //初始化 engine.init(); //获得模板 Template template = engine.getTemplate("template.html"); //创建上下文,填充数据 VelocityContext context = new VelocityContext(); context.put("baseInfo",baseInfo); PrintUtils pdfUtil = new PrintUtils(); context.put("PrintUtils", pdfUtil); //现在,把模板和数据合并,输出到Writer String vmpath = sysurl + "\pdf\template\a.html"; Writer writer = new PrintWriter(new FileOutputStream(new File(vmpath))); template.merge(context,writer); writer.flush(); //生成PDF电子文档 String sysurl = PdfBO.class.getProtectionDomain().getCodeSource().getLocation().getPath(); sysurl = sysurl.substring(0,sysurl.indexOf("WEB-INF/",0)); sysurl = java.net.URLDecoder.decode(sysurl, "UTF-8"); //转换的文档路径 String inFileUrl = sysurl + "\pdf\template\a.html"; String url = new File(inFileUrl).toURI().toURL().toString(); //转换后PDF文件的输出路径 String outFile_url_ = sysurl + "\pdf\doc\a.pdf"; OutputStream output = new FileOutputStream(outFile_url_); //实例ITextRenderer,加载html文档 ITextRenderer renderer = new ITextRenderer(); renderer.setDocument(url); //支持中文 ITextFontResolver fontResolver = renderer.getFontResolver(); fontResolver.addFont("C:/Windows/Fonts/ARIALUNI.TTF",BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); //PDF页眉图片路径 renderer.getSharedContext().setBaseURL("file:"+sysurl+"\images\a.gif"); renderer.layout(); renderer.createPDF(output); output.close(); PdfReader reader = new PdfReader(outFile_url_); // 加水印后PDF文件输出路径 String filePath_stamper = sysurl + "\pdf\doc\my.pdf"; PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(filePath_stamper)); BaseFont base = BaseFont.createFont("STSong-Light", "UniGB- UCS2-H",BaseFont.NOT_EMBEDDED); int total = reader.getNumberOfPages() + 1; //水印图片的路径 String imageFilePath = sysurl + "\images\b.gif"; Image image = Image.getInstance(imageFilePath); image.setAbsolutePosition(20, 50); image.scalePercent(50); PdfContentByte under; for (int i = 1; i < total; i++) { under = stamper.getUnderContent(i); under.addImage(image); under.beginText(); under.setColorFill(Color.CYAN); under.setFontAndSize(base, 30); under.endText(); } stamper.close();} } 5、生成PDF完成