Java实现PDF打印
准备工作
1.PDF编辑工具:Adobe Acrobat
该文件官网下载需要收费,已经放到百度网盘里了,有需自取
通过百度网盘分享的文件:Acrobat DC2024安装包.zip
链接:https://pan.baidu.com/s/1eAE27Ce_W0vEQpXPsuqhzA
提取码:7777
安装好之后需要制作一个pdf模板,用来存放生成的数据
1.1, 创建表格
制作一个简单的Excel表格,然后转为PDF文件
1.2 使用Adobe Acrobat编辑PDF
打开转换好的PDF文件
编辑改PDF文件即可,上方工具栏中有文本域,图像域,日期域等工具,主需要内容处设置好文本域即可,并起好属性名
保存起来当做PDF模版文件,后续生成PDF文件都会根据这个文件来生成
2.pom依赖文件
<!--常用工具类 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- io常用工具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.7</version>
</dependency>
<!-- pdf -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>7.0.2</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.12</version>
</dependency>
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>fontbox</artifactId>
<version>2.0.12</version>
</dependency>
<!--二维码-->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.2.1</version>
</dependency>
3.工具类
大致需要以下工具类
PdfPrintData自定义工具类,用于封装PDF打印所需要的数据
QRUtils:生辰图片工具类,可以生成二维码,条形码等图片
PDFUtils:处理PDF文档的工具类,生成,合并,打印PDF
FileDirConfig:配置和管理文件目录
PDFConfig:PDF字体库路径
SpringUtils:spring工具类 方便在非spring管理环境中获取bean,可以替换
PDFService:PDF服务类,封装好数据,调用工具类,生成PDF,返回给前端
4.字体库文件
simsun.ttc 必需的文件,否则无法打印,添加到配置文件内,类似这样:
通过百度网盘分享的文件:simsun.ttc
链接:https://pan.baidu.com/s/1eO5KE0WXzDDir0LTlx26hA
提取码:7777
5.java实现PDF打印,打印表单
需要编写前端,前端添加打印按钮,调用后端的接口,把必要的数据(需要生成PDF文件的数据),通过接口传输到后端,后端处理这些数据,并生成PDF文件
5.1 单个打印PDF表单
类似下面这种效果:
controller类
/**
* 打印PDF(单个),表单
*/
@GetMapping("/printPdf")
public void printPdf(HttpServletResponse response,
@RequestParam(value = "orderId",required = false) String orderId) throws IOException {
ServletOutputStream outputStream = response.getOutputStream();
//根据orderId查询相关数据
QueryWrapper<Orders> ordersQueryWrapper = new QueryWrapper<>();
ordersQueryWrapper.eq("order_id",orderId);
Orders order = orderService.getOne(ordersQueryWrapper);
// 封装打印数据
OrderPdfData orderPdfData = new OrderPdfData();
BeanUtils.copyBeanProp(orderPdfData,order);
//使用FileInputStream创建输入流,读取pdfService.printOrder方法返回的文件路径对应的标签文件内容
FileInputStream inputStream = new FileInputStream(new File(pdfService.printOrder(orderPdfData,1)));
int len = 0;
// 初始化一个1024字节数组
byte[] bytes = new byte[1024];
// 使用inputStream.read(buffer)从输入流中读取数据到buffer,并将实际读取的字节数赋值给len
// 将读取的数据写入输出流out,直到in无更多数据可读
while ((len = inputStream.read(bytes)) > 0) {
outputStream.write(bytes, 0, len);
}
response.setContentType("application/pdf");
inputStream.close();
}
PDFService.java
PDF服务类,封装好数据,调用工具类,生成PDF,返回给前端
@Service
public class PDFService {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 打印运单信息,单个打印,打印表单信息
* @param orderPdfData 要打印的数据
* @param num 打印份数
* @return 打印的pdf文件路径
* @throws Exception 抛出异常
*/
public String printOrder(OrderPdfData orderPdfData,int num) throws Exception {
logger.info("打印订单信息");
if (num < 1){
num = 1;
}
// 填充数据,map的key值,就是PDF模板文件中,表单域的属性值,二者要对应
Map<String, String> map = new HashMap<>();
map.put("order_id",orderPdfData.getOrderId());
map.put("robot_id",orderPdfData.getRobotId());
map.put("pickup_dock_id",orderPdfData.getPickupDockId());
map.put("dropoff_time", DateUtil.parseDateToStr(DateUtil.YYYY_MM_DD,orderPdfData.getDropoffTime()));
map.put("delivery_dock_id",orderPdfData.getDeliveryDockId());
map.put("order_status", OrderStatusEnum.getDescriptionByStatus(orderPdfData.getStatusId()));
//日期需要转为String类型的
map.put("create_time",DateUtil.parseDateToStr(DateUtil.YYYY_MM_DD,orderPdfData.getCreateTime()));
ArrayList<PdfPrintData> pdfList = new ArrayList<>();
for (int i = 0; i < num; i++){
// 存放生成的二维码图片
HashMap<String, String> map2 = new HashMap<>();
// 生成二维码图片,key值是图片域中的属性名,value值是图片路径
map2.put("qr_af_image", QRUtils.qrout("orderId"+orderPdfData.getOrderId()));
PdfPrintData pdfPrintData = new PdfPrintData();
// 填入表单数据
pdfPrintData.setField(map);
// 填入图片
pdfPrintData.setImage(map2);
// 填入模板文件
pdfPrintData.setTemplatePath("pdf/order.pdf");
pdfList.add(pdfPrintData);
}
return PDFUtils.pdfouts(pdfList);
}
}
5.2 批量打印PDF表单
controller类
/**
* 批量打印PDF
*/
@GetMapping("/printPdfBatch/{orderIds}")
public void printPdfBatch(HttpServletResponse response, @PathVariable("orderIds") String[] orderIds) throws Exception {
ArrayList<OrderPdfData> dataArrayList = new ArrayList<>();
OutputStream out = null;
InputStream in = null;
try {
out = response.getOutputStream();
for (String orderId : orderIds){
QueryWrapper<Orders> ordersQueryWrapper = new QueryWrapper<>();
ordersQueryWrapper.eq("order_id",orderId);
Orders order = orderService.getOne(ordersQueryWrapper);
OrderPdfData orderPdfData = new OrderPdfData();
BeanUtils.copyBeanProp(orderPdfData,order);
dataArrayList.add(orderPdfData);
}
in = new FileInputStream(new File(pdfService.printOrder(dataArrayList)));
int len = 0;
byte[] buffer = new byte[1024];
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
response.setContentType("application/pdf");
}catch (Exception e){
e.printStackTrace();
}finally {
try {
out.close();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
PDFService.java
PDF服务类,封装好数据,调用工具类,生成PDF,返回给前端
/**
* 批量打印运单信息
*/
public String printOrder(List<OrderPdfData> orderPdfDataList) throws Exception {
HashMap<String, String> map = null;
ArrayList<PdfPrintData> pdfDatalist = new ArrayList<>();
for (OrderPdfData orderPdfData : orderPdfDataList){
//处理数据
map = new HashMap<>();
map.put("order_id",orderPdfData.getOrderId());
map.put("robot_id",orderPdfData.getRobotId());
map.put("pickup_dock_id",orderPdfData.getPickupDockId());
map.put("dropoff_time", DateUtil.parseDateToStr(DateUtil.YYYY_MM_DD,orderPdfData.getDropoffTime()));
map.put("delivery_dock_id",orderPdfData.getDeliveryDockId());
map.put("order_status", OrderStatusEnum.getDescriptionByStatus(orderPdfData.getStatusId()));
map.put("create_time",DateUtil.parseDateToStr(DateUtil.YYYY_MM_DD,orderPdfData.getCreateTime()));
//处理图片
HashMap<String, String> map2 = new HashMap<>();
map2.put("qr_af_image", QRUtils.qrout("orderId"+orderPdfData.getOrderId()));
PdfPrintData pdfPrintData = new PdfPrintData();
pdfPrintData.setField(map);
pdfPrintData.setImage(map2);
pdfPrintData.setTemplatePath("pdf/order.pdf");
pdfDatalist.add(pdfPrintData);
}
return PDFUtils.pdfouts(pdfDatalist);
}
6.java实现PDF打印,打印表单+表格数据
类似下面这种效果,格式有点不匹配,需要调一下,比较麻烦,下面那个是生成的表格数据,也没有自适应,显得比较突兀
controller类
/**
* 打印PDF表单+表格数据
*/
@GetMapping("/printPdf/{orderId}")
public void printPdfTable(HttpServletResponse response, @PathVariable("orderId") String orderId) throws Exception {
ServletOutputStream outputStream = response.getOutputStream();
QueryWrapper<Orders> ordersQueryWrapper = new QueryWrapper<>();
ordersQueryWrapper.eq("order_id",orderId);
List<Orders> ordersList = orderService.list(ordersQueryWrapper);
ArrayList<OrderPdfData> pdfDataList = new ArrayList<OrderPdfData>();
if (ordersList != null && ordersList.size() > 0){
for (Orders orders : ordersList){
OrderPdfData orderPdfData = new OrderPdfData();
BeanUtils.copyBeanProp(orderPdfData,orders);
pdfDataList.add(orderPdfData);
}
}
FileInputStream fileInputStream = new FileInputStream(new File(pdfService.printOrderTable(pdfDataList)));
int len = 0;
byte[] bytes = new byte[1024];
while ((len = fileInputStream.read(bytes)) > 0) {
outputStream.write(bytes, 0, len);
}
response.setContentType("application/pdf");
fileInputStream.close();
outputStream.close();
}
pdfService.java
/**
* 打印表单+表格数据
*/
public String printOrderTable(List<OrderPdfData> orderPdfDataList) throws Exception {
PdfPrintData pdfPrintData = new PdfPrintData();
HashMap<String, String> map = new HashMap<>();
// 创建二维数组
String[][] data = null;
//表格标题
pdfPrintData.setTitle(new String[]{"行号","机器人id","取货库位","卸货库位","完成时间","任务状态"});
//表格列宽
pdfPrintData.setColum(new float[]{1,1,1,1,1,1});
//初始化二维数组
data = new String[orderPdfDataList.size() + 1][6];
int i = 1;
for (OrderPdfData orderPdfData : orderPdfDataList){
map.put("order_id",orderPdfData.getOrderId());
map.put("create_time",DateUtil.parseDateToStr(DateUtil.YYYY_MM_DD,orderPdfData.getCreateTime()));
data[i][0] = String.valueOf(i);
data[i][1] = orderPdfData.getRobotId();
data[i][2] = orderPdfData.getPickupDockId();
data[i][3] = orderPdfData.getDeliveryDockId();
data[i][4] = DateUtil.parseDateToStr(DateUtil.YYYY_MM_DD,orderPdfData.getDropoffTime());
data[i][5] = OrderStatusEnum.getDescriptionByStatus(orderPdfData.getStatusId());
i++;
}
// 设置表单数据
pdfPrintData.setField(map);
// 设置表格数据
pdfPrintData.setData(data);
// 设置表格域名
pdfPrintData.setTable("table_af_image");
//生成二维码图片
HashMap<String, String> imgMap = new HashMap<>();
imgMap.put("qr_af_image", QRUtils.qrout("orderId"+orderPdfDataList.get(0).getOrderId()));
pdfPrintData.setImage(imgMap);
pdfPrintData.setTemplatePath("pdf/order_table_data.pdf");
return PDFUtils.pdfout(pdfPrintData, Color.LIGHT_GRAY, Color.BLACK);
}
7.必要的工具类以及配置类
QRUtils.java
,生成二维码/条形码图片
/**
* @author itchao
* @create 2024-09-11
*/
public class QRUtils {
/**
* 生成二维码
* @param content 二维码内容
* @return 二维码路径
* @throws Exception 抛出异常
*/
public static String qrout(String content) throws Exception{
File file = new File(FileDirConfig.FILE_TEMP);
if (!file.exists()){
file.mkdirs();
}
//生成二维码
int width = 200;
int height = 200;
Map<EncodeHintType, Object> config = new HashMap<>();
//设置字符集
config.put(EncodeHintType.CHARACTER_SET, "UTF-8");
// 误差纠正级别:
// L(Low):约7%的纠错能力。
// M(Medium):约15%的纠错能力。
// Q(Quartile):约25%的纠错能力。
// H(High):约30%的纠错能力。
config.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
//设置二维码的边距
config.put(EncodeHintType.MARGIN, 0);
// 生成二维码
BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, config);
String guid = String.valueOf(OrderDateNumberGenerator.generate());
String path = FileDirConfig.FILE_TEMP+ guid;
//创建一个输出流的文件,文件命名为uuid.png二维码图片
OutputStream out = new FileOutputStream(new File(path));
MatrixToImageWriter.writeToStream(bitMatrix,"png",out);
out.close();
return path ;
}
/**
* 生成带有logo的二维码图片
* @param content 二维码内容
* @param logoUrl logo图片地址
* @return 二维码路径
* @throws Exception 抛出异常
*/
public static String qrLogoOut(String content, String logoUrl) throws Exception{
//生成二维码
int width = 200;
int height = 200;
Map<EncodeHintType, Object> config = new HashMap<>();
config.put(EncodeHintType.CHARACTER_SET, "UTF-8");
config.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
config.put(EncodeHintType.MARGIN, 0);
BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, config);
BufferedImage matrixImage = MatrixToImageWriter.toBufferedImage(bitMatrix);
// 读取二维码图片,并构建绘图对象
Graphics2D g2 = matrixImage.createGraphics();
// 消除锯齿。不设置的话,圆弧效果会出现锯齿
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int matrixWidth = matrixImage.getWidth();
int matrixHeight = matrixImage.getHeight();
// 读取logo图片
BufferedImage logo = ImageIO.read(new URL(logoUrl));
// 在底图(源二维码)上绘制logo。logo大小为地图的2/5
// g2.drawImage(logo, matrixWidth / 5 * 2, matrixHeight / 5 * 2,
// matrixWidth / 5, matrixHeight / 5, null);//绘制
// 采用平滑缩放的方式,来解决logo质量变差的问题
g2.drawImage(logo.getScaledInstance(matrixWidth / 5,matrixWidth / 5, Image.SCALE_SMOOTH),
matrixWidth / 5 * 2, matrixHeight / 5 * 2, null);
// 圆滑的粗线条
// 参见:https://blog.csdn.net/li_tengfei/article/details/6098093
BasicStroke stroke = new BasicStroke(5, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
// 设置画笔的画出的线条
g2.setStroke(stroke);
// 指定弧度的圆角矩形(空心的,白色边的)
RoundRectangle2D.Double round = new RoundRectangle2D.Double(matrixWidth / 5 * 2, matrixHeight / 5 * 2,
matrixWidth / 5, matrixHeight / 5, 5, 5);
g2.setColor(Color.white);
// 用于绘制logo外层的白边
g2.draw(round);
// 圆滑的细线条
BasicStroke stroke2 = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
g2.setStroke(stroke2);
// 设置圆角矩形
RoundRectangle2D.Double round2 = new RoundRectangle2D.Double(matrixWidth / 5 * 2 + 2, matrixHeight / 5 * 2 + 2,
matrixWidth / 5 - 4, matrixHeight / 5 - 4, 5, 5);
g2.setColor(new Color(211, 211, 211));
// 绘制logo内部的灰色边框
g2.draw(round2);
g2.dispose();
matrixImage.flush();
// 输出测试
File file = new File(FileDirConfig.FILE_TEMP);
if (!file.exists()){
file.mkdirs();
}
String guid = String.valueOf(OrderDateNumberGenerator.generate());
String path = FileDirConfig.FILE_TEMP+ guid;
OutputStream out = new FileOutputStream(new File(path));
ImageIO.write(matrixImage, "png", out);
out.close();
return path ;
}
}
PdfPrintData.java
自定义工具类,用于封装PDF打印所需要的数据
/**
* @author itchao
* @create 2024-09-11
*/
@Data
public class PdfPrintData {
public String templatePath;//模板文件地址
public Map<String, String> field; //表单
public Map<String, String> image;//图片
public String table;//table 的所有域
public float[] colum;//table表格的宽度比
public String[] title;//表格标题
public String[][] data;//表格数据
}
PDFUtils.java
处理PDF文档的工具类,生成,合并,打印PDF
/**
* @author itchao
* @create 2024-09-11
*/
public class PDFUtils {
/**
* 单个pdf打印,表单
* @param pdfPrintData 模板数据
* @return PDF路径
* @throws Exception 抛出异常
*/
public static String pdfout(PdfPrintData pdfPrintData) throws Exception {
File file = new File(FileDirConfig.FILE_TEMP);
if (!file.exists()) {
file.mkdirs();
}
// 生成的新文件路径
String newPDFPath = FileDirConfig.FILE_TEMP + OrderDateNumberGenerator.generate();
// 加载模板文件地址,模板文件在resources下
ClassPathResource cpr = new ClassPathResource(pdfPrintData.getTemplatePath());
// 读取模板文件的输入流
PdfReader reader = new PdfReader(cpr.getInputStream());
// 将文档路径写入输出流
PdfWriter writer = new PdfWriter(new FileOutputStream(newPDFPath));
// 创建pdf文档
PdfDocument pdfDoc = new PdfDocument(reader, writer);
// 操作PDF
Document document = new Document(pdfDoc);
pdfDoc.setDefaultPageSize(PageSize.A4);
PDFConfig pdfConfig = SpringUtils.getBean(PDFConfig.class);
// /usr/share/fonts/simsun.ttc,1
// 获取字体库路径,设置编码方式,false:字体是否嵌入
PdfFont sysFont = PdfFontFactory.createFont(pdfConfig.getPdfFontLibrary(), PdfEncodings.IDENTITY_H, false);
try {
// 设置页边距,上:20 右:5 下:20 左:5
document.setMargins(20,5,20,5);
// 设置字体大小
document.setFontSize(8);
// 设置页面大小的高度
document.setHeight(pdfDoc.getDefaultPageSize().getHeight());
// 获取表单对象,设置表单自动生成外观图像
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
form.setGenerateAppearance(true);
//文字类的内容处理
// 获取PDF表单中的所有字段
Map<String, PdfFormField> fields = form.getFormFields();
// 获取PDF打印数据中的字段映射
Map<String, String> datemap = pdfPrintData.getField();
// 如果字段映射不为空
if (datemap != null) {
for (String key : datemap.keySet()) {
// 获取当前键对应的值
String value = datemap.get(key);
// 根据键获取对应的PDF表单字段
PdfFormField pdfFormField = fields.get(key);
if (pdfFormField !=null && value != null){
// 设置表单字段的字体
pdfFormField.setFont(sysFont);
// 设置表单字段的值
pdfFormField.setValue(value);
}
}
}
// 关闭表单编辑功能,即将表单字段进行扁平化处理,防止后续编辑
form.flattenFields();
//图片类的内容处理
Map<String, String> imgmap = pdfPrintData.getImage();
if (imgmap != null) {
for (String key : imgmap.keySet()) {
String value = imgmap.get(key);
String imgpath = value;
PdfFormField pdfFormField = form.getField(key);
if (pdfFormField == null){
continue;
}
// 获取key对应的矩形区域,转换城Rectangle对象
Rectangle rectangle = form.getField(key).getWidgets().get(0).getRectangle().toRectangle();
// //根据路径读取图片
// 1.使用FileInputStream读取图片文件路径imgpath对应文件的内容。然后使用IOUtils.toByteArray把输入流转为字节数组
// 2.使用ImageDataFactory.create(byte[] data)创建图片对象
// 最终构造成一个Image对象
Image image = new Image(ImageDataFactory.create(IOUtils.toByteArray(new FileInputStream(imgpath))));
;
// //图片大小自适应
image.scaleToFit(rectangle.getWidth(), rectangle.getHeight());
image.setFixedPosition(rectangle.getX(), rectangle.getY());
// //添加图片
document.add(image);
}
}
//表格处理
if (StringUtils.isNotEmpty(pdfPrintData.getTable())) {
// 获取对应字段的矩形区域位置信息
Rectangle rectangle = form.getField(pdfPrintData.getTable()).getWidgets().get(0).getRectangle().toRectangle();
// 根据column表格列宽,创建一个新的表格对象,用来存储数据
Table table = new Table(UnitValue.createPercentArray(pdfPrintData.getColum()));
//获取PDF文档的第一页的页面大小和高度值,
float pageheight = document.getPdfDocument().getPage(1).getPageSize().getHeight();
//设置表格的顶部间距 = 页面高度 - 矩形Y坐标 - 高度值
table.setMarginTop(pageheight - rectangle.getY() -rectangle.getHeight());
//设置表格左侧和右侧间距,都为x值
table.setMarginLeft(rectangle.getX());
table.setMarginRight(rectangle.getX());
//设置标题
for (String title : pdfPrintData.getTitle()) {
// 设置标题,将标题添加到段落中,设置字体,设置背景颜色,设置字体颜色,设置文字对齐方式:居中(center)
table.addCell(new Cell().add(new Paragraph(title).setFont(sysFont)).setBackgroundColor(Color.BLUE).setFontColor(Color.WHITE)
.setTextAlignment(TextAlignment.CENTER));
}
// 添加数据
for (String[] datas : pdfPrintData.getData()) {
for (String data : datas) {
// 没有数据就设置为空
if (data == null){
data = "" ;
}
// 添加数据,设置字体(上方的字体库路径),设置对齐方式:左对齐(left)
table.addCell(new Cell().add(new Paragraph(data).setFont(sysFont))
.setTextAlignment(TextAlignment.LEFT));
}
}
// 创建列数为1,宽度根据标题长度变化的单元格,设置内容,设置字体,设置对齐方式:居中(center)
table.addFooterCell(new Cell(1,pdfPrintData.getTitle().length)
.add(new Paragraph("- - - 此页结束 - - -").setFont(sysFont))
.setTextAlignment(TextAlignment.CENTER));
// 把表格数据添加到pdf中
document.add(table);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
document.close();
}
return newPDFPath;
}
/**
* 单个pdf打印,表单+表格数据类型
* @param pdfPrintData pdf打印数据
* @param backgroundColor 背景颜色
* @param fontColor 字体颜色
* @return pdf文件路径
* @throws Exception 抛出异常
*/
public static String pdfout(PdfPrintData pdfPrintData, Color backgroundColor, Color fontColor) throws Exception {
File file = new File(FileDirConfig.FILE_TEMP);
if (!file.exists()) {
file.mkdirs();
}
// 生成的新文件路径
String newPDFPath = FileDirConfig.FILE_TEMP + OrderDateNumberGenerator.generate();
ClassPathResource cpr = new ClassPathResource(pdfPrintData.getTemplatePath());
PdfReader reader = new PdfReader(cpr.getInputStream());
PdfWriter writer = new PdfWriter(new FileOutputStream(newPDFPath));
PdfDocument pdfDoc = new PdfDocument(reader, writer);
Document document = new Document(pdfDoc);
pdfDoc.setDefaultPageSize(PageSize.A4);
//获取字体库配置类的bean实例
PDFConfig pdfConfig = SpringUtils.getBean(PDFConfig.class);
// /usr/share/fonts/simsun.ttc,1 配置字体
PdfFont sysFont = PdfFontFactory.createFont(pdfConfig.getPdfFontLibrary(),PdfEncodings.IDENTITY_H, false);
try {
document.setMargins(20,5,20,5);
document.setFontSize(8);
document.setHeight(pdfDoc.getDefaultPageSize().getHeight());
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
form.setGenerateAppearance(true);
//文字类的内容处理
Map<String, PdfFormField> fields = form.getFormFields();
Map<String, String> datemap = pdfPrintData.getField();
if (datemap != null) {
for (String key : datemap.keySet()) {
String value = datemap.get(key);
PdfFormField pdfFormField = fields.get(key);
if (pdfFormField !=null && value != null){
pdfFormField.setFont(sysFont);
pdfFormField.setValue(value);
}
}
}
form.flattenFields();//关闭表单编辑
//图片类的内容处理
Map<String, String> imgmap = pdfPrintData.getImage();
if (imgmap != null) {
for (String key : imgmap.keySet()) {
String value = imgmap.get(key);
String imgpath = value;
PdfFormField pdfFormField = form.getField(key);
if (pdfFormField == null){
continue;
}
Rectangle rectangle = form.getField(key).getWidgets().get(0).getRectangle().toRectangle();
// //根据路径读取图片
Image image = new Image(ImageDataFactory.create(IOUtils.toByteArray(new FileInputStream(imgpath))));
;
// //图片大小自适应
image.scaleToFit(rectangle.getWidth(), rectangle.getHeight());
image.setFixedPosition(rectangle.getX(), rectangle.getY());
// //添加图片
document.add(image);
}
}
//表格处理
if (StringUtils.isNotEmpty(pdfPrintData.getTable())) {
Rectangle rectangle = form.getField(pdfPrintData.getTable()).getWidgets().get(0).getRectangle().toRectangle();
Table table = new Table(UnitValue.createPercentArray(pdfPrintData.getColum()));
float pageheight = document.getPdfDocument().getPage(1).getPageSize().getHeight();
table.setMarginTop(pageheight - rectangle.getY() -rectangle.getHeight());
table.setMarginLeft(rectangle.getX());
table.setMarginRight(rectangle.getX());
//设置标题
for (String title : pdfPrintData.getTitle()) {
table.addCell(new Cell().add(new Paragraph(title).setFont(sysFont)).setBackgroundColor(backgroundColor).setFontColor(fontColor)
.setTextAlignment(TextAlignment.CENTER));
}
for (String[] datas : pdfPrintData.getData()) {
for (String data : datas) {
if (data == null){
data = "" ;
}
table.addCell(new Cell().add(new Paragraph(data).setFont(sysFont))
.setTextAlignment(TextAlignment.LEFT));
}
}
table.addFooterCell(new Cell(1,pdfPrintData.getTitle().length)
.add(new Paragraph("- - - 此页结束 - - -").setFont(sysFont))
.setTextAlignment(TextAlignment.CENTER));
document.add(table);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
document.close();
}
return newPDFPath;
}
/**
* 多个PDF合并
* @param pdfPrintDataList 模板数据,二维码图片,模板路径文件...
* @return 合并后的PDF路径
* @throws Exception 抛出异常
*/
public static String pdfouts(List<PdfPrintData> pdfPrintDataList) throws Exception {
// 创建临时目录, 用于存放临时文件
File file = new File(FileDirConfig.FILE_TEMP);
if (!file.exists()) {
file.mkdirs();
}
// 遍历入参列表,存到新的集合中
List<String> files = new ArrayList<>();
for (PdfPrintData pdfPrintData : pdfPrintDataList) {
files.add(pdfout(pdfPrintData));
}
// 生成的新文件路径,""+uuid
String newPDFPath = FileDirConfig.FILE_TEMP + OrderDateNumberGenerator.generate();
//将文档路径写入输出流
PdfWriter writer = new PdfWriter(new FileOutputStream(newPDFPath));
// 创建pdf文档
PdfDocument pdfDes = new PdfDocument(writer);
//合并多个PDF文档
PdfMerger merger = new PdfMerger(pdfDes);
for (String filePath : files) {
PdfReader reader = new PdfReader(filePath);
PdfDocument pdfResource = new PdfDocument(reader);
merger.setCloseSourceDocuments(true).merge(pdfResource, 1, pdfResource.getNumberOfPages());
pdfResource.close();
}
pdfDes.close();
return newPDFPath;
}
}
FileDirConfig.java
需要在配置文件中配置文件路径,被@Value()读取到,否则会报错,类似这样,可以定义自己的路径
配置和管理文件目录
/**
* @author itchao
* @create 2024-09-11
*/
@Component
public class FileDirConfig {
public static final String FILE_DOWNLOAD_URL = "/file/";
public static final String FILE_PREVIEW_URL = "/file/preview/";
public static String FILE_DIR ="";
public static String FILE_TEMP = "";
public static String FILE_GYTZ = "";
/**
* 设置文件目录及其子目录,不存在则创建
* @param fileDir 文件目录路径
*/
@Value("${filedir}")
public void setFileDir(String fileDir) {
FILE_DIR = fileDir;
File file = new File(fileDir);
if (!file.exists()){
file.mkdirs();
}
FILE_GYTZ = FILE_DIR + "GYTZ/";
file = new File(FILE_GYTZ);
if (!file.exists()){
file.mkdirs();
}
}
/**
* 设置文件目录,不存在则创建
* @param fileDir
*/
@Value("${filetemp}")
public void setFileTemp(String fileDir) {
FILE_TEMP = fileDir;
File file = new File(fileDir);
if (!file.exists()){
file.mkdirs();
}
}
/**
* 创建一个新的文件
*
* @return
*/
public static File getTempFile(String filename) {
File dir = new File(FileDirConfig.FILE_TEMP);
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(FileDirConfig.FILE_TEMP, filename);
return file;
}
/**
* 获取磁盘上的文件
*
* @param filename
* @return
*/
public static File getDiskFile(String dirs, String filename) {
File dir = new File(FileDirConfig.FILE_DIR + dirs);
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(FileDirConfig.FILE_DIR + dirs, filename);
return file;
}
}
PDFConfig.java
从配置文件中,获取PDF字体库路径,字体库文件路径改成自己的
/**
* pdf字体库路径
* @author itchao
* @create 2024-09-11
*/
@Component
public class PDFConfig {
// 配置文件中配置自己的文件路径
@Value("${pdf.pdf_font_library}")
private String pdf_font_library;
public String getPdfFontLibrary(){
return pdf_font_library;
}
}
SpringUtils.java
spring工具类 方便在非spring管理环境中获取bean
/**
* spring工具类 方便在非spring管理环境中获取bean
* @author itchao
* @create 2024-09-11
*/
@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware {
/** Spring应用上下文环境 */
private static ConfigurableListableBeanFactory beanFactory;
private static ApplicationContext applicationContext;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
{
SpringUtils.beanFactory = beanFactory;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
SpringUtils.applicationContext = applicationContext;
}
/**
* 获取对象
*
* @param name
* @return Object 一个以所给名字注册的bean的实例
* @throws org.springframework.beans.BeansException
*
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException
{
return (T) beanFactory.getBean(name);
}
/**
* 获取类型为requiredType的对象
*
* @param clz
* @return
* @throws org.springframework.beans.BeansException
*
*/
public static <T> T getBean(Class<T> clz) throws BeansException
{
T result = (T) beanFactory.getBean(clz);
return result;
}
}
DateUtil.java
日期工具类,主要是为了把日期转为字符串
/**
* 时间日期工具类
* 针对 java.util.Date 日期时间处理
* @author itchao
* @create 2024-09-11
*/
public class DateUtil extends org.apache.commons.lang3.time.DateUtils{
public static String YYYY = "yyyy";
public static String YYYY_MM = "yyyy-MM";
public static String YYYY_MM_DD = "yyyy-MM-dd";
public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
private static String[] parsePatterns = {
"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
"yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
/**
* 获取当前Date型日期
*
* @return Date() 当前日期
*/
public static Date getNowDate() {
return new Date();
}
/**
* 获取当前日期, 默认格式为yyyy-MM-dd
*
* @return String
*/
public static String getDate() {
return dateTimeNow(YYYY_MM_DD);
}
public static final String getTime() {
return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
}
public static final String dateTimeNow() {
return dateTimeNow(YYYYMMDDHHMMSS);
}
public static final String dateTimeNow(final String format) {
return parseDateToStr(format, new Date());
}
public static final String dateTime(final Date date) {
return parseDateToStr(YYYY_MM_DD, date);
}
public static final String parseDateToStr(final String format, final Date date) {
return new SimpleDateFormat(format).format(date);
}
}
8.前端部分
8.1 PDF打印,打印表单
8.1.2 单个打印PDF表单
打印按钮调用的函数,类似这样的,根据自己的自行修改,id不一样
// 打印方法
handleComplement(item){
printLabel(item.labelId).then(response => {
const binaryData = [];
binaryData.push(response);
let pdfUrl = window.URL.createObjectURL(new Blob(binaryData, { type: "application/pdf" }));
window.open(pdfUrl);
});
}
8.1.3 批量打印PDF表单
/** 批量打印 */
handlePrintBatch(row){
const labelIds = row.labelId || this.ids;
printLabelBatch(labelIds).then(response => {
const binaryData = [];
binaryData.push(response);
let pdfUrl = window.URL.createObjectURL(new Blob(binaryData, { type: "application/pdf" }));
window.open(pdfUrl);
});
}
8.2 PDF打印,打印表单+表格数据
8.2.1 打印PDF表单+表格数据
// 打印方法
handleComplement(item){
printLabel(item.labelId).then(response => {
const binaryData = [];
binaryData.push(response);
let pdfUrl = window.URL.createObjectURL(new Blob(binaryData, { type: "application/pdf" }));
window.open(pdfUrl);
});
}