前言

本篇博客主要解决java后台动态生成word(docx格式),并将word转换为pdf并添加水印。

思考

项目需求是要导出带水印的pdf,表格样式还是有点复杂的,之前考虑过用itextpdf根据html来生成pdf,但框架用的是前后台分

离的,前台用的是react,并且是在没有展示出表格的情况下,所以没法通过前台获取html代码块生成,后来又自己手动拼接

html,但代码量太大,难维护,且样式不怎么好看。所以决定用freemarker模板生成word,再转成pdf。翻阅网上很多资料给

出了很多方案将word转pdf,有用poi的、有用第三方工具的等等。用poi的写的都太复杂,jar引用很多,用第三方工具的有局

限性,不适合夸平台,需要安装服务。所以决定用docx4j,但docx4j只支持docx格式的word转pdf,所以需要freemarker

生成docx的word。

动手

1、pom引入依赖

        <dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.4.3</version>
</dependency> <dependency>
<groupId>freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency> <dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j</artifactId>
<version>6.1.2</version>
</dependency>
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j-export-fo</artifactId>
<version>6.0.0</version>
</dependency>

2、freemarker生成word(docx)

  之前用freemarker生成word的时候都是生成doc格式的,将doc模板文件保存为xml格式再生成word,生成出来的其实是XML文件,

只不过文件后缀名是doc的,即使在生成的时候将文件后缀名改为docx,生成出来的文件是打不开的。其实docx本质上是个压缩包,

里面有包含word主要内容的document.xml文件、资源定义文件document.xml.rels、还有页眉页脚文件、图片资源等等,解决思路是替

换内容区的document.xml文件。

1)、准备docx模板文件,编辑插入占位符。

2)、将docx文件复制一份,将复制的文件后缀名改为zip

3)、拷贝出zip包里word路径下的document.xml文件,这个就是word的主内容文件。之前的那个tmp.docx和拷贝出的document.xml

这两个文件是我们所需要用到的两个模板文件,拷贝到项目工程里面去。

4)、工具类代码

 package com.eazytec.zqtong.common.utils;

 import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import org.docx4j.Docx4J;
