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

网友投稿 513 2022-11-04


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

在一些业务场景中,需要生成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));

Configurationhttp:// 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(), BaseFohttp://nt.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 http://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模板

姓名:${name}

证件号码:${cardNo}

日期:${date}

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


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:动车票查询API(动车票信息查询)
下一篇:一次人大金仓剔除锁经历
相关文章

 发表评论

暂时没有评论,来抢沙发吧~