您好,欢迎来到外链网!
当前位置:外链网 » 站长资讯 » 专业问答 » 文章详细 订阅RssFeed

flying saucer做导PDF踩过的坑,装修踩过的坑

来源:互联网 浏览:28次 时间:2023-04-08

???? 最近公司要求做一个导出PDF报表的功能。由于时间比较紧张,而且导出的内容暂时为一个报表而已,所以我采用了flying saucer+freemaker来做。

flying sauce源码:https://github.com/flyingsaucerproject/flyingsaucer

至于为什么选择flying saucer,详细请看:https://blog.csdn.net/qq_36961530/article/details/72628028

结合方案对比,详细请看:https://blog.csdn.net/blackmonkey/article/details/75096084

flying saucer是基于itext的,其最大的优势,是对css2.1的支持,页面渲染效果很好~特别是在做html转PDF时。话不多说,你看!

1.jar引入

Gradle项目:

compile ('org.xhtmlrenderer:flying-saucer-pdf:9.0.8')

Maven项目:

<dependency> <groupId>org.xhtmlrenderer</groupId> <artifactId>flying-saucer-pdf</artifactId> <version>9.0.8</version></dependency>

具体的PDFUtils代码下次补上~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(华丽分割线)~~~~~~~~~~~~~~~~~~~~~~~~~~~

***************************************坑点大全******************************************

1.中文支持问题

因为这个框架是外国人写的,中文对于外国人太过复杂,特别是支持语言包的编写上。所以,所有的flying-saucer包均不支持中文。

第一步:虽然不支持中文,但是外国人给我们一个字体类:BaseFont类。只要在这个类中,加入你想要支持的字体文件(.afm、.pfm、.ttf、.otf、.ttc均支持哦,有兴趣的同学可以去研究下这个BaseFont源码,欢迎评论区留言~),具体代码如下:

ITextRenderer render = new ITextRenderer();render.getFontResolver().addFont(fontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);/*参数说明:fontPath字体的路径BaseFont.IDENTITY_H 字体水平书写 IDENTITY_V 为垂直书写BaseFont.NOT_EMBEDDED 字体不需要嵌入BaseFont Api详细:https://www.coderanch.com/how-to/javadoc/itext-2.1.7/com/lowagi/text/pdf/BaseFont.html*/

第二步:在你的html上加上字体!html上加上字体!html上加上字体!重要的事情说三遍!!!

<body style = "font-family: SimHei; font-size:small;"></body>

2.中文换行问题

org.xhtmlrenderer:flying-saucer-pdf这个jar包在9.0.1版本之前都是不支持中文换行的。(解决方案:需要对Jar包中的Breaker.class进行修改,很多国内大佬都有修改方案,详细请看:https://blog.csdn.net/conan1210/article/details/50750849)

附上修改主要代码:

private static boolean isChinese(char c){? Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);?? if(ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS){ return true; } return false;}??private static int getStrRight(String s, int left){?if(left >= s.length())?return -1;?char[] ch = s.toCharArray();?for(int i = left; i < ch.length; i++){? if(isChinese(ch[i]) || ' ' == ch[i]){? return i==0?i+1:i;? }? }?return -1;}

2.1 这样修改源码只是对中文做了一个简单的换行处理,并不能自动识别标点比如逗号或者表单数据中,句段的合理换行。所以,为了能让你的PDF好看点,强烈不推荐使用9.0.1及其之前的版本!!!

3.路径问题(这个绝对是最最最最坑的,差点让我放弃flying saucer)

3.1 首先,让我们来研究下:不同系统的路径问题。(主要是:windows和linux)

就拿字体文件路径举例:比如我需要引入项目中 resources/fonts/simhei.ttf??????

在windows系统上:

不管你用ClassLoaber.getResource()或System.getProperty("user.dir")或者getServletContext().getRealPath("/")。。。只要能获取项目的绝对路径都OK。输出的路径应为这样的形式: /E:/MyWorkspace/项目名/xxx/classes/

String fontPath = Thread.currentThread().getContextClassLoader(). getResource(font).getPath();//font为:fonts/simhei.ttfrender.getFontResolver().addFont(fontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); 在linux系统上:

首先,你要明白,在linux下file:/无法被识别,运用file:///来声明根目录 详细请看:https://blog.csdn.net/u010515155/article/details/76186728

解决方案:建议把simhei.ttf字体文件放在在linux系统中 : /user/share/font/simhei.ttf??

render.getFontResolver().addFont(fontPath, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);//fontPath为: "/usr/share/simhei.ttf"

3.2 然后,我们再来研究下要导出的html中有图片,图片的路径

图片的路径也必须是绝对路径才可以识别哦~代码如下:

render.getSharedContext().setBaseURL("file://"+Thread.currentThread().getContextClassLoader().getResource(imagePath).getPath());//imagePath 为:图片在html中使用的路径//这里路径eg : file:///E:...的路径,在windows和linux中均可以识别。

3.3 最后,这里还有个大坑,就是项目的所需资源打成外jar包时,路径访问的问题,是不支持new File()读取文件的!!!

我在使用getFontResolver().addFont()这个方法时,由于生产环境的项目是打成jar包的形式,居然无法识别路径,无法指定到字体文件或图片。经过千辛万苦,不断读源码,终于找到原因:https://blog.csdn.net/b_h_l/article/details/7767829

你猜的不错,addFont()方法就不支持从Jar包中获取资源文件!!!

其实,render.getFontResolver().addFont()这个方法的底层是BaseFont.createFont()实现的,源码如下:

?然后我们再来看看BaseFont的createFont()的底层实现:(源码一时找不到了,找到了会补充进来~)

但是,这个方法是通过File f = new File(path)实现的。所以无法从jar包获取资源

针对这个问题,我个人提出了三种解决方案:

1.使用原生的itext5来写html文件,其中可以利用中文支持包iText-asian来引入字体,从而解决中文问题。

BaseFont bfChinese = BaseFont.createFont( "STSongStd-Light" ,"UniGB-UCS2-H",BaseFont.NOT_EMBEDDED);

但需要注意:flying sauce是基于iText2.1.7,低版本的iText-asian包和iText2.1.7会有包名冲突。需要改包名。

详细请看:https://blog.csdn.net/lvqfly/article/details/50681539

2.jar包中的资源是可以被读取的,比如说利用流的形式。思路为:先从Jar包中获取资源文件,写入存到项目文件中,然后通过项目文件路径获取资源。

3.在flying sauce中,字体处理器ITextFontResolver的父类FontResolver,其还有另一个实现类:AWTFontResolver。尝试继承这个类并重写以字节流的形式读取路径文件,再通过render.getSharedContext().setFontResolver(),强制转换一波应该可以实现。目前还在实验中~有兴趣的同学可以加我QQ(714635093)一起讨论~~~

76241343