欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

freemarker动态生成word并将生成的word转为PDF,openoffice转换word乱码

程序员文章站 2022-06-28 20:19:09
freemarker动态生成word并将生成的word转为PDF,openoffice转换word乱码 ......

之前项目有个需求,需要先动态生成word内容,然后再预览生成word的内容(不能修改).整理一下,方便以后使用.

网上参考了好多大神的博客.具体也忘了参考谁的了,如有侵权,请告知修改.

思路一:

将目标word文件另存为xml文件,将里面的需要动态生成的内容用freemarker的表达式${}替换.

用freemarker生成word的工具类,动态生成word. 这样生成的word实际上是xml文件,用办公软件能正常打开使用.

但是转pdf的时候发现转不成功.转过之后的pdf显示的不是word的格式字符,而是像xml文件的标签及字符,失败!

思路二:

word的docx文件其实属于zip的一种. 这里只需要对它的核心内容部分进行操作.其他数据不动.具体做法为:

1.用办公软件(wps/office)打开模板文件,将需要修改的内容,用freemarker的表达式${}替换.

(注意:需要循环展示的内容还需要在xml文件中处理)如下:

 freemarker动态生成word并将生成的word转为PDF,openoffice转换word乱码

 

2.将模板docx文件重命名为.zip的压缩文件.

3.用解压工具打开,取出word/document.xml 文件.

 freemarker动态生成word并将生成的word转为PDF,openoffice转换word乱码

 

4.此时用文本工具打开document.xml,内容不太好看,将文件格式化一下.(我这里没找到好的格式化工具,使用notepad没格好,最后用idea还行).格式化后如下.

 freemarker动态生成word并将生成的word转为PDF,openoffice转换word乱码

 

5.在xml中需要循环的内容前增加如下标签:

 freemarker动态生成word并将生成的word转为PDF,openoffice转换word乱码

 

6.说明

word中要填充的数据为map格式,${}中为map的key.如果还需要循环填充可以如下操作:

map1   map2   list

map1.put("username",name);

list.add(map1);

map2.put("list",list);

map2.put("title",title);

map2即为要填充的所有数据.这样给list一个别名listkey 后,${}中如下填写即可.

 freemarker动态生成word并将生成的word转为PDF,openoffice转换word乱码

 

7.将模板文件与xml文件保存到一个固定位置.我这里保存到了项目中:

 freemarker动态生成word并将生成的word转为PDF,openoffice转换word乱码

 

8.准备工作完成,生成word工具类如下:

import freemarker.template.templateexception;

import java.io.*;
import java.util.enumeration;
import java.util.map;
import java.util.zip.zipentry;
import java.util.zip.zipexception;
import java.util.zip.zipfile;
import java.util.zip.zipoutputstream;

/**
* 其实docx属于zip的一种,这里只需要操作word/document.xml中的数据,其他的数据不用动
*
* @author
*
*/
public class xmltodocx {

/**
*
* @param xmltemplate xml的文件名
* @param docxtemplate docx的路径和文件名
* @param xmltemp 填充完数据的临时xml
* @param tofilepath 目标文件名
* @param map 需要动态传入的数据
* @throws ioexception
* @throws templateexception
*/
public static void todocx(string xmltemplate,string docxtemplate,string xmltemp ,string tofilepath,map map) {
try {
// 1.map是动态传入的数据
// 这个地方不能使用filewriter因为需要指定编码类型否则生成的word文档会因为有无法识别的编码而无法打开
writer w1 = new outputstreamwriter(new fileoutputstream(xmltemp), "gb2312");
// 2.把map中的数据动态由freemarker传给xml
xmltplutil.process(xmltemplate, map, w1);
// 3.把填充完成的xml写入到docx中
xmltodocx xtd = new xmltodocx();
xtd.outdocx(new file(xmltemp), docxtemplate, tofilepath);
}catch (exception e) {
e.printstacktrace();
}
}
/**
*
* @param documentfile 动态生成数据的docunment.xml文件
* @param docxtemplate docx的模板
* @param tofilepath 需要导出的文件路径
* @throws zipexception
* @throws ioexception
*/

public void outdocx(file documentfile, string docxtemplate, string tofilepath) throws zipexception, ioexception {

try {
file docxfile = new file(docxtemplate);
zipfile zipfile = new zipfile(docxfile);
enumeration<? extends zipentry> zipentrys = zipfile.entries();
zipoutputstream zipout = new zipoutputstream(new fileoutputstream(tofilepath));
int len = -1;
byte[] buffer = new byte[1024];
while (zipentrys.hasmoreelements()) {
zipentry next = zipentrys.nextelement();
inputstream is = zipfile.getinputstream(next);
// 把输入流的文件传到输出流中 如果是word/document.xml由我们输入
zipout.putnextentry(new zipentry(next.tostring()));
if ("word/document.xml".equals(next.tostring())) {
inputstream in = new fileinputstream(documentfile);
while ((len = in.read(buffer)) != -1) {
zipout.write(buffer, 0, len);
}
in.close();
} else {
while ((len = is.read(buffer)) != -1) {
zipout.write(buffer, 0, len);
}
is.close();
}
}
zipout.close();

} catch (exception e) {
e.printstacktrace();
}
}
}
9.生成pdf工具类


