{"id":6355,"date":"2012-08-24T17:32:44","date_gmt":"2012-08-24T12:02:44","guid":{"rendered":"http:\/\/www.tothenew.com\/blog\/?p=6355"},"modified":"2012-08-24T17:32:44","modified_gmt":"2012-08-24T12:02:44","slug":"using-data-urls-for-embedding-images-in-flying-saucer-generated-pdfs","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/using-data-urls-for-embedding-images-in-flying-saucer-generated-pdfs\/","title":{"rendered":"Using Data URLs for embedding images in Flying Saucer generated PDFs"},"content":{"rendered":"<p>We extensively use Flying Saucer to generate PDFs from GSPs in our grails applications. However, there is always the issue of embedding images from within the application because the URLs are usually relative to the environment and as such, embedding them in PDFs with a URL in the src attribute is cumbersome.<\/p>\n<p>To get around this, we decided to write our own implementation of the <a href=\"http:\/\/grepcode.com\/file\/repo1.maven.org\/maven2\/org.xhtmlrenderer\/core-renderer\/R8pre2\/org\/xhtmlrenderer\/extend\/ReplacedElementFactory.java\" target=\"_blank\">ReplacedElementFactory<\/a> taking some help from this excellent <a href=\"https:\/\/gist.github.com\/915348\" target=\"_blank\">snippets of code<\/a>. However, we didn&#8217;t find a need to go with our custom implementation of Base64 encoding and as such, we ended up using the <a href=\"http:\/\/grepcode.com\/file\/repository.grepcode.com\/java\/root\/jdk\/openjdk\/6-b14\/sun\/misc\/BASE64Decoder.java\">sun.misc.BASE64Decoder<\/a>. The resulting class looked like this :<\/p>\n<p>[java]<\/p>\n<p>import com.lowagie.text.BadElementException<br \/>\nimport com.lowagie.text.Image<br \/>\nimport org.w3c.dom.Element<br \/>\nimport org.xhtmlrenderer.extend.FSImage<br \/>\nimport org.xhtmlrenderer.extend.ReplacedElement<br \/>\nimport org.xhtmlrenderer.extend.ReplacedElementFactory<br \/>\nimport org.xhtmlrenderer.extend.UserAgentCallback<br \/>\nimport org.xhtmlrenderer.layout.LayoutContext<br \/>\nimport org.xhtmlrenderer.pdf.ITextFSImage<br \/>\nimport org.xhtmlrenderer.pdf.ITextImageElement<br \/>\nimport org.xhtmlrenderer.render.BlockBox<br \/>\nimport org.xhtmlrenderer.simple.extend.FormSubmissionListener<\/p>\n<p>public class B64ImgReplacedElementFactory implements ReplacedElementFactory {<\/p>\n<p> public ReplacedElement createReplacedElement(LayoutContext c, BlockBox box, UserAgentCallback uac, int cssWidth, int cssHeight) {<br \/>\n     Element e = box.getElement();<br \/>\n     if (e == null) {<br \/>\n         return null;<br \/>\n     }<br \/>\n     String nodeName = e.getNodeName();<br \/>\n     if (nodeName.equals(&quot;img&quot;)) {<br \/>\n         String attribute = e.getAttribute(&quot;src&quot;);<br \/>\n         FSImage fsImage;<br \/>\n         try {<br \/>\n             fsImage = buildImage(attribute, uac);<br \/>\n         } catch (BadElementException e1) {<br \/>\n             fsImage = null;<br \/>\n         } catch (IOException e1) {<br \/>\n             fsImage = null;<br \/>\n         }<br \/>\n         if (fsImage != null) {<br \/>\n             if (cssWidth != -1 || cssHeight != -1) {<br \/>\n                 fsImage.scale(cssWidth, cssHeight);<br \/>\n             }<br \/>\n             return new ITextImageElement(fsImage);<br \/>\n         }<br \/>\n     }<br \/>\n     return null;<br \/>\n }<\/p>\n<p> protected FSImage buildImage(String srcAttr, UserAgentCallback uac) throws IOException, BadElementException {<br \/>\n      FSImage fsImage;<br \/>\n      if (srcAttr.startsWith(&quot;data:image\/&quot;)) {<br \/>\n         String b64encoded = srcAttr.substring(srcAttr.indexOf(&quot;base64,&quot;) + &quot;base64,&quot;.length(), srcAttr.length());<br \/>\n         byte[] decodedBytes = new sun.misc.BASE64Decoder().decodeBuffer(b64encoded);<br \/>\n         fsImage = new ITextFSImage(Image.getInstance(decodedBytes));<br \/>\n      } else {<br \/>\n         fsImage = uac.getImageResource(srcAttr).getImage();<br \/>\n      }<br \/>\n      return fsImage;<br \/>\n }<\/p>\n<p> public void remove(Element e) {<br \/>\n }<\/p>\n<p> public void reset() {<br \/>\n }<\/p>\n<p> @Override<br \/>\n public void setFormSubmissionListener(FormSubmissionListener listener) {<br \/>\n }<br \/>\n}<br \/>\n[\/java]<\/p>\n<p>Now, in the code where we call the Renderer, we used :<\/p>\n<pre>\r\n[java]\r\n byte[] generatePdf(String content) {\r\n        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();\r\n        byte[] bytes = null\r\n        ITextRenderer renderer = new ITextRenderer();\r\n        SharedContext sharedContext = renderer.getSharedContext();\r\n        sharedContext.setPrint(true);\r\n        sharedContext.setInteractive(false);\r\n        sharedContext.setReplacedElementFactory(new B64ImgReplacedElementFactory());\r\n        sharedContext.getTextRenderer().setSmoothingThreshold(0);\r\n        try {\r\n            renderer.setDocumentFromString(content);\r\n            renderer.layout();\r\n            renderer.createPDF(byteArrayOutputStream);\r\n            bytes = byteArrayOutputStream.toByteArray()\r\n        }\r\n        catch (Throwable e) {\r\n            log.error(&quot;Error while generating pdf ${e.message}&quot;, e)\r\n        }\r\n        return bytes\r\n    }\r\n[\/java]\r\n<\/pre>\n<p>Now, we can embed images in the form of data URLs in our GSPs using base64 encoded version of the image bytes.<br \/><\/p>\n","protected":false},"excerpt":{"rendered":"<p>We extensively use Flying Saucer to generate PDFs from GSPs in our grails applications. However, there is always the issue of embedding images from within the application because the URLs are usually relative to the environment and as such, embedding them in PDFs with a URL in the src attribute is cumbersome. To get around [&hellip;]<\/p>\n","protected":false},"author":10,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":125},"categories":[7],"tags":[880,879,292],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/6355"}],"collection":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/users\/10"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=6355"}],"version-history":[{"count":0,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/6355\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=6355"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=6355"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=6355"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}