Java生成pdf文件或jpg图片的案例讲解

在一些业务场景中,需要生成pdf文件或者jpg图片,有时候还需要带上水印。我们可以事先用freemarker定义好html模板,然后把模板转换成pdf或jpg文件。

同时freemarker模板还支持变量的定义,在使用时可以填充具体的业务数据。

1、Maven导包

<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>2.1.4.RELEASE</version>
</parent>
<dependencies>
 <!-- freemarker -->
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-context-support</artifactId>
 </dependency>
 <dependency>
 <groupId>org.freemarker</groupId>
 <artifactId>freemarker</artifactId>
 </dependency>
 <!-- pdf核心包 -->
 <dependency>
 <groupId>com.itextpdf</groupId>
 <artifactId>itextpdf</artifactId>
 <version>5.5.12</version>
 </dependency>
 <!-- 适配中文字体 -->
 <dependency>
 <groupId>com.itextpdf</groupId>
 <artifactId>itext-asian</artifactId>
 <version>5.2.0</version>
 </dependency>
 <!-- html转pdf -->
 <dependency>
 <groupId>com.itextpdf.tool</groupId>
 <artifactId>xmlworker</artifactId>
 <version>5.5.12</version>
 </dependency>
 <!-- pdf转图片 -->
 <dependency>
 <groupId>org.apache.pdfbox</groupId>
 <artifactId>pdfbox</artifactId>
 <version>2.0.5</version>
 </dependency>
</dependencies>

2、接口定义

2.1、请求

@Data
public class GeneratePdfReq {
 /**
 * 生成pdf文件的绝对路径
 */
 @NotBlank(message = "生成pdf文件的绝对路径不能为空")
 @Pattern(regexp = "^.*(\\.pdf|\\.jpg)$", message = "生成的文件必须以.pdf或.jpg结尾")
 private String absolutePath;
 /**
 * 使用html模板的绝对路径
 */
 @NotBlank(message = "使用的模板路径不能为空")
 private String templateName;
 /**
 * 渲染模板的业务数据
 */
 private Object dataModel;
 /**
 * 水印信息
 */
 private WaterMarkInfo waterMarkInfo;
 /**
 * pdf文件的宽,默认A4
 */
 private float width = 595;
 /**
 * pdf文件的高,默认A4
 */
 private float height = 842;
}

2.2、水印

@Data
public class WaterMarkInfo {
 /**
 * 如果为null设置水印时会报错
 */
 private String waterMark = "";
 /**
 * 水印透明度,值越小透明度越高
 */
 private float opacity = 0.2F;
 /**
 * 水印字体,如果乱码设置为本地宋体字体:fonts/simsun.ttc,1
 */
 private String fontName = "STSong-Light";
 /**
 * 水印编码格式,如果乱码设置为:BaseFont.IDENTITY_H
 */
 private String encoding = "UniGB-UCS2-H";
 /**
 * 字体大小
 */
 private float fontSize = 24;
 /**
 * 横坐标在页面宽度的百分比,左下角为原点
 */
 private float x = 50;
 /**
 * 纵坐标在页面高度的百分比,左下角为原点
 */
 private float y = 40;
 /**
 * 水印旋转角度
 */
 private float rotation = 45;
}

2.3、响应

@Data
public class GeneratePdfResp {
 /**
 * 生成pdf的绝对路径
 */
 private String absolutePath;
}

3、应用代码

3.1、渲染freemarker模板获取html网页

