前言

本篇博客主要解决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. wamp环境下composer及laravel的安装配置

    laravel: PHP Web开发框架 composer: PHP 的一个依赖管理工具.它允许你申明项目所依赖的代码库,它会在你的项目中为你安装他们. 一.composer安装 参考:Windows ...

  2. HBase安装学习

    一.下载安装 $ wget http://archive.cloudera.com/cdh5/cdh/5/hbase-1.2.0-cdh5.7.0.tar.gz $ tar -zxvf hbase-1 ...

  3. docker系列(三):docker容器

    1 引言 在前面博文中,我们介绍了镜像.如果说镜像犹如面向对象中的类,本节要说的容器就是由类实例化出来的对象了,有了类才可以创建容器. 先从拉取一个官方提供的ubuntu最新镜像: $ docker ...

  4. c++ 模板特化与局部特化

    c++ 模板特化与局部特化 模板的由来是要处理泛化,也就是任何类型都可以处理.但是泛化的同时,如果针对某种特殊的类型,又更加效率的处理方法.c++提供针对特殊的类型,可以定义不同的处理方法.针对某种特 ...

  5. wsl笔记

    目录 环境 修改更新源 新建用户 设置超级用户 环境变量 zsh美化终端 设zsh为默认shell 环境变量PATH 开启 ssh 远程连接 apt命令与问题 windows 和 wsl 互相访问文件 ...

  6. Linux:使用LVM进行磁盘管理

    LVM的概念 LVM 可以实现对磁盘的动态管理,在磁盘不用重新分区的情况下动态调整文件系统的大 小,利用 LVM 管理的文件系统可以跨越磁盘. "/boot"分区用于存放系统引导文 ...

  7. fiddler---Fiddler接口测试

    前面介绍了Fiddler一些简单的使用功能,Fiddler不仅可以抓包也可以做接口工具使用,在没有接口文档的时候我们也可以通过Fiddler查看接口具体有哪些内容 Fiddler发送请求 在Fiddl ...

  8. linux学习(三)系统目录结构

    登录系统后,在当前命令窗口下输入命令: ls / 你会看到如下图所示: 树状目录结构: 以下是对这些目录的解释: /bin:bin是Binary的缩写, 这个目录存放着最经常使用的命令. /boot: ...

  9. Linux通过端口号查看使用进程-结束进程

    1. 查看进程(参数带 - 与不带有区别): command [options] 例:ps  -a(配合其他options参数以展示进程更多参数) ps -ef | grep 进程名(返回值是该进程的 ...

  10. zz自动驾驶复杂环境下高精度定位技术

    今天为大家分享下,自动驾驶在复杂环境下的高精度定位技术. 定位/导航负责实时提供载体的运动信息,包括载体的:位置.速度.姿态.加速度.角速度等信息. 自动驾驶对定位系统的基本要求: 1. 高精度:达到 ...