vue +SpringBoot + FreeMarker + FlyingSaucer can preview, print and download PDF Online

Posted by jude0311 on Sat, 11 Dec 2021 13:50:23 +0100

First of all, thank you

This link Blogger, selflessly dedicate his strength

Thank you for the selflessness and sincerity of the Internet

I completed my requirements with this article and downloaded my pdf resume, so I recorded a record here

The general functions of the project are as follows vue. Click the download button on the page to download the resume and Google browser

 

Click the resume in the browser, as follows, including printing and downloading

First, you need to introduce the following} pom dependencies

   <!-- freemarker rely on -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>

        <!-- FlyingSaucer rely on -->
        <dependency>
            <groupId>org.xhtmlrenderer</groupId>
            <artifactId>flying-saucer-pdf</artifactId>
            <version>9.1.12</version>
        </dependency>

II. Tools

Next, tools: they are really powerful and can be modified according to their own business

package com.ruoyi.common.util;


import com.lowagie.text.pdf.BaseFont;
import com.platform.domain.ProcessJobresumeinfo;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import org.w3c.dom.Document;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
/**
 *Function: pdf processing tool class
 *
 */
public class PdfUtils {
    private PdfUtils() {
    }

    private static final Logger LOGGER = LoggerFactory.getLogger(PdfUtils.class);

