ftl生成pdf支持分页和复杂表格

SpringBoot通过FreeMarker生成pdf中遇到的难点记录

一、Configuration加载ftl模板

1.如果模板是放在本地盘符中

String templateName = "abc.ftl";
Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
cfg.setDefaultEncoding("UTF-8");
cfg.setDirectoryForTemplateLoading(new File("E:/"));
Template template = cfg.getTemplate(templateName);

2如果模板是放在Springboot项目的resource路径下

String templateName = "abc.ftl";
Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
cfg.setDefaultEncoding("UTF-8");
cfg.setClassForTemplateLoading(this.getClass(), "/");
Template template = cfg.getTemplate(templateName);

3如果生成了模板可以使用字符流接收然后转换成文件流,不需要生成到本地

 private StringWriter htmHandler(String templateName,
                                    Map<String, Object> paramMap) throws Exception {
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
//        Configuration cfg = new Configuration();
        cfg.setDefaultEncoding("UTF-8");
        // 获取模板的路径
        System.out.println("==================获取模板路径=======================");
//        cfg.setClassForTemplateLoading(this.getClass(), "/");
        cfg.setDirectoryForTemplateLoading(new File("E:/"));
        System.out.println(cfg.getTemplateLoader().toString());
        System.out.println("===================获取模板路径成功=======================");
        //设置模板
        Template template = cfg.getTemplate(templateName);
        StringWriter out = new StringWriter();
        template.process(paramMap, out);
        return out;
    }

二、ftl文件生成pdf,导入项目中的图片

  1. 如果直接在src中写死一个本地路径也是可以读取显示的,不过无法做到动态赋值的效果

  1. 如果使用动态赋值的方式,java中的代码只能通过uri的方式,这里使用com.itextpdf.text.Image类来接收一个url并转成uri路径

URL url = Thread.currentThread().getContextClassLoader().getResource("baidu.jpg");
        Image image = Image.getInstance(url);
        params.put("image", image.getUrl().toURI());

三、遇到一个需求是打印pdf时表格分页截断后需要在第二页有表头,这个纯css就可以实现。

关键样式

// 固定页面大小
@page {
            margin-left: 7mm;
            margin-right: 7mm;
            margin-bottom: 20mm;
            margin-top: 13mm;
        }

表格中需要有<table><thead></thead><tbody></tbody></table>结构

表格样式

.srv-table{
            page-break-inside:auto;
            -fs-table-paginate:paginate;
            border-spacing:0;
            word-break:break-strict;
        }
        .srv-table-tr{
          page-break-inside:avoid; 
          page-break-after:auto;
        }

效果

完整代码提供

<!DOCTYPE html>
<html lang="zh-CN" style="width: 210mm;height: 297mm;background: white;font-size: 10px;font-family: simsun">
<head>
    <style type="text/css">
        /*解决html转pdf文件中文不显示的问题*/
        body {
            font-family: SimSun;
            font-weight: bold;
            font-size: 11px;
        }

        /*设定纸张大小*/
        /* A4纸 */
        /* @page{size:210mm*297mm} */
        /*@page{size: 210mm 297mm}*/
        /*@page{size: 210mm 297mm}*/
        table {
            width: 100%;
            margin-top: 4mm;
        }

        img {
            background: #fff;
            box-shadow: none;
        }
        @page :first {
            margin-top: 0mm;
        }
        @page {
            margin-left: 7mm;
            margin-right: 7mm;
            margin-bottom: 20mm;
            margin-top: 13mm;
        }
        .srv-table{
            page-break-inside:auto;
            -fs-table-paginate:paginate;
            border-spacing:0;
            word-break:break-strict;
        }
        .srv-table-tr{
          page-break-inside:avoid; 
          page-break-after:auto;
        }

    </style>
</head>
<body>
<div style="margin-left:90mm;width: 107mm;height: 26mm;">
    图片 <img style="width: 26mm;height: 26mm;margin-left:-1mm" src="${image!}"/>
</div>

<div style="width: 190mm;">
    <div style="background:gray;width: 10px;height: 10px; border-bottom-right-radius:10;display: inline-block;"></div>
    <div style="display: inline-block;">明细</div>
    <table class="srv-table" style="margin-top: 0mm;">
        <thead>
        <tr class="srv-table-tr">
            <td>NO</td>
            <td>编码</td>
            <td>名称</td>
            <td style="margin: 0 100px;">类型</td>
            <td>作业组</td>
            <td>价格</td>
        </tr>
        </thead>
        <tbody>
         <#list srvList as arr>
            <tr>
                <td>${arr_index + 1}</td>
                <td>${arr.srvCode!}</td>
                <td>${arr.srvName!?html}</td>
                <td style="margin: 0 0 0 101px;">${arr.srvTypeName!}</td>
                <td>${arr.workByCode!}</td>
                <td>¥${(arr.settlType! == 2) ? string(0, arr.sellPrice!?string(",##0.00"))}</td>
                <#if (arr.msuiteCode??&&arr.msuiteCode!='') || (arr.selfDealerSetMealCode??&&arr.selfDealerSetMealCode!='')>
                    <td>
                        ${(arr.msuiteCode! == '')?string(arr.selfDealerSetMealCode!, arr.msuiteCode!)}
                    </td>
                </#if>
            </tr>
        </#list>
        </tbody>
    </table>
  
