如何将SVG图像添加到使用HTML和Flying Saucer库(和Batik)构建的PDF?

时间:2021-09-19 21:07:55

Im working on generation of PDFs with XHTML using the flying saucer library (old but open source). I got that working but I also want to add SVG images. Ive started working on integrating batik to try and get it to work but I'm running into issues. The SVG images are not drawn. The XHTML still renders, but it doesnt seem to show the SVG. I've gotten SVG to render on separate PDFs but never together with the flying saucer results. I've added the usual ReplacedElementFactory (which works with regular images as well but havent included that code). The only relevant method (that does get called and everything) is the following:

我正在使用飞碟库(旧的但开源的)使用XHTML生成PDF。我有那个工作,但我也想添加SVG图像。我开始致力于整合蜡染试图让它工作,但我遇到了问题。未绘制SVG图像。 XHTML仍然呈现,但它似乎没有显示SVG。我已经让SVG在单独的PDF上渲染,但从未与飞碟结果一起渲染。我添加了通常的ReplacedElementFactory(它也适用于常规图像,但还没有包含该代码)。唯一相关的方法(确实被调用和所有内容)如下:

@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();
    if ("img".equals(nodeName)) {
        SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName());

        SVGDocument svgImage = null;
        try {
            svgImage = factory.createSVGDocument(new File("logo.svg").toURL()
                    .toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
        Element svgElement = svgImage.getDocumentElement();
        Document htmlDoc = element.getOwnerDocument();
        Node importedNode = htmlDoc.importNode(svgElement, true);
        element.appendChild(importedNode);
        return new SVGReplacedElement(svgImage, cssWidth, cssHeight);
    }
    return this.superFactory.createReplacedElement(layoutContext, blockBox, userAgentCallback, cssWidth, cssHeight);
}

Afterwards I try to paint it with:

之后我尝试用它绘画:

import java.awt.Graphics2D;
import java.awt.Point;

import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.DocumentLoader;
import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.bridge.UserAgentAdapter;
import org.apache.batik.gvt.GraphicsNode;
import org.w3c.dom.svg.SVGDocument;
import org.xhtmlrenderer.css.style.CalculatedStyle;
import org.xhtmlrenderer.layout.LayoutContext;
import org.xhtmlrenderer.pdf.ITextOutputDevice;
import org.xhtmlrenderer.pdf.ITextReplacedElement;
import org.xhtmlrenderer.render.BlockBox;
import org.xhtmlrenderer.render.PageBox;
import org.xhtmlrenderer.render.RenderingContext;

import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfTemplate;

public class SVGReplacedElement implements ITextReplacedElement {

private Point location = new Point(0, 0);
private SVGDocument svg;
private int cssWidth;
private int cssHeight;

public SVGReplacedElement(SVGDocument importedNode, int cssWidth, int cssHeight) {
    this.cssWidth = cssWidth;
    this.cssHeight = cssHeight;
    this.svg = importedNode;
}

@Override
Methods....

@Override
public void paint(RenderingContext renderingContext, ITextOutputDevice outputDevice, 
        BlockBox blockBox) {

    UserAgent userAgent = new UserAgentAdapter();
    DocumentLoader loader = new DocumentLoader(userAgent);
    BridgeContext ctx = new BridgeContext(userAgent, loader);
    ctx.setDynamicState(BridgeContext.DYNAMIC);
    GVTBuilder builder = new GVTBuilder();

    blockBox.paintDebugOutline(renderingContext);

    PdfContentByte cb = outputDevice.getWriter().getDirectContent();

    float width = cssWidth / outputDevice.getDotsPerPoint();
    float height = cssHeight / outputDevice.getDotsPerPoint();

    PdfTemplate map = cb.createTemplate(width, height);

    Graphics2D g2d = map.createGraphics(width, height);

    GraphicsNode mapGraphics = builder.build(ctx, svg);
    mapGraphics.paint(g2d);
    g2d.dispose();

    PageBox page = renderingContext.getPage();
    float x = blockBox.getAbsX() + page.getMarginBorderPadding(renderingContext, CalculatedStyle.LEFT);
    float y = (page.getBottom() - (blockBox.getAbsY() + cssHeight)) + page.getMarginBorderPadding(
            renderingContext, CalculatedStyle.BOTTOM);

    cb.addTemplate(map, x, y);
}
}

Interestingly enough, the blockBox.paintDebugOutline(renderingContext); does draw the outlines of where the images should be. Eclipse debugging also revealed that the right files are connected to the IMG elements. The CSS looks as follows:

有趣的是,blockBox.paintDebugOutline(renderingContext);确实绘制了图像应该在哪里的轮廓。 Eclipse调试还显示正确的文件连接到IMG元素。 CSS看起来如下:

.header {
position: absolute;
display: inline-block;
right: 0;
top: 0;
width: 150px;
height: 54px;
}

I've also tried with display:block; . Examples xhtml I tried:

我也尝试过display:block; 。示例xhtml我试过:

<img class='header' src='icon.svg' alt='Logo'/> 
<svg class='header' type='image/svg+xml' data='icon.svg' />
<object class='header' type='image/svg+xml' data='icon.svg' />

Thanks a lot for your attention and feedback (and possibly answers)

