java - 使用 Flying Saucer 在内存中将图像渲染为 PDF

标签 java image xhtml pdf-generation flying-saucer

我正在使用 Flying Saucer 将 XHTML 转换为 PDF 文档。我已经获得了仅使用基本 HTML 和内联 CSS 的代码,但是,现在我正尝试将图像作为一种标题添加到 PDF 中。我想知道的是,是否有任何方法可以通过将图像文件作为 Java Image 对象读取,然后以某种方式将其添加到 PDF(或 XHTML——就像它获得一个虚拟“url”一样)来添加图像表示可用于呈现 PDF 的图像对象)。有没有人做过这样的事情?

在此先感谢您提供的任何帮助!

最佳答案

上周我不得不这样做,所以希望我能马上给你答复。

飞碟

最简单的方法是在使用 Flying Saucer 渲染之前,在 HTML 模板中添加您想要的图像作为标记。在 Flying Saucer 中,您必须实现 ReplacedElementFactory这样您就可以在使用图像数据进行渲染之前替换任何标记。

/**
 * Replaced element in order to replace elements like 
 * <tt>&lt;div class="media" data-src="image.png" /></tt> with the real
 * media content.
 */
public class MediaReplacedElementFactory implements ReplacedElementFactory {
    private final ReplacedElementFactory superFactory;

    public MediaReplacedElementFactory(ReplacedElementFactory superFactory) {
        this.superFactory = superFactory;
    }

    @Override
    public ReplacedElement createReplacedElement(LayoutContext layoutContext, BlockBox blockBox, UserAgentCallback userAgentCallback, int cssWidth, int cssHeight) {
        Element element = blockBox.getElement();
        if (element == null) {
            return null;
        }
        String nodeName = element.getNodeName();
        String className = element.getAttribute("class");
        // Replace any <div class="media" data-src="image.png" /> with the
        // binary data of `image.png` into the PDF.
        if ("div".equals(nodeName) && "media".equals(className)) {
            if (!element.hasAttribute("data-src")) {
                throw new RuntimeException("An element with class `media` is missing a `data-src` attribute indicating the media file.");
            }
            InputStream input = null;
            try {
                input = new FileInputStream("/base/folder/" + element.getAttribute("data-src"));
                final byte[] bytes = IOUtils.toByteArray(input);
                final Image image = Image.getInstance(bytes);
                final FSImage fsImage = new ITextFSImage(image);
                if (fsImage != null) {
                    if ((cssWidth != -1) || (cssHeight != -1)) {
                        fsImage.scale(cssWidth, cssHeight);
                    }
                    return new ITextImageElement(fsImage);
                }
            } catch (Exception e) {
                throw new RuntimeException("There was a problem trying to read a template embedded graphic.", e);
            } finally {
                IOUtils.closeQuietly(input);
            }
        }
        return this.superFactory.createReplacedElement(layoutContext, blockBox, userAgentCallback, cssWidth, cssHeight);
    }

    @Override
    public void reset() {
        this.superFactory.reset();
    }

    @Override
    public void remove(Element e) {
        this.superFactory.remove(e);
    }

    @Override
    public void setFormSubmissionListener(FormSubmissionListener listener) {
        this.superFactory.setFormSubmissionListener(listener);
    }
}

你会注意到我在这里硬编码了/base/folder这是 HTML 文件所在的文件夹,因为它将是 Flying Saucer 用于解析媒体的根 URL。您可以将其更改为正确的位置,来自您想要的任何位置(例如属性)。

HTML

在您的 HTML 标记中,您在某处指示了一个 <div class="media" data-src="somefile.png" />像这样:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>My document</title>
        <style type="text/css">
        #logo { /* something if needed */ }
        </style>
    </head>
    <body>
        <!-- Header -->
        <div id="logo" class="media" data-src="media/logo.png" style="width: 177px; height: 60px" />
        ...
    </body>
</html>

渲染

最后您只需要指明您的 ReplacedElementFactory渲染时飞碟:

String content = loadHtml();
ITextRenderer renderer = new ITextRenderer();
renderer.getSharedContext().setReplacedElementFactory(new MediaReplacedElementFactory(renderer.getSharedContext().getReplacedElementFactory()));
renderer.setDocumentFromString(content.toString());
renderer.layout();
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
renderer.createPDF(baos);
// baos.toByteArray();

我一直在使用 Freemarker 从模板生成 HTML,然后将结果提供给 FlyingSaucer,并取得了巨大成功。这是一个非常简洁的库。

关于java - 使用 Flying Saucer 在内存中将图像渲染为 PDF,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11477065/

相关文章:

java - 创建 JsonArray

java - servlet中 'main method'的作用是什么?

java - 如何在 Groovy Jenkins 管道中使用全局外部 Java 库中的方法?

python - 如何在不使用OpenCV库的情况下在Python中读取多 channel 16位/ channel 图像?

css - 没有javascript的样式选择菜单

java - 为什么程序中输入错误

java - 有没有办法从随机文件中确定图像格式?

javascript - 使用过渡更改 Canvas 上的图像

css - 我们是否应应客户要求,针对Web标准,W3C验证和语义放宽我们的编码道德规范

java - 当文档以小写 &lt;!doctype 开头时, Karate HTML 解析抛出 SaxException