@Service("freeMarkerService")
@Slf4j
public class FreeMarkerServiceImpl implements FreeMarkerService {
 @Autowired
 private FreeMarkerConfigurer freeMarkerConfigurer;
 /**
 * 渲染html后获取整个页面内容
 *
 * @param templatePath 模板路径
 * @param dataModel 业务数据,一般以map形式传入
 * @return
 */
 @Override
 public String getHtml(String templatePath, Object dataModel) {
 log.info("开始将模板{}渲染为html,业务数据{}", templatePath, JSONUtil.toJsonPrettyStr(dataModel));
 Configuration cfg = freeMarkerConfigurer.getConfiguration();
 cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); // freemaker异常时仍旧抛出,统一异常处理
 cfg.setClassicCompatible(true);// 不需要对null值预处理,否则需要在模板取值时判断是否存在,不然报错
 StringWriter stringWriter = new StringWriter();
 try {
  // 设置模板所在目录,绝对路径方式,不打进jar包
//  cfg.setDirectoryForTemplateLoading(new File(templatePath).getParentFile());
//  Template temp = cfg.getTemplate(new File(templatePath).getName());
  // 相对路径设置模板所在目录,模板打进jar包,默认就是resources目录下的/templates目录。
  cfg.setClassForTemplateLoading(this.getClass(), "/templates");
  Template temp = cfg.getTemplate(templatePath);
  temp.process(dataModel, stringWriter);
 } catch (Exception e) {
  log.error(PdfErrorCode.PDF_TEMPLATE_RENDER_FAIL.getDesc(), e);
  throw new PdfBizException(PdfErrorCode.PDF_TEMPLATE_RENDER_FAIL);
 }
 return stringWriter.toString();
 }
}

3.2、将html网页转pdf,并添加水印

@Service("pdfService")
@Slf4j
public class PdfServiceImpl implements PdfService {
 public static final String FONT_PATH = "fonts/simsun.ttc,1";
 @Autowired
 private WaterMarkerService waterMarkerService;
 /**
 * html页面内容转pdf,并给每页附上水印
 *
 * @param html  html页面内容
 * @param width  pdf的宽
 * @param height pdf的高
 * @param waterMarkInfo 水印信息
 * @return
 */
 @Override
 public byte[] html2Pdf(String html, float width, float height, WaterMarkInfo waterMarkInfo) {
 log.info("=================开始将html转换为pdf=================");
 ByteArrayOutputStream out = new ByteArrayOutputStream();
 this.html2Pdf(html, width, height, out);
 byte[] bytes = out.toByteArray();
 // 设置水印
 if (waterMarkInfo != null) {
  bytes = waterMarkerService.addWaterMarker(bytes, waterMarkInfo);
 }
 return bytes;
 }
 /**
 * html转pdf
 *
 * @param html html页面内容
 * @param width pdf的宽
 * @param height pdf的高
 * @param out 输出流,pdf文件用此流输出,需要pdf文档关闭后流中才会有数据
 */
 @Override
 @SneakyThrows
 public void html2Pdf(String html, float width, float height, OutputStream out) {
 @Cleanup Document document = new Document(new RectangleReadOnly(width, height)); // 默认A4纵向
 // 这里需要关闭document才能让生成的pdf字节数据刷到输出流中
 PdfWriter writer = PdfWriter.getInstance(document, out); // 关闭可能导致生成的pdf显示异常(Chrome)
 document.open();
 // 设置字体,这里统一用simsun.ttc即宋体
 XMLWorkerFontProvider asianFontProvider = new XMLWorkerFontProvider() {
  @Override
  public Font getFont(String fontname, String encoding, boolean embedded, float size, int style, BaseColor color, boolean cached) {
  Font font;
  try {
   font = new Font(BaseFont.createFont(FONT_PATH, BaseFont.IDENTITY_H, BaseFont.EMBEDDED));
  } catch (Exception e) {
   log.error(PdfErrorCode.SET_PDF_FONT_FAIL.getDesc(), e);
   throw new PdfBizException(PdfErrorCode.SET_PDF_FONT_FAIL);
  }
  font.setStyle(style);
  font.setColor(color);
  if (size > 0) {
   font.setSize(size);
  }
  return font;
  }
 };
 // 生成pdf
 try {
  XMLWorkerHelper.getInstance().parseXHtml(writer, document, new ByteArrayInputStream(html.getBytes("UTF-8")), null, Charset.forName("UTF-8"), asianFontProvider);
  // 如果系统已经装有simsun.ttc字体,则不需要单独设置字体也不需要itext-asian jar包
//  XMLWorkerHelper.getInstance().parseXHtml(writer, document, new ByteArrayInputStream(html.getBytes("UTF-8")), null, Charset.forName("UTF-8"));
 } catch (RuntimeWorkerException e) {
  log.error(PdfErrorCode.HTML_CONVERT2PDF_FAIL.getDesc(), e);
  throw new PdfBizException(PdfErrorCode.HTML_CONVERT2PDF_FAIL);
 }
 }
}

