itextpdf freemarker渲染

现有需求涉及到打印pdf操作,简单找了俩种方式

  1. 在现有的模板上进行编辑,操作难度比较大
  2. 通过freemarker生成静态页面,在进行转换html,完美。

关于动态生成pdf,网上参考的挺多的,看来看去还是觉得通过FreeMarker+IText生成pdf最为简单
参考地址 https://www.cnblogs.com/yunfeiyang-88/p/10984740.html
github demo地址 https://github.com/chywx/spring-boot-pdf/blob/freemarker-print/src/main/java/cn/chendahai/html2pdf/JavaToPdfHtmlFreeMarkerBet.java

撸起来

建立springboot工程
添加maven依赖

    <!-- freemarker依赖 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-freemarker</artifactId>
    </dependency>

    <!-- itextpdf依赖 -->
    <dependency>
      <groupId>com.itextpdf</groupId>
      <artifactId>itextpdf</artifactId>
      <version>5.5.13.2</version>
    </dependency>

    <!-- 更好的css支持 -->
    <dependency>
      <groupId>org.xhtmlrenderer</groupId>
      <artifactId>flying-saucer-pdf</artifactId>
      <version>9.1.20</version>
    </dependency>

application.properties配置freemarker,默认配置

spring.freemarker.charset=UTF-8
spring.freemarker.suffix=.ftl
spring.freemarker.content-type=text/html; charset=utf-8
spring.freemarker.template-loader-path=classpath:/templates
spring.mvc.static-path-pattern=/static/**

测试代码

public class JavaToPdfHtmlFreeMarkerBet {

    private static final String DEST = "target/bet-gg.pdf";
    private static final String HTML = "index7.html";

    private static final String IMG_PATH = "file:///D:/project/javaproject/spring-boot-pdf/src/main/resources/static/";

    private static Configuration freemarkerCfg;

    static {
        freemarkerCfg = new Configuration();
        //freemarker的模板目录
        try {
            freemarkerCfg.setDirectoryForTemplateLoading(new File("src/main/resources/templates/betPrint"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException, DocumentException, com.lowagie.text.DocumentException {
        Map<String, Object> data = new HashMap();
        data.put("name", "dahai");
        String content = JavaToPdfHtmlFreeMarkerBet.freeMarkerRender(data, HTML);
        JavaToPdfHtmlFreeMarkerBet.createPdf(content, DEST);
    }

    /**
     * freemarker渲染html
     */
    public static String freeMarkerRender(Map<String, Object> data, String htmlTmp) {
        Writer out = new StringWriter();
        try {
            // 获取模板,并设置编码方式
            Template template = freemarkerCfg.getTemplate(htmlTmp);
            template.setEncoding("UTF-8");
            // 合并数据模型与模板
            template.process(data, out); //将合并后的数据和模板写入到流中,这里使用的字符流
            out.flush();
            return out.toString();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return null;
    }

    public static void createPdf(String content, String dest) throws IOException, com.lowagie.text.DocumentException {
        ITextRenderer render = new ITextRenderer();
        // 解析html生成pdf
        render.setDocumentFromString(content);
        //解决图片相对路径的问题
        render.getSharedContext().setBaseURL(IMG_PATH);
        render.layout();
        render.createPDF(new FileOutputStream(dest));
    }
}

web环境

获取模板的话可以直接通过注入FreeMarkerConfigurer来获取

    @Autowired
    FreeMarkerConfigurer freeMarkerConfigurer;

Template template = freeMarkerConfigurer.getConfiguration().getTemplate("betPrint/print_match.ftl");

设置边距

由于是通过模板进行渲染,所以边距需要前端来进行控制

@page {
    margin: 0in 0.1in 0in 0.1in;
    mso-header-margin: .5in;
    mso-footer-margin: .5in;
    mso-page-orientation: landscape;
}

但是打印的时候如果选择实际大小,那会导致前面的配置失效

pdf流形式返回

该实例是直接在本地磁盘生成pdf,如果是web环境下,可以通过生成流的方式,直接返回给客户端,避免了磁盘io
方式就是将
render.createPDF(new FileOutputStream(dest));
修改为
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
render.createPDF(outputStream);

总结:

  1. html的格式要求符合xml格式,必须要有闭合标签。比如<input />只能写成<input></input>
  2. 由于这是服务端生成的,自然跟客户端扯不上关系,这一点很好。但是跟window和linux还是有些细小的区别。比如linux字体的最小为12px
  3. CSS有些语法不支持,比如C3就不支持
  4. 加载图片可以使用file://协议,linux也是支持的。还有,简单起见,可以直接使用http加载网络图片
  5. 由于是通过模板渲染,边距,分页等设置就是前端的事了 比如height: 100%;box-sizing: border-box;即可实现

Dylan

面朝大海·春暖花开

发表评论