PDF 生成插件 flying saucer 和 iText
2014-03-14 15:18
435 查看
最近的项目中遇到了需求,用户在页面点击下载,将页面以PDF格式下载完成供用户浏览,所以上网找了下实现方案。
在Java世界,要想生成PDF,方案不少,所以简单做一个小结吧。
在此之前,先来勾画一下我心中比较理想的一个解决方案。在企业应用中,碰到的比较多的PDF的需求,可能是针对某个比较典型的具备文档特性的内容,导出成为PDF进行存档。由于我们现在往往使用一些开源框架,诸如ssh来构建我们的应用,所以我们相对熟悉的方案是针对具体的业务逻辑设计实体,使用开源框架来实现我们的业务逻辑。而PDF的导出,最好不要破坏现有的程序框架,甚至能复用我们业务逻辑层的代码。因为如果把PDF作为一种特殊的表现形式的话,实际上它有点类似模板。最佳的情况,是我们能够通过编写某种模板,把PDF的大概样子确定下来,然后把数据和模板做一次整合,得到最后的结果
带着这个目标,开始在网上搜索解决方案。也找到了一些方案,下面简单小结一下:
JasperReport的设计思路,本身是不违反我上面所说的初衷的。因为我们的努力方向是先生成模板,然后得到数据,最后将两者整合得到结果。但是JasperReport的问题在于,其生成模板的方式过于复杂,即使有IDE的帮助,我们还是需要对其中的众多规则有所了解才行,否则就会给调试带来极大的麻烦。
所以,我认为JasperReport是一个半调子方案,这种强依赖于IDE进行可视化编辑的方式令我很不爽。同时,由此带来的诸多的限制,相信也让很多使用者颇为头疼。在经历了一番痛苦的挣扎后,决定放弃使用这种方案。
java代码:
这是从网上找到的一个例子。从代码中,我们可以看到,iText本身提供了一个简单的HTML的解析器,它可以把HTML转化成我们需要的PDF的document。
有了这个东西,基本上我的目标就能达成一大半了。接下来我的任务就是根据实际情况去编写HTML代码,然后扔进这个方法,就OK了。而真正的HTML代码,我们则可以在这里使用真正的模板技术,Freemarker或者Velocity去生成我们所需要的内容。当然,这已经是我们熟门熟路的东西了。
正当我觉得这个方案基本能符合我的要求的时候,我也同样找到了它的很多弱项:
1.无法识别很多HTML的tag和attribute(应该是iText的HTMLParser不够强大)
2.无法识别CSS
如果说第一点我还可以勉强接受的话,那么第二点我就完全不能接受了。无法识别简单的CSS,就意味着HTML失去了最基本的活力,也无法根据实际要求调整样式。
所以这种方案也必然无法成为我的方案。
在这种情况下,我几乎已经燃起了自己编写一个支持CSS解析的HTMLParser的想法。幸好,在一个非常偶然的情况下,我在google中搜到了这样一个开源项目,它能够满足我的一切需求。这就是flyingsauser,项目主页是:https://xhtmlrenderer.dev.java.net/
项目的首页非常吸引人:AnXML/XHTML/CSS2.1Renderer。这不正是我要的东西么?
仔细再看里面的文档:
引用
FlyingSaucerisanXML/CSSrenderer,whichmeansittakesXMLfilesasinput,appliesformattingandstylingusingCSS,andgeneratesarenderedrepresentationofthatXMLasoutput.Theoutputmaygotothescreen(inaGUI),toanimage,ortoaPDFfile.Becausewebelievemostpeoplewillbeinterestedinre-usingtheirknowledgeofweblayout,ourmaintargetforcontentisXHTML1.0(strict),anXMLdocumentformatthatstandardizesHTML.
完美了。这东西能解析HTML和CSS,而且能输出成image,PDF等格式。哇!我们来看看sample代码(代码丑陋,不过已经能说明问题了):
java代码:
运行,成功!实在太简单了!API帮你完成了一切!
有了这个东西,我们就可以将PDF的生成流程变成这样:
1)编写Freemarker或者Velocity模板,打造HTML,勾画PDF的样式(请任意使用CSS)
2)在你的业务逻辑层引入Freemarker的引擎或者Velocity的引擎,并将业务逻辑层中可以获取的数据和模板,使用引擎生成最终的内容
3)将我上面的sample代码做简单封装后,调用,生成PDF
这样,我想作为一个web程序员来说,上面的3点,都不会成为你的绊脚石。你可以轻松驾驭PDF了。
在FlyingSaucer的官方文档中,有一些Q&A,可以解决读者们大部分的问题。包括PDF的字体、PDF的格式、Image如何处理等等。大家可以尝试着去阅读。
还有一篇文章,好像是作者写的,非常不错:http://today.java.net/pub/a/today/2007/06/26/generating-pdfs-with-flying-saucer-and-itext.html
Freemarker+Flyingsauser+Itext整合生成PDF
Freemarker、Flyingsauser、Itext,这三个框架的作用就不详细介绍了,google一下就知道了。
Itext提供了很多底层的API,让我们可以用java代码画一个pdf出来,但是很不灵活,布局渲染代码都hardcode进java类里面了。
当需求发生改变时,哪怕只需要更改一个属性名,我们都要重新修改那段代码,很不符合开放关闭的原则。想到用模版来做渲染,但自己实现起来比较繁琐,然后google了下,找到了用freemarker做模版,Flyingsauser照着模版做渲染,让Itext做输出生成PDF的方案。
freemarker和itext都比较熟悉了,Flyingsauser第一次听说,看完官方的userguide(http://flyingsaucerproject.github.com/flyingsaucer/r8/guide/users-guide-R8.html)后,自己着手做了个demo实践:
测试数据模型:
java代码:
java代码:
html生成器:
java代码:
pdf生成器:
java代码:
用来做测试的ftl模版,用到部分css3.0的属性来控制pdf强制分页和输出分页信息
html代码:
最后写个测试用例看看:
java代码:
到C盘下打开sample.pdf,看看输出的结果:
flyingsaucer使用中的一些问题(java导出pdf)
flyingsaucer(源代码托管在githubhttps://github.com/flyingsaucerproject/flyingsaucer)是java导出pdf的一种解决方案,最早是从downpour老大的文章里看到它:http://www.iteye.com/topic/509417,感觉比之前的iText好用许多,它可以解析css,即我将页面先设置好,然后传递给它,它既可以给我生成一个pdf出来,跟页面一样,当时感觉很酷,于是就研究了一下,现在项目中也用到了,效果还不错。
优点很明显,之前也提到了,可以解析css,这样很方便,大大的减少了工作量。pdf加水印也变得很简单——只需为body设置一个background-image即可。
说说使用中需要注意的一些问题吧:
[list=1]
中文换行问题
老外做的东西,没有考虑到中文问题。默认提供的包里,中文不会换行,有人修改了源代码,解决了这个问题,重新编译好的包在附件里,可以下载。需要注意的是,在官网提供的jar包里,有两个包,一个是core-renderer.jar,另一个是core-renderer-minimal.jar。引用时,只需引用前者就行。有人曾经说用这个重新编译后的包替换了原来的包之后,不起作用,原因就在此。
中文换行包下载地址:http://community.csdn.net/detail/shanliangliuxing
另外,想要中文换行,如果是table,那么table的style必须加上这句话
html代码:
css路径问题
在一个javaproject里,使用相对css路径是可以的,效果也都不错。但在javawebproject里,使用css相对路径是不可以的(最起码这里困扰了我很久,差点就放弃flyingsaucer了)。例如,我有一个模板叫addOne.jsp,里面引用到了某个css,就应该这样写(windows)
jHTML代码:
只有这样写了之后,它才能找到这个css,很诡异。每次换了机器之后都要改路径,很麻烦。
中文字体问题
downpour老大在它那篇文章里提到了怎样处理中文字体的,他可能高估了许多人的水平。其实说起来,很简单,就两点:一是在java代码里引用字体,二是在页面上引用字体。
引用字体:
java代码:
这里引用了arialuni.ttf字体,它位于C盘windows/fonts文件夹下,将它引用后,还需要在页面上使用这个字体
html代码:
这里的ArialUnicodeMS即刚才的arialuni.ttf字体的名字,换了其它字体后要注意修改这里的名称。这样才可以在pdf中显示中文。
许多人有这样一个问题——按照以上两个步骤做了之后,页面中还是没有中文,这时,请检查你引用的css文件,其中一定设置了其它字体,只需将它去掉即可
缺点:
我在使用中发现,flyingsaucer不支持富文本,如果用到了KindEditor此类富文本编辑器,
还要将其中的内容转化成pdf,那对flyingsaucer来说就是个灾难。会报一堆错误,目前我还没有找到解决方案。还好这次项目中不是必须使用富文本编辑器,对于有此类需求的同学来说,请慎重选择flyingsaucer。另外,flyingsaucer严格遵守html规则,一个小小的错误,都会导致它报错。诸如
html代码:
此类的html代码在jsp中是不会有问题的,可是flyingsaucer却会报错,曾经这个问题导致我花了一小时时间来寻找问题所在。不过很难说这到底是缺点还是优点
最后贴一个较完整的例子:
我使用springmvc,在controller里
java代码:
jsp页面如下
html代码:
Flyingsaucer使用iText2.0.8作为其pdf输出的基础工具,另外增加了解析html/xml并形成pdf式排版的功能.最重要的它还支持css样式表.组合这些能力后,它就可以将网页变成pdf了.但是,它也有他的问题.大家知道iText的版本现在已经升级为5.0以上了,而flyingsaucer却依然沿用2.0.8的版本.为什么呢?因为这个灰碟貌似自2007年就已经停止维护了,最终版本flyingsaucer-R8.也就是说这是个几年前的工具包,至于新的替代此功能的包又在哪里?我没有找到,倒是有个功能相似但是收费的,不知道是不是它的成长版.这是题外话我们不研究.只看这个flyingsaucer-R8这个版本能否满足我们的基本要求吧.
在使用过程中发现flyingsaucer-R8对导出pdf的网页有一些要求.
1.所有的标签必须都闭合.
2.网页开头引入的DTD必须与网页体中使用的标签一致.
3.部分不太常用的html标签貌似不认.比如<u>.
因为flyingsaucer解析html文档是遵照xml标准来的,所以这个网页写起来不能像我们平时的网页那么随意.xml该有的规则都要遵守.这个要求并不高我们可以做到,而且试了下导出的pdf文档没啥问题,因此我们还是可以使用它来满足我们的需求.(至于,这个工具包怎么用,这里有不多说了,google一下一大片.这里只写目前google不到的东西.)
既然要用它不可回避的就会遇到其对中文支持不好的问题.
问题1:来源自渲染器输出时没有使用支持中文的字体.虽然我们看到iText有亚洲语言包iTextAsian.jar,但是仅仅引入此包并不能使我们的中文字符输出.以至于网上有个哥们写到:
打开ITextOutputDevice这个类找到:
java代码:
改成:
java代码:
Ok,改了以后我们终于可以看到pdf里有中文了.但是别高兴的太早哦,问题并没有完全解决.如果一段标签中有且只有中文字符的时候,导出pdf后内容便会消失.比如<div>中文</div>,这样的代码将什么也输出不了,而<div>中文a</div>则会将标签内容全部输出.通过测试我们发现,纯中文是无法输出的,但是加上一点点英文、数字或符号就可以输出了.有同学可能要说我们把纯中文后面加上空格不就行了?我只能说很不幸,加空格是不管用的.如果你的页面上纯中文的地方可以随便让你加字母/数字/符号,那可以不必往下看了.但是我觉得大多数的人恐怕不会这么干的,即使我们想客户也不让啊.那就要解决这个问题.
开源的东西有个好处,可以看源代码.从源码中我发现是字体的问题,于是乎,google一下,找到以下方案:
在导出的代码里加入这两句引入字体
java代码:
于是我照做了.杯具的是情况没有任何好转.即使有好转这个绝对路径的字体引入方式也很让人不舒服吧.所以这个不是我们想要的解决方案.
Google不到只能继续从代码里找办法了.通过跟深入的研究代码,发现问题根源所在.输出PDF时在计算字符宽度的时候,对中文字符计算出的宽度居然是0.这也就能解释为什么纯中文字符串输出后会看不到了,因为字符总体的宽度被计算为0也就失去了被输出的机会,而加上一点非中文字符的则至少会有一点宽度,即获得了输出的机会,即使实际宽度比计算的宽度要宽也是可以输出的.
于是修改BaseFont中的getWidth(Stringtext)方法
java代码:
这几行改为:
java代码:
再次测试,通过.至此,使用flyingsaucer将网页导出成pdf的中文问题总算解决了.可是总觉得这个解决的方法有点不太正宗,因为修改了父类嘛.但又没有找到其他正宗的解决方案,只能先这样解决一下了.发出此文,只当抛砖引玉,如果有哪位高人有更好的解决方案请不吝赐教啊.
附件提供修改了的flyingsaucer-R8的两个jar包:core-renderer.jar和iText2.0.8.jar另有一个iText亚洲语言包.
下载修改源码后的jar包地址:http://download.csdn.net/detail/shanliangliuxing/3640286
下面是我自己利用flyingsaucer技术生成pdf文档的实现代码:
Servlet方式:
html代码:
java代码:
Struts1形式:
html代码:
java代码:
原文地址:http://blog.csdn.net/shanliangliuxing/article/details/6833471
在Java世界,要想生成PDF,方案不少,所以简单做一个小结吧。
在此之前,先来勾画一下我心中比较理想的一个解决方案。在企业应用中,碰到的比较多的PDF的需求,可能是针对某个比较典型的具备文档特性的内容,导出成为PDF进行存档。由于我们现在往往使用一些开源框架,诸如ssh来构建我们的应用,所以我们相对熟悉的方案是针对具体的业务逻辑设计实体,使用开源框架来实现我们的业务逻辑。而PDF的导出,最好不要破坏现有的程序框架,甚至能复用我们业务逻辑层的代码。因为如果把PDF作为一种特殊的表现形式的话,实际上它有点类似模板。最佳的情况,是我们能够通过编写某种模板,把PDF的大概样子确定下来,然后把数据和模板做一次整合,得到最后的结果
带着这个目标,开始在网上搜索解决方案。也找到了一些方案,下面简单小结一下:
JasperReport
看到的市面上采用的最多的方案,是JasperReport。相关的文档也很多,不过很杂,需要完全掌握,我认为还是有些坡度和时间的。这个时间和坡度我认为主要来自于对iReport这个IDE的反复尝试,对里面的每个属性的摸索。JasperReport的设计思路,本身是不违反我上面所说的初衷的。因为我们的努力方向是先生成模板,然后得到数据,最后将两者整合得到结果。但是JasperReport的问题在于,其生成模板的方式过于复杂,即使有IDE的帮助,我们还是需要对其中的众多规则有所了解才行,否则就会给调试带来极大的麻烦。
所以,我认为JasperReport是一个半调子方案,这种强依赖于IDE进行可视化编辑的方式令我很不爽。同时,由此带来的诸多的限制,相信也让很多使用者颇为头疼。在经历了一番痛苦的挣扎后,决定放弃使用这种方案。
iText
其实JasperReport是基于iText的。于是有的人会说,那么直接使用iText不是一种倒退么?的确,直接使用iText似乎就需要直接使用原生的API进行编程了。不过幸好iText其实提供了一些方便的API,通过使用这些API,我们可以直接将HTML代码转化成iText可识别的Document对象,从而导出PDF文档。java代码:
importjava.io.FileOutputStream; importjava.io.FileReader; importjava.util.ArrayList; importcom.lowagie.text.Document; importcom.lowagie.text.Element; importcom.lowagie.text.html.simpleparser.HTMLWorker; importcom.lowagie.text.html.simpleparser.StyleSheet; importcom.lowagie.text.pdf.PdfWriter; publicclassMainClass{ publicstaticvoidmain(String[]args)throwsException{ Documentdocument=newDocument(); StyleSheetst=newStyleSheet(); st.loadTagStyle("body","leading","16,0"); PdfWriter.getInstance(document,newFileOutputStream("html2.pdf")); document.open(); ArrayListp=HTMLWorker.parseToList(newFileReader("example.html"),st); for(intk=0;k<p.size();++k) document.add((Element)p.get(k)); document.close(); } }
这是从网上找到的一个例子。从代码中,我们可以看到,iText本身提供了一个简单的HTML的解析器,它可以把HTML转化成我们需要的PDF的document。
有了这个东西,基本上我的目标就能达成一大半了。接下来我的任务就是根据实际情况去编写HTML代码,然后扔进这个方法,就OK了。而真正的HTML代码,我们则可以在这里使用真正的模板技术,Freemarker或者Velocity去生成我们所需要的内容。当然,这已经是我们熟门熟路的东西了。
正当我觉得这个方案基本能符合我的要求的时候,我也同样找到了它的很多弱项:
1.无法识别很多HTML的tag和attribute(应该是iText的HTMLParser不够强大)
2.无法识别CSS
如果说第一点我还可以勉强接受的话,那么第二点我就完全不能接受了。无法识别简单的CSS,就意味着HTML失去了最基本的活力,也无法根据实际要求调整样式。
所以这种方案也必然无法成为我的方案。
flyingsauser
在这种情况下,我几乎已经燃起了自己编写一个支持CSS解析的HTMLParser的想法。幸好,在一个非常偶然的情况下,我在google中搜到了这样一个开源项目,它能够满足我的一切需求。这就是flyingsauser,项目主页是:项目的首页非常吸引人:AnXML/XHTML/CSS2.1Renderer。这不正是我要的东西么?
仔细再看里面的文档:
引用
FlyingSaucerisanXML/CSSrenderer,whichmeansittakesXMLfilesasinput,appliesformattingandstylingusingCSS,andgeneratesarenderedrepresentationofthatXMLasoutput.Theoutputmaygotothescreen(inaGUI),toanimage,ortoaPDFfile.Becausewebelievemostpeoplewillbeinterestedinre-usingtheirknowledgeofweblayout,ourmaintargetforcontentisXHTML1.0(strict),anXMLdocumentformatthatstandardizesHTML.
完美了。这东西能解析HTML和CSS,而且能输出成image,PDF等格式。哇!我们来看看sample代码(代码丑陋,不过已经能说明问题了):
java代码:
/* *ITextRendererTest.java* *Copyright2009ShanghaiTuDou. *Allrightsreserved. */ packageitext; importjava.io.File; importjava.io.FileOutputStream; importjava.io.OutputStream; importorg.xhtmlrenderer.pdf.ITextFontResolver; importorg.xhtmlrenderer.pdf.ITextRenderer; importcom.lowagie.text.pdf.BaseFont; /** *TODOclassdescription* * *@authorpcwang * *@version1.0,上午11:03:26create$Id$ */ publicclassITextRendererTest{ publicstaticvoidmain(String[]args)throwsException{ StringinputFile="conf/template/test.html"; Stringurl=newFile(inputFile).toURI().toURL().toString(); StringoutputFile="firstdoc.pdf"; OutputStreamos=newFileOutputStream(outputFile); ITextRendererrenderer=newITextRenderer(); renderer.setDocument(url); //解决中文支持问题 ITextFontResolverfontResolver=renderer.getFontResolver(); fontResolver.addFont("C:/Windows/Fonts/arialuni.ttf",BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED); //解决图片的相对路径问题 renderer.getSharedContext().setBaseURL("file:/D:/Work/Demo2do/Yoda/branch/Yoda%20-%20All/conf/template/"); renderer.layout(); renderer.createPDF(os); os.close(); } }
运行,成功!实在太简单了!API帮你完成了一切!
有了这个东西,我们就可以将PDF的生成流程变成这样:
1)编写Freemarker或者Velocity模板,打造HTML,勾画PDF的样式(请任意使用CSS)
2)在你的业务逻辑层引入Freemarker的引擎或者Velocity的引擎,并将业务逻辑层中可以获取的数据和模板,使用引擎生成最终的内容
3)将我上面的sample代码做简单封装后,调用,生成PDF
这样,我想作为一个web程序员来说,上面的3点,都不会成为你的绊脚石。你可以轻松驾驭PDF了。
在FlyingSaucer的官方文档中,有一些Q&A,可以解决读者们大部分的问题。包括PDF的字体、PDF的格式、Image如何处理等等。大家可以尝试着去阅读。
还有一篇文章,好像是作者写的,非常不错:
Freemarker+Flyingsauser+Itext整合生成PDF
Freemarker、Flyingsauser、Itext,这三个框架的作用就不详细介绍了,google一下就知道了。
Itext提供了很多底层的API,让我们可以用java代码画一个pdf出来,但是很不灵活,布局渲染代码都hardcode进java类里面了。
当需求发生改变时,哪怕只需要更改一个属性名,我们都要重新修改那段代码,很不符合开放关闭的原则。想到用模版来做渲染,但自己实现起来比较繁琐,然后google了下,找到了用freemarker做模版,Flyingsauser照着模版做渲染,让Itext做输出生成PDF的方案。
freemarker和itext都比较熟悉了,Flyingsauser第一次听说,看完官方的userguide(
测试数据模型:
java代码:
packagecom.jeemiss.pdfsimple.entity; publicclassUser{ privateStringname; privateintage; privateintsex; /** *Constructorwithallfields * *@paramname *@paramage *@paramsex */ publicUser(Stringname,intage,intsex){ super(); this.name=name; this.age=age; this.sex=sex; } ///////////////////////getterandsetter/////////////////////////////////////////// publicStringgetName(){ returnname; } publicvoidsetName(Stringname){ this.name=name; } publicintgetAge(){ returnage; } publicvoidsetAge(intage){ this.age=age; } publicintgetSex(){ returnsex; } publicvoidsetSex(intsex){ this.sex=sex; } }
java代码:
packagecom.jeemiss.pdfsimple.freemarker; importfreemarker.template.Configuration; publicclassFreemarkerConfiguration{ privatestaticConfigurationconfig=null; /** *Staticinitialization. * *InitializetheconfigurationofFreemarker. */ static{ config=newConfiguration(); config.setClassForTemplateLoading(FreemarkerConfiguration.class,"template"); } publicstaticConfigurationgetConfiguation(){ returnconfig; } }
html生成器:
java代码:
packagecom.jeemiss.pdfsimple.generator; importjava.io.BufferedWriter; importjava.io.StringWriter; importjava.util.Map; importcom.jeemiss.pdfsimple.freemarker.FreemarkerConfiguration; importfreemarker.template.Configuration; importfreemarker.template.Template; publicclassHtmlGenerator{ /** *Generatehtmlstring. * *@paramtemplatethenameoffreemarkerteamlate. *@paramvariablesthedataofteamlate. *@returnhtmlStr *@throwsException */ publicstaticStringgenerate(Stringtemplate,Map<String,Object>variables)throwsException{ Configurationconfig=FreemarkerConfiguration.getConfiguation(); Templatetp=config.getTemplate(template); StringWriterstringWriter=newStringWriter(); BufferedWriterwriter=newBufferedWriter(stringWriter); tp.setEncoding("UTF-8"); tp.process(variables,writer); StringhtmlStr=stringWriter.toString(); writer.flush(); writer.close(); returnhtmlStr; } }
pdf生成器:
java代码:
packagecom.jeemiss.pdfsimple.generator; importjava.io.ByteArrayInputStream; importjava.io.OutputStream; importjavax.xml.parsers.DocumentBuilder; importjavax.xml.parsers.DocumentBuilderFactory; importorg.w3c.dom.Document; importorg.xhtmlrenderer.pdf.ITextRenderer; publicclassPdfGenerator{ /** *Outputapdftothespecifiedoutputstream * *@paramhtmlStrthehtmlstr *@paramoutthespecifiedoutputstream *@throwsException */ publicstaticvoidgenerate(StringhtmlStr,OutputStreamout) throwsException{ DocumentBuilderbuilder=DocumentBuilderFactory.newInstance().newDocumentBuilder(); Documentdoc=builder.parse(newByteArrayInputStream(htmlStr.getBytes())); ITextRendererrenderer=newITextRenderer(); renderer.setDocument(doc,null); renderer.layout(); renderer.createPDF(out); out.close(); } }
用来做测试的ftl模版,用到部分css3.0的属性来控制pdf强制分页和输出分页信息
html代码:
<html> <head> <title>${title}</title> <style> table{ width:100%;border:greendotted;border-width:2002 } td{ border:greendotted;border-width:0220 } @page{ size:8.5in11in; @bottom-center{ content:"page"counter(page)"of"counter(pages); } } </style> </head> <body> <h1>Justablankpage.</h1> <divstyle="page-break-before:always;"> <divalign="center"> <h1>${title}</h1> </div> <table> <tr> <td><b>Name</b></td> <td><b>Age</b></td> <td><b>Sex</b></td> </tr> <#listuserListasuser> <tr> <td>${user.name}</td> <td>${user.age}</td> <td> <#ifuser.sex=1> male <#else> female </#if> </td> </tr> </#list> </table> </div> </body> </html>
最后写个测试用例看看:
java代码:
packagecom.jeemiss.pdfsimple.test; importjava.io.FileOutputStream; importjava.io.OutputStream; importjava.util.ArrayList; importjava.util.HashMap; importjava.util.List; importjava.util.Map; importorg.junit.Test; importcom.jeemiss.pdfsimple.entity.User; importcom.jeemiss.pdfsimple.generator.HtmlGenerator; importcom.jeemiss.pdfsimple.generator.PdfGenerator; publicclassTestCase { @Test publicvoidgeneratePDF(){ try{ StringoutputFile="C:\\sample.pdf"; Map<String,Object>variables=newHashMap<String,Object>(); List<User>userList=newArrayList<User>(); Usertom=newUser("Tom",19,1); Useramy=newUser("Amy",28,0); Userleo=newUser("Leo",23,1); userList.add(tom); userList.add(amy); userList.add(leo); variables.put("title","UserList"); variables.put("userList",userList); StringhtmlStr=HtmlGenerator.generate("sample.ftl",variables); OutputStreamout=newFileOutputStream(outputFile); PdfGenerator.generate(htmlStr,out); }catch(Exceptionex){ ex.printStackTrace(); } } }
到C盘下打开sample.pdf,看看输出的结果:
flyingsaucer使用中的一些问题(java导出pdf)
优点很明显,之前也提到了,可以解析css,这样很方便,大大的减少了工作量。pdf加水印也变得很简单——只需为body设置一个background-image即可。
说说使用中需要注意的一些问题吧:
[list=1]
中文换行问题
老外做的东西,没有考虑到中文问题。默认提供的包里,中文不会换行,
中文换行包下载地址:http://community.csdn.net/detail/shanliangliuxing
另外,想要中文换行,如果是table,那么table的style必须加上这句话
html代码:
style="table-layout:fixed;word-break:break-strict;"
css路径问题
在一个javaproject里,使用相对css路径是可以的,效果也都不错。但在javawebproject里,使用css相对路径是不可以的(最起码这里困扰了我很久,差点就放弃flyingsaucer了)。例如,我有一个模板叫addOne.jsp,里面引用到了某个css,就应该这样写(windows)
jHTML代码:
<linkhref="file:///D|/project/WebContent/commons/css/module-pdf.css"rel="stylesheet"type="text/css"/>
只有这样写了之后,它才能找到这个css,很诡异。每次换了机器之后都要改路径,很麻烦。
中文字体问题
downpour老大在它那篇文章里提到了怎样处理中文字体的,他可能高估了许多人的水平。其实说起来,很简单,就两点:一是在java代码里引用字体,二是在页面上引用字体。
引用字体:
java代码:
//解决中文支持问题
ITextFontResolverfontResolver=renderer.getFontResolver();
fontResolver.addFont("C:/Windows/Fonts/arialuni.ttf",BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);
这里引用了arialuni.ttf字体,它位于C盘windows/fonts文件夹下,将它引用后,还需要在页面上使用这个字体
html代码:
<bodystyle="font-family:'ArialUnicodeMS'">
这里的ArialUnicodeMS即刚才的arialuni.ttf字体的名字,换了其它字体后要注意修改这里的名称。这样才可以在pdf中显示中文。
许多人有这样一个问题——按照以上两个步骤做了之后,页面中还是没有中文,这时,请检查你引用的css文件,其中一定设置了其它字体,只需将它去掉即可
缺点:
我在使用中发现,flyingsaucer不支持富文本,如果用到了
还要将其中的内容转化成pdf,那对flyingsaucer来说就是个灾难。会报一堆错误,目前我还没有找到解决方案。还好这次项目中不是必须使用富文本编辑器,对于有此类需求的同学来说,请慎重选择flyingsaucer。另外,flyingsaucer严格遵守html规则,一个小小的错误,都会导致它报错。诸如
html代码:
<tdcolspan="2""2">
此类的html代码在jsp中是不会有问题的,可是flyingsaucer却会报错,曾经这个问题导致我花了一小时时间来寻找问题所在。不过很难说这到底是缺点还是优点
最后贴一个较完整的例子:
我使用springmvc,在controller里
java代码:
@RequestMapping("/pdf/{projectId}")
publicModelAndViewgeneratePdf(HttpServletRequestrequest,
HttpServletResponseresponse,@PathVariable
StringprojectId){
Projectproject=this.projectService.getProjectById(projectId);
ModelAndViewmav=newModelAndView();
if(project==null){
mav.setViewName("forward:/error/page-not-found");
returnmav;
}
//中文需转义
StringpdfName="pdfName";
response.setHeader("Content-disposition","attachment;filename="+pdfName;
response.setContentType("application/pdf");
OutputStreamos=response.getOutputStream();
ITextRendererrenderer=newITextRenderer();
//指定模板地址
renderer.setDocument("http://localhost/project/preview/"+projectId);
ITextFontResolverfontResolver=renderer.getFontResolver();
if(StringUtils.isOSWindow())
fontResolver.addFont("C:/Windows/Fonts/ARIALUNI.TTF",
BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);
else
fontResolver.addFont("/usr/share/fonts/TTF/ARIALUNI.TTF",
BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);
renderer.layout();
renderer.createPDF(os);
os.close();
returnnull;
}
@RequestMapping("/preview/{projectId}")
publicModelAndViewpdf(@PathVariable
StringprojectId){
Projectproject=this.projectService.getProjectById(projectId);
ModelAndViewmav=newModelAndView();
if(project==null){
mav.setViewName("forward:/error/page-not-found");
returnmav;
}
mav.setViewName("pdf");
mav.addObject("project",project);
returnmav;
}
jsp页面如下
html代码:
@RequestMapping("/pdf/{projectId}")
publicModelAndViewgeneratePdf(HttpServletRequestrequest,
HttpServletResponseresponse,@PathVariable
StringprojectId){
Projectproject=this.projectService.getProjectById(projectId);
ModelAndViewmav=newModelAndView();
if(project==null){
mav.setViewName("forward:/error/page-not-found");
returnmav;
}
//中文需转义
StringpdfName="pdfName";
response.setHeader("Content-disposition","attachment;filename="+pdfName;
response.setContentType("application/pdf");
OutputStreamos=response.getOutputStream();
ITextRendererrenderer=newITextRenderer();
//指定模板地址
renderer.setDocument("http://localhost/project/preview/"+projectId);
ITextFontResolverfontResolver=renderer.getFontResolver();
if(StringUtils.isOSWindow())
fontResolver.addFont("C:/Windows/Fonts/ARIALUNI.TTF",
BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);
else
fontResolver.addFont("/usr/share/fonts/TTF/ARIALUNI.TTF",
BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);
renderer.layout();
renderer.createPDF(os);
os.close();
returnnull;
}
@RequestMapping("/preview/{projectId}")
publicModelAndViewpdf(@PathVariable
StringprojectId){
Projectproject=this.projectService.getProjectById(projectId);
ModelAndViewmav=newModelAndView();
if(project==null){
mav.setViewName("forward:/error/page-not-found");
returnmav;
}
mav.setViewName("pdf");
mav.addObject("project",project);
returnmav;
}
使用flyingsaucer将网页转换为pdf之中文问题彻底解决
前几天遇到个导出pdf的需求,在网络上查找了一下java导出pdf的方案.多数人推荐使用iText,研究了一下,感觉直接写pdf的方法太笨,可维护性差,一旦pdf格式要变化改起来很费劲.还有一个方案,可以先预先定义一个pdf作为模板文件,然后用业务数据进行填空.是个不错的方案,只可惜不适合我的需求.需求中有些行是动态加行的,这个方案无法实现.后来发现有可以将网页直接转成pdf的开源包flyingsaucer(中文名:灰碟),逐将注意力转移到这上面,发现是个不错的选择.只要写网页就可以了,而且pdf格式变化维护起来也方便,代码也会比较干净.只是它对中文支持的不好,但这不是无法解决的.下面就来说说这个flyingsaucer.Flyingsaucer使用iText2.0.8作为其pdf输出的基础工具,另外增加了解析html/xml并形成pdf式排版的功能.最重要的它还支持css样式表.组合这些能力后,它就可以将网页变成pdf了.但是,它也有他的问题.大家知道iText的版本现在已经升级为5.0以上了,而flyingsaucer却依然沿用2.0.8的版本.为什么呢?因为这个灰碟貌似自2007年就已经停止维护了,最终版本flyingsaucer-R8.也就是说这是个几年前的工具包,至于新的替代此功能的包又在哪里?我没有找到,倒是有个功能相似但是收费的,不知道是不是它的成长版.这是题外话我们不研究.只看这个flyingsaucer-R8这个版本能否满足我们的基本要求吧.
在使用过程中发现flyingsaucer-R8对导出pdf的网页有一些要求.
1.所有的标签必须都闭合.
2.网页开头引入的DTD必须与网页体中使用的标签一致.
3.部分不太常用的html标签貌似不认.比如<u>.
因为flyingsaucer解析html文档是遵照xml标准来的,所以这个网页写起来不能像我们平时的网页那么随意.xml该有的规则都要遵守.这个要求并不高我们可以做到,而且试了下导出的pdf文档没啥问题,因此我们还是可以使用它来满足我们的需求.(至于,这个工具包怎么用,这里有不多说了,google一下一大片.这里只写目前google不到的东西.)
既然要用它不可回避的就会遇到其对中文支持不好的问题.
问题1:来源自渲染器输出时没有使用支持中文的字体.虽然我们看到iText有亚洲语言包iTextAsian.jar,但是仅仅引入此包并不能使我们的中文字符输出.以至于网上有个哥们写到:
打开ITextOutputDevice这个类找到:
java代码:
cb.setFontAndSize(_font.getFontDescription().getFont(),_font.getSize2D()/_dotsPerPoint);
改成:
java代码:
try{
cb.setFontAndSize(BaseFont.createFont("STSong-Light","UniGB-UCS2-H",BaseFont.NOT_EMBEDDED),_font.getSize2D()/_dotsPerPoint);
}catch(Exceptione){
}
Ok,改了以后我们终于可以看到pdf里有中文了.但是别高兴的太早哦,问题并没有完全解决.如果一段标签中有且只有中文字符的时候,导出pdf后内容便会消失.比如<div>中文</div>,这样的代码将什么也输出不了,而<div>中文a</div>则会将标签内容全部输出.通过测试我们发现,纯中文是无法输出的,但是加上一点点英文、数字或符号就可以输出了.有同学可能要说我们把纯中文后面加上空格不就行了?我只能说很不幸,加空格是不管用的.如果你的页面上纯中文的地方可以随便让你加字母/数字/符号,那可以不必往下看了.但是我觉得大多数的人恐怕不会这么干的,即使我们想客户也不让啊.那就要解决这个问题.
开源的东西有个好处,可以看源代码.从源码中我发现是字体的问题,于是乎,google一下,找到以下方案:
在导出的代码里加入这两句引入字体
java代码:
ITextFontResolverfontResolver=renderer.getFontResolver();
fontResolver.addFont("C:/WINDOWS/Fonts/ARIALUNI.TTF",BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);
于是我照做了.杯具的是情况没有任何好转.即使有好转这个绝对路径的字体引入方式也很让人不舒服吧.所以这个不是我们想要的解决方案.
Google不到只能继续从代码里找办法了.通过跟深入的研究代码,发现问题根源所在.输出PDF时在计算字符宽度的时候,对中文字符计算出的宽度居然是0.这也就能解释为什么纯中文字符串输出后会看不到了,因为字符总体的宽度被计算为0也就失去了被输出的机会,而加上一点非中文字符的则至少会有一点宽度,即获得了输出的机会,即使实际宽度比计算的宽度要宽也是可以输出的.
于是修改BaseFont中的getWidth(Stringtext)方法
java代码:
if(char1<128||(char1>=160&&char1<=255))
total+=widths[char1];
else
total+=widths[PdfEncodings.winansi.get(char1)];
这几行改为:
java代码:
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······15001.if(char1<128||(char1>=160&&char1<=255))
02.total+=widths[char1];
03.elseif((char1>=19968)&&(char1<=40869))//如果是中文字符加宽度500
04.total+=500;
05.else
06.total+=widths[PdfEncodings.winansi.get(char1)];
再次测试,通过.至此,使用flyingsaucer将网页导出成pdf的中文问题总算解决了.可是总觉得这个解决的方法有点不太正宗,因为修改了父类嘛.但又没有找到其他正宗的解决方案,只能先这样解决一下了.发出此文,只当抛砖引玉,如果有哪位高人有更好的解决方案请不吝赐教啊.
附件提供修改了的flyingsaucer-R8的两个jar包:core-renderer.jar和iText2.0.8.jar另有一个iText亚洲语言包.
下载修改源码后的jar包地址:
下面是我自己利用flyingsaucer技术生成pdf文档的实现代码:
Servlet方式:
html代码:
<%@pagelanguage="java"import="java.util.*"pageEncoding="ISO-8859-1"%>
<%
Stringpath=request.getContextPath();
StringbasePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.01Transitional//EN">
<html>
<head>
<basehref="<%=basePath%>">
<title>Html2PdfServlet</title>
<metahttp-equiv="pragma"content="no-cache">
<metahttp-equiv="cache-control"content="no-cache">
<metahttp-equiv="expires"content="0">
<metahttp-equiv="keywords"content="keyword1,keyword2,keyword3">
<metahttp-equiv="description"content="Thisismypage">
<!--
<linkrel="stylesheet"type="text/css"href="styles.css">
-->
</head>
<body>
<formaction="http://localhost:8081/createpdf/servlet/html2PdfServlet"method="get">
<table>
<tr>
<td>
<inputtype="text"id="username"name="username"value=""/>
</td>
<td>
<inputtype="submit"id="submit"name="submit"value="submit"/>
</td>
</tr>
</table>
</form>
</body>
</html>
java代码:
packagecom.test;
importjava.io.IOException;
importjava.io.OutputStream;
importjavax.servlet.ServletContext;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importorg.xhtmlrenderer.pdf.ITextFontResolver;
importorg.xhtmlrenderer.pdf.ITextRenderer;
importcom.lowagie.text.pdf.BaseFont;
publicclassHtml2PdfServletextendsHttpServlet{
privatestaticfinallongserialVersionUID=1L;
publicvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)
throwsServletException,IOException{
//pageContext.getServletContext().getRealPath("/")
ServletContextsc=request.getSession().getServletContext();
Stringpath=sc.getRealPath("");//值为D:\apache-tomcat-6.0.26\webapps\createpdf
System.out.println("原path:"+path);
//把路径中的反斜杠转成正斜杠
path=path.replaceAll("\\\\","/");//值为D:/apache-tomcat-6.0.26/webapps/createpdf
System.out.println(path);
Stringpath2=sc.getRealPath("/");
System.out.println("path2:"+path2);
System.out.println(Thread.currentThread().getContextClassLoader().getResource(""));
System.out.println("request.getRequestURI:"+request.getRequestURI());
//获取使用的端口号
System.out.println(request.getLocalPort());
Stringpath3=request.getContextPath();
StringbasePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path3+"/";
System.out.println("basepath:"+basePath);
response.setContentType("application/pdf");
//response.setHeader("Content-Disposition","attachment;filename=WebReport.pdf");
response.setHeader("Content-Disposition","inline;filename=WebReport.pdf");
StringBufferhtml=newStringBuffer();
//组装成符合W3C标准的html文件,否则不能正确解析
html.append("<!DOCTYPEhtmlPUBLIC\"-//W3C//DTDXHTML1.0Transitional//EN\"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
html.append("<htmlxmlns=\"http://www.w3.org/1999/xhtml\">")
.append("<head>")
.append("<metahttp-equiv=\"Content-Type\"content=\"text/html;charset=UTF-8\"/>")
.append("<styletype=\"text/css\"mce_bogus=\"1\">body{font-family:SimSun;}</style>")
.append("<styletype=\"text/css\">img{width:700px;}</style>")
.append("</head>")
.append("<body>");
html.append("<center><h1>统计报表</h1></center>");
html.append("<center>");
html.append("<imgsrc=\"images/chart.jpg\"/>");
html.append("</center>");
html.append("</body></html>");
//parseourmarkupintoanxmlDocument
try{
ITextRendererrenderer=newITextRenderer();
/**
*引入了新的jar包,不用再导入字体了
ITextFontResolverfontResolver=renderer.getFontResolver();
fontResolver.addFont("C:/Windows/fonts/simsun.ttc",
BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);
*/
renderer.setDocumentFromString(html.toString());
//解决图片的相对路径问题
//renderer.getSharedContext().setBaseURL("file:/C:/DocumentsandSettings/dashan.yin/workspace/createpdf/WebRoot/images");
//renderer.getSharedContext().setBaseURL("file:/D:/apache-tomcat-6.0.26/webapps/createpdf/images");
renderer.getSharedContext().setBaseURL("file:/"+path+"/images");
renderer.layout();
OutputStreamos=response.getOutputStream();
renderer.createPDF(os);
os.close();
}catch(Exceptione){
e.printStackTrace();
}
}
publicvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)
throwsServletException,IOException{
doGet(request,response);
}
}
Struts1形式:
html代码:
<%@pagelanguage="java"import="java.util.*"pageEncoding="UTF-8"%>
<%@pageimport="Config.application.*"%>
<%@includefile="/pages/common/global.jsp"%>
<%@tagliburi="/WEB-INF/tlds/birt.tld"prefix="birt"%>
<scripttype="text/javascript">
functioninteractivity(){
varurl=contextName+"/viewAction.do?method=viewLinkResult";
varstartDate_s=$("#startDate_s").val();
varendDate_s=$("#endDate_s").val();
varserviceInstance=$("#serviceInstance").val();
varserviceInstance=encodeURIComponent(serviceInstance);
varstatus=$("#status").val();
if(startDate_s){
url+="&startDate_s="+startDate_s;
}
if(endDate_s){
url+="&endDate_s="+endDate_s;
}
if(serviceInstance){
url+="&serviceInstance="+serviceInstance;
}
if(status){
url+="&status="+status;
}
url+="&random="+Math.random();
//alert(url);
retrieveURL(url,'viewResult');
//tipsWindown("详细信息","url:get?"+url,"800","700","true","","true","text");
}
functionviewreturn(){
varurl=contextName+"/viewAction.do?method=viewReturnResult";
//时间参数设空
document.getElementById("startDate_s").value="";
document.getElementById("endDate_s").value="";
url+="&random="+Math.random();
retrieveURL(url,'viewResult');
//tipsWindown("详细信息","url:get?"+url,"800","700","true","","true","text");
}
</script>
<html:formaction="/viewAction.do?method=viewExportPDF">
<tableclass="form_t"style="width:100%">
<tr>
<td>
<inputtype="submit"id="submit"name="submit"value="导出"class="button4C"/>
</td>
</tr>
</table>
</html:form>
<html:formaction="/viewAction.do?method=viewResult">
<tableclass="form_t"style="width:100%">
<tr>
<thclass="tablelogo"colspan="3">
统计时间
</th>
</tr>
<tr>
<tdstyle="width:175px;text-align:right;">
开始时间
</td>
<td>
<inputtype="text"name="startDate_s"id="startDate_s"
class="Wdate"
onclick="WdatePicker({dateFmt:'yyyy-MM-ddHH:mm:ss',maxDate:'#F{$dp.$D(\'endDate_s\')||\'%y-%M-%d%H:{%m-1}:%s\'}'})"/>
</td>
<td>
<inputtype="hidden"name="serviceInstance"id="serviceInstance"value=""/>
<inputtype="hidden"name="status"id="status"value=""/>
</td>
</tr>
<tr>
<tdstyle="width:175px;text-align:right;">
结束时间
</td>
<td>
<inputtype="text"name="endDate_s"id="endDate_s"class="Wdate"
onclick="WdatePicker({dateFmt:'yyyy-MM-ddHH:mm:ss',minDate:'#F{$dp.$D(\'startDate_s\')}',maxDate:'%y-%M-%d%H:%m:%s'})"/>
</td>
<td>
</td>
</tr>
<tr>
<tdstyle="padding-left:175px;"colspan="2">
<inputtype="button"value="查看"class="button4C"
onclick="javascript:viewForm(this.form,'viewResult');"/>
<inputtype="button"value="返回"class="button4C"
onclick="javascript:viewreturn();"/>
</td>
<td>
</td>
</tr>
</table>
</html:form>
<tablewidth="100%">
<tr>
<tdid="viewResult"width="100%"></td>
</tr>
</table>
java代码:
publicvoidviewExportPDF(ActionMappingmapping,ActionFormform,
HttpServletRequestrequest,HttpServletResponseresponse)
throwsException{
ServletContextsc=request.getSession().getServletContext();
Stringrootpath=sc.getRealPath("");//值为D:\apache-tomcat-6.0.26\webapps\webmonitor
//把路径中的反斜杠转成正斜杠
rootpath=rootpath.replaceAll("\\\\","/");//值为D:/apache-tomcat-6.0.26/webapps/webmonitor
//临时存储目录
StringpdfPathName=rootpath+"/WebReport.pdf";
ArrayList<String>list=newArrayList<String>();
for(inti=0;i<outstreamlist.size();i++){
list.add(outstreamlist.get(i));
}
//调用方法
booleanflag=createPdf(list,pdfPathName,request,response);
if(flag==true){
//要实现另存为下载,必须满足两个条件:导入commons-upload.jar包,表单提交
try{
OutputStreamout=response.getOutputStream();
byteby[]=newbyte[1024];
FilefileLoad=newFile(pdfPathName);
response.reset();
response.setContentType("application/pdf");
response.setHeader("Content-Disposition",
"attachment;filename=WebReport.pdf");
longfileLength=fileLoad.length();
Stringlength1=String.valueOf(fileLength);
response.setHeader("Content_Length",length1);
FileInputStreamin=newFileInputStream(fileLoad);
intn;
while((n=in.read(by))!=-1){
out.write(by,0,n);
}
in.close();
out.flush();
}catch(Exceptione){
e.printStackTrace();
}
}else{
renderText(response,ERR_MESSAGE);
}
return;
}
//生成pdf
publicbooleancreatePdf(ArrayList<String>list,StringpdfPathName,
HttpServletRequestrequest,HttpServletResponseresponse)
throwsException{
/**
*用rootpath指定目录也可以生成pdf文件,只不过不能在myeclipse的左边导航窗口中看不到而已
*左边导航窗口对应C盘目录下的workspace目录下程序
*用rootpath指定的目录是D盘Tomcat目录
*/
ServletContextsc=request.getSession().getServletContext();
Stringrootpath=sc.getRealPath("");//值为D:\apache-tomcat-6.0.26\webapps\webmonitor
//把路径中的反斜杠转成正斜杠
rootpath=rootpath.replaceAll("\\\\","/");//值为D:/apache-tomcat-6.0.26/webapps/webmonitor
booleanflag=false;
StringoutputFile=pdfPathName;
//指定目录导出文件
OutputStreamos=newFileOutputStream(outputFile);
ITextRendererrenderer=newITextRenderer();
StringBufferhtml=newStringBuffer();
//组装成符合W3C标准的html文件,否则不能正确解析
html.append("<!DOCTYPEhtmlPUBLIC\"-//W3C//DTDXHTML1.0Transitional//EN\"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
html.append("<htmlxmlns=\"http://www.w3.org/1999/xhtml\">")
.append("<head>")
.append("<metahttp-equiv=\"Content-Type\"content=\"text/html;charset=UTF-8\"/>")
.append("<styletype=\"text/css\"mce_bogus=\"1\">body{font-family:SimSun;}</style>")
.append("<styletype=\"text/css\">img{width:500px;}</style>")
.append("<styletype=\"text/css\">table{font-size:13px;}</style>")
.append("</head>")
.append("<body>");
html.append("<center>");
html.append("<h1>统计报表</h1>");
for(inti=0;i<list.size();i++){
html.append("<div>"+list.get(i)+"</div>");
}
html.append("</center>");
html.append("</body></html>");
try{
renderer.setDocumentFromString(html.toString());
//解决图片的相对路径问题,图片路径必须以file开头
renderer.getSharedContext().setBaseURL("file:/"+rootpath);
renderer.layout();
renderer.createPDF(os);
os.close();
flag=true;
}catch(Exceptione){
flag=false;
e.printStackTrace();
}
returnflag;
}
原文地址:http://blog.csdn.net/shanliangliuxing/article/details/6833471
相关文章推荐
- iText和flying saucer结合生成pdf的技术
- iText和flying saucer结合生成pdf--显示分页页码
- iText和flying saucer结合生成pdf的技术
- Java Itext插件生成Pdf(一) 解析PDF模板生成PDF表单
- 利用Flying Saucer 和 iText 实现生成PDF报表
- iText和flying saucer结合生成pdf的技术
- itext+Flying Saucer生成pdf
- 基于iText和flying saucer结合freemark java生成pdf 范例
- iText+Flying Saucer生成pdf文档二:给pdf添加页脚
- iText和flying saucer结合生成pdf的技术
- 基于iText和flying saucer结合freemark java生成pdf 范例
- iText 插件将页面以输出流的形式进行pdf下载
- jfreechart,pdf生成组件iText,jasper report报表组件及POI操作excel等在企业软件开发中常遇到的第三方应用
- PDF格式报表生成IText
- asp生成PDF不使用插件
- java使用iText生成pdf表格
- 解决iText 5.0.6生成pdf,出现异常 Font 'STSong-Light' with 'UniGB-UCS2-H' is not recognized.的异常
- 如何利用iText在JSP中生成PDF报表- -
- 用itext生成pdf报表上篇
- java使用iText生成pdf:解决中文