添加水印实现类

@Service("waterMarkerService")
@Slf4j
public class WaterMarkerServiceImpl implements WaterMarkerService {
 /**
 * 给pdf文件每页添加水印
 *
 * @param source pdf文件的字节数组形式
 * @param waterMarkInfo 水印信息
 * @return
 */
 @Override
 public byte[] addWaterMarker(byte[] source, WaterMarkInfo waterMarkInfo) {
 log.info("开始设置水印数据{}", JSONUtil.toJsonPrettyStr(waterMarkInfo));
 ByteArrayOutputStream out = new ByteArrayOutputStream();
 this.addWaterMarker(source, waterMarkInfo, out);
 return out.toByteArray();
 }
 /**
 * 给pdf文件每页添加水印
 *
 * @param source pdf文件的字节数组形式
 * @param waterMarkInfo 水印信息
 * @param out  输出流,pdf文件用此流输出,需要pdf文档关闭后流中才会有数据
 */
 @Override
 @SneakyThrows
 public void addWaterMarker(byte[] source, WaterMarkInfo waterMarkInfo, OutputStream out) {
 @Cleanup PdfReader reader = new PdfReader(source);
 // 这里需要关闭PdfStamper才能让生成的pdf字节数据刷到输出流中
 @Cleanup PdfStamper pdfStamper = new PdfStamper(reader, out);
 BaseFont font = BaseFont.createFont(waterMarkInfo.getFontName(), waterMarkInfo.getEncoding(), BaseFont.EMBEDDED);
 PdfGState gs = new PdfGState();
 gs.setFillOpacity(waterMarkInfo.getOpacity());
 // 给每页pdf生成水印
 for (int i = 1; i <= reader.getNumberOfPages(); i++) {
  PdfContentByte waterMarker = pdfStamper.getUnderContent(i);
  waterMarker.beginText();
  // 设置水印透明度
  waterMarker.setGState(gs);
  // 设置水印字体和大小
  waterMarker.setFontAndSize(font, waterMarkInfo.getFontSize());
  // 设置水印位置、内容、旋转角度
  float X = reader.getPageSize(i).getWidth() * waterMarkInfo.getX() / 100;
  float Y = reader.getPageSize(i).getHeight() * waterMarkInfo.getY() / 100;
  waterMarker.showTextAligned(Element.ALIGN_CENTER, waterMarkInfo.getWaterMark(), X, Y, waterMarkInfo.getRotation());
  // 设置水印颜色
  waterMarker.setColorFill(BaseColor.GRAY);
  waterMarker.endText();
 }
 }
}

3.3、整合实现

@Slf4j
@Service("generatePdfService")
public class GeneratePdfServiceImpl implements RestService {
 @Autowired
 private FreeMarkerService freeMarkerService;
 @Autowired
 private PdfService pdfService;
 @Override
 @SneakyThrows
 public GeneratePdfResp service(GeneratePdfReq generatePdfReq) {
 log.info("开始生成pdf文件,请求报文:{}", JSONUtil.toJsonPrettyStr(generatePdfReq));
 /*
 1.根据freemarker模板填充业务数据获取完整的html字符串
  */
 String html = freeMarkerService.getHtml(generatePdfReq.getTemplateName(), generatePdfReq.getDataModel());
 /*
 2.生成pdf文件(内存)
  */
 byte[] bytes = pdfService.html2Pdf(html, generatePdfReq.getWidth(), generatePdfReq.getHeight(), generatePdfReq.getWaterMarkInfo());
 /*
 3.本地保存pdf文件
  */
 File targetFile = new File(generatePdfReq.getAbsolutePath());
 // 上级目录不存在则创建
 if (!targetFile.getParentFile().exists()) {
  targetFile.getParentFile().mkdirs();
 }
 // 根据不同文件名后缀生成对应文件
 if (generatePdfReq.getAbsolutePath().endsWith("pdf")) {
  FileUtils.writeByteArrayToFile(targetFile, bytes);
 } else {
  @Cleanup PDDocument document = PDDocument.load(bytes);
  PDFRenderer renderer = new PDFRenderer(document);
  BufferedImage bufferedImage = renderer.renderImageWithDPI(0, 150);// 只打第一页,dpi越大图片越高清也越耗时
  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  ImageIO.write(bufferedImage, "jpg", baos);
  FileUtils.writeByteArrayToFile(targetFile, baos.toByteArray());
 }
 log.info("文件本地保存完成,文件路径:[{}]", targetFile.getAbsolutePath());
 /*
 4.组织返回
  */
 GeneratePdfResp generatePdfResp = new GeneratePdfResp();
 generatePdfResp.setAbsolutePath(targetFile.getAbsolutePath());
 return generatePdfResp;
 }
}