非常感谢您的关注和反馈(以及可能的答案)

EDIT: Originally the problems was slightly different but I've resolved that. The SVGImage could not be appended to the actual document. Now it just doesnt draw. I've added the CSS to display:block etc as mentioned in guides.

编辑:最初的问题略有不同,但我已经解决了。 SVGImage无法附加到实际文档中。现在它只是没有画。我已经添加了CSS来显示:块等,如指南中所述。

EDIT: Cleaner code

编辑:清洁代码

EDIT: Added more on what I've tried

编辑:添加了更多关于我尝试过的内容

1 个解决方案

#1


I dont know why exactly, but replacing the code in SVGReplacedElement.paint(...) fixed it.

我不知道为什么,但更换SVGReplacedElement.paint(...)中的代码修复它。

New code:

@Override
public void paint(RenderingContext renderingContext, ITextOutputDevice outputDevice, 
        BlockBox blockBox) {
    PdfContentByte cb = outputDevice.getWriter().getDirectContent();
    float width = (float) (cssWidth / outputDevice.getDotsPerPoint());
    float height = (float) (cssHeight / outputDevice.getDotsPerPoint());

    PdfTemplate template = cb.createTemplate(width, height);
    Graphics2D g2d = template.createGraphics(width, height);
    PrintTranscoder prm = new PrintTranscoder();
    TranscoderInput ti = new TranscoderInput(svg);
    prm.transcode(ti, null);
    PageFormat pg = new PageFormat();
    Paper pp = new Paper();
    pp.setSize(width, height);
    pp.setImageableArea(0, 0, width, height);
    pg.setPaper(pp);
    prm.print(g2d, pg, 0);
    g2d.dispose();

    PageBox page = renderingContext.getPage();
    float x = blockBox.getAbsX() + page.getMarginBorderPadding(renderingContext, CalculatedStyle.LEFT);
    float y = (page.getBottom() - (blockBox.getAbsY() + cssHeight)) + page.getMarginBorderPadding(
            renderingContext, CalculatedStyle.BOTTOM);
    x /= outputDevice.getDotsPerPoint(); 
    y /= outputDevice.getDotsPerPoint();

    cb.addTemplate(template, x, y);
}

got it here: http://www.samuelrossille.com/posts/2013-08-13-render-html-with-svg-to-pdf-with-flying-saucer.html

得到它:http://www.samuelrossille.com/posts/2013-08-13-render-html-with-svg-to-pdf-with-flying-saucer.html

Might have had something to do with the new UserAgent and DocumentLoader etc I created that were not linked to the original document. In any case it works now. Hope it will help someone in the future. If people want to comment or reanswer as to why this works now, that might help other reading this later.

可能与我创建的没有链接到原始文档的新UserAgent和DocumentLoader等有关。无论如何它现在都有效。希望它能帮助将来的某个人。如果人们想要评论或重新说明为什么现在这样做,这可能有助于其他人稍后阅读。

#1


I dont know why exactly, but replacing the code in SVGReplacedElement.paint(...) fixed it.

我不知道为什么,但更换SVGReplacedElement.paint(...)中的代码修复它。

New code:

@Override
public void paint(RenderingContext renderingContext, ITextOutputDevice outputDevice, 
        BlockBox blockBox) {
    PdfContentByte cb = outputDevice.getWriter().getDirectContent();
    float width = (float) (cssWidth / outputDevice.getDotsPerPoint());
    float height = (float) (cssHeight / outputDevice.getDotsPerPoint());

    PdfTemplate template = cb.createTemplate(width, height);
    Graphics2D g2d = template.createGraphics(width, height);
    PrintTranscoder prm = new PrintTranscoder();
    TranscoderInput ti = new TranscoderInput(svg);
    prm.transcode(ti, null);
    PageFormat pg = new PageFormat();
    Paper pp = new Paper();
    pp.setSize(width, height);
    pp.setImageableArea(0, 0, width, height);
    pg.setPaper(pp);
    prm.print(g2d, pg, 0);
    g2d.dispose();

    PageBox page = renderingContext.getPage();
    float x = blockBox.getAbsX() + page.getMarginBorderPadding(renderingContext, CalculatedStyle.LEFT);
    float y = (page.getBottom() - (blockBox.getAbsY() + cssHeight)) + page.getMarginBorderPadding(
            renderingContext, CalculatedStyle.BOTTOM);
    x /= outputDevice.getDotsPerPoint(); 
    y /= outputDevice.getDotsPerPoint();

    cb.addTemplate(template, x, y);
}

got it here: http://www.samuelrossille.com/posts/2013-08-13-render-html-with-svg-to-pdf-with-flying-saucer.html

得到它:http://www.samuelrossille.com/posts/2013-08-13-render-html-with-svg-to-pdf-with-flying-saucer.html

Might have had something to do with the new UserAgent and DocumentLoader etc I created that were not linked to the original document. In any case it works now. Hope it will help someone in the future. If people want to comment or reanswer as to why this works now, that might help other reading this later.

可能与我创建的没有链接到原始文档的新UserAgent和DocumentLoader等有关。无论如何它现在都有效。希望它能帮助将来的某个人。如果人们想要评论或重新说明为什么现在这样做,这可能有助于其他人稍后阅读。