import org.docx4j.convert.out.FOSettings;
import org.docx4j.fonts.IdentityPlusMapper;
import org.docx4j.fonts.Mapper;
import org.docx4j.fonts.PhysicalFonts;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.springframework.core.io.ClassPathResource; import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream; public class PdfUtil {
private static String separator = File.separator;//文件夹路径分格符 //=========================================生成申请表pdf=================================== /**
* freemark生成word----docx格式
* @param dataMap 数据源
* @param documentXmlName document.xml模板的文件名
* @param docxTempName docx模板的文件名
* @return 生成的文件路径
*/
public static String createApplyPdf(Map<String,Object> dataMap,String documentXmlName,String docxTempName) {
ZipOutputStream zipout = null;//word输出流
File tempPath = null;//docx格式的word文件路径
try {
//freemark根据模板生成内容xml
//================================获取 document.xml 输入流================================
ByteArrayInputStream documentInput = FreeMarkUtils.getFreemarkerContentInputStream(dataMap, documentXmlName, separator + "template" + separator + "downLoad" + separator);
//================================获取 document.xml 输入流================================
//获取主模板docx
ClassPathResource resource = new ClassPathResource("template" + File.separator + "downLoad" + File.separator + docxTempName);
File docxFile = resource.getFile(); ZipFile zipFile = new ZipFile(docxFile);
Enumeration<? extends ZipEntry> zipEntrys = zipFile.entries(); //输出word文件路径和名称
String fileName = "applyWord_" + System.currentTimeMillis() + ".docx";
String outPutWordPath = System.getProperty("java.io.tmpdir").replaceAll(separator + "$", "") + separator + fileName; tempPath = new File(outPutWordPath);
//如果输出目标文件夹不存在,则创建
if (!tempPath.getParentFile().exists()) {
tempPath.mkdirs();
}
//docx文件输出流
zipout = new ZipOutputStream(new FileOutputStream(tempPath)); //循环遍历主模板docx文件,替换掉主内容区,也就是上面获取的document.xml的内容
//------------------覆盖文档------------------
int len = -1;
byte[] buffer = new byte[1024];
while (zipEntrys.hasMoreElements()) {
ZipEntry next = zipEntrys.nextElement();
InputStream is = zipFile.getInputStream(next);
if (next.toString().indexOf("media") < 0) {
zipout.putNextEntry(new ZipEntry(next.getName()));
if ("word/document.xml".equals(next.getName())) {
//写入填充数据后的主数据信息
if (documentInput != null) {
while ((len = documentInput.read(buffer)) != -1) {
zipout.write(buffer, 0, len);
}
documentInput.close();
}
}else {//不是主数据区的都用主模板的
while ((len = is.read(buffer)) != -1) {
zipout.write(buffer, 0, len);
}
is.close();
}
}
}
//------------------覆盖文档------------------
zipout.close();//关闭 //----------------word转pdf--------------
return convertDocx2Pdf(outPutWordPath); } catch (Exception e) {
e.printStackTrace();
try {
if(zipout!=null){
zipout.close();
}
}catch (Exception ex){
ex.printStackTrace();
}
}
return "";
} /**
* word(docx)转pdf
* @param wordPath docx文件路径
* @return 生成的带水印的pdf路径
*/
public static String convertDocx2Pdf(String wordPath) {
OutputStream os = null;
InputStream is = null;
try {
is = new FileInputStream(new File(wordPath));
WordprocessingMLPackage mlPackage = WordprocessingMLPackage.load(is);
Mapper fontMapper = new IdentityPlusMapper();
fontMapper.put("隶书", PhysicalFonts.get("LiSu"));
fontMapper.put("宋体", PhysicalFonts.get("SimSun"));
fontMapper.put("微软雅黑", PhysicalFonts.get("Microsoft Yahei"));
fontMapper.put("黑体", PhysicalFonts.get("SimHei"));
fontMapper.put("楷体", PhysicalFonts.get("KaiTi"));
fontMapper.put("新宋体", PhysicalFonts.get("NSimSun"));
fontMapper.put("华文行楷", PhysicalFonts.get("STXingkai"));
fontMapper.put("华文仿宋", PhysicalFonts.get("STFangsong"));
fontMapper.put("宋体扩展", PhysicalFonts.get("simsun-extB"));
fontMapper.put("仿宋", PhysicalFonts.get("FangSong"));
fontMapper.put("仿宋_GB2312", PhysicalFonts.get("FangSong_GB2312"));
fontMapper.put("幼圆", PhysicalFonts.get("YouYuan"));
fontMapper.put("华文宋体", PhysicalFonts.get("STSong"));
fontMapper.put("华文中宋", PhysicalFonts.get("STZhongsong")); mlPackage.setFontMapper(fontMapper); //输出pdf文件路径和名称
String fileName = "pdfNoMark_" + System.currentTimeMillis() + ".pdf";
String pdfNoMarkPath = System.getProperty("java.io.tmpdir").replaceAll(separator + "$", "") + separator + fileName; os = new java.io.FileOutputStream(pdfNoMarkPath); //docx4j docx转pdf
FOSettings foSettings = Docx4J.createFOSettings();
foSettings.setWmlPackage(mlPackage);
Docx4J.toFO(foSettings, os, Docx4J.FLAG_EXPORT_PREFER_XSL); is.close();//关闭输入流
os.close();//关闭输出流 //添加水印
return addTextMark(pdfNoMarkPath);
} catch (Exception e) {
e.printStackTrace();
try {
if(is != null){
is.close();
}
if(os != null){
os.close();
}
}catch (Exception ex){
ex.printStackTrace();
}
}finally {
File file = new File(wordPath);
if(file!=null&&file.isFile()&&file.exists()){
file.delete();
}
}
return "";
} /**
* 添加水印图片
* @param inPdfPath 无水印pdf路径
* @return 生成的带水印的pdf路径
*/
private static String addTextMark(String inPdfPath) {
PdfStamper stamp = null;
PdfReader reader = null;
try {
//输出pdf带水印文件路径和名称
String fileName = "pdfMark_" + System.currentTimeMillis() + ".pdf";
String outPdfMarkPath = System.getProperty("java.io.tmpdir").replaceAll(separator + "$", "") + separator + fileName; //添加水印
reader = new PdfReader(inPdfPath, "PDF".getBytes());
stamp = new PdfStamper(reader, new FileOutputStream(new File(outPdfMarkPath)));
PdfContentByte under;
int pageSize = reader.getNumberOfPages();// 原pdf文件的总页数
//水印图片
ClassPathResource resource = new ClassPathResource("template" + File.separator + "downLoad" + File.separator + "mark.png");
File file = resource.getFile();
Image image = Image.getInstance(file.getPath());
for (int i = 1; i <= pageSize; i++) {
under = stamp.getUnderContent(i);// 水印在之前文本下
image.setAbsolutePosition(100, 210);//水印位置
under.addImage(image);
}
stamp.close();// 关闭
reader.close();//关闭 return outPdfMarkPath;
} catch (Exception e) {
e.printStackTrace();
try {
if (stamp != null) {
stamp.close();
}
if (reader != null) {
reader.close();//关闭
}
} catch (Exception ex) {
ex.printStackTrace();
}
} finally {
//删除生成的无水印pdf
File file = new File(inPdfPath);
if (file != null && file.exists() && file.isFile()) {
file.delete();
}
}
return "";
} public static void main(String[] args) {
// createApplyPdf();
// convert();
// addTextMark("D:\\test\\test.pdf", "D:\\test\\test2.pdf");
} }
package com.eazytec.zqtong.common.utils;

