freemark 模板生成doc文件,此doc文件为xml格式,无法直接转pdf
需要doc转docx文件,才能转pdf
方法1:docx4j 转 pdf
方法2: e-iceblue docx 转 pdf、xml-doc 转 pdf
打包方式,需要本地引入jar包,下载后,将lib里的jar包放入项目中
Free Spire.Doc for Java | 100% 免费 Java Word 组件
pom文件引入 ${project.basedir} 是系统变量,代表本目录,build配置是打包时,将本地文件加入项目
e-iceblue
spire.pdf
5.2.0
system
${project.basedir}/src/main/resources/lib/Spire.Doc.jar
${project.artifactId}
org.springframework.boot
spring-boot-maven-plugin
true
方法3:poi (可以自己搜教程)
依赖
com.itextpdf
itextpdf
5.5.13
org.xhtmlrenderer
flying-saucer-pdf-itext5
9.1.5
org.freemarker
freemarker
2.3.31
commons-io
commons-io
2.11.0
compile
org.docx4j
docx4j
6.1.2
org.slf4j
slf4j-log4j12
org.docx4j
docx4j-export-fo
6.0.0
e-iceblue
spire.doc.free
5.2.0
public
阿里云公共仓库
https://maven.aliyun.com/repository/public/
true
com.e-iceblue
https://repo.e-iceblue.cn/repository/maven-public/
/**
* @Author: LiSaiHang
* @Date: 2023/4/23 3:26 下午
*/
@RestController
@RequestMapping("/module/documents")
@Slf4j
public class FreeMarkController {
@GetMapping("/generate")
public void doc(HttpServletResponse response) {
Map dataMap = new HashMap<>();
///姓名
dataMap.put("name", "张三");
dataMap.put("phone", "15532123328");
dataMap.put("idCard", "130429199711113617");
dataMap.put("sex", "男");
String result = "";
String docName = System.currentTimeMillis() + (int) (Math.random() * 90000 + 10000) + ".doc";
String pdfName = System.currentTimeMillis() + (int) (Math.random() * 90000 + 10000) + ".pdf";
response.setHeader("content-Type", "application/octet-stream");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=" + pdfName);
// 下载输出流
ServletOutputStream outputStream = null;
// doc文件输出流
InputStream inputStream = null;
// pdf文件输出流
ByteArrayOutputStream fileOutputStream = null;
try {
// 获取渲染数据
result = FreeMarkUtils.freemarkerRender(dataMap, "test.doc");
inputStream = new ByteArrayInputStream(result.getBytes(StandardCharsets.UTF_8));
// 生成 doc 文件
FileUtils.copyInputStreamToFile(inputStream, new File(docName));
// doc 文件转docx文件
final String docxFile = this.docToDocx(docName);
// 方法1:docx文件转pdf文件
// this.docx4jWordToPdf(docxFile, pdfName);
// 方法2:docx文件转pdf文件
// com.spire.doc.Document 实例化Document类的对象
Document doc = new Document();
// 加载Word
doc.loadFromFile(docxFile);
// 保存为PDF格式 com.spire.doc.FileFormat
doc.saveToFile(pdfName, FileFormat.PDF);
// pdf 缩放设置
pdfPageSetting(pdfName);
// 获取文件输出流
fileOutputStream = FreeMarkUtils.getFileOutputStream(new File(pdfName));
// 设置文件名
outputStream = response.getOutputStream();
outputStream.write(fileOutputStream.toByteArray());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
if (fileOutputStream != null) {
fileOutputStream.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* doc转docx
* @param docPath doc文件路径
* @return
*/
private String docToDocx(String docPath) {
String docxPath = docPath.replaceAll("(\\.docx)|(\\.doc)", ".docx");
try (FileInputStream inputStream = new FileInputStream(docPath);) {
WordprocessingMLPackage wmlPackage = Docx4J.load(inputStream);
//转换为DOCX
try (FileOutputStream docx = new FileOutputStream(docxPath);) {
Docx4J.save(wmlPackage, docx, Docx4J.FLAG_SAVE_ZIP_FILE);
docPath = docxPath;
}
} catch (Exception e) {
System.out.println((docPath + ":不需要转换:" + e.getLocalizedMessage()));
}
return docPath;
}
/**
* 通过docx4j 实现word转pdf
*
* @param sourcePath 源文件地址 如 /root/example.doc
* @param targetPath 目标文件地址 如 /root/example.pdf
*/
public String docx4jWordToPdf(String sourcePath, String targetPath) {
try {
WordprocessingMLPackage pkg = Docx4J.load(new File(sourcePath));
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("FangSong"));
fontMapper.put("幼圆", PhysicalFonts.get("YouYuan"));
fontMapper.put("华文宋体", PhysicalFonts.get("STSong"));
fontMapper.put("华文中宋", PhysicalFonts.get("STZhongsong"));
fontMapper.put("等线", PhysicalFonts.get("SimSun"));
fontMapper.put("等线 Light", PhysicalFonts.get("SimSun"));
fontMapper.put("华文琥珀", PhysicalFonts.get("STHupo"));
fontMapper.put("华文隶书", PhysicalFonts.get("STLiti"));
fontMapper.put("华文新魏", PhysicalFonts.get("STXinwei"));
fontMapper.put("华文彩云", PhysicalFonts.get("STCaiyun"));
fontMapper.put("方正姚体", PhysicalFonts.get("FZYaoti"));
fontMapper.put("方正舒体", PhysicalFonts.get("FZShuTi"));
fontMapper.put("华文细黑", PhysicalFonts.get("STXihei"));
fontMapper.put("宋体扩展", PhysicalFonts.get("simsun-extB"));
fontMapper.put("仿宋_GB2312", PhysicalFonts.get("FangSong_GB2312"));
fontMapper.put("新細明體", PhysicalFonts.get("SimSun"));
PhysicalFonts.addPhysicalFonts("SimSun", WordUtils.class.getResource("/simsun.ttc"));
pkg.setFontMapper(fontMapper);
Docx4J.toPDF(pkg, new FileOutputStream(targetPath));
} catch (Exception e) {
log.error("[docx4j] word转pdf失败:{}", e.toString());
}
return targetPath;
}
/**
* 对pdf页面设置,缩放比例设置
* @param pdfName pdf文件名称
*/
private void pdfPageSetting(String pdfName) {
//创建PdfDocument实例
PdfDocument originalDoc = new PdfDocument();
//加载PDF文件
originalDoc.loadFromFile(pdfName);
//创建一个新的PdfDocument实例
PdfDocument newDoc = new PdfDocument();
//遍历所有PDF 页面
for (int i = 0; i < originalDoc.getPages().getCount(); i++) {
PdfPageBase page = originalDoc.getPages().get(i);
//设置新文档的页边距为10
PdfMargins margins = new PdfMargins(10);
//设置新文档的页面大小为A2
PdfPageBase newPage = newDoc.getPages().add(PdfPageSize.A2, margins);
//调整画布,设置内容也根据页面的大小进行缩放
double wScale = (PdfPageSize.A2.getWidth() - 20) / PdfPageSize.A4.getWidth();
double hScale = (PdfPageSize.A2.getHeight() - 20) / PdfPageSize.A4.getHeight();
newPage.getCanvas().scaleTransform(wScale, hScale);
//复制原文档的内容到新文档
newPage.getCanvas().drawTemplate(page.createTemplate(), new Point2D.Float());
}
//保存PDF
newDoc.saveToFile(pdfName);
}
}
工具类
package com.dpxdata.framework.common.utils;
import com.itextpdf.text.*;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.*;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.stereotype.Component;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.ui.freemarker.SpringTemplateLoader;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;
import java.io.*;
import java.util.Map;
/**
* @Description: pdf 导出工具类
* @Author: LiSaiHang
* @Date: 2022/1/7 11:30 上午
*/
@Component
@Slf4j
public class FreeMarkUtils {
private FreeMarkUtils() {
}
private volatile static Configuration configuration;
static {
if (configuration == null) {
synchronized (FreeMarkUtils.class) {
if (configuration == null) {
configuration = new Configuration(Configuration.VERSION_2_3_28);
}
}
}
}
/**
* freemarker 引擎渲染 html
*
* @param dataMap 传入 html 模板的 Map 数据
* @param fileName 模板文件名称
* eg: "templates/template.ftl"
* @return
*/
public static String freemarkerRender(Map dataMap, String fileName) {
configuration.setDefaultEncoding("UTF-8");
configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
try {
// TODO Jar包运行时,是无法读取classpath下资源文件,可以通过 SpringTemplateLoader 读取资源文件目录
// 本地运行时,可以通过本地文件流读取资源文件
final SpringTemplateLoader templateLoader = new SpringTemplateLoader(new DefaultResourceLoader(), "classpath:templates");
configuration.setTemplateLoader(templateLoader);
// 服务器运行时,可以通过目录,获取文件流读取资源文件
// configuration.setDirectoryForTemplateLoading(new File("/Users/lisaihang/Documents/"));
configuration.setLogTemplateExceptions(false);
configuration.setWrapUncheckedExceptions(true);
Template template = configuration.getTemplate(fileName);
log.info("填充模板动态数据");
return FreeMarkerTemplateUtils.processTemplateIntoString(template, dataMap);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 使用 iText 生成 PDF 文档
*
* @param htmlTmpStr html 模板文件字符串
* @param fontFile 所需字体文件(相对路径+文件名)
*/
public static byte[] createHtml2Pdf(String htmlTmpStr, String fontFile) {
log.info("根据模板生成PDF文件");
ByteArrayOutputStream outputStream = null;
byte[] result = null;
// 生成 字体临时文件
// TODO jar包运行后,是无法直接读取 classpath 下的资源文件,需要通过文件流读取,生成临时文件
final String tempFont = "simsun_temp_"+ ".ttc";
File existFile = new File(tempFont);
if (!existFile.exists()) {
final File tempFile = getTempFile(fontFile, tempFont);
existFile = tempFile;
}
// TODO jar包运行后,是无法直接读取 classpath 下的资源文件,需要通过文件流读取,生成临时文件
try {
outputStream = new ByteArrayOutputStream();
ITextRenderer renderer = new ITextRenderer();
renderer.setDocumentFromString(htmlTmpStr);
ITextFontResolver fontResolver = renderer.getFontResolver();
// 解决中文支持问题,需要所需字体(ttc)文件
fontResolver.addFont(existFile.getAbsolutePath(), BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
renderer.layout();
renderer.createPDF(outputStream);
// 对Html2Pdf 的文件插入水印图片
final String fileName = System.currentTimeMillis() + (int) (Math.random() * 90000 + 10000) + ".pdf";
addWaterMark(outputStream.toByteArray(), fileName);
final ByteArrayOutputStream fileOutputStream = getFileOutputStream(new File(fileName));
result = fileOutputStream.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
} finally {
try {
if (outputStream != null) {
outputStream.flush();
outputStream.close();
}
// if (tempFile.exists()) {
// tempFile.delete();
// }
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
/**
* 文件转字节输出流
*
* @param outFile 文件
* @return
*/
public static ByteArrayOutputStream getFileOutputStream(File outFile) {
// 获取生成临时文件的输出流
InputStream input = null;
ByteArrayOutputStream bytestream = null;
try {
input = new FileInputStream(outFile);
bytestream = new ByteArrayOutputStream();
int ch;
while ((ch = input.read()) != -1) {
bytestream.write(ch);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
input.close();
bytestream.close();
log.info("删除临时文件");
if (outFile.exists()) {
outFile.delete();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return bytestream;
}
/**
* 获取资源文件的临时文件
* 资源文件打jar包后,不能直接获取,需要通过流获取生成临时文件
*
* @param fileName 文件路径 temp/temp.ftl
* @return
*/
public static File getTempFile(String fileName, String tempFont) {
final File tempFile = new File(tempFont);
InputStream fontTempStream = null;
try {
fontTempStream = FreeMarkUtils.class.getClassLoader().getResourceAsStream(fileName);
FileUtils.copyInputStreamToFile(fontTempStream, tempFile);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fontTempStream != null) {
fontTempStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return tempFile;
}
/**
* 插入图片水印
* @param srcByte 已生成PDF的字节数组(流转字节)
* @param destFile 生成有水印的临时文件 temp.pdf
* @return
*/
public static FileOutputStream addWaterMark(byte[] srcByte, String destFile) {
// 待加水印的文件
PdfReader reader = null;
// 加完水印的文件
PdfStamper stamper = null;
FileOutputStream fileOutputStream = null;
try {
reader = new PdfReader(srcByte);
fileOutputStream = new FileOutputStream(destFile);
stamper = new PdfStamper(reader, fileOutputStream);
int total = reader.getNumberOfPages() + 1;
PdfContentByte content;
// 设置字体
//BaseFont font = BaseFont.createFont();
// 循环对每页插入水印
for (int i = 1; i < total; i++) {
final PdfGState gs = new PdfGState();
// 水印的起始
content = stamper.getUnderContent(i);
// 开始
content.beginText();
// 设置颜色 默认为蓝色
//content.setColorFill(BaseColor.BLUE);
// content.setColorFill(Color.GRAY);
// 设置字体及字号
//content.setFontAndSize(font, 38);
// 设置起始位置
// content.setTextMatrix(400, 880);
//content.setTextMatrix(textWidth, textHeight);
// 开始写入水印
//content.showTextAligned(Element.ALIGN_LEFT, text, textWidth, textHeight, 45);
// 设置水印透明度
// 设置笔触字体不透明度为0.4f
gs.setStrokeOpacity(0f);
Image image = null;
image = Image.getInstance("https://dpxdata.oss-cn-beijing.aliyuncs.com/upload/kbt/null_1660715685801.png");
// 设置坐标 绝对位置 X Y
image.setAbsolutePosition(472, 785);
// 设置旋转弧度
image.setRotation(0);// 旋转 弧度
// 设置旋转角度
image.setRotationDegrees(0);// 旋转 角度
// 设置等比缩放
image.scalePercent(4);// 依照比例缩放
// image.scaleAbsolute(200,100);//自定义大小
// 设置透明度
content.setGState(gs);
// 添加水印图片
content.addImage(image);
// 设置透明度
content.setGState(gs);
//结束设置
content.endText();
content.stroke();
}
} catch (IOException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
} finally {
try {
stamper.close();
fileOutputStream.close();
reader.close();
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return fileOutputStream;
}
}