3.4、controller

@Slf4j
@RestController
public class PdfController {
 @Autowired
 private RestService generatePdfService;
 @PostMapping(value = "/html2Pdf")
 public GeneratePdfResp html2Pdf(@RequestBody @Validated GeneratePdfReq req) {
 GeneratePdfResp resp = generatePdfService.service(req);
 return resp;
 }
}

4、应用

4.1、freemarker模板(html模板)

<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <meta http-equiv="Content-Style-Type" content="text/css"/>
 <style>
 body {
  font-family: SimSun
 }
 </style>
 <title>html模板</title>
</head>
<body>
<div>
 <p style="margin:0pt; orphans:0; text-align:center; widows:0">
 <span style="font-family:SimSun; font-size:16pt">html模板</span><br/>
 </p>
 <p>姓名:${name}</p>
 <p>证件号码:${cardNo}</p>
 <p>日期:${date}</p>
</div>
</body>
</html>

4.2、接口调用生成pdf

5、说明

1、根据参数后缀名可以生成pdf或jpg文件,生成的pdf文件默认为A4大小,也可以通过请求参数设置大小。

2、pdf文件会根据html模板内容大小自动分页。

3、如果生成图片,多页不会生成多张图片,可以把高度设置大一些,最后会生成长图。

4、水印每页都会自动添加。

5、为了提高代码的复用性和可维护性,工程内渲染html模板、生成pdf文件、添加水印都有单独的接口实现。

代码地址

github:https://github.com/senlinmu1008/spring-boot/tree/master/html2pdf

gitee:https://gitee.com/ppbin/spring-boot/tree/master/html2pdf

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。

(0)