import freemarker.template.Configuration;
import freemarker.template.Template; import java.io.*;
import java.util.Map; /**
* 获取freemarker模板字符串
*/
public class FreeMarkUtils { /**
* 获取模板字符串
* @param dataMap 参数
* @param templateName 模板名称
* @param temp_path 模板路径 classes下的路径 如果 classes/templates 传入 /templates即可
* @return
*/
public static String getFreemarkerContent(Map dataMap, String templateName, String temp_path) {
String result = "";
try {
//创建配置实例
Configuration configuration = new Configuration(); //设置编码
configuration.setDefaultEncoding("UTF-8"); configuration.setClassForTemplateLoading(FreeMarkUtils.class, temp_path);
//获取模板
Template template = configuration.getTemplate(templateName); StringWriter swriter = new StringWriter();
//生成文件
template.process(dataMap, swriter);
result = swriter.toString(); } catch (Exception e) {
e.printStackTrace();
}
return result;
} /**
* 生成主数据模板xml
*
* @param dataMap 数据参数
* @param templateName 模板名称 eg: xxx.xml
* @param pathPrefix 模板路径 eg: /templates/
* @param filePath 生成路径 eg: d:/ex/ee/xxx.xml
*/
public static void createTemplateXml(Map dataMap, String templateName, String pathPrefix, String filePath) {
try {
//创建配置实例
Configuration configuration = new Configuration(); //设置编码
configuration.setDefaultEncoding("UTF-8"); //ftl模板文件统一放至 com.lun.template 包下面
// configuration.setDirectoryForTemplateLoading(new File("D:/idea_workspace/alarm/alarm/src/main/resources/template/"));
// configuration.setClassForTemplateLoading(FreemarkerWordUtils.class,"/template/doc");
configuration.setClassForTemplateLoading(FreeMarkUtils.class, pathPrefix);
//获取模板
Template template = configuration.getTemplate(templateName);
// System.out.println("filePath ==> " + filePath);
//输出文件
File outFile = new File(filePath);
// System.out.println("outFile.getParentFile() ==> " + outFile.getParentFile());
//如果输出目标文件夹不存在,则创建
if (!outFile.getParentFile().exists()) {
outFile.getParentFile().mkdirs();
} //将模板和数据模型合并生成文件
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "UTF-8")); //生成文件
template.process(dataMap, out); //关闭流
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 获取模板字符串输入流
* @param dataMap 参数
* @param templateName 模板名称
* @param tempPath 模板路径 classes下的路径 如果 classes/templates 传入 /templates即可
* @return
*/
public static ByteArrayInputStream getFreemarkerContentInputStream(Map dataMap, String templateName, String tempPath) {
ByteArrayInputStream in = null; try {
//创建配置实例
Configuration configuration = new Configuration(); //设置编码
configuration.setDefaultEncoding("UTF-8"); //ftl模板文件统一放至 com.lun.template 包下面
// configuration.setDirectoryForTemplateLoading(new File("D:/idea_workspace/alarm/alarm/src/main/resources/template/"));
configuration.setClassForTemplateLoading(FreeMarkUtils.class, tempPath);
//获取模板
Template template = configuration.getTemplate(templateName); StringWriter swriter = new StringWriter();
//生成文件
template.process(dataMap, swriter);
String result = swriter.toString();
in = new ByteArrayInputStream(swriter.toString().getBytes()); } catch (Exception e) {
e.printStackTrace();
}
return in;
}
}

问题

生成文件下载的时候感觉稍微有点慢,其实文件不大,有个8、9秒的样子才会有下载框出现,不知道是不是前后台分离的原因。

