使用freemarker+itextpdf通过HTML模版导出PDF踩过的坑

文章目录

  • 使用freemarker+itextpdf通过HTML模版导出PDF踩过的坑
  • 1. 在linux中路径的问题
  • 2.导出的pdf中文不显示
  • 3.springcloud gateway nullpointerexception (NettyRoutingFilter)
  • 4.分页处理
  • 5.pdf导出图片时,图片过长导致分页问题,后面空白页


1. 在linux中路径的问题

在windows中使用this.class.getClassLoader().getResource("").getPath()获取路径没问题,
在linux中提示找不到模板文件
解决办法:
如果直接读可以使用getClass().getClassLoader().getResourceAsStream(path) 想深入了解的小伙伴可以参考:jar读取资源配置文件,jar包内包外,以及包内读取目录的方法 如果想通过classpath下的templates文件夹获取可以配置freemarker。

@Configuration
public class FreeMarkerConfig {

    @Autowired
    protected FreeMarkerConfigurer configurer;

    @PostConstruct
    public void setSharedVariable() {
        configurer.setTemplateLoaderPath("classpath:/templates/");
        freemarker.template.Configuration config = null;
        try {
            config = configurer.createConfiguration();
        } catch (IOException | TemplateException e) {
            e.printStackTrace();
        }
        config.setDefaultEncoding("UTF-8");
        config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        config.setLogTemplateExceptions(false);
        config.setNumberFormat("#");
        config.setDateFormat("yyyy/MM/dd");
        config.setDateTimeFormat("yyyy-MM-dd HH:mm:ss");
        configurer.setConfiguration(config);
    }
}

由于作者对freemarker不是很熟,在网上也没有找到很好的配置,所以配置可能有其他缺陷或者修正的地方,还请麻烦大佬们帮忙指出,多谢。

Template template = freemarkerConfig.getConfiguration().getTemplate(fileName + ".ftl");

这样直接getTemplate就可以获取到classpath下的templates的模板。

2.导出的pdf中文不显示

增加依赖

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

增加类

/**
 * 处理中文不显示和乱码问题
 */
class ChineseFontsProvider extends XMLWorkerFontProvider {
    @Override
    public Font getFont(final String fontname, String encoding, float size, final int style) {
        try {
            BaseFont bfChinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            return new Font(bfChinese, size, style);
        } catch (DocumentException | IOException e) {
            e.printStackTrace();
        }
        return super.getFont(fontname, encoding, size, style);
    }
}

调用时选择字体为新加的类对象

XMLWorkerHelper.getInstance().parseXHtml(writer,document,
	new ByteArrayInputStream(htmlString.getBytes()),
	XMLWorkerHelper.class.getResourceAsStream("/default.css"),
	Charset.forName("UTF-8"),new ChineseFontsProvider());

3.springcloud gateway nullpointerexception (NettyRoutingFilter)

springcloud的版本问题,header中的ContentType为空导致。像下载文件,或者在controller中进行跳转时,都会出现这种情况。

这个bug在spring cloud gateway的github上作者已经说明的解决方案:将springcloud gateway升级至2.0.1或以上的版本即可,我之前使用的是2.0.0,对应springcloud的版本 为 Finchley.RELEASE
想深入了解的小伙伴可以参考:springcloud gateway nullpointerexception (NettyRoutingFilter) 如果因为部分原因不能换其他的版本(我就是这种情况),可以将导出pdf的流上传至oss,然后返回链接再下载。

4.分页处理

如果想在分页时做处理

public class MyPdfPageEventHelperextends PdfPageEventHelper

可以继承PdfPageEventHelper,重写方法
并在调用

XMLWorkerHelper.getInstance().parseXHtml(writer,document,
	new ByteArrayInputStream(htmlString.getBytes()),
	XMLWorkerHelper.class.getResourceAsStream("/default.css"),
	Charset.forName("UTF-8"),new ChineseFontsProvider());

前设置writer.setPageEvent(new MyPdfPageEventHelper());

5.pdf导出图片时,图片过长导致分页问题,后面空白页

itext
取出有图片的list,通过sourceImg获取他的长和宽,在满足某些条件的时候,设置属性,并把该属性应用到freemarker中。

collect.forEach(c -> {
    BufferedImage sourceImg= null;
    try {
        sourceImg = ImageIO.read(new URL(c.getFileUrl()).openStream());
    } catch (IOException e) {
        e.printStackTrace();
    }
    if (sourceImg != null) {
        int height = sourceImg.getHeight();
        int width = sourceImg.getWidth();
        if (width / height < 4 / 3) {
            c.setHtmlExtend("height: 400px");
        }
    }
});
<img src="${p.fileUrl}" style="max-width: 100%; ${p.htmlExtend!""}"/>

是个取巧的办法,找了很久都没有很好的解决办法,所以只能暂时使用这种,并且并不能确认100%是因为图片长导致的,因为有些比例1:1的图片,虽然很长但是会自动分页,调试了一波后使用4/3这种比较好(因需求不同,条件和css都不同),这里不是很规范,推荐写成常量判断。

最后,文章内有出错的地方麻烦大家帮忙指正,如果您有更好的解决办法请一定留言评论,说不定会帮到更多的人。多谢大家的阅读,非常感谢。

解决问题参考的非常好的文章:
java操作PDF之iText超入门iText5使用HTML生成PDF设置重复表头和强制翻页使用freemarker+itextpdf通过HTML模版导出PDF 可能有用的文章:
Freemarker如何使用jar中的模板