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) { }
    }

到这里就结束了

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好!使用iText进行PDF图片需要用到iText的核心模块 `itextpdf` 和 `pdf-renderer` 模块。这两个模块都是免费的。以下是一个基于Spring Boot的示例,演示如何使用iText将PDF换为图像: 首先,在您的pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.13.2</version> </dependency> <dependency> <groupId>org.xhtmlrenderer</groupId> <artifactId>flying-saucer-pdf</artifactId> <version>9.1.22</version> </dependency> ``` 接下来,创建一个名为 `PdfToImageService` 的服务类,该类具有一个名为 `pdfToImage` 的方法,该方法将接受一个PDF文件路径作为输入,并返回一个图像列表: ```java import com.itextpdf.text.DocumentException; import com.itextpdf.text.pdf.PdfReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import org.xhtmlrenderer.swing.Java2DRenderer; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; @Service public class PdfToImageService { private static final Logger LOGGER = LoggerFactory.getLogger(PdfToImageService.class); public List<byte[]> pdfToImage(String pdfFilePath) throws IOException, DocumentException { LOGGER.info("Converting PDF to image: {}", pdfFilePath); List<byte[]> imageBytesList = new ArrayList<>(); PdfReader pdfReader = new PdfReader(pdfFilePath); for (int page = 1; page <= pdfReader.getNumberOfPages(); page++) { LOGGER.info("Converting page {} of PDF: {}", page, pdfFilePath); // 使用Flying Saucer将PDF页面换为BufferedImage Java2DRenderer renderer = new Java2DRenderer(pdfReader.getPageContent(page)); BufferedImage bufferedImage = renderer.getImage(); // 将BufferedImage换为byte数组 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); javax.imageio.ImageIO.write(bufferedImage, "png", outputStream); imageBytesList.add(outputStream.toByteArray()); outputStream.close(); } pdfReader.close(); return imageBytesList; } } ``` 最后,在您的控制器中注入 `PdfToImageService`,并将其用于将PDF换为图像: ```java import com.itextpdf.text.DocumentException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ByteArrayResource; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import java.io.IOException; import java.util.List; @Controller @RequestMapping("/pdf-to-image") public class PdfToImageController { private static final Logger LOGGER = LoggerFactory.getLogger(PdfToImageController.class); @Autowired private PdfToImageService pdfToImageService; @GetMapping(value = "/{pdfFileName}", produces = MediaType.IMAGE_PNG_VALUE) public ResponseEntity<ByteArrayResource> pdfToImage(@PathVariable String pdfFileName) throws IOException, DocumentException { LOGGER.info("Converting PDF to image: {}", pdfFileName); String pdfFilePath = "path/to/pdf/" + pdfFileName + ".pdf"; List<byte[]> imageBytesList = pdfToImageService.pdfToImage(pdfFilePath); if (imageBytesList.isEmpty()) { return ResponseEntity.notFound().build(); } else if (imageBytesList.size() == 1) { return ResponseEntity.ok(new ByteArrayResource(imageBytesList.get(0))); } else { // 将多个图像合并为一个图像 // ... byte[] mergedImageBytes = new byte[0]; // 合并后的图像的字节数组 return ResponseEntity.ok(new ByteArrayResource(mergedImageBytes)); } } } ``` 这个示例代码假设您已经将要换的PDF文件放在 `path/to/pdf` 目录下。当您访问 `/pdf-to-image/{pdfFileName}` 时,它将返回换后的PNG图像。如果PDF文件具有多个页面,则可以将它们合并为一个图像,或者返回多个图像。希望这可以帮助到您!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值