java实现word生成并转pdf的更多相关文章

  1. java操作office和pdf文件java读取word,excel和pdf文档内容

    在平常应用程序中,对office和pdf文档进行读取数据是比较常见的功能,尤其在很多web应用程序中.所以今天我们就简单来看一下Java对word.excel.pdf文件的读取.本篇博客只是讲解简单应 ...

  2. java操作word,excel,pdf

    在平常应用程序中,对office和pdf文档进行读取数据是比较常见的功能,尤其在很多web应用程序中.所以今天我们就简单来看一下java对word.excel.pdf文件的读取.本篇博客只是讲解简单应 ...

  3. java 实现Word或Excel 转Pdf

    1:首先需要引入相关的jar word转pdf需要引入 aspose-words-15.8.0-jdk16.jar 下载JAR包 Word http://note.youdao.com/notesha ...

  4. word生成目录的pdf

    在很多情况下,需要将Word转换为带目录书签的PDF,方便pdf阅读,所以可以使用word自带的pdf转换,在转换时设置相关即可 注意:待转换Word中应该有目录,可以用Word中的标题来自动生成目录 ...

  5. [转载]java操作word生成水印

    应用场景 为了保护版权或辨别文件的真伪,有时需要在生成的Word文件中动态添加水印,PageOffice组件的WaterMark类就封装了给在线编辑的Word文件添加水印这一功能,调用接口非常简单. ...

  6. [原创]java操作word生成水印

    应用场景 为了保护版权或辨别文件的真伪,有时需要在生成的Word文件中动态添加水印,PageOffice组件的WaterMark类就封装了给在线编辑的Word文件添加水印这一功能,调用接口非常简单. ...

  7. Java实现windows,linux服务器word,excel转为PDF;aspose-words,Documents4j

    Java实现windows,linux服务器word,excel转为PDF:aspose-words,Documents4j 一.通过aspose-words将word,Excel文档转为PDF 1. ...

  8. 用java实现word转pdf

    摘要:如何用java实现word文档转pdf呢 最近在网上看了很多资料,遇到了很多头疼的问题,看了各类大神写的方法,最初想要研究的是在线预览word 现在来看,不太现实,除了微软研究的一套在线预览的u ...

  9. Linux系统下Java 转换Word到PDF时,结果文档内容乱码的解决方法

    本文分享在Linux系统下,通过Java 程序代码将Word转为PDF文档时,结果文档内容出现乱码该如何解决.具体可参考如下内容: 1.问题出现的背景 在Windows系统中,使用Spire.Doc ...

随机推荐

  1. vs code切换中英文界面

    1. 在vs code的应用扩展中搜索这个插件: Chinese (Simplified) Language Pack for Visual Studio Code 这个插件,安装完毕重新加载即可生效 ...

  2. [日常] gocron源码阅读-go语言的变量

    变量的声明形式是这样的 var 变量名字 类型 = 表达式var ( AppVersion = "1.5" BuildDate, GitCommit string)类型可以被省略, ...

  3. CodeForces - 1248D1 (思维+暴力)

    题意 有一个括号序列,你可以选择两个位置i,j(i可以等于j),进行交换.使得最后的循环位置(i的数目)最大. 循环位置:i(0<=i<len),将前i个字符移到最后,得到的新序列是合法的 ...

  4. pushgateway

    下载pushgateway wget https://github.com/prometheus/pushgateway/releases/download/v0.9.0/pushgateway-0. ...

  5. TeamyinyinFish-> 测试报告

    测试人员 1. 姓名:鱼嘤嘤 2. 年龄: 21 3. 职业: 大三学生 4. 感情状况: 单身 用户故事 软件工程课上,老师想要他们组实现一个小组项目,鱼嘤嘤原本想摸鱼,但是转念一想觉得 还是需要好 ...

  6. postgres 计算时差

    计算时间差秒数 select extract(epoch FROM (now() - (now()-interval '1 day') ));

  7. win10 x64 VS2017 PJSIP 视频通话编译流程

    win10 x64 VS2017 PJSIP 视频通话编译流程 1. 下载PJSIP源码 PJSIP源码下载地址:https://www.pjsip.org/ 2. 阅读一遍官方的文档 文档地址:ht ...

  8. JavaScript对象及初识面向对象

    一.对象 1.1对象是什么 对象是包含相关属性和方法的集合体 1.2什么是面向对象 面向对象仅仅是一个概念或者编程思想 通过一种叫做原型的方式来实现面向对象编程 二.创建对象 2.1自定义对象 2.1 ...

  9. 论文阅读: v-charge项目: 电动车的自动泊车和充电

    Abstract AVP服务会缓和电动车现有两个缺点: 有限的行驶范围和很长的充电时间. v-charge用相机和超声波在GPS-denied的区域全自动形式. 这篇paper叙述了下述几方面的优势: ...

  10. java优化细节记录

    此处是为了记录一些优化细节,从网上收集而来,仅供后续代码开发参考使用,如发现更好的,会不断完善 首先确认代码优化的目标是: 减小代码的体积 提高代码运行的效率 代码优化细节 1.尽量指定类.方法的fi ...