6 Replies Latest reply on Feb 9, 2017 5:38 AM by seblauf

    How can I print my page

    tonyukuk

      I want to print my web page. Do I have to use an applet or is there an easy way to get it done under seam framework?

        • 1. Re: How can I print my page
          schlegel

          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

            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

              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:&#160; 
                      <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/>
                  &#160;<br/>
                  und nun kommt der Text....
              </div>
              </body>
              </html>
              



              • 4. Re: How can I print my page
                nickarls

                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



                1. Render the view to a string

                2. Run the HTML through an XHTML:ifyer

                3. Make a DOM document of it

                4. Run the document through FS

                5. 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

                  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

                    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