说明
调用方法生成PDF时,使用的ftl模版,以及字体都是从jar中读取的,无需担心多节点部署
引用jar
org.freemarker
freemarker-gae
2.3.23
commons-io
commons-io
2.6
com.alibaba
fastjson
1.2.76
文件存放路径
// 字体文件
resources/fonts
// 模版文件
resources/templates
代码
import com.lowagie.text.pdf.baseFont;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.xhtmlrenderer.pdf.ITextRenderer;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringWriter;
import java.util.Map;
@Component
public class PdfHelper {
private String classpath = getClass().getResource("/").getPath();
private String templatePath = "/templates";
private String templateFileName = "pdf.ftl";
private String imagePath = "/images/";
private String fontPath = "fonts/";
private String font = "simsun.ttc";
private String encoding = "UTF-8";
@Value("{$file.path}")
private String filePath;
public void createPDF(Map data, OutputStream out, String templateName) throws Exception {
// 创建一个FreeMarker实例, 负责管理FreeMarker模板的Configuration实例
Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
// 指定FreeMarker模板文件的位置
ITextRenderer renderer = new ITextRenderer();
String simsun = System.getProperty("user.dir") + "/simsun.ttc";
String arialuni = System.getProperty("user.dir") + "/arialuni.ttf";
File fondFile = new File(simsun);
if (!fondFile.exists()) {
getFondPath();
}
// 设置 css中 的字体样式(暂时仅支持宋体和黑体)
renderer.getFontResolver().addFont(simsun, baseFont.IDENTITY_H, baseFont.NOT_EMBEDDED);
renderer.getFontResolver().addFont(arialuni, baseFont.IDENTITY_H, baseFont.NOT_EMBEDDED);
StringTemplateLoader stringLoader = new StringTemplateLoader();
String templateString = getFileData("templates/" + templateName);
stringLoader.putTemplate("myTemplate", templateString);
cfg.setTemplateLoader(stringLoader);
Template tpl = cfg.getTemplate("myTemplate", "utf-8");
StringWriter writer = new StringWriter();
// 把null转换成""
Map dataMap = CommunalTool.objectToMap(data);
// 将数据输出到html中
tpl.process(data, writer);
writer.flush();
String html = writer.toString();
// 把html代码传入渲染器中
renderer.setdocumentFromString(html);
renderer.layout();
renderer.createPDF(out, false);
renderer.finishPDF();
out.flush();
out.close();
}
// 读取jar中的ftl模版文件
private String getFileData(String path) {
InputStream stream = getClass().getClassLoader().getResourceAsStream(path);
// log.info("infile:"+infile);
StringBuffer sb = new StringBuffer();
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
String s = null;
while ((s = br.readLine()) != null) {
sb.append(s);
}
br.close();
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (br != null) {
try {
br.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private String getFondPath() throws IOException {
String fontPath = System.getProperty("user.dir");
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("fonts/simsun.ttc");
//在根目录生成一个文件
File targetFile = new File(fontPath + "/simsun.ttc");
// //将流转成File格式
FileUtils.copyInputStreamToFile(inputStream, targetFile);
InputStream arialuniStream = getClass().getClassLoader().getResourceAsStream("fonts/arialuni.ttf");
File arialuni = new File(fontPath + "/arialuni.ttf");
FileUtils.copyInputStreamToFile(arialuniStream, arialuni);
return fontPath;
}
public void setClasspath(String classpath) {
this.classpath = classpath;
}
public void setTemplatePath(String templatePath) {
this.templatePath = templatePath;
}
public void setTemplateFileName(String templateFileName) {
this.templateFileName = templateFileName;
}
public void setImagePath(String imagePath) {
this.imagePath = imagePath;
}
public void setFontPath(String fontPath) {
this.fontPath = fontPath;
}
public void setFont(String font) {
this.font = font;
}
public void setEncoding(String encoding) {
this.encoding = encoding;
}
}
工具类
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.ValueFilter;
import org.apache.commons.lang3.StringUtils;
import sun.misc.base64Encoder;
import java.io.File;
import java.io.FileInputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
public class CommunalTool {
public static Object objectToData(Object obj, Class> beanClass) throws Exception {
return JSONObject.parseObject(objectToString(obj), beanClass);
}
public static Map objectToMap(Map map) throws Exception {
return JSONObject.parseObject(objectToString(map), Map.class);
}
public static List objectToList(List list) throws Exception {
return JSONObject.parseObject(objectToString(list), List.class);
}
// fistJson 的一个过滤器
static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
static DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
private static ValueFilter filter = new ValueFilter() {
@Override
public Object process(Object obj, String s, Object v) {
if (v instanceof Date) {
return simpleDateFormat.format(v);
} else {
if (v == null) {
return "";
} else {
return v;
}
}
}
};
public static String objectToString(Object obj) {
// JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd";
return JSON.toJSONString(obj, filter, SerializerFeature.WriteNonStringKeyAsString, SerializerFeature.WriteNullStringAsEmpty);
}
public static String encodebase64Picture(String path) throws Exception {
File file = new File(path);
FileInputStream inputFile = new FileInputStream(file);
byte[] buffer = new byte[(int) file.length()];
inputFile.read(buffer);
inputFile.close();
String result = new base64Encoder().encode(buffer);
if (StringUtils.isNotBlank(result)) {
result = "data:image/png;base64," + result;
} else {
result = null;
}
return result;
}
public static String getTransformationDate(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(date);
}
public static String getTimeDate(Date date) {
int minte = getMinute(date);
minte = minte % 5;
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
Date afterDate = new Date(date.getTime() + 300000);
System.out.println(sdf.format(afterDate));
return "1";
}
public static int getMinute(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return calendar.get(Calendar.MINUTE);
}
public static String FORMAT_FULL = "yyyy-MM-dd HH:mm:ss";
public static String getTime(Date date) {
SimpleDateFormat df = new SimpleDateFormat(FORMAT_FULL);
return df.format(date).substring(12, 16);
}
}
ftl模版
电子化模版单个表格
模版
${name}
巡视类型:例行巡视
生成时间:2021-12-16
| 工程名称 |
|
| 合同名称 |
|
| 合同编号 |
|
| 违约事实 |
|
| 约谈情况 |
|
| 供应商 |
|
| 物资公司 |
|
| 项目管理部门 |
|
| 物资部 |
|
| 财务部 |
|
|
注:1.涉及技术的违约事实,由项目管理部门确认;涉及商务的违约事实,由物资公司/供应中心确认;技术、商务均涉及的,由项目管理部门,由物资公司/供应中心确认。各个单位可根据实际情况增加其他部门(单位)确认。
调用主函数生成
import com.sgcc.cn.utils.PdfHelper;
import java.io.File;
import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.Map;
public class test {
public static void main(String[] args) {
try {
PdfHelper pdfHelper = new PdfHelper();
Map map = new HashMap<>();
map.put("name","唯有碎银解千愁");
FileOutputStream out = new FileOutputStream(new File( "D:\1.pdf"));
pdfHelper.createPDF(map,out,"gererate.ftl");
} catch (Exception e) {
e.printStackTrace();
}
}
}
电子化模版嵌套表格
调配申请单
调配申请单
| 申请单位 |
${receive.zbutxt} |
申请日期 |
${receive.createDate} |
调配类型 |
市内调配/省内跨市调配 |
需求 描述 |
调配方式 |
划转 |
拟调配地址 |
${receive.zdpdz} |
| 联系人 |
${receive.zxqflxr} |
联系电话 |
${receive.zxqflxfs} |
| 序号 |
物料编码 |
物料名称 |
规格型号 |
单位 |
需求数量 |
备注 |
<#list checkList as item>
| ${item_index+1} |
${item.matnr} |
${item.maktx} |
${item.zggxh} |
${item.meins} |
${item.menge} |
${item.remark} |
#list>
|
|
| 市公司分管领导(签字) |
|
市公司物资部/物资供应中心(签字、盖章) |
|
市公司专业部门(签字、盖章) |
|
| 本调配单一式四份,省/地市物资部、省物资公司/县公司供应分中心、调入方和调出方各一份 |
如果不想把文件存在本地,想在生成之后返回流文件,代码如下
public byte[] createPDF(Map data, String templateName) throws Exception {
// 创建一个FreeMarker实例, 负责管理FreeMarker模板的Configuration实例
Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
// 指定FreeMarker模板文件的位置
ITextRenderer renderer = new ITextRenderer();
String simsun = System.getProperty("user.dir") + "/simsun.ttc";
String arialuni = System.getProperty("user.dir") + "/arialuni.ttf";
File fondFile = new File(simsun);
if (!fondFile.exists()) {
getFondPath();
}
// 设置 css中 的字体样式(暂时仅支持宋体和黑体)
renderer.getFontResolver().addFont(simsun, baseFont.IDENTITY_H, baseFont.NOT_EMBEDDED);
renderer.getFontResolver().addFont(arialuni, baseFont.IDENTITY_H, baseFont.NOT_EMBEDDED);
StringTemplateLoader stringLoader = new StringTemplateLoader();
String templateString = getFileData("templates/" + templateName);
stringLoader.putTemplate("myTemplate", templateString);
cfg.setTemplateLoader(stringLoader);
Template tpl = cfg.getTemplate("myTemplate", "utf-8");
StringWriter writer = new StringWriter();
// 把null转换成""
Map dataMap = CommunalTool.objectToMap(data);
// 将数据输出到html中
tpl.process(data, writer);
writer.flush();
String html = writer.toString();
// 把html代码传入渲染器中
renderer.setdocumentFromString(html);
ByteArrayOutputStream out = new ByteArrayOutputStream();
renderer.layout();
renderer.createPDF(out);
renderer.finishPDF();
byte[] fileByte = out.toByteArray();
out.flush();
out.close();
return fileByte;
}
如果想要动态读取模版也可以
templateString 这个就是模版字符串,然后你可以写一个扩展方法去读取,如你想从数据库中存储和读取
String templateString = getFileData("templates/" + templateName);
stringLoader.putTemplate("myTemplate", templateString);
设置mysql数据库存储文件的字段为大字段类型blob
// 这里只提供一个公共方法,读取文件后返回一个字符串。
public String uploadModelSql(MultipartFile file) {
BufferedReader reader = null;
StringBuffer sbf = new StringBuffer();
try {
// Reader read = new InputStreamReader(file.getInputStream(), "UTF-8");
Reader read = new InputStreamReader(file.getInputStream(), "utf-8");
reader = new BufferedReader(read);
String tmpString = null;
//一行一行的读取文件里面的内容,这个是防止文件存储数据库之后,在读取模版布局问题
while ((tmpString = reader.readLine()) != null) {
sbf.append(tmpString).append("rn");
}
if (sbf == null) {
throw new RuntimeException("文件内容为空!");
}
return sbf.toString();
} catch (IOException e) {
e.printStackTrace();
return sbf.toString();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
模版下载
@RequestMapping("/downLoad")
public void downLoadFile(HttpServletResponse response, @RequestParam String id) {
try {
//根据id查询数据库中的记录
SignatureTemplatesFile record = signatureTemplatesFileService.downLoadFile(id);
// 清空response
response.reset();
// 设置response的Header,record.getTemplateName() 文件名称
response.addHeader("Content-Disposition", "attachment;filename=" + new String(record.getTemplateName()));
// response.addHeader("Content-Length", "" + file.length());
OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
//大字段中的字符串,转换成byte数组
byte[] bytes = record.getTemplateFile().getBytes();
toClient.write(bytes);
toClient.flush();
toClient.close();
} catch (Exception e) {
e.printStackTrace();
}
}
总结
代码写的有点乱,总体来说该有的功能都有了,如:从jar中读取字体和ftl模版,如果你不想从jar中读取,可以自己改造一下,就是不管你是从其他地方读文件或者从数据库中字符串,总之你要把他转换成字符串,最后给(templateString)它就行。还有PDF生成的时候也可以返回流文件,你可以把返回的流文件,存储fastDFS(文件管理插件)上面去统一管理。