    /**
     *Generate html strings according to templates and parameters, and then convert them to documents recognized by flying saucer
     *
     *@ param templatename freemaker template name
     *@ param variables freemaker template parameters
     * @return Document
     */
    private static Document generateDoc(FreeMarkerConfigurer configurer, String templateName, ProcessJobresumeinfo variables)  {
        Template tp;
        try {
            tp = configurer.getConfiguration().getTemplate(templateName);
        } catch (IOException e) {
            LOGGER.error(e.getMessage(), e);
            return null;
        }

        StringWriter stringWriter = new StringWriter();
        try(BufferedWriter writer = new BufferedWriter(stringWriter)) {
            try {
                tp.process(variables, writer);
                writer.flush();
            } catch (TemplateException e) {
                LOGGER.error("template does not exist or path error", e);
            } catch (IOException e) {
                LOGGER.error("IO exception", e);
            }
            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            return builder.parse(new ByteArrayInputStream(stringWriter.toString().getBytes()));
        }catch (Exception e){
            LOGGER.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     *Core: generate pdf document according to freemaker template
     *
     *@ param configurer freemaker configuration
     *@ param templatename freemaker template name
     *@ param out output stream
     *@ param info freemaker template parameters
     *@ throws Exception template cannot be found, template syntax error, IO exception
     */
    private static void generateAll(FreeMarkerConfigurer configurer, String templateName, OutputStream out,ProcessJobresumeinfo  info) throws Exception {
        if (info==null) {
            LOGGER.warn("warning: resume template parameter is empty!");
            return;
        }

        ITextRenderer renderer = new ITextRenderer();
        Document doc = generateDoc(configurer, templateName, info);
        renderer.setDocument(doc, null);
        //Set the character set (Song typeface). It must be consistent with < body style = "font family: Simsun" > in the template. It is case sensitive and cannot be written as the Chinese character "Song typeface"
        ITextFontResolver fontResolver = renderer.getFontResolver();
        fontResolver.addFont("simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        //Presentation and output pdf
        renderer.layout();
        renderer.createPDF(out, false);
        renderer.finishPDF(); // Complete pdf writing
    }

    /**
     *pdf download
     *
     *@ param configurer freemaker configuration
     *@ param templatename freemaker template name (with suffix. ftl)
     *@ param info template parameter set
     * @param response     HttpServletResponse
     *@ param fileName download file name (with file extension suffix)
     */
    public static void download(FreeMarkerConfigurer configurer, String templateName, ProcessJobresumeinfo info, HttpServletResponse response, String fileName) {
        //Set encoding, file ContentType, file header and download file name
        response.setCharacterEncoding("utf-8");
        response.setContentType("multipart/form-data");
        try {
            response.setHeader("Content-Disposition", "attachment;fileName=" +
                    new String(fileName.getBytes("gb2312"), "ISO8859-1"));
        } catch (UnsupportedEncodingException e) {
            LOGGER.error(e.getMessage(), e);
        }
        try (ServletOutputStream out = response.getOutputStream()) {
            generateAll(configurer, templateName, out, info);
            out.flush();
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
    }

    /**
     *pdf Preview
     *
     *@ param configurer freemaker configuration
     *@ param templatename freemaker template name (with suffix. ftl)
     *@ param info template parameter set
     * @param response     HttpServletResponse
     */
    public static void preview(FreeMarkerConfigurer configurer, String templateName, ProcessJobresumeinfo info, HttpServletResponse response) {
        try (ServletOutputStream out = response.getOutputStream()) {
            generateAll(configurer, templateName, out, info);
            out.flush();
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
    }
}

Note that there are three things here. Add pdfresume to the templates under resources FTL template (add it yourself) and a Simsun TTC font, which is said to be available in the window system, but not in my system. You can search and download resources on Baidu. It is not shared here

 

 

flt template

<!DOCTYPE html>
<html>
<head lang="en">
    <title>${name}Resume information</title>
    <style>
        @page {
            size: 210mm 297mm; /*Set the paper size: A4 (210mm, 297mm), A3 (297mm, 420mm) and vice versa in the transverse direction*/
            margin: 0.25in;
            padding: 1em;
            @bottom-center {
                content: "Copyright wanxiangzhonghe";
                font-family: SimSun;
                font-size: 12px;
                color: red;
            };
            @top-center {
                content: element(header)
            };
            @bottom-right {
                content: "The first" counter(page) "Page of " counter(pages) "page";
                font-family: SimSun;
                font-size: 12px;
                color: #000;
            };
        }
    </style>
</head>
<body style="font-family: 'SimSun'">
<h2>curriculum vitae</h2><br/>
<div>
    <label>full name: ${name}</label><br/>
</div>
<div>
    <label>Gender:
        <#if sex==0>
            unknown
        </#if>
        <#if sex==1>
            male
        </#if>
        <#if sex==2>
            female
        </#if>
    </label><br/>
</div>
<#if birthday??>
    <div>
        <label>date of birth: ${birthday?string("yyyy-MM-dd")}</label>
    </div>
</#if>
<#if phone??>
    <div>
        <label>contact number: ${phone} </label>
    </div>
</#if>
<#if weixin??>
    <div>
        <label>wechat number: ${weixin}</label>
    </div>
</#if>
<#if advantage??>
    <div>
        <label>Personal advantage: ${advantage}</label>
    </div><br/>
</#if>

<h3>Job expectation</h3>

<#if hopes?exists>
    <#list hopes as hope>
        <h4>Job expectation ${hope_index+1}</h4>
        <#if hope.jobtype??>
            <div>
                <label>Job type: ${hope.jobtype}</label>
            </div>
        </#if>

        <#if hope.jobcity??>
            <div>
                <label>Work city: ${hope.jobcity}</label>
            </div>
        </#if>
        <#if hope.jobtype??>
            <div>
                <label>Expected position: ${hope.jobtype}</label>
            </div>
        </#if>

        <#if hope.job??>
            <div>
                <label>Expected industry: ${hope.job}</label>
            </div>
        </#if>
        <div>
            <label>Salary requirements:
                <#if hope.paymin??>
                    ${hope.paymin}
                </#if>
                <#if hope.paymax??>
                    ${hope.paymax}
                </#if>
            </label>
        </div><br/><br/>
    </#list>
</#if>
<h3>work experience</h3>
<#if workExpers?exists>
    <#list workExpers as we>
        <h4>work experience ${we_index+1}</h4>
        <#if we.cname??>
            <div>
                <label>corporate name: ${we.cname}</label>
            </div>
        </#if>

        <#if we.business??>
            <div>
                <label>Industry: ${we.business}</label>
            </div>
        </#if>
        <#if we.addtime??>
            <div>
                <label>Entry time: ${we.addtime}</label>
            </div>
        </#if>

        <#if we.endtime??>
            <div>
                <label>Departure time: ${we.endtime}</label>
            </div>
        </#if>
        <#if we.jobtitle??>
            <div>
                <label>Job title: ${we.jobtitle}</label>
            </div>
        </#if>
        <#if we.department??>
            <div>
                <label>Department: ${we.department}</label>
            </div>
        </#if>
        <#if we.jobcontent??>
            <div>
                <label>job content: ${we.jobcontent}</label>
            </div>
        </#if>
        <br/>
    </#list>
</#if>

<h3>Project experience</h3>
<#if projectExps?exists>
    <#list projectExps as pj>
        <h4>project ${pj_index+1}</h4>
        <#if pj.projectname??>
            <div>
                <label>entry name: ${pj.projectname}</label>
            </div>
        </#if>
        <#if pj.role??>
            <div>
                <label>Play a role: ${pj.role}</label>
            </div>
        </#if>
        <#if pj.projectstarttime??>
            <div>
                <label>Project start time: ${pj.projectstarttime}</label>
            </div>
        </#if>
        <#if pj.projectendtime??>
            <div>
                <label>Project end time: ${pj.projectendtime}</label>
            </div>
        </#if>
        <#if pj.projectinfo??>
            <div>
                <label>Project link: ${pj.projectinfo}</label>
            </div>
        </#if>
        <br/>
    </#list>
</#if>

<h3>Educational experience</h3>
<#if educations?exists>
    <#list educations as ed>
        <h4>Educational experience ${ed_index+1}</h4>
        <#if ed.schoolname??>
            <div>
                <label>School name: ${ed.schoolname}</label>
            </div>
        </#if>
        <#if ed.education??>
            <div>
                <label>education: ${ed.education}</label>
            </div>
        </#if>
        <#if ed.profession??>
            <div>
                <label>major: ${ed.profession}</label>
            </div>
        </#if>
        <#if ed.estart??>
            <div>
                <label>time slot:
                    ${ed.estart} -- ${ed.eend}</label>
            </div>
        </#if>
        <#if ed.schoolexperience??>
            <div>
                <label>association activity :
                    ${ed.schoolexperience} </label>
            </div>
        </#if>
        <br/>
    </#list>
</#if>

<#if certificate??>
    <div>
        <label>qualification:
            ${certificate} </label>
    </div>
</#if>

</body>
</html>

III. self introduction of web Framework

controller info is the resume information. The operations of querying the database are not shared here

   
  /**
     * pdf preview
     *
     * @param request  HttpServletRequest
     * @param response HttpServletResponse
     */
    @RequestMapping(value = "/preview", method = RequestMethod.GET)
    public void preview(HttpServletRequest request, HttpServletResponse response) {
        String id = request.getParameter("id");
        Jobresumeinfo info = infoService.selectinfoById(id);
        PdfUtils.preview(configurer,"pdfResume.ftl",info,response);
    }



 /**
     * pdf download
     *
     * @param request  HttpServletRequest
     * @param response HttpServletResponse
     */
    @RequestMapping(value = "/download", method = RequestMethod.GET)
    public void download(HttpServletRequest request, HttpServletResponse response) {
        String id = request.getParameter("id");
        Jobresumeinfo info = infoService.selectinfoById(id);
        PdfUtils.download(configurer,"pdfResume.ftl",info,response,"Resume information");
    }

vue part

html

      <el-table-column label="operation" align="center" class-name="small-padding fixed-width">
        <template slot-scope="scope">
          <el-button size="mini"    type="text"  @click="downPDF(scope.row.user)">download</el-button>
          <el-button
            size="mini"
            type="text"
            @click="handleUpdate(scope.row)"
            v-hasPermi="['platform:jobResumeInfo:edit']"
          >see</el-button>
          <el-button  v-if="scope.row.status == '3' && scope.row.status != '4'"
            size="mini"
            type="text"
            @click="editWorkHours1(scope.row)"
            v-hasPermi="['platform:jobResumeInfo:edit']"
          >Fill in working hours</el-button>

          <el-button v-if="scope.row.status != '2' && scope.row.status != '3' && scope.row.status != '4' "
            size="mini"
            type="text"
            @click="refuseSubmitForm1(scope.row)"
            v-hasPermi="['platform:jobResumeInfo:edit']"
          >Decline</el-button>

          <el-button v-if="scope.row.status != '3' && scope.row.status != '4'  "
            size="mini"
            type="text"
            @click="passSubmitForm1(scope.row)"

          >adopt</el-button>


          <!--<el-button
            size="mini"
            type="text"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
            v-hasPermi="['platform:jobResumeInfo:remove']"
          >delete</el-button> -->
        </template>
      </el-table-column>

js

 /** Download pdf resume**/
    downPDF(row){
   
      var pdfname = row.name + "-"+row.phone;
      downPDF(row).then(response => {
         console.log(this.pdfUrl)
        const elink = document.createElement("a");

        elink.href = window.URL.createObjectURL(new Blob([response], {type: `application/pdf`}));
        elink.style.display = 'none';
        elink.setAttribute('download', pdfname);
        document.body.appendChild(elink);
        elink.click();
        URL.revokeObjectURL(elink.href); // Release URL object
        document.body.removeChild(elink);

      });

Topics: Spring Boot Vue.js Back-end pdf