-
1. Re: How can I print my page
schlegel Sep 12, 2008 10:04 AM (in response to tonyukuk)Hi georgiua
Do you mean something like this...
http://today.java.net/pub/a/today/2006/10/31/combine-facelets-and-flying-saucer-renderer.html
if you, ore someone else is interested I can provide you with one working integration example for seam.
After the installation you would be able to call all your views with the addition of a Query-String
?RenderOutputType=pdf
and you get a pdf of the current page.
Additionally you are able to generate pretty sophisticated pdf-Reports without any special tags - you use the normal JSF/Richfaces/IceFaces or your own Facelet-Tags - and some CSS3 instructions.
There is one
limitation
- the rendered response must conform to xhtml1 specification!Let me know...
-
2. Re: How can I print my page
nickarls Sep 13, 2008 6:30 AM (in response to tonyukuk)I'm currently investigating something similar. The XHTML can be pre-processed by some cleanup-library so that isn't a problem but if you have ideas on how to limit the export to a single component while preserving the CSS, I'm all ears.
-
3. Re: How can I print my page
schlegel Sep 13, 2008 1:40 PM (in response to tonyukuk)Hi Nicklas
First of all - congratulation for your excel implementation!
But now to your actual question about component tree manipulation. To be honest I don't know very much
about that - sorry.What I can do is to give you all the information and code you need for a quick start with flying saucer.
Ok?- Documentation and resources:
http://today.java.net/pub/a/today/2007/06/26/generating-pdfs-with-flying-saucer-and-itext.html
http://today.java.net/pub/a/today/2006/10/31/combine-facelets-and-flying-saucer-renderer.html
http://www.jroller.com/wesleyhales/entry/convertingxhtmltopdfor
http://ljungblad.blogspot.com/2008/02/flying-saucer-renderer-in-jboss-seam.html
Dependencies:
All flying saucer jars
nekohtml
Code for HttpServletResponseWrapper:
import org.cyberneko.html.parsers.DOMParser; import static org.jboss.seam.ScopeType.APPLICATION; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import java.io.*; @Scope(APPLICATION) @Name("PDFContentCaptureServletResponse") public class ContentCaptureServletResponse extends HttpServletResponseWrapper { private ByteArrayOutputStream contentBuffer; private PrintWriter writer; public ContentCaptureServletResponse(HttpServletResponse originalResponse) { super(originalResponse); } @Override public PrintWriter getWriter() throws IOException { if (writer == null) { contentBuffer = new ByteArrayOutputStream(); writer = new PrintWriter(contentBuffer); } return writer; } public String getContent() throws IOException, SAXException, TransformerException { getWriter().flush(); String xhtmlContent = new String(contentBuffer.toByteArray()); // thead and tbody seam to work in the current version of saucer ?! // xhtmlContent = xhtmlContent.replaceAll("<thead>|</thead>|"+"<tbody>|</tbody>",""); DOMParser parser = new DOMParser(); parser.setFeature("http://cyberneko.org/html/features/balance-tags", true); parser.setProperty("http://cyberneko.org/html/properties/names/elems", "lower"); parser.setFeature("http://cyberneko.org/html/features/override-namespaces", true); parser.setFeature("http://cyberneko.org/html/features/insert-namespaces", true); parser.setProperty("http://cyberneko.org/html/properties/namespaces-uri", "http://www.w3.org/1999/xhtml"); parser.parse(new InputSource(new StringReader(xhtmlContent))); Document node = parser.getDocument(); StringWriter sw = new StringWriter(); Transformer t = null; t = TransformerFactory.newInstance().newTransformer(); t.setOutputProperty(OutputKeys.METHOD, "xml"); t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); t.transform(new DOMSource(node), new StreamResult(sw)); return sw.toString(); } }
Code for SAX EntityResolver:
import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import java.net.URL; public class LocalHostEntityResolver implements EntityResolver { private static final String XHTML1_TRANSITIONAL_DTD = "xhtml1-transitional.dtd"; public InputSource resolveEntity(String publicID, String systemID) throws SAXException { if ("-//W3C//DTD XHTML 1.0 Transitional//EN".equals(publicID) || "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd".equals(systemID)) { // extract Resource from facelets.jar URL url = Thread.currentThread().getContextClassLoader().getResource(XHTML1_TRANSITIONAL_DTD); return new InputSource(url.toString()); } // If no match, returning null makes process continue normally return null; } }
Code for PDF Rendering Filter:
import com.lowagie.text.DocumentException; import static org.jboss.seam.ScopeType.APPLICATION; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.jboss.seam.annotations.Startup; import org.jboss.seam.annotations.intercept.BypassInterceptors; import org.jboss.seam.annotations.web.Filter; import org.jboss.seam.web.AbstractFilter; import org.w3c.dom.Document; import org.xhtmlrenderer.pdf.ITextRenderer; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import java.io.IOException; import java.io.OutputStream; import java.io.StringReader; import java.net.URL; @Startup @Scope(APPLICATION) @Name("PDFRendererFilter") @BypassInterceptors @Filter(around = "org.jboss.seam.web.ajax4jsfFilter") public class RendererFilter extends AbstractFilter { FilterConfig config; private DocumentBuilder documentBuilder; private URL basePath = null; private static final String APPLICATION = "application/pdf"; private static final String RENDER_TYPE = "RenderOutputType"; private static final String MIME_TYPE = "pdf"; @Override public void init(FilterConfig config) throws ServletException { this.config = config; // fix ITextRenderer Problem/Bug // see http://ljungblad.blogspot.com/2008/02/flying-saucer-renderer-in-jboss-seam.html System.setProperty("xr.util-logging.loggingEnabled", "false"); try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); documentBuilder = factory.newDocumentBuilder(); } catch (ParserConfigurationException e) { throw new ServletException(e); } } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; String renderType = request.getParameter(RENDER_TYPE); if (basePath == null) { basePath = new URL(request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/"); } // check to see if this filter should apply. if (renderType != null && MIME_TYPE.equalsIgnoreCase(renderType)) { // capture the content for this request with this HttpServletResponseWrapper ContentCaptureServletResponse capContent = new ContentCaptureServletResponse(response); filterChain.doFilter(request, capContent); try { // parse the XHTML content of a document that is readable by the XHTML renderer StringReader contentReader = new StringReader(capContent.getContent()); InputSource source = new InputSource(contentReader); // without this the xml parser scans the internet ?? documentBuilder.setEntityResolver(new LocalHostEntityResolver()); Document xhtmlContent = documentBuilder.parse(source); ITextRenderer renderer = new ITextRenderer(); // basePath for css renderer.setDocument(xhtmlContent, basePath.toString()); renderer.layout(); response.setContentType(APPLICATION); OutputStream browserStream = response.getOutputStream(); renderer.createPDF(browserStream); } catch (SAXException e) { throw new ServletException(e); } catch (DocumentException e) { throw new ServletException(e); } catch (TransformerException e) { e.printStackTrace(); } } else { // normal processing filterChain.doFilter(request, response); } } @Override public void destroy() { } }
And finally an example of an reporting view with the css for page header, footer and page numbers:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:s="http://jboss.com/products/seam/taglib" xmlns:c="http://java.sun.com/jstl/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:obas="http://mycomp.org/form/jsf"> <head> <title>Bla</title> <style type="text/css"> .strong { font-weight: bold; } .left { text-align: left; } .center { text-align: center; } .right { text-align: right; } body { font-family: Arial, Verdana,sans-serif; font-size: 11px; margin: 0; padding: 0; border: 0; } @page { size: landscape; /* portrait */ margin: 3cm 1.5cm 2cm 1.5cm; -fs-flow-top: "header"; -fs-flow-bottom: "footer"; } #header { font-weight: bold; font-size: 13pt; position: absolute; top: 1.5cm; left: 0; width: 26.7cm; -fs-move-to-flow: "header"; } #footer { font-weight: bold; position: absolute; bottom: 1cm; left: 0; width: 26.7cm; -fs-move-to-flow: "footer"; } #pagenumber:before { content: counter(page); } #pagecount:before { content: counter(pages); } .avoidBreakInside { page-break-inside: avoid; } #sysUsersTbl tr td { border-bottom: solid 0.01mm #000; padding: 0.3mm 0; vertical-align: top; } #sysUsersTblHeader tr td { padding: 0.1cm 0; vertical-align: top; } </style> </head> <body> <div id="header" style="float: left; border-bottom: solid 0.01mm #000;"> <div style="float: left;">Benutzer-Verwaltung</div> <div style="float: right; font-size: 11px;">Datum:  <h:outputText value="#{obas:getCurrentDateTime()}"> <s:convertDateTime locale="#{locale}" dateStyle="medium"/> </h:outputText> </div> <div id="tblHeader" style="float: left; clear: left;"> <table id="sysUsersTblHeader" cellpadding="0" cellspacing="0" border="0" style="font-size: 11px;"> <tr> <td> <div style="width: 3cm" class="left strong"> Name </div> </td> <td> <div style="width: 10cm" class="left strong"> Name<br/>Vollstaendiger Name </div> </td> <td> <div style="width: 3cm" class="left strong"> Aktiv </div> </td> <td> <div style="width: 5.5cm" class="right strong"> Gueltig Ab<br/>Gueltig Bis </div> </td> <td> <div style="width: 5.15cm" class="right strong"> Bilder Test </div> </td> </tr> </table> </div> </div> <div id="footer" style="border-top: solid 0.01mm #000; padding-top: 0.2cm;"> <div style="float: left"><img src="/obas/img/appl/codes.gif" alt=" " width="32px" height="32px" border="0"/></div> <div style="float: left; margin-left: 5px;">Wegelin und Co. Bankiers</div> <div style="float: right; text-align: right;">Seite <span id="pagenumber"/> von <span id="pagecount"/></div> </div> <h:dataTable id="sysUsersTbl" value="#{demoList}" var="dataRow" rendered="#{demoList.size>0}" cellpadding="0" cellspacing="0" border="0" rowClasses="avoidBreakInside"> <h:column> <div style="width: 3cm" class="left"> #{dataRow.name} </div> </h:column> <h:column> <s:fragment> <div style="width: 10cm" class="left"> #{dataRow.name} </div> <div style="width: 10cm;" class="left strong"> #{dataRow.realName} </div> </s:fragment> </h:column> <h:column> <div style="width: 3cm; font-style: italic; font-weight: bold; font-size: 16px; color: red;" class="left"> #{dataRow.active} </div> </h:column> <h:column> <div style="width: 5.5cm" class="right"> <h:outputText value="#{dataRow.validFrom}"> <s:convertDateTime locale="#{locale}" dateStyle="medium"/> </h:outputText> </div> <div style="width: 5.5cm" class="right"> <h:outputText value="#{dataRow.validFrom}"> <s:convertDateTime locale="#{locale}" dateStyle="medium"/> </h:outputText> </div> </h:column> <h:column> <div style="width: 5.15cm; margin: 1px;" class="right"> <img src="/obas/img/appl/codes.gif" alt=" " width="32px" height="32px" border="0"/> </div> </h:column> </h:dataTable> <div style="page-break-before: always; clear: left; float: left; margin-top: 50px;"> <strong>Das ist ein Disclamer:</strong><br/>  <br/> und nun kommt der Text.... </div> </body> </html>
-
4. Re: How can I print my page
nickarls Sep 13, 2008 2:47 PM (in response to tonyukuk)Thanks for the info. I had an idea of doing it something like a component #{screenshot.pdf()} (or svg, jpg or other FS supported) that would pretty much do
- Render the view to a string
- Run the HTML through an XHTML:ifyer
- Make a DOM document of it
- Run the document through FS
- Add headers and dump the result to the response
There is the slight overhead of re-rendering the view to the temp string but on the upside there is no extra setup of capturing stuff every reponse etc. I think the re-render to HTML is not that intensive compared to the PDF conversion in the grand scheme of things, anyway.
I'll look closer at it next week.
Now the tricky part is how to extend the screenshotter to parts of a page (a div, a datatable etc, #{screenshot.pdf('target_id'}) so that the output is the targetted component itself with styling preserved). If I hide the parents, the component is hidden. If I rip it out from the parent, it looses the cascade.
I'll see if I end up with something like
find component, iterate up through parents, prepending the style/styleClass, place component at root of body
or something if noone comes up with a cleaner approach... -
5. Re: How can I print my page
tejl Apr 23, 2010 4:01 PM (in response to tonyukuk)Did any of you guys try this solution with an Ajax framework like Richfaces? I am thinking of some Richfaces components like ScrollableDataTable for which the rendered output is mostly (or maybe all of it?) javascript and that Flying Saurcer may not be able to handle that?
-
6. Re: How can I print my page
seblauf Feb 9, 2017 5:38 AM (in response to schlegel)i am very interested in your example, because i am very new in jsf and i need a convertion of a xhtml facrelet page to a pdf file
i am from germany
thank you for your help
sebastian lauf