gitee java pdf转图片_Java生成pdf文件或jpg图片的案例讲解

这篇博客介绍了如何在Java中利用Freemarker模板生成HTML,再通过iText将其转换为PDF文件,同时添加水印。文章详细阐述了所需的依赖、接口定义、水印配置以及实际的代码实现,包括模板渲染、HTML转PDF、添加水印等步骤。示例代码在GitHub和Gitee上可供参考。
摘要由CSDN通过智能技术生成

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

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

1、Maven导包

org.springframework.boot

spring-boot-starter-parent

2.1.4.RELEASE

org.springframework

spring-context-support

org.freemarker

freemarker

com.itextpdf

itextpdf

5.5.12

com.itextpdf

itext-asian

5.2.0

com.itextpdf.tool

xmlworker

5.5.12

org.apache.pdfbox

pdfbox

2.0.5

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模板)

body {

font-family: SimSun

}

html模板

html模板

姓名:${name}

证件号码:${cardNo}

日期:${date}

4.2、接口调用生成pdf

51df33b1eca26552033a196a0b229dd5.png

38a0047fc6d21c7a67b97148edbfa6ee.png

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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值