springboot使用xhtmlrenderer将html转图片 支持img标签、css
需要实现的需求
目前需要实现一个需求,通过填充现有模板来生成图片。之前模板使用图片,但修改起来不够灵活,现在改用html。结合freemarker填充数据,然后使用xhtmlrenderer转成图片。但遇到img标签不起作用的问题,查了很多博客,受这篇启发xhtmlrenderer 将html转换成pdf,完美css,带图片,这篇博主是转pdf,我是转为图片,处理上大同小异,本质还是自定义了一个xhtmlrenderer的替换元素工厂解决问题。下面我们按部就班。
引入依赖
<!-- freemarker处理html模板 填充数据-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- xhtmlrenderer将html转为图片-->
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>core-renderer</artifactId>
<version>R8</version>
</dependency>
html模板放置位置和获取方法
html模板我放在这里
获取放方法:
// 要获取的文件名
String template = "doorPlate.html";
Configuration cfg = new Configuration(Configuration.VERSION_2_3_30);
cfg.setDirectoryForTemplateLoading(
ResourceUtils.getFile("classpath:templates"));
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
//得到模板
Template temp = cfg.getTemplate(template);
使用freemarker填充模板
HashMap<String, Object> data = new HashMap();
// data.put("key",value)
StringWriter stringWriter = new StringWriter();
// 填入数据
temp.process(data, stringWriter);
stringWriter.flush();
stringWriter.close();
//获得html内容的字符串
String htmlStr = stringWriter.toString();
将html字符串转为图片
这里通过自定义ReplacedElementFactory来支持ima标签,注意:我这里img标签存放的是base64编码图片,而不是图片地址。因为用base64图片可以直接从标签中读取值,用地址的话需要手动去处理地址,获取图片,稍微麻烦一些。
// 看代码
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document document = builder.parse(new ByteArrayInputStream(html.getBytes()));
Java2DRenderer renderer = new Java2DRenderer(document, width, height);
// 重点在这里,自定义工厂 处理img标签
renderer.getSharedContext().setReplacedElementFactory(new Base64ImgReplacedElementFactory());
renderer.getSharedContext().getTextRenderer().setSmoothingThreshold(1);
BufferedImage image = renderer.getImage();
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
// 可以输出到流
ImageIO.write(image, "png", outputStream);
// 也可以直接保存到文件
ImageIO.write(image, "png", new File("/home/gyc/Desktop/test.png"));
return Base64.getEncoder().encodeToString(outputStream.toByteArray());
} catch (Exception e) {
throw new CustomException("桌牌图片转换失败");
}
// 自定义元素工厂的代码
public class Base64ImgReplacedElementFactory implements ReplacedElementFactory {
@SneakyThrows
@Override
public ReplacedElement createReplacedElement(LayoutContext layoutContext, BlockBox blockBox,UserAgentCallback userAgentCallback, int width, int height) {
Element e = blockBox.getElement();
if (e == null) {
return null;
}
String nodeName = e.getNodeName();
if ("img".equals(nodeName)) {
// 这里直接从标签获取base64图片的值,如果是地址的话需要通过地址去获取图片
String attribute = e.getAttribute("src");
// 这里的width和height就是标签的css属性
return new ImageReplacedElement(buildImage(attribute, userAgentCallback), width, height);
}
return null;
}
/**
* 将base64编码解码并生成图像
*
* @param srcAttr 属性
* @param uac 回调
* @return FSImage
* @throws IOException io异常
* @throws BadElementException BadElementException
*/
protected java.awt.Image buildImage(String srcAttr, UserAgentCallback uac) throws IOException{
if (srcAttr.startsWith("data:image/")) {
String b64encoded = srcAttr.substring(srcAttr.indexOf("base64,") + "base64,".length()
);
// 解码
byte[] decodedBytes = Base64.getDecoder().decode(b64encoded);
ByteArrayInputStream bais = new ByteArrayInputStream(decodedBytes);
return ImageIO.read(bais);
}
return null;
}
@Override
public void reset() { }
@Override
public void remove(Element element) { }
@Override
public void setFormSubmissionListener(FormSubmissionListener formSubmissionListener) { }
}