SpringBoot+FreeMarker+flying-saucer-pdf实现PDF预览、分页需求

文章目录

  • SpringBoot+FreeMarker+flying-saucer-pdf实现PDF预览、分页需求
    • 需求说明
    • 程序示例
      • 程序示例说明
      • 添加依赖包
      • FreeMarker模板文件编写
      • 工具类编写
      • 程序示例控制器编写
      • PDF分页实现
    • 参考链接
    • 源代码地址

SpringBoot+FreeMarker+flying-saucer-pdf实现PDF预览、分页需求

需求说明

  1. MicroSoft Word文档转换PDF文档
  2. 实际工作场景中,类似于业务部门提供合同的Word文档范本,预览合同、签署合同、下载合同均需求使用PDF文档。

程序示例

程序示例说明

示例程序通过两种方式实现了FreeMarker+flying-saucer-pdf的PDF预览、分页需求

  1. FreeMarker模板位于应用程序资源目录下:/resources/templates/freemarkers
  2. FreeMarker模板存储于其它存储单元,例如数据库

添加依赖包


<dependency>
    <groupId>org.xhtmlrenderergroupId>
    <artifactId>flying-saucer-pdfartifactId>
    <version>9.1.22version>
dependency>

<dependency>
	<groupId>org.springframework.bootgroupId>
	<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>

FreeMarker模板文件编写

<html>

<head>
    <style>
        span{
            border-bottom: 1px solid black;
        }
        p{
            line-height: 1.5;
        }
        @page {
            size: 210mm 297mm; /*设置纸张大小:A4(210mm 297mm)、A3(297mm 420mm) 横向则反过来*/
            margin-bottom: 1cm;

            padding: 1em;

            @top-center {
                content: "页眉中间位置";
                font-family: SimSun;
                font-size: 15px;
                color:#000;
            };

            @bottom-center{
                content: "页脚中间位置";
                font-family: SimSun;
                font-size: 15px;
                color:#000;
            };

            @bottom-right{
                content: "第" counter(page) "页 共" counter(pages) "页";
                font-family: SimSun;
                font-size: 15px;
                color:#000;
            };
        }

        #pagenumber:before {
            content: counter(page);
        }

        #pagecount:before {
            content: counter(pages);
        }

    style>
head>

<body style="font-family: SimSun; ">
    <p>
        <h1 style="text-align: center;">公司授权委托书h1>
    p>
    <br /><br />
    <p>致:<span>${tenEntName!}span>p>
    <p>    我单位现委托<span>${userName!}span>作为我单位合法委托代理人,授权其代表我单位进行<span>${indexTitle!}span>账户相关管理工作。该委托代理人的授权范围为:代表我单位在<span>${indexTitle!}span>上注册、签署文件、使用<span>${indexTitle!}span>、资金交易、融资等与<span>${indexTitle!}span>有关的一切事务。在整个<span>${indexTitle!}span>使用过程中,该代理人的一切行为,均代表本单位,与本单位的行为具有同等法律效力。本单位将承担该代理人行为带来的全部法律后果和法律责任。p>
    <p>    以上授权委托有效期自盖章之日(含当日)起至<span>${indexTitle!}span>账户注销和/或<span>${indexTitle!}span>全部融资款项结算完毕终止。p>
    <p>    代理人无权转换代理权。p>
    <p>    特此委托。p>
    <p>    代理人姓名:<span>${userName!}span>p>
    <p>    身份证号码:<span>${userNum!}span>p>
    <br /><br />
    <p>    委托人(盖章):p>
    <p>    日期:<span>${year!}span><span>${month!}span><span>${day!}span>p>
    <br /><br />    <br /><br />    <br /><br />    <br /><br />    <br /><br />    <br /><br />
    <br /><br />    <br /><br />    <br /><br />
    <p>附:委托代理人身份证复印件(<span>正反面、span>加盖公章)p>

    <div id = "header">div>

body>

html>

注意事项:

  1. 在body标签中指定模板文件字体。本文是宋体:font-family: SimSun;
  2. FreeMarker文件对Html标签要求很严格,尽量使用正确的标签。
  3. 模板文件位置应用程序资源目录下:/resources/templates/freemarkers

工具类编写

  • FreeMarker工具类
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import java.io.IOException;
import java.io.StringWriter;
import java.util.Locale;
import java.util.Map;

public class FreemarkerUtil {


    /**
     * 从模板文件中加载
     * @param templateFileName
     * @param data
     * @return
     * @throws IOException
     * @throws TemplateException
     */
    public static String loadTemplateFromFile(String templateFileName, Map<String, String> data) throws IOException, TemplateException {

        // 创建一个FreeMarker实例, 负责管理FreeMarker模板的Configuration实例
        Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        // 指定FreeMarker模板文件的位置
        cfg.setClassForTemplateLoading(FreemarkerUtil.class,"/templates/freemarkers");

        // 设置模板的编码格式
        cfg.setEncoding(Locale.CHINA, "UTF-8");
        // 获取模板文件
        Template template = cfg.getTemplate(templateFileName, "UTF-8");
        StringWriter writer = new StringWriter();

        // 将数据输出到html中
        template.process(data, writer);
        writer.flush();

        String html = writer.toString();

        return html;
    }

    /**
     * 从存储单元中中加载
     * @param templateFileContent
     * @param data
     * @return
     * @throws IOException
     * @throws TemplateException
     */
    public static String loadTemplateFromStorage(String templateFileContent, Map<String, String> data) throws IOException, TemplateException {

        String templateName = "自定义模板名称";

        StringWriter stringWriter = new StringWriter();

        StringTemplateLoader loader = new StringTemplateLoader();
        loader.putTemplate(templateName, templateFileContent);

        Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
        cfg.setTemplateLoader(loader);
        cfg.setDefaultEncoding("UTF-8");
        Template template = cfg.getTemplate(templateName);
        template.process(data, stringWriter);

        String html = stringWriter.toString();

        return html;
    }


}
  • PDF工具类
