001: /*
002: * Copyright 2002-2005 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.web.servlet.view.document;
018:
019: import java.io.ByteArrayOutputStream;
020: import java.io.OutputStream;
021: import java.util.Map;
022:
023: import javax.servlet.ServletOutputStream;
024: import javax.servlet.http.HttpServletRequest;
025: import javax.servlet.http.HttpServletResponse;
026:
027: import com.lowagie.text.Document;
028: import com.lowagie.text.DocumentException;
029: import com.lowagie.text.PageSize;
030: import com.lowagie.text.pdf.PdfWriter;
031:
032: import org.springframework.web.servlet.view.AbstractView;
033:
034: /**
035: * Abstract superclass for PDF views, using Bruno Lowagie's
036: * <a href="http://www.lowagie.com/iText">iText</a> package.
037: * Application-specific view classes will extend this class.
038: * The view will be held in the subclass itself, not in a template.
039: *
040: * <p>Note: Internet Explorer requires a ".pdf" extension, as
041: * it doesn't always respect the declared content type.
042: *
043: * @author Rod Johnson
044: * @author Jean-Pierre Pawlak
045: * @author Juergen Hoeller
046: */
047: public abstract class AbstractPdfView extends AbstractView {
048:
049: private static final int OUTPUT_BYTE_ARRAY_INITIAL_SIZE = 4096;
050:
051: /**
052: * This constructor sets the appropriate content type "application/pdf".
053: * Note that IE won't take much notice of this, but there's not a lot we
054: * can do about this. Generated documents should have a ".pdf" extension.
055: */
056: public AbstractPdfView() {
057: setContentType("application/pdf");
058: }
059:
060: protected final void renderMergedOutputModel(Map model,
061: HttpServletRequest request, HttpServletResponse response)
062: throws Exception {
063:
064: // The following simple method doesn't work in IE, which
065: // needs to know the content length.
066:
067: // PdfWriter writer = PdfWriter.getInstance(document, response.getOutputStream());
068: // document.open();
069: // buildPdfDocument(model, document, writer, request, response);
070: // document.close();
071:
072: // See http://www.lowagie.com/iText/faq.html#msie
073: // for an explanation of why we can't use the obvious form above.
074:
075: // IE workaround: write into byte array first.
076: ByteArrayOutputStream baos = new ByteArrayOutputStream(
077: OUTPUT_BYTE_ARRAY_INITIAL_SIZE);
078: Document document = newDocument();
079: PdfWriter writer = newWriter(document, baos);
080:
081: // Apply preferences and build metadata.
082: prepareWriter(model, writer, request);
083: buildPdfMetadata(model, document, request);
084:
085: // Build PDF document.
086: document.open();
087: buildPdfDocument(model, document, writer, request, response);
088: document.close();
089:
090: // Write content type and also length (determined via byte array).
091: response.setContentType(getContentType());
092: response.setContentLength(baos.size());
093:
094: // Flush byte array to servlet output stream.
095: ServletOutputStream out = response.getOutputStream();
096: baos.writeTo(out);
097: out.flush();
098: }
099:
100: /**
101: * Create a new document to hold the PDF contents.
102: * <p>By default returns an A4 document, but the subclass can specify any
103: * Document, possibly parameterized via bean properties defined on the View.
104: * @return the newly created iText Document instance
105: * @see com.lowagie.text.Document#Document(com.lowagie.text.Rectangle)
106: */
107: protected Document newDocument() {
108: return new Document(PageSize.A4);
109: }
110:
111: /**
112: * Create a new PdfWriter for the given iText Document.
113: * @param document the iText Document to create a writer for
114: * @param os the OutputStream to write to
115: * @return the PdfWriter instance to use
116: * @throws DocumentException if thrown during writer creation
117: */
118: protected PdfWriter newWriter(Document document, OutputStream os)
119: throws DocumentException {
120: return PdfWriter.getInstance(document, os);
121: }
122:
123: /**
124: * Prepare the given PdfWriter. Called before building the PDF document,
125: * that is, before the call to <code>Document.open()</code>.
126: * <p>Useful for registering a page event listener, for example.
127: * The default implementation sets the viewer preferences as returned
128: * by this class's <code>getViewerPreferences()</code> method.
129: * @param model the model, in case meta information must be populated from it
130: * @param writer the PdfWriter to prepare
131: * @param request in case we need locale etc. Shouldn't look at attributes.
132: * @throws DocumentException if thrown during writer preparation
133: * @see com.lowagie.text.Document#open()
134: * @see com.lowagie.text.pdf.PdfWriter#setPageEvent
135: * @see com.lowagie.text.pdf.PdfWriter#setViewerPreferences
136: * @see #getViewerPreferences()
137: */
138: protected void prepareWriter(Map model, PdfWriter writer,
139: HttpServletRequest request) throws DocumentException {
140:
141: writer.setViewerPreferences(getViewerPreferences());
142: }
143:
144: /**
145: * Return the viewer preferences for the PDF file.
146: * <p>By default returns <code>AllowPrinting</code> and
147: * <code>PageLayoutSinglePage</code>, but can be subclassed.
148: * The subclass can either have fixed preferences or retrieve
149: * them from bean properties defined on the View.
150: * @return an int containing the bits information against PdfWriter definitions
151: * @see com.lowagie.text.pdf.PdfWriter#AllowPrinting
152: * @see com.lowagie.text.pdf.PdfWriter#PageLayoutSinglePage
153: */
154: protected int getViewerPreferences() {
155: return PdfWriter.AllowPrinting | PdfWriter.PageLayoutSinglePage;
156: }
157:
158: /**
159: * Populate the iText Document's meta fields (author, title, etc.).
160: * <br>Default is an empty implementation. Subclasses may override this method
161: * to add meta fields such as title, subject, author, creator, keywords, etc.
162: * This method is called after assigning a PdfWriter to the Document and
163: * before calling <code>document.open()</code>.
164: * @param model the model, in case meta information must be populated from it
165: * @param document the iText document being populated
166: * @param request in case we need locale etc. Shouldn't look at attributes.
167: * @see com.lowagie.text.Document#addTitle
168: * @see com.lowagie.text.Document#addSubject
169: * @see com.lowagie.text.Document#addKeywords
170: * @see com.lowagie.text.Document#addAuthor
171: * @see com.lowagie.text.Document#addCreator
172: * @see com.lowagie.text.Document#addProducer
173: * @see com.lowagie.text.Document#addCreationDate
174: * @see com.lowagie.text.Document#addHeader
175: */
176: protected void buildPdfMetadata(Map model, Document document,
177: HttpServletRequest request) {
178: }
179:
180: /**
181: * Subclasses must implement this method to build an iText PDF document,
182: * given the model. Called between <code>Document.open()</code> and
183: * <code>Document.close()</code> calls.
184: * <p>Note that the passed-in HTTP response is just supposed to be used
185: * for setting cookies or other HTTP headers. The built PDF document itself
186: * will automatically get written to the response after this method returns.
187: * @param model the model Map
188: * @param document the iText Document to add elements to
189: * @param writer the PdfWriter to use
190: * @param request in case we need locale etc. Shouldn't look at attributes.
191: * @param response in case we need to set cookies. Shouldn't write to it.
192: * @throws Exception any exception that occured during document building
193: * @see com.lowagie.text.Document#open()
194: * @see com.lowagie.text.Document#close()
195: */
196: protected abstract void buildPdfDocument(Map model,
197: Document document, PdfWriter writer,
198: HttpServletRequest request, HttpServletResponse response)
199: throws Exception;
200:
201: }
|