Using Flying Saucer to Render Images to PDF In Memory

后端 未结 3 1511
傲寒
傲寒 2020-12-25 14:51

I\'m using Flying Saucer to convert XHTML to a PDF document. I\'ve gotten the code to work with just basic HTML and in-line CSS, however, now I\'m attempting to add an image

3条回答
  •  鱼传尺愫
    2020-12-25 15:40

    I had to do that last week so hopefully I will be able to answer you right away.

    Flying Saucer

    The easiest way is to add the image you want as markup in your HTML template before rendering with Flying Saucer. Within Flying Saucer you will have to implement a ReplacedElementFactory so that you can replace any markup before rendering with the image data.

    /**
     * Replaced element in order to replace elements like 
     * <div class="media" data-src="image.png" /> 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 
    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); } }

    You will notice that I have hardcoded here /base/folder which is the folder where the HTML file is located as it will be the root url for Flying Saucer for resolving medias. You may change it to the correct location, coming from anywhere you want (Properties for example).

    HTML

    Within your HTML markup you indicate somewhere a

    like so:

    
    
    
        
            My document
            
        
        
            
            

    Rendering

    And finally you just need to indicate your ReplacedElementFactory to Flying-Saucer when rendering:

    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();
    

    I have been using Freemarker to generate the HTML from a template and then feeding the result to FlyingSaucer with great success. This is a pretty neat library.

提交回复
热议问题