import com.lowagie.text.pdf.BaseFont;
import org.xhtmlrenderer.pdf.ITextRenderer;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;

public class PDFUtil {

    public static ByteArrayOutputStream createPDFFromHtml(String html) throws Exception {

        ITextRenderer renderer = new ITextRenderer();
        OutputStream out = new ByteArrayOutputStream();

        // 设置 css中 的字体样式(暂时仅支持宋体和黑体) 必须,不然中文不显示
        renderer.getFontResolver().addFont("/font/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        // 把html代码传入渲染器中
        renderer.setDocumentFromString(html);

//            // 设置模板中的图片路径 (这里的images在resources目录下) 模板中img标签src路径需要相对路径加图片名 如
//            String url = PDFTemplateUtil.class.getClassLoader().getResource("images").toURI().toString();
//            renderer.getSharedContext().setBaseURL(url);
        renderer.layout();

        renderer.createPDF(out, false);
        renderer.finishPDF();
        out.flush();

        ByteArrayOutputStream byteArrayOutputStream = (ByteArrayOutputStream) out;

        return byteArrayOutputStream;
    }

}

程序示例控制器编写

import cn.tyrone.springboot.freemarker.util.FreemarkerUtil;
import cn.tyrone.springboot.freemarker.util.PDFUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/freemarker/pdf")
public class Freemarker2PDFController {

    /**
     * 从模板文件中加载
     * @param response
     * @throws Exception
     */
    @GetMapping("/template/file")
    public void loadFromTemplate(HttpServletResponse response) throws Exception {

        String templateFileName = "demo.ftl";

        Map<String, String> data = new HashMap<>();
        data.put("userName", "孙悟空");
        data.put("userNum", "110101199003074071");
        data.put("year", "2022");
        data.put("month", "01");
        data.put("day", "24");
        data.put("siteName", "帅链平台");
        data.put("ticketTitle", "帅票");
        data.put("tenEntName", "郑州市公共交通集团有限公司");
        data.put("indexTitle", "帅链科技");

        String html = FreemarkerUtil.loadTemplateFromFile(templateFileName, data);

        ByteArrayOutputStream baos = PDFUtil.createPDFFromHtml(html);
        response.setContentType("application/pdf");
        OutputStream out = response.getOutputStream();
        baos.writeTo(out);
        baos.close();

    }

    /**
     * 从存储单元中加载
     * @param response
     * @throws Exception
     */
    @GetMapping("/template/storage")
    public void loadFromStorage(HttpServletResponse response) throws Exception {

        Map<String, String> data = new HashMap<>();
        data.put("userName", "孙悟空");
        data.put("userNum", "110101199003074071");
        data.put("year", "2022");
        data.put("month", "01");
        data.put("day", "24");
        data.put("siteName", "帅链平台");
        data.put("ticketTitle", "帅票");
        data.put("tenEntName", "郑州市公共交通集团有限公司");
        data.put("indexTitle", "帅链科技");

        String templateContent = "

公司授权委托书



致:${tenEntName!}

    我单位现委托${userName!}作为我单位合法委托代理人,授权其代表我单位进行${indexTitle!}账户相关管理工作。该委托代理人的授权范围为:代表我单位在${indexTitle!}上注册、签署文件、使用${indexTitle!}、资金交易、融资等与${indexTitle!}有关的一切事务。在整个${indexTitle!}使用过程中,该代理人的一切行为,均代表本单位,与本单位的行为具有同等法律效力。本单位将承担该代理人行为带来的全部法律后果和法律责任。

    以上授权委托有效期自盖章之日(含当日)起至${indexTitle!}账户注销和/或${indexTitle!}全部融资款项结算完毕终止。

    代理人无权转换代理权。

    特此委托。

    代理人姓名:${userName!}

    身份证号码:${userNum!}



    委托人(盖章):

    日期:${year!}${month!}${day!}



















附:委托代理人身份证复印件(正反面、加盖公章)

"
; String html = FreemarkerUtil.loadTemplateFromStorage(templateContent, data); ByteArrayOutputStream baos = PDFUtil.createPDFFromHtml(html); response.setContentType("application/pdf"); OutputStream out = response.getOutputStream(); baos.writeTo(out); baos.close(); } }

PDF分页实现

flying-saucer-pdf是通过css样式实现分页的,如下:

<style>
        span{
            border-bottom: 1px solid black;
        }
        p{
            line-height: 1.5;
        }
        @page {
            size: 210mm 297mm; /*设置纸张大小:A4(210mm 297mm)、A3(297mm 420mm) 横向则反过来*/
            margin-bottom: 1cm;

            padding: 1em;

            @top-center {
                content: "页眉中间位置";
                font-family: SimSun;
                font-size: 15px;
                color:#000;
            };

            @bottom-center{
                content: "页脚中间位置";
                font-family: SimSun;
                font-size: 15px;
                color:#000;
            };

            @bottom-right{
                content: "第" counter(page) "页 共" counter(pages) "页";
                font-family: SimSun;
                font-size: 15px;
                color:#000;
            };
        }

        #pagenumber:before {
            content: counter(page);
        }

        #pagecount:before {
            content: counter(pages);
        }

    style>

参考链接

https://www.iteye.com/blog/wuxiangxin-1550349

源代码地址

https://github.com/myNameIssls/springboot-study/tree/master/springboot-freemarker

你可能感兴趣的:(SpringBoot,企业级应用实战,SpringBoot,freemarker,spring,boot,FreeMarker)