背景
最近接到一个新需求导出PDF报告,之前都是导出EXCEL、WORD格式,这次要新增导出PDF的功能或者说是WORD2PDF功能,无码可依,只好又开始我的“面向百度(CSDN)”编程了~ ~ ~
摸索
经过在网上一番折腾,总结了如下几种方法:
- WORD转图片再转PDF :在网上一查才发现自己too young too simple,一般WORD转图片都是通过PDF作为中介,故PASS;
- WORD——》HTML——》PDF: 这种方法感觉有点麻烦,而且帖子有时间有点久,暂时先不考虑,附上链接:Java中Word转PDF解决方案;
- 钱是万能的-Spire.Doc :Spire.Doc for Java 是一款专业的 Java Word 组件,开发人员使用它可以轻松地将 Word 文档创建、读取、编辑、转换和打印等功能集成到自己的 Java 应用程序中。作为一款完全独立的组件,Spire.Doc for Java 的运行环境无需安装 Microsoft Office。但是收费,当然也有免费版本,只是免费版本对文档操作页数有限制 ,有兴趣的也可以根据免费版本自己改造。官网地址;
- jacob :这个方法的优点和缺点一样明显,优点就是代码实现比较相对简单;缺点就是,配置繁琐一点,而且依赖windows本地的wps,目前不支持LINUX环境,有兴趣的可以查看相关帖子;
- itextpdf+Adobe Acrobat 9 Pro :终于迎来了本文的猪脚,经过多方对比,我最终选择了itextpdf+Adobe Acrobat 9 Pro(制作pdf模板!!!)。因为报告分为固定值填充(抠模板)和table表的动态生成;因此代码也是分这两步实现,言归正传,撸代码~ ~ ~
maven依赖
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.11</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
代码实现
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import com.jfinal.kit.PathKit;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.*;
public class Word2PdfDemo3 {
static List<String> head = Arrays.asList("序号", "语文", "数学", "英语", "物理", "生物", "政治", "化学");
static List<String> fields = Arrays.asList("num", "Chinese", "math", "english", "physics", "biological", "political", "chemical");
public static void main(String[] args) {
setPdfData();
}
public static String setPdfData() {
String name = "word2pdf";
// 填充模板数据
Map<String, Object> datas = new HashMap<>();
datas.put("code", "0000000001");
datas.put("name", "word2pdf");
datas.put("user", "张三");
datas.put("date", "2021-03-11");
// 动态填充table表的数据
List<Map<String, String>> list = new ArrayList<>();
Map<String, String> map = new HashMap<>();
map.put("num", "1");
map.put("Chinese", "95");
map.put("math", "100");
map.put("english", "100");
map.put("physics", "98");
map.put("biological", "97");
map.put("political", "90");
map.put("chemical", "99");
list.add(map);
PdfReader reader = null;
PdfStamper stamp = null;
FileOutputStream fileOutputStream = null;
// 最终文件生成路径
String finalPath = "";
// 生成临时pdf
String tempPath = createTempPdf(name, datas);
try {
// 读取临时文件
reader = new PdfReader(tempPath);
Rectangle pageSize = reader.getPageSize(1);
Document document = new Document(pageSize);
finalPath = "E:/"+ "《"+name+"》评估报告.pdf";
// 最终文件输出
File deskFile = new File(finalPath);
fileOutputStream = new FileOutputStream(deskFile);
createPDF(fileOutputStream, tempPath, list);
} catch (Exception e) {
e.printStackTrace();
}finally {
if (reader != null) {
reader.close();
}
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
File file = new File(tempPath);
if (file.exists()) {
file.delete();
}
}
return finalPath;
}
/**
* 创建临时的pdf文件
* @param name
* @param datas
* @return
*/
private static String createTempPdf(String name, Map<String, Object> datas) {
String finalPath;//填充创建pdf
PdfReader reader = null;
PdfStamper stamp = null;
FileOutputStream fileOutputStream = null;
// 临时生成文件路径
String pathStr = "";
try {
reader = new PdfReader("C:/Users/chaid/Desktop/cq_dw.pdf");
pathStr = "C:/Users/chaid/Desktop/" + "《"+name+"》评估报告.pdf";
//创建生成报告名称
File deskFile = new File(pathStr);
fileOutputStream = new FileOutputStream(deskFile);
stamp = new PdfStamper(reader, fileOutputStream);
//设置中文字体
BaseFont bf = BaseFont.createFont("STSong-Light","UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
ArrayList<BaseFont> fontList = new ArrayList<BaseFont>();
fontList.add(bf);
//取出报表模板中的所有字段
AcroFields form = stamp.getAcroFields();
form.setSubstitutionFonts(fontList);
// 填充数据
datas.entrySet().forEach(item->{
try {
form.setField(item.getKey(), null==item.getValue()?"":item.getValue().toString());
} catch (Exception e) {
e.printStackTrace();
}
});
stamp.setFormFlattening(true);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (stamp != null) {
try {
stamp.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (reader != null) {
reader.close();
}
if (null != fileOutputStream) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return pathStr;
}
/**
* 生成最终pdf
* @param fileOutputStream
* @param tempPath
* @param data
* @throws DocumentException
* @throws IOException
*/
public static void createPDF(FileOutputStream fileOutputStream,String tempPath,List<Map<String,String>> data) throws DocumentException, IOException{
//设置字体格式
BaseFont bfChinese = BaseFont.createFont("STSong-Light","UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
Font FontChinese18 = new com.itextpdf.text.Font(bfChinese,18,com.itextpdf.text.Font.BOLD);
Font FontChinese8Bold = new com.itextpdf.text.Font(bfChinese,8, Font.BOLD);
Font FontChinese8 = new com.itextpdf.text.Font(bfChinese,8, Font.NORMAL);
PdfReader reader = null;
//数据获取
String resultPath = "C:/Users/chaid/Desktop/cq_dw.pdf";
try {
File ft = new File(resultPath);
if (!ft.exists()) {
ft.mkdirs();
}
PdfWriter writer = null;
// 最终文件输出
// fileOutputStream = new FileOutputStream(rootPath );
// 读取临时文件pdf
reader = new PdfReader(tempPath);
Document document = new Document(reader.getPageSize(1));
writer = PdfWriter.getInstance(document, fileOutputStream);
document.open();
PdfContentByte cbUnder = writer.getDirectContent();
int pageOfCurrentReaderPDF = 0;
int currentPageNumber = 0;
while (pageOfCurrentReaderPDF < reader.getNumberOfPages()) {
document.newPage();
pageOfCurrentReaderPDF++;
currentPageNumber++;
PdfImportedPage page = writer.getImportedPage(reader, pageOfCurrentReaderPDF);
cbUnder.addTemplate(page, 0, 0);
}
//新创建一页来存放后面生成的表格
document.newPage();
Paragraph zds_tm = new Paragraph("XXXXXXX总表", FontChinese18);
zds_tm.setAlignment(Element.ALIGN_CENTER);
document.add(zds_tm);
Paragraph zds_kg1 = new Paragraph(" ", FontChinese18);
zds_kg1.setAlignment(Element.ALIGN_CENTER);
document.add(zds_kg1);
//table第一行
//动态填充数据
// 表头
PdfPTable table = new PdfPTable(8);
int width[] = {20, 100, 40, 40, 30, 30, 30, 30};
table.setWidths(width);
head.forEach(item -> {
PdfPCell pdfPCell = new PdfPCell(new Paragraph(item, FontChinese8Bold));
pdfPCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
pdfPCell.setFixedHeight(25);
table.addCell(pdfPCell);
});
document.add(table);
// 填充table表数据
data.forEach(item -> {
try {
PdfPTable pdfPTable = new PdfPTable(8);
pdfPTable.setWidths(width);
fields.forEach(field -> {
PdfPCell pdfPCell = new PdfPCell(new Paragraph(item.get(field) + "", FontChinese8));
pdfPCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
pdfPCell.setFixedHeight(25);
pdfPTable.addCell(pdfPCell);
});
document.add(pdfPTable);
} catch (DocumentException e) {
e.printStackTrace();
}
});
document.close();
} catch (Exception e) {
e.getStackTrace();
}finally {
if (null != reader) {
reader.close();
}
}
}
}
模板及结果展示
模板
模板域
模板
链接: 模板下载
结果
彩蛋
彩蛋一:本地临时文件删除不了
代码中已经写了
File file = new File(tempPath);
if (file.exists()) {
file.delete();
}
郁闷的是临时文件还存在,我一度自我怀疑是不是方法不生效,但是删除文件的方法就是这个呀。直到我手动删除临时文件的时候提示如下:
我才有所恍悟~ ~ ~
原因:文件流没有全部关闭----检查文件流是否全部关闭
彩蛋二:导出横向A4PDF
创建document 时:
Document document = new Document(new Rectangle(PageSize.A4.getHeight(), PageSize.A4.getWidth()), 20 , 20 , 40 , 40 );//左右上下
这样整个PDF都是横向A4纸的样式;同时,各种纸张的问题是不是都可以解决了~