Coverage Report - net.sourceforge.pebble.web.view.PdfView
 
Classes in this File Line Coverage Branch Coverage Complexity
PdfView
7%
8/102
0%
0/14
3.6
PdfView$1
0%
0/3
0%
0/4
3.6
 
 1  
 /*
 2  
  * Copyright (c) 2003-2011, Simon Brown
 3  
  * All rights reserved.
 4  
  *
 5  
  * Redistribution and use in source and binary forms, with or without
 6  
  * modification, are permitted provided that the following conditions are met:
 7  
  *
 8  
  *   - Redistributions of source code must retain the above copyright
 9  
  *     notice, this list of conditions and the following disclaimer.
 10  
  *
 11  
  *   - Redistributions in binary form must reproduce the above copyright
 12  
  *     notice, this list of conditions and the following disclaimer in
 13  
  *     the documentation and/or other materials provided with the
 14  
  *     distribution.
 15  
  *
 16  
  *   - Neither the name of Pebble nor the names of its contributors may
 17  
  *     be used to endorse or promote products derived from this software
 18  
  *     without specific prior written permission.
 19  
  *
 20  
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 21  
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 22  
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 23  
  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 24  
  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 25  
  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 26  
  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 27  
  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 28  
  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 29  
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 30  
  * POSSIBILITY OF SUCH DAMAGE.
 31  
  */
 32  
 package net.sourceforge.pebble.web.view;
 33  
 
 34  
 import java.io.ByteArrayInputStream;
 35  
 import java.io.BufferedOutputStream;
 36  
 import java.io.IOException;
 37  
 import java.io.File;
 38  
 import java.io.FilenameFilter;
 39  
 
 40  
 import org.xml.sax.InputSource;
 41  
 import org.xml.sax.SAXException;
 42  
 
 43  
 import org.w3c.dom.Document;
 44  
 
 45  
 import javax.xml.parsers.DocumentBuilder;
 46  
 import javax.xml.parsers.DocumentBuilderFactory;
 47  
 import javax.xml.parsers.ParserConfigurationException;
 48  
 
 49  
 import net.sourceforge.pebble.PebbleContext;
 50  
 import net.sourceforge.pebble.domain.Tag;
 51  
 import net.sourceforge.pebble.domain.BlogEntry;
 52  
 import net.sourceforge.pebble.util.StringUtils;
 53  
 import net.sourceforge.pebble.web.listener.PebblePDFCreationListener;
 54  
 
 55  
 import javax.servlet.ServletContext;
 56  
 import javax.servlet.ServletException;
 57  
 import javax.servlet.http.HttpServletRequest;
 58  
 import javax.servlet.http.HttpServletResponse;
 59  
 
 60  
 import java.util.Iterator;
 61  
 
 62  
 import org.apache.commons.logging.Log;
 63  
 import org.apache.commons.logging.LogFactory;
 64  
 
 65  
 import com.lowagie.text.pdf.PdfWriter;
 66  
 import com.lowagie.text.pdf.BaseFont;
 67  
 import com.lowagie.text.DocumentException;
 68  
 
 69  
 import org.xhtmlrenderer.pdf.ITextRenderer;
 70  
 import org.xhtmlrenderer.pdf.TrueTypeUtil;
 71  
 
 72  
 
 73  
 /**
 74  
  * Represents a binary view component and prepares the model for display.
 75  
  *
 76  
  * @author    Alexander Zagniotov
 77  
  */
 78  
 public class PdfView extends BinaryView {
 79  
 
 80  
 
 81  4
   private static Log log = LogFactory.getLog(PdfView.class);
 82  
 
 83  
   private static final String SEP = "/";
 84  
   private static final String FONTS_PATH = "fonts";
 85  
   private static final String THEMES_PATH = "themes";
 86  
   private static final String DEFAULT_ENCODING = "UTF-8";
 87  
   private static final String PDF_CSS = "pdf.css";
 88  
   private static final String SYSTEM_THEME_PATH = HtmlView.SYSTEM_THEME;
 89  
 
 90  8
   private String filename = "default.pdf";
 91  
   private BlogEntry entry;
 92  8
   private long length = 0;
 93  
 
 94  8
   public PdfView(BlogEntry entry, String filename) {
 95  8
         this.entry = entry;
 96  8
     this.filename = filename;
 97  8
   }
 98  
 
 99  
   /**
 100  
    * Gets the title of this view.
 101  
    *
 102  
    * @return the title as a String
 103  
    */
 104  
   public String getContentType() {
 105  8
     return "application/pdf";
 106  
   }
 107  
 
 108  
   public long getContentLength() {
 109  0
     return length;
 110  
   }
 111  
 
 112  
   /**
 113  
    * Dispatches this view.
 114  
    *
 115  
    * @param request  the HttpServletRequest instance
 116  
    * @param response the HttpServletResponse instance
 117  
    * @param context
 118  
    */
 119  
   public void dispatch(HttpServletRequest request, HttpServletResponse response, ServletContext context) throws ServletException {
 120  
 
 121  
                         try {
 122  0
                                 ITextRenderer renderer = new ITextRenderer();
 123  
 
 124  
                                 //This will be an attachment
 125  0
                                 response.setHeader("Content-Disposition", "attachment; filename=" + filename);
 126  0
                                 response.setHeader("Expires", "0");
 127  0
                                 response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
 128  0
                                 response.setHeader("Pragma", "public");
 129  
 
 130  0
                                 String author = entry.getUser().getName();
 131  0
                                 String title = entry.getTitle();
 132  0
                                 String subtitle = entry.getSubtitle();
 133  0
                                 String body = entry.getBody();
 134  0
                                 String blogName = entry.getBlog().getName();
 135  0
                                 String entryPermalink = entry.getPermalink();
 136  0
                                 String entryDescription = entry.getBlog().getDescription();
 137  
 
 138  
                                 //Some of the HTML entities need to be escaped to Unicode notation \\uXXXX for XHTML markup to validate
 139  0
                                 title = StringUtils.transformHTML(title);
 140  0
                                 subtitle = StringUtils.transformHTML(subtitle);
 141  0
                                 body = StringUtils.unescapeHTMLEntities(body);
 142  
                                 
 143  
                                 //Build absolute path to: <pebble_root>/themes/_pebble/fonts/ 
 144  0
                                 String webApplicationRoot = PebbleContext.getInstance().getWebApplicationRoot() + SEP + THEMES_PATH;
 145  
 
 146  
                                 //<pebble_root> + / + themes + / + _pebble + / + fonts 
 147  0
                                 String fontDirAbsolutePath = webApplicationRoot + SEP + SYSTEM_THEME_PATH + SEP + FONTS_PATH;
 148  0
                                 File fontDir = new File(fontDirAbsolutePath);
 149  
 
 150  
 
 151  
                                 //Get blog entry tags for PDF metadata 'keywords'
 152  0
                                 StringBuffer tags = new StringBuffer();
 153  0
                                 Iterator<Tag> currentEntryTags = entry.getAllTags().iterator();
 154  
                                 
 155  
                                 //Build a string out of blog entry tags and seperate them by comma
 156  0
                                 while (currentEntryTags.hasNext()) {
 157  0
                                         Tag currentTag = currentEntryTags.next();
 158  0
                                         if (currentTag.getName() != null && !currentTag.getName().equals("")) {
 159  0
                                                  tags.append(currentTag.getName());
 160  0
                                                  if (currentEntryTags.hasNext()) {
 161  0
                                                         tags.append(",");
 162  
                                                 }
 163  
                                         }
 164  0
                                 }
 165  
 
 166  
                                 //Build valid XHTML source from blog entry for parsing 
 167  0
                                 StringBuffer buf = new StringBuffer();
 168  0
                                 buf.append("<html>");
 169  0
                                 buf.append("<head>");
 170  0
                                 buf.append("<meta name=\"title\" content=\"" + title + " - " + blogName + "\"/>");
 171  0
                                 buf.append("<meta name=\"subject\" content=\"" + title + "\"/>");
 172  0
                                 buf.append("<meta name=\"keywords\" content=\"" + tags.toString().trim() + "\"/>");
 173  0
                                 buf.append("<meta name=\"author\" content=\"" + author + "\"/>");
 174  0
                                 buf.append("<meta name=\"creator\" content=\"Pebble (by pebble.sourceforge.net)\"/>");
 175  0
                                 buf.append("<meta name=\"producer\" content=\"Flying Saucer (by xhtmlrenderer.dev.java.net)\"/>");
 176  0
                                 buf.append("<link rel='stylesheet' type='text/css' href='" + entry.getBlog().getUrl() + 
 177  
                                                                                                                                                         THEMES_PATH + SEP + 
 178  
                                                                                                                                                         SYSTEM_THEME_PATH + SEP + 
 179  
                                                                                                                                                         PDF_CSS + "' media='print' />");
 180  0
                                 buf.append("</head>");
 181  0
                                 buf.append("<body>");
 182  0
                                 buf.append("<div id=\"header\" style=\"\">" + blogName + " - " + entryDescription + "</div>");
 183  0
                                 buf.append("<p>");
 184  
 
 185  
                                 //Gets TTF or OTF font file from the font directory in the system theme folder
 186  0
                                  if (fontDir.isDirectory()) {
 187  0
                                                 File[] files = fontDir.listFiles(new FilenameFilter() {
 188  
                                                         public boolean accept(File dir, String name) {
 189  0
                                                                 String lower = name.toLowerCase();
 190  
 
 191  
                                                                 //Load TTF or OTF files
 192  0
                                                                 return lower.endsWith(".otf") || lower.endsWith(".ttf");
 193  
                                                         }
 194  
                                                 });
 195  
                                                 
 196  0
                                                 if (files.length > 0) {
 197  0
                                                         String fontFamilyName = "";
 198  
                                                         //You should always embed TrueType fonts.
 199  0
                                                         renderer.getFontResolver().addFont(files[0].getAbsolutePath(), BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
 200  0
                                                         log.info("Added font: " + files[0].getAbsolutePath());
 201  
 
 202  
                                                         //Get font family name from the BaseFont object. All this work just to get font family name
 203  0
                                                         BaseFont font = BaseFont.createFont(files[0].getAbsolutePath(), BaseFont.IDENTITY_H , BaseFont.NOT_EMBEDDED);
 204  0
                                                         fontFamilyName = TrueTypeUtil.getFamilyName(font);
 205  
                                                         
 206  0
                                                         if (!fontFamilyName.equals("")) {
 207  
                                                                 //Wrap DIV with font family name around the content of the blog entry
 208  0
                                                                 author = "<div style=\"font-family: " + fontFamilyName + ";\">" + author + "</div>";
 209  0
                                                                 title = "<div style=\"font-family: " + fontFamilyName + ";\">" + title + "</div>";
 210  0
                                                                 subtitle = "<div style=\"font-family: " + fontFamilyName + ";\">" + subtitle + "</div>";
 211  0
                                                                 body = "<div style=\"font-family: " + fontFamilyName + ";\">" + body + "</div>";
 212  0
                                                                 log.info("PDFGenerator - Added font family: '" + fontFamilyName + "' to PDF content");
 213  
                                                         }                
 214  
                                                 }
 215  
                                  }
 216  
 
 217  0
                                 buf.append("<h1>" + title  + "</h1>");
 218  0
                                 buf.append("<h2>" + subtitle + "</h2>");
 219  0
                                 buf.append("</p>");
 220  0
                                 buf.append("<p>" + body + "</p>");
 221  0
                                 buf.append("<p><br /><br /><br />");
 222  0
                                 buf.append("<i>Published by " + author + "</i><br />");
 223  0
                                 buf.append("<i>" + entry.getDate().toString() + "</i><br />");
 224  0
                                 buf.append("<i><a href=\"" + entryPermalink + "\" title=\"" + entryPermalink + "\">" + entryPermalink + "</a></i>");
 225  0
                                 buf.append("</p>");
 226  0
                                 buf.append("</body>");
 227  0
                                 buf.append("</html>");
 228  
 
 229  0
                                 byte[] bytes = buf.toString().getBytes(DEFAULT_ENCODING);
 230  
 
 231  0
                                 ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
 232  0
                                 DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
 233  0
                                 InputSource is = new InputSource(bais);
 234  0
                                 Document doc = builder.parse(is);
 235  
 
 236  
                                 //Listener that will parse HTML header meta tags, and will set them to PDF document as meta data
 237  0
                                 PebblePDFCreationListener pdfListener = new PebblePDFCreationListener();
 238  0
                                 pdfListener.parseMetaTags(doc);
 239  0
                                 renderer.setListener(pdfListener);
 240  0
                                 renderer.setDocument(doc, null);
 241  0
                                 renderer.layout();
 242  
 
 243  0
                                 BufferedOutputStream bufferedOutput = new BufferedOutputStream(response.getOutputStream());
 244  
                                 
 245  0
                                 renderer.createPDF(bufferedOutput);
 246  0
                                 bufferedOutput.flush();
 247  0
                                 bufferedOutput.close();
 248  
 
 249  0
                                 log.info("Successfully generated PDF document: " + filename);
 250  
                         }
 251  
 
 252  0
                         catch (ParserConfigurationException e)  {
 253  0
                                 log.error("Could not create PDF, could not get new instance of DocumentBuilder: " + e);
 254  
                         } 
 255  
 
 256  0
                         catch (SAXException e)  {
 257  0
                                 log.error("Could not create PDF, could not parse InputSource: " + e);
 258  
                         }
 259  
 
 260  0
                         catch (IOException e)  {
 261  0
                                 log.error("Could not create PDF: " + e);
 262  
                         }
 263  
 
 264  0
                         catch (DocumentException e)  {
 265  0
                                 log.error("iText could not create PDF document: " + e);
 266  
                         }
 267  
 
 268  0
                         catch (Exception e)  {
 269  0
                                 log.error("Could not create PDF: " + e);
 270  0
                         }
 271  0
                 }
 272  
         }