相关推荐

  • 详解Java生成PDF文档方法

    最近项目需要实现PDF下载的功能,由于没有这方面的经验,从网上花了很长时间才找到相关的资料.整理之后,发现有如下几个框架可以实现这个功能. 1. 开源框架支持 iText,生成PDF文档,还支持将XML.Html文件转化为PDF文件: Apache PDFBox,生成.合并PDF文档: docx4j,生成docx.pptx.xlsx文档,支持转换为PDF格式. 比较: iText开源协议为AGPL,而其他两个框架协议均为Apache License v2.0. 使用PDFBox生成PDF就像画图

  • java根据富文本生成pdf文件过程解析

    这篇文章主要介绍了java根据富文本生成pdf文件过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 public class PdfUtil { /* * 生成pdf工具类 * wmy 12:40 2019/8/9 * @Param [guideBook, pdfPath] * @return java.lang.Boolean **/ public static Boolean htmlToPdf(GuideBook guideBook

  • Java pdf和jpg互转案例

    pdfbox: jpg转pdf: /** * 使用pdfbox将jpg转成pdf * @param jpgStream jpg输入流 * @param pdfPath pdf文件存储路径 * @throws IOException IOException */ public static void jpgToPdf(InputStream jpgStream, String pdfPath) throws IOException { PDDocument pdDocument = new PDD

  • Java将图片组合成PDF文件的方法

    本文实例为大家分享了Java将图片组合成PDF文件的具体代码,供大家参考,具体内容如下 程序界面图: 代码清单: package 将图片组合成PDF文件; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.FileFilter; import java.io.FileOutputStream; import java.io.IOE

  • Java生成pdf文件或jpg图片的案例讲解

    在一些业务场景中,需要生成pdf文件或者jpg图片,有时候还需要带上水印.我们可以事先用freemarker定义好html模板,然后把模板转换成pdf或jpg文件. 同时freemarker模板还支持变量的定义,在使用时可以填充具体的业务数据. 1.Maven导包 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</ar

  • JAVA生成pdf文件的实操指南

    目录 一.简介 二.实操 三.原理解析 1.是什么? 1.1.关键技术 2.怎么做?为什么? 3.参考 总结 一.简介 PDF文件格式可以将文字.字型.格式.颜色及独立于设备和分辨率的图形图像等封装在一个文件中.本文实现将html页面转PDF. 二.实操 生成pdf文件成功,但是文字对不上.当修改”GetHtmlContent“部分的编码之后,再次执行生成PDF文件即可完成正确的实现. Edit Configurations 三.原理解析 从这几点深入剖析和总结这个小项目: 1.是什么? 该项目

  • Java生成PDF文件的实例代码

    复制代码 代码如下: package com.qhdstar.java.pdf; import java.awt.Color;import java.io.FileOutputStream; import com.lowagie.text.Chapter;import com.lowagie.text.Document;import com.lowagie.text.Font;import com.lowagie.text.FontFactory;import com.lowagie.text.

  • Windows中使用Java生成Excel文件并插入图片的方法

    生成简单的Excel文件  在现实的办公中,我们常常会有这样一个要求:要求把报表直接用excel打开.在实习中有这样一个需求.根据所选择的资源查询用户所提供附件的全部信息并生成excel供下载.但是在查询的时候我们需要来检测用户所提供的附件里面的信息是否有错误(身份证).有错误的生成错误信息excel.      Apache的POI项目,是目前比较成熟的HSSF接口,用来处理Excel对象.其实POI不仅仅只能处理excel,它还可以处理word.PowerPoint.Visio.甚至Outl

  • Java使用iTextPDF生成PDF文件的实现方法

    iText介绍和说明 因为项目需要生成PDF文件,所以去找了一下能够生成PDF的Java工具,看到了iText可以说好评如潮. 如果你想通过java操作PDF文件,那么 iText 绝对是你的首选. 引入依赖 这里使用的是iText5 <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.10</ve

  • java实现pdf按页转换为图片

    本文实例为大家分享了java实现pdf按页转换为图片的具体代码,供大家参考,具体内容如下 本程序是利用jacob.jar包实现的,关于jacob.jar的配置见我上一篇文章,程序中可配置参数选择图片清晰图. package core.util; import java.awt.Image; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.io.File; import java.io.File

  • 利用Java对PDF文件进行电子签章的实战过程

    目录 一. 概述 二. 技术选型 三. 生成一个图片签章 1. 生成一个如下图的签章图片 2. 相关代码 四. 如何按模板生成PDF文件 1. 制作PDF模板 2. 制作一个如下图的PDF模板,该模板是带有PDF的表单域的 五. 如何生成PKCS12证书 1. PKCS的简单介绍 2. 使用JAVA生成一个PKCS12证书并进行存贮,相关分析见代码注解 六. 如何生成一个高清晰的签章 1. 由PDF模板生成一个PDF文件,见代码注解 2. 对PDF文件进行签章 3. 高清签章 七. 如何进行多次

  • 利用Python的Django框架生成PDF文件的教程

    便携文档格式 (PDF) 是由 Adobe 开发的格式,主要用于呈现可打印的文档,其中包含有 pixel-perfect 格式,嵌入字体以及2D矢量图像. You can think of a PDF document as the digital equivalent of a printed document; indeed, PDFs are often used in distributing documents for the purpose of printing them. 可以方

  • 使用PHP把HTML生成PDF文件的几个开源项目介绍

    利用PHP编码生成PDF文件是一个非常耗时的工作.在早期,开发者使用PHP并借助FPDF来生成PDF文件.但是如今,已经有很多函数库可以使用了,并且能够从你提供的HTML文件生成PDF文档.这让原先耗时的工作变得非常简单了. FPDF是很早就被使用的,其特点如下: FPDF FPDF是一个允许使用纯PHP生成PDF文档的PHP类,换句话说,没有使用PDFlib 函数库.FPDF中的F代表免费和自由:你可以在任何情况下使用,并且支持自定义,来满足你特定的需求 特点: 1.可以选择单元.页面格式和边

随机推荐