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模板我放在这里
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) { }
    }

到这里就结束了

可以使用开源框架Flying Saucer,它是一个基于W3C标准的开源HTML/CSS渲染引擎,可以将HTML页面换为PDF、图片和SVG等格式。 以下是使用Flying Saucer实现HTMLPDF的步骤: 1. 在pom.xml中添加Flying Saucer依赖: ```xml <dependency> <groupId>org.xhtmlrenderer</groupId> <artifactId>flying-saucer-core</artifactId> <version>9.1.21</version> </dependency> <dependency> <groupId>org.xhtmlrenderer</groupId> <artifactId>flying-saucer-pdf</artifactId> <version>9.1.21</version> </dependency> ``` 2. 编写换方法: ```java import java.io.FileOutputStream; import java.io.OutputStream; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import org.xhtmlrenderer.pdf.ITextRenderer; public class HtmlToPdfConverter { public static void convert(String html, String pdfFilename) throws Exception { // 解析HTML Document document = Jsoup.parse(html); // 处理CSS样式 document.head().append("<style>" + css() + "</style>"); // 处理图片相对路径 Elements imgs = document.getElementsByTag("img"); for (Element img : imgs) { String src = img.attr("src"); if (!src.startsWith("http")) { String absSrc = "file:///" + new File(src).getAbsolutePath(); img.attr("src", absSrc); } } // 换为PDF OutputStream os = new FileOutputStream(pdfFilename); ITextRenderer renderer = new ITextRenderer(); renderer.setDocumentFromString(document.toString()); renderer.layout(); renderer.createPDF(os); os.close(); } private static String css() { // CSS样式 return "body { font-family: 'Microsoft YaHei'; font-size: 14px; }"; } } ``` 3. 调用换方法: ```java String html = "<html><body><h1>Hello, World!</h1></body></html>"; HtmlToPdfConverter.convert(html, "hello.pdf"); ``` 以上是使用Flying Saucer实现HTMLPDF的简单示例,具体的实现方式还需根据业务需求进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值