import com.artofsolving.jodconverter.documentconverter;
import com.artofsolving.jodconverter.openoffice.connection.openofficeconnection;
import com.artofsolving.jodconverter.openoffice.connection.socketopenofficeconnection;
import com.artofsolving.jodconverter.openoffice.converter.openofficedocumentconverter;
import org.apache.poi.xwpf.converter.pdf.pdfconverter;
import org.apache.poi.xwpf.converter.pdf.pdfoptions;
import org.apache.poi.xwpf.usermodel.xwpfdocument;

import java.io.*;


public class xmltodoc {

/**
* 生成pdf
*/
public static string makepdfbyxcode(string docx) {
string filename = null;
file outfile = null;
try {

// document.setparagraph(new pa );
if (docx.contains(".docx")) {
xwpfdocument document=new xwpfdocument(new fileinputstream(new file(docx)));
outfile=new file(docx.replace(".docx",".pdf"));
filename=docx.replace(".docx",".pdf");

outfile.getparentfile().mkdirs();
outputstream out=new fileoutputstream(outfile);
// ifontprovider fontprovider = new abstractfontregistry();
pdfoptions options= pdfoptions.create(); //gb2312
pdfconverter.getinstance().convert(document,out,options);

} else {
file inputfile = new file(docx);
outfile = new file(docx.replace(".doc", ".pdf"));
filename = docx.replace(".doc", ".pdf");
outfile.getparentfile().mkdirs();

openofficeconnection connection = new socketopenofficeconnection(8100);
connection.connect();

// convert
documentconverter converter = new openofficedocumentconverter(connection);
converter.convert(inputfile, outfile);

// close the connection
connection.disconnect();
}


}catch (illegalargumentexception e){
system.err.println("未知文件格式");
}
catch (exception e) {
e.printstacktrace();
}
return filename;

}


}
import freemarker.template.configuration;
import freemarker.template.template;
import freemarker.template.templateexception;

import java.io.file;
import java.io.ioexception;
import java.io.writer;
import java.util.map;

public class xmltplutil {

private static xmltplutil tplm = null;
private configuration cfg = null;

private xmltplutil() {
cfg = new configuration();
try {
// 注册tmlplate的load路径
// 这的路径是xml的路径
string pathname = xmltplutil.class.getclassloader().getresource("").getpath();
string path = pathname.substring(1, pathname.lastindexof("/"));
string parentpath1 = new file(path).getparent();//获取项目的上一级目录
string parentpath2 = new file(parentpath1).getparent();//获取项目的上一级目录
string xmlpath = parentpath2 + "/static/excelmodel";
cfg.setdirectoryfortemplateloading(new file(xmlpath));
} catch (exception e) {
e.printstacktrace();
}
}

private static template gettemplate(string name) throws ioexception {
if (tplm == null) {
tplm = new xmltplutil();
}
template template = tplm.cfg.gettemplate(name);
return template;
}

/**
*
* @param templatefile 模板文件
* @param param 需要填充的内容
* @param out 填充完成输出的文件
* @throws ioexception
* @throws templateexception
*/
public static void process(string templatefile, map param, writer out) throws ioexception, templateexception {
// 获取模板
template template = xmltplutil.gettemplate(templatefile);
template.setoutputencoding("gbk");
// 合并数据
template.process(param, out);
if (out != null) {
out.close();
}
}
}
注意:生成pdf需要安装openoffice 软件,安装完成后,

cd openoffice目录下有个openoffice 4\program

然后输入命令

  soffice -headless -accept="socket,host=127.0.0.1,port=8100;urp;" -nofirststartwizard 

就ok了.

10.用到的maven包

<dependency>
<groupid>com.artofsolving</groupid>
<artifactid>jodconverter</artifactid>
<version>2.2.1</version>
</dependency>
<!--openoffice-->
<dependency>
<groupid>org.openoffice</groupid>
<artifactid>jurt</artifactid>
<version>3.0.1</version>
</dependency>
<dependency>
<groupid>org.openoffice</groupid>
<artifactid>ridl</artifactid>
<version>3.0.1</version>
</dependency>
<dependency>
<groupid>org.openoffice</groupid>
<artifactid>juh</artifactid>
<version>3.0.1</version>
</dependency>
<dependency>
<groupid>org.openoffice</groupid>
<artifactid>unoil</artifactid>
<version>3.0.1</version>
</dependency>
<dependency>
<groupid>fr.opensagres.xdocreport</groupid>
<artifactid>org.apache.poi.xwpf.converter.pdf</artifactid>
<version>1.0.6</version>
</dependency>
<dependency>
<groupid>org.freemarker</groupid>
<artifactid>freemarker</artifactid>
<version>2.3.22</version>
</dependency>
---------------------
作者:菜鸟-也-想飞
来源:csdn
原文:https://blog.csdn.net/qq_21306669/article/details/84313569

注意:springboot打成jar无法放入webapp下生成,