</div>

</body>
</html>
package com.pdf.gtmcafterpdf.pdf;

import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.BaseFont;
import com.pdf.gtmcafterpdf.Templates.SrvList;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.xhtmlrenderer.pdf.ITextRenderer;

import javax.xml.bind.DatatypeConverter;
import java.io.*;
import java.net.URL;
import java.util.*;

public class BlogPdf {
    public static void main(String[] args) throws Exception {
        String templateName ="blog.ftl";
        Map<String, Object> params = new HashMap<>(16);

        URL url = Thread.currentThread().getContextClassLoader().getResource("baidu.jpg");
        Image image = Image.getInstance(url);
        params.put("image", image.getUrl().toURI());
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入项目数量:");
        if (scanner.hasNextLine()){
            Integer srvNum = Integer.parseInt(scanner.nextLine());
            System.out.println("目前项目数量:" + srvNum);
            params.put("srvList", getSrvList(srvNum));
        }else{
            params.put("srvList", getSrvList(0));
        }
        StringWriter stringWriter = new BlogPdf().htmHandler(templateName, params);
        new BlogPdf().blogPdfHandler(stringWriter);
    }

    public static List<SrvList> getSrvList(int num){
        List<SrvList> list = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            SrvList srvList =  new SrvList();
            srvList.setSrvCode("100000"+i);
            srvList.setSrvName("作业项目");
            srvList.setSrvTypeName("A");
            srvList.setSettlType(2);
            srvList.setSellPrice(100.00);
            srvList.setSelfDealerSetMealCode("2001");
            list.add(srvList);
        }
        return  list;
    }
    private StringWriter htmHandler(String templateName,
                                    Map<String, Object> paramMap) throws Exception {
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
//        Configuration cfg = new Configuration();
        cfg.setDefaultEncoding("UTF-8");
        // 获取模板的路径
        System.out.println("==================获取模板路径=======================");
//        cfg.setClassForTemplateLoading(this.getClass(), "/");
        cfg.setDirectoryForTemplateLoading(new File("E:/"));
        System.out.println(cfg.getTemplateLoader().toString());
        System.out.println("===================获取模板路径成功=======================");
        //设置模板
        Template template = cfg.getTemplate(templateName);
        StringWriter out = new StringWriter();
        template.process(paramMap, out);
        return out;
    }
    private  void blogPdfHandler(StringWriter writer)throws Exception {
        StringBuffer buffer = writer.getBuffer();
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ITextRenderer renderer = new ITextRenderer();
        System.out.println(buffer.toString());
        renderer.setDocumentFromString(buffer.toString());
        org.xhtmlrenderer.pdf.ITextFontResolver fontResolver = renderer
                .getFontResolver();
        try {
            System.out.println("====================================获取字体路径================================");
            String path = Thread.currentThread().getContextClassLoader().getResource("simsun.ttf").toString();
            System.out.println("=====================================获取字体路径成功"+path+"===========================================");
            fontResolver.addFont(path, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        } catch (Exception e) {
            e.printStackTrace();
        }
        renderer.layout();
        try {
            renderer.createPDF(os);
        } catch (Exception e) {
            e.printStackTrace();
        }
        os.close();
        // 将文件流转成base64返回前端
        String binary = printBase64Binary(os);
        System.out.println(binary);
//        String savePath = "E:\\";
        String savePath = "E:/";
        String fileName = "blog.pdf";
        System.out.println("文件保存路径=>{}"+savePath+fileName);
        base64ToFile(binary, fileName, savePath);
    }

    // 文件转成base64
    public  String printBase64Binary(ByteArrayOutputStream os){
        byte[] bytes = os.toByteArray();
        String strPDF = DatatypeConverter.printBase64Binary(bytes);
        return strPDF;
    }

    // base64转成pdf
    public  void base64ToFile(String base64, String fileName, String savePath) {
        File file = null;
        //创建文件目录
        String filePath = savePath;
        File dir = new File(filePath);
        if (!dir.exists() && !dir.isDirectory()) {
            dir.mkdirs();
        }
        BufferedOutputStream bos = null;
        java.io.FileOutputStream fos = null;
        try {
            byte[] bytes = Base64.getDecoder().decode(base64);
            file=new File(filePath + fileName);
            fos = new java.io.FileOutputStream(file);
            bos = new BufferedOutputStream(fos);
            bos.write(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java FTL生成PDF是一种将FreeMarker模板Java代码结合使用的技术,用于生成PDF文件。FreeMarker是一个模板引擎,它提供了模板的定义和数据的注入机制,可以将模板与数据结合起来生成输出内容。 在Java中使用FTL生成PDF的过程通常包括以下几个步骤: 1. 导入所需的JavaFTL库:首先,需要在Java项目中引入相应的FTL库,以及用于生成PDF的相关Java库,例如iText。这些库可以通过Maven等构建工具进行导入。 2. 创建FreeMarker模板:使用FreeMarker模板定义需要生成PDF的样式和格式。模板中可以包含静态内容,也可以通过模板语言标记占位符,用于插入动态数据。 3. 编写Java代码:在Java中,通过调用FreeMarker库的相关类和方法,可以将数据与模板进行合并,生成包含动态数据的文本内容。 4. 生成PDF文件:在得到合并后的文本内容后,使用iText等相关Java库将文本内容转化为PDF格式。这些库提供生成PDF的方法和API,可以指定生成PDF文件的路径和样式。 使用Java FTL生成PDF的好处是,可以通过模板和数据分离的方式,实现PDF的动态生成,从而提高开发效率和灵活性。通过模板的复用,可以实现对于不同数据的PDF生成,从而满足不同的需求。同时,使用Java的优势也能够提供更多的功能和扩展性,比如添加图片、表格等多样化的内容。 当然,需要注意的是,Java FTL生成PDF仍然需要对Java和FreeMarker的使用有一定的了解和掌握。在使用过程中,也需要关注文件路径、字体设置、编码等细节,以确保生成PDF文件符合预期的要求。 ### 回答2: Java FTL(FreeMarker Template Language)可以用来生成PDF文件。FTL是一种模板语言,常用于生成动态的网页内容。但是,我们也可以通过将FTL模板与相应的数据进行结合,然后使用Java代码来渲染模板生成PDF文件。 首先,我们需要设置一个FTL模板,其中包含我们想要在PDF中显示的内容。这个模板可以包含HTML标记和FTL标签,用于在生成PDF时插入动态数据。 然后,我们需要在Java代码中使用FreeMarker库来实现模板的渲染和生成PDF文件的功能。这可以通过以下步骤完成: 1. 创建一个Configuration对象,并设置模板文件的路径。这将告诉FreeMarker在哪里查找FTL模板文件。 2. 使用Configuration对象创建一个Template对象,将需要使用的模板文件作为参数传递给它。 3. 创建一个Writer对象,用于将生成PDF内容写入。 4. 创建一个Map对象,并将需要在FTL模板中使用的数据放入其中。这些数据将替换模板中的FTL标签。 5. 使用Template对象的process方法,将Map对象和Writer对象作为参数传递给它。这将生成PDF文件的内容并将其写入Writer对象。 6. 最后,将Writer对象中的内容写入到PDF文件中,完成PDF文件的生成。 总结起来,通过使用JavaFTL模板,我们可以生成动态的PDF文件。首先设置好模板文件,然后使用Java代码来渲染模板并将数据填充到模板中,最后将生成的内容写入到PDF文件中即可。 ### 回答3: Java与FreeMarker模板引擎(FTL)可以结合生成PDF文档。具体过程如下: 首先,我们需要使用Java的相关库来操作PDF文档。常用的库有iText、Apache PDFBox等。这些库提供了一些API来创建、编辑和保存PDF文件。 其次,我们需要准备好FTL模板文件。FTL是一种模板引擎语言,可以将数据动态地填充到模板中,生成相应的输出文本。 然后,我们需要在Java代码中使用FTL模板引擎。Java中有一些FTL模板引擎的实现,比如FreeMarker和Velocity。我们可以选择其中之一,然后使用相应的API来加载模板文件。 接下来,我们需要准备数据。通过Java代码,我们可以从数据库、文件或其他资源中获取数据。然后,将数据与FTL模板进行结合,填充到模板中。 最后,我们使用PDF库的API将生成的文本保存为PDF文件。根据选择的PDF库,可能会有一些具体的步骤和API调用。 总结来说,使用JavaFTL模板引擎生成PDF的过程包括准备PDF库、准备FTL模板文件、使用Java代码加载模板、准备数据并填充到模板中,最后使用PDF库将生成的文本保存为PDF文件。这样,我们就可以通过JavaFTL生成PDF文档了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值