Clover Coverage Report - Pebble 2.5-SNAPSHOT
Coverage timestamp: Sat Jun 12 2010 09:39:29 EST
../../../../../img/srcFileCovDistChart1.png 47% of files have more coverage
92   272   17   18,4
12   163   0,18   5
5     3,4  
1    
This report was generated with an evaluation server license. Purchase Clover or configure your license.
 
  PdfView       Line # 78 92 0% 17 104 4,6% 0.04587156
 
  (4)
 
1    /*
2    * Copyright (c) 2003-2006, 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    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    private String filename = "default.pdf";
91    private BlogEntry entry;
92    private long length = 0;
93   
 
94  4 toggle public PdfView(BlogEntry entry, String filename) {
95  4 this.entry = entry;
96  4 this.filename = filename;
97    }
98   
99    /**
100    * Gets the title of this view.
101    *
102    * @return the title as a String
103    */
 
104  4 toggle public String getContentType() {
105  4 return "application/pdf";
106    }
107   
 
108  0 toggle 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  0 toggle public void dispatch(HttpServletRequest request, HttpServletResponse response, ServletContext context) throws ServletException {
120   
121  0 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    }
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  0 toggle 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    catch (ParserConfigurationException e) {
253  0 log.error("Could not create PDF, could not get new instance of DocumentBuilder: " + e);
254    }
255   
256    catch (SAXException e) {
257  0 log.error("Could not create PDF, could not parse InputSource: " + e);
258    }
259   
260    catch (IOException e) {
261  0 log.error("Could not create PDF: " + e);
262    }
263   
264    catch (DocumentException e) {
265  0 log.error("iText could not create PDF document: " + e);
266    }
267   
268    catch (Exception e) {
269  0 log.error("Could not create PDF: " + e);
270    }
271    }
272    }