<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
@GetMapping("/doc")
public void doc(HttpServletResponse response) {
Map<String, Object> dataMap = new HashMap<>();
///姓名
dataMap.put("name", "zhangsan");
String result = "";
String fileName = System.currentTimeMillis() + (int) (Math.random() * 90000 + 10000) + ".doc";
response.setHeader("content-Type", "application/octet-stream");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName);
// 写入输出流
ServletOutputStream outputStream = null;
try {
// 获取渲染数据
result = FreeMarkUtils.freemarkerRender(dataMap, "test.ftl");
// 设置文件名
outputStream = response.getOutputStream();
outputStream.write(result.getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
test.ftl 放入 resource/templates文件夹中
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<String, Object> 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.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_" + System.currentTimeMillis() + ".ttc";
final File tempFile = getTempFile(fontFile, tempFont);
// TODO jar包运行后,是无法直接读取 classpath 下的资源文件,需要通过文件流读取,生成临时文件
try {
outputStream = new ByteArrayOutputStream();
ITextRenderer renderer = new ITextRenderer();
renderer.setDocumentFromString(htmlTmpStr);
ITextFontResolver fontResolver = renderer.getFontResolver();
// 解决中文支持问题,需要所需字体(ttc)文件
fontResolver.addFont(tempFile.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;
}
}
创建模板文件-test.ftl
1. 创建word
2. 另存为 Xml文件
3. 修改拓展名重命名 ftl
编辑变量,根据自己的业务需求创建动态变量,变量语法可参考Freemark 语法
下载后的word