3

I'm rendering PDFs with XHTML and flying saucer. I've added SVG images (icons etc) as well. However, when I try to draw a lot of images (like 5000+) the rendering takes really long (obviously). There are only 10 or so different images to draw, but just repeating them a lot of times (same size).

Is there a way/library to do this efficiently?

Currently using batik, flying saucer combo to draw images. The following code is used to parse the xhtml and find the img tags to place the SVG images:

@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(element.getAttribute("src")).toURL().toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
        Element svgElement = svgImage.getDocumentElement();
        element.appendChild(element.getOwnerDocument().importNode(svgElement, true));
        return new SVGReplacedElement(svgImage, cssWidth, cssHeight);
    }
    return this.superFactory.createReplacedElement(layoutContext, blockBox, userAgentCallback, cssWidth, cssHeight);
}

And to draw the images i use:

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

    PdfContentByte cb = outputDevice.getWriter().getDirectContent();
    float width = cssWidth / outputDevice.getDotsPerPoint();
    float height = 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);
}

An idea of the scaling. 100 images take 2 seconds, 5000 images take about 42 seconds on an i5 8gb RAM.

So is there a way to store a drawn SVG in memory and paste it more quickly or something? Because right now it seems to take all images as separate images and eat all my memory and take forever.

Daxterwous
  • 299
  • 1
  • 3
  • 14

1 Answers1

1

Managed to optimize the memory and speed by doing two things. I pre-generated the SVGDocuments in the createReplacedElement method which sped it up a bit. The main improvement was pre-generating all pdfTemplates for all the images. This greatly increased speed as the templates already contained the rendered images. The rendering of all regular text is still slow, so I might turn down the DPI.

EDIT: further optimization see Is there any way improve the performance of FlyingSaucer?

Community
  • 1
  • 1
Daxterwous
  • 299
  • 1
  • 3
  • 14