通过FreeMarker生成word文档及到处PDF文件

1.导出流程

本次PDF简历信息导出的处理流程可以简化为如下操作,下面会详细说明每一步的具体操作。

  • 创建好导出用Word模板并转存为xml文件
  • 用FreeMarker语法替换内容生成ftl模板
  • 将生成的word上传到文件服务器,并返回地址Url给前端
  • 前端通过url进行网页预览或导出PDF操作

2.创建Word模板、将word转存为xml文件

导出PDF文件自己总结以下两种方式:

第一种 Doc文件转换成PDF文件

第二种 Html转换成PDF文件

在考虑将所需要信息填充入过程中Doc或Html中时,发现可以通过FreeMarker将所需信息填充入Word模板中,考虑到后期有打印简历的需要,用word文档打印的效果会更好,所以本次考虑用word创建模板再转换成PDF文件。

FreeMarker是一款模板引擎:基于模板和要改变的数据,生成输出文本(Word、Html)的通用工具。它是一款简单的、专用语言,在模板中,我们只需专注如何显示数据,而在模板外专注于展示的数据。

3.生成ftl文件模板

3.1 格式化xml文档

通过网站https://www.bejson.com/otherformat/xml/对另存为生成的xml文件进行格式化,格式化能够是我们方便的看清楚文档的结构,以及做内容的替换。

3.2 进行模板内容的替换

maven包导入:

<dependency>
      <groupId>org.freemarker</groupId>
      <artifactId>freemarker</artifactId>
      <version>2.3.20</version>
  </dependency>

对模板进行编辑,将我们需要替换的内容给替换上去,例如图片中姓名那块对应如下代码。

<w:tblPrEx>
    <w:tblBorders>
        <w:top w:val="none" w:color="auto" w:sz="0" w:space="0"/>
        <w:left w:val="none" w:color="auto" w:sz="0" w:space="0"/>
        <w:bottom w:val="none" w:color="auto" w:sz="0" w:space="0"/>
        <w:right w:val="none" w:color="auto" w:sz="0" w:space="0"/>
        <w:insideH w:val="none" w:color="auto" w:sz="0" w:space="0"/>
        <w:insideV w:val="none" w:color="auto" w:sz="0" w:space="0"/>
    </w:tblBorders>
    <w:tblLayout w:type="fixed"/>
    <w:tblCellMar>
        <w:top w:w="0" w:type="dxa"/>
        <w:left w:w="108" w:type="dxa"/>
        <w:bottom w:w="0" w:type="dxa"/>
        <w:right w:w="108" w:type="dxa"/>
    </w:tblCellMar>
</w:tblPrEx>
<w:trPr>
    <w:trHeight w:val="522" w:hRule="atLeast"/>
</w:trPr>
<w:tc>
    <w:tcPr>
        <w:tcW w:w="2590" w:type="dxa"/>
    </w:tcPr>
    <w:p>
        <w:pPr>
            <w:jc w:val="left"/>
            <w:rPr>
                <w:rFonts w:ascii="Heiti SC Light" w:eastAsia="Heiti SC Light"/>
            </w:rPr>
        </w:pPr>
        <w:r>
            <w:rPr>
                <w:rFonts w:hint="eastAsia" w:ascii="Heiti SC Light" w:eastAsia="Heiti SC Light"/>
                <w:color w:val="7F7F7F" w:themeColor="background1" w:themeShade="80"/>
            </w:rPr>
            <w:t>姓名:</w:t>
        </w:r>
        <w:r>
            <w:rPr>
                <w:rFonts w:hint="eastAsia" w:ascii="Heiti SC Light" w:eastAsia="Heiti SC Light"/>
            </w:rPr>
            <w:t>xxxxxxxx</w:t>
        </w:r>
    </w:p>
</w:tc>

而我们需要做的是把xxxxxxxx替换成${name}的FreeMarker语法,这样在生成时,就会用name的值去替换xxxxxx这块内容了。在完成模板的修改后,将xml文件另存为ftl文件。

3.3 导入模板用到的FreeMarker语法:
3.3.1 文字内容替换: ${name} ,xml模板中图片会以base64储存,若需要替换图片只需${base64Pic}传入对应图片base64码即可。
3.3.2 if else 判断:
<#if isRelativesInCompany == "0" >
        aaa
    <#else>
        bbb
    </#if>
3.3.3 list集合遍历: <#list workList as work> 即会遍历集合,通过work.就可以将每次遍历出来的数据取出来。
<#list workInfo as work>
        <w:r>
          <w:rPr>
              <w:rFonts w:hint="eastAsia" w:ascii="Heiti SC Light" w:eastAsia="Heiti SC Light"/>
              <w:color w:val="7F7F7F" w:themeColor="background1" w:themeShade="80"/>
          </w:rPr>
          <w:t>起止日期:</w:t>
      </w:r>
      <w:r>
          <w:rPr>
              <w:rFonts w:hint="eastAsia" w:ascii="Heiti SC Light" w:eastAsia="Heiti SC Light"/>
          </w:rPr>
          <w:t>${work.time}</w:t>
      </w:r>
    </#list>
3.3.4 判断list集合长度:<#if (workList?size == 0 )></#if>
3.3.5 判断list集合是否到达最后一条:<#if work_has_next></#if>

4.生成doc文件并上传到文件服务器

//1、生成template模板,一共有三种方式,我通过的是获取web项目上下文去获取
    try {
        Configuration configuration = new Configuration();
        configuration.setDefaultEncoding("UTF-8");
        configuration.setServletContextForTemplateLoading(session.getServletContext(),"/temp");
        Template template = configuration.getTemplate("applicant.ftl"); //文件名
    //本地创建空文件  
        String fileName = "outFile"+sdf.format(new Date())+(int)(Math.random()*100)+".doc";
        File outFile = new File(fileName);
        outFile.createNewFile();
        Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),"UTF-8"));
    //将内容写进创建的outFile文件中
        template.process(hashMap, out);
    //将生成的doc上传到文件服务器,并返回访问地址
        filePath = UploadUtils.getDfsProductPath(outFile);
        out.close();
    //删除本地创建的文件
        outFile.delete();
    } catch (Exception e) {
        e.printStackTrace();
    }

5.实现文档在线预览与PDF文件下载

在拿到生成后的doc文档后,接下来要做的便是将文件转换成PDF的问题了,于是百度、Google了一通发现大致的解决方法分为:

1.使用Jacob,但是使用jacob中要依赖Office,部分博文中还会依赖插件,如SaveAsPDFandXPS.exe。但是也发现不需要依赖Office,可以使用wps、pdfcreator,在使用wps的时候还不需要安装插件(注意:wps有linux版,office到现在为止还没有linux版)

2.OpenOffice,可以结合Jodconverter开源框架和OpenOffice.org办公软件,具有跨平台的优点,转化速度快,但是部分office的格式似乎不支持。

3.Adobe Acrobat + jacob,这个用到什么虚拟打印机,和微软的一起使用效果比较好。(这个我不太懂)

4.Jcom + Adobe Acrobat ,会用到IDispatch。 (这4段是拷贝的)

原本以为doc转pdf会很简单,没想到看似简单的功能却有大学问,在转换时要么会兼容性不好样式出现错乱,要么是跨平台兼容性问题,总之转换起来很麻烦。在纠结了许久后,偶然想到公司知识库有文档预览功能。发现预览功能是直接购买的外部服务,既然是RMB玩家,那就去该公司的官网去看,还真发现了有帮你将doc文件转成PDF的功能,美滋滋的我就直接拿来用了。(有的文件预览也有免费的体验版)可以实现的功能是

文件浏览器预览: http://xxxx/?i=您的网站ID&fname=简历预览&furl=要预览的Office文件下载地址 

PDF下载:http://xxxx/?i=您的网站ID&fname=简历&furl=要预览的Office文件下载地址

6.最后总结:

看似很复杂的生成PDF文档功能,没想到在实际操作实现过程中并没有想象中那么复杂(除了word转PDF那块),通过FreeMarker的应用,能够轻松的实现word模板数据的填充,拿到我们需要的DOC的word文件。当然这个实现方法也有一个最大的弊端:每一次模板的改动,就需要重新FreeMarker去重新填充,这样操作起来会非常的机械,如果下次还有机会的话就去尝试用HTML生成PDF了。。。。。。