1 pdf 文件生成
生成pdf有两种方法,如下:
1) 使用 wkhtmltox 工具将 html 转 pdf
优点:一次安装,永久使用。便捷,生成效果好
缺点:并不是所有 html 都能成功转成 pdf,至于为什么需要深度研究研究。
安装
去官网下载:https://wkhtmltopdf.org/downloads.html
分 windows 环境 linux 环境,各自取用适合的包。
如果是 windows 环境,工具解压后打开 dos 窗口,cd 到工具 bin/ 目录下,执行:
wkhtmltopdf https://wkhtmltopdf.org/downloads.html E:\dir\test.pdf
命令说明: wkhtmltopdf 【html路径】【存储路径】
存储路径一定是要先存在的,否则会报错,文件名是自定义的。生成效果如下:
如果是 linux 环境,以 CentOS7 系统为例。
安装:rpm -ivh --nodeps wkhtmltox-0.12.5-1.centos7.x86_64.rpm
加了 --nodeps 会将缺少的依赖一起安装。
使用与在 windows 系统是一样的,它还有很多命令,需要的自己去研究啦~
这个工具还可以将 html 转成图片,有兴趣的可以去试试。
补充:
当在 linux 环境上面将 html转换成 pdf 时,如果内容是中文,可能转出来的 pdf 里面存在空白,这可能是 linux 环境上缺乏中文字体导致的。需要将 windows 的宋字字体 simsun.ttc 拷贝到在/usr/share/fonts中,然后重新执行命令生成 pdf。simsun.ttc 一般在 C:\Windows\Fonts 目录下。
2) itext 创建 pdf 文件
优点:强大的 java 插件,可以自定义生成各种样式的 pdf 、word。
缺点:需要自己使用代码进行内容填充,稍微复杂一点。
生成 pdf 需要的 maven 依赖:
<!-- itext pdf相关 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
<!-- itext asian 字体相关 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
生成 pdf 核心代码示例:
Document document = new Document(PageSize.A4);
// 新建一个pdf文件
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("E:\\test\\test.pdf"));
document.open();
// 中文字体,解决中文不能显示问题
BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
// 红标题字体风格
Font redTitleFont = new Font(bfChinese, 20, Font.BOLD, BaseColor.RED);
// 红标题抬头
Paragraph redTitle = new Paragraph(serviceConfig.getOrganization(), redTitleFont);
redTitle.setAlignment(Element.ALIGN_CENTER); // 居中
document.add(redTitle); // 文档中加入红标题字段
//参数 1.线宽度 2.直线长度,是个百分百,0-100之间 3.直线颜色 4.直线位置 5.上下移动位置
document.add(new LineSeparator(4f,100, BaseColor.RED, Element.ALIGN_CENTER,-15f)); // 画线
document.add(new LineSeparator(1f,100, BaseColor.RED, Element.ALIGN_CENTER,-20f));
// 标题字体风格
Font titleFont = new Font(bfChinese, 15, Font.BOLD);
// 标题内容
Paragraph title = new Paragraph("关于xx的分析报告", titleFont);
title.setSpacingBefore(25); // 离上一段落(标题)空的行数
title.setAlignment(Element.ALIGN_CENTER); // 设置标题格式对齐方式
document.add(title); // // 文档中加入标题字段
// 正文字体风格
Font contextFont = new Font(bfChinese, 10, Font.NORMAL);
// 循环生成多个相同的表格
for (Person person: persons) {
Paragraph info = new Paragraph("工作人员" + count++, contextFont);
info.setSpacingBefore(10); // 离上一段落(标题)空的行数
document.add(info);
// 两列的表
PdfPTable table = new PdfPTable(2);
table.setWidthPercentage(100); // 宽度100%填充
table.setSpacingBefore(10f); // 前间距
table.setSpacingAfter(10f); // 后间距
List<PdfPRow> listRow = table.getRows();
// 设置列宽比例 1:3
float[] columnWidths = {1f, 3f};
table.setWidths(columnWidths);
// 第1行
PdfPCell[] cells1 = new PdfPCell[2]; // 一行两格
PdfPRow row1 = new PdfPRow(cells1);
cells1[0] = new PdfPCell(new Paragraph("姓名", contextFont)); // 第一个单元格
cells1[1] = new PdfPCell(new Paragraph(person.getName(), contextFont)); // 第二个单元格
// 第2行
PdfPCell[] cells2 = new PdfPCell[2];
PdfPRow row2 = new PdfPRow(cells2);
cells2[0] = new PdfPCell(new Paragraph("性别", contextFont)); //单元格内容
cells2[1] = new PdfPCell(new Paragraph(person.getGender(), contextFont));
// 第3行
PdfPCell[] cells3 = new PdfPCell[2];
PdfPRow row3 = new PdfPRow(cells3);
cells3[0] = new PdfPCell(new Paragraph("图片", contextFont)); //单元格内容
cells3[0].setHorizontalAlignment(Element.ALIGN_LEFT); // 水平居左
cells3[0].setVerticalAlignment(Element.ALIGN_MIDDLE); // 垂直居中
// 填充多个图片
String[] paths = person.getImagePath().split(","); // 图片全路径以逗号分隔的
int column = 4; // 1行展示图片的数量
int row;
if (paths.length % column == 0) {
row = paths.length / column;
} else {
row = paths.length / column + 1;
}
PdfPTable picTable = new PdfPTable(column);
picTable.setWidthPercentage(100); // 宽度100%填充
List<PdfPRow> rows = picTable.getRows();
int count = 0;
for (int i = 0; i < row; i ++) {
PdfPCell[] cells = new PdfPCell[column];
PdfPRow newRow = new PdfPRow(cells);
for (int j = 0; j < column; j ++) {
if (count < paths.length) {
Image image = Image.getInstance(paths[count++]);
//设置图片的宽度和高度
image.scaleAbsolute(100, 100);
cells[j] = new PdfPCell(image);
}
}
rows.add(newRow);
}
cells3[1] = new PdfPCell(picTable); // 图片组成的table加入到第三行的第二格中
listRow.add(row1);
listRow.add(row2);
listRow.add(row3);
// 把表格添加到文件中
document.add(victimTable);
}
2 下载 pdf 文件
【效果】打开浏览器发送 get 请求,浏览器自动下载。
Controller层请求接口
/**
* 下载工具
*/
@Resource
private DownloadUtil downloadUtil;
@GetMapping("/download")
public void export(HttpServletRequest request, HttpServletResponse response) {
String filePath = "E:\test\";
String fileName = "test.pdf";
downloadUtil.downloadFile(filePath, fileName, request, response);
}
DownloadUtil.java
package xxx.common.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
/**
* 下载工具类
*
* @author xianhuang
* @date 2019/03/11 10:21
* @since 1.0
*/
@Component
public class DownloadUtil {
/**
* 日志记录
*/
private static final Logger LOGGER = LoggerFactory.getLogger(DownloadUtil.class);
/**
* 下载文件
* @param filePath 文件所在目录全路径
* @param fileName 文件全名,包括后缀
* @param request http请求
* @param response http返回
* @throws IOException 异常
*/
public void downloadFile(String filePath, String fileName, HttpServletRequest request,
HttpServletResponse response) throws IOException {
String fullPath = filePath + fileName;
if (fullPath.contains("\\")) {
fullPath.replaceAll("\\\\","\\\\\\\\");
}
File f = new File(fullPath);
if (!f.exists()) {
LOGGER.error("文件{}不存在", f.getName());
return;
}
// 解决中文名称乱码问题
final String userAgent = request.getHeader("USER-AGENT");
if (userAgent.contains("MSIE")
|| userAgent.contains("Trident")
|| userAgent.contains("Edge")) { //IE浏览器
fileName = URLEncoder.encode(fileName, "UTF-8");
} else if (userAgent.contains("Mozilla")) { //google,火狐浏览器
fileName = new String(fileName.getBytes(), "ISO8859-1");
} else {
fileName = URLEncoder.encode(fileName, "UTF-8"); //其他浏览器
}
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attchement;filename=" + fileName );
try (FileInputStream fileInputStream = new FileInputStream(fullPath)) {
OutputStream outputStream = response.getOutputStream();
byte[] b = new byte[1024];
int j;
while ((j = fileInputStream.read(b)) > 0) {
outputStream.write(b, 0, j);
}
outputStream.flush();
outputStream.close();
} catch (IOException e) {
LOGGER.error(e.getMessage());
}
}
}