001/* 002 * Copyright 2002-2018 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 * https://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 017package org.springframework.web.servlet.view.document; 018 019import java.io.ByteArrayOutputStream; 020import java.io.IOException; 021import java.util.Map; 022 023import javax.servlet.http.HttpServletRequest; 024import javax.servlet.http.HttpServletResponse; 025 026import com.lowagie.text.pdf.PdfReader; 027import com.lowagie.text.pdf.PdfStamper; 028 029import org.springframework.util.Assert; 030import org.springframework.web.servlet.view.AbstractUrlBasedView; 031 032/** 033 * Abstract superclass for PDF views that operate on an existing 034 * document with an AcroForm. Application-specific view classes 035 * will extend this class to merge the PDF form with model data. 036 * 037 * <p>This view implementation uses Bruno Lowagie's 038 * <a href="https://www.lowagie.com/iText">iText</a> API. 039 * Known to work with the original iText 2.1.7 as well as its fork 040 * <a href="https://github.com/LibrePDF/OpenPDF">OpenPDF</a>. 041 * <b>We strongly recommend OpenPDF since it is actively maintained 042 * and fixes an important vulnerability for untrusted PDF content.</b> 043 * 044 * <p>Thanks to Bryant Larsen for the suggestion and the original prototype! 045 * 046 * @author Juergen Hoeller 047 * @since 2.5.4 048 * @see AbstractPdfView 049 */ 050public abstract class AbstractPdfStamperView extends AbstractUrlBasedView { 051 052 public AbstractPdfStamperView(){ 053 setContentType("application/pdf"); 054 } 055 056 057 @Override 058 protected boolean generatesDownloadContent() { 059 return true; 060 } 061 062 @Override 063 protected final void renderMergedOutputModel( 064 Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { 065 066 // IE workaround: write into byte array first. 067 ByteArrayOutputStream baos = createTemporaryOutputStream(); 068 069 PdfReader reader = readPdfResource(); 070 PdfStamper stamper = new PdfStamper(reader, baos); 071 mergePdfDocument(model, stamper, request, response); 072 stamper.close(); 073 074 // Flush to HTTP response. 075 writeToResponse(response, baos); 076 } 077 078 /** 079 * Read the raw PDF resource into an iText PdfReader. 080 * <p>The default implementation resolve the specified "url" property 081 * as ApplicationContext resource. 082 * @return the PdfReader instance 083 * @throws IOException if resource access failed 084 * @see #setUrl 085 */ 086 protected PdfReader readPdfResource() throws IOException { 087 String url = getUrl(); 088 Assert.state(url != null, "'url' not set"); 089 return new PdfReader(obtainApplicationContext().getResource(url).getInputStream()); 090 } 091 092 /** 093 * Subclasses must implement this method to merge the PDF form 094 * with the given model data. 095 * <p>This is where you are able to set values on the AcroForm. 096 * An example of what can be done at this level is: 097 * <pre class="code"> 098 * // get the form from the document 099 * AcroFields form = stamper.getAcroFields(); 100 * 101 * // set some values on the form 102 * form.setField("field1", "value1"); 103 * form.setField("field2", "Vvlue2"); 104 * 105 * // set the disposition and filename 106 * response.setHeader("Content-disposition", "attachment; FILENAME=someName.pdf");</pre> 107 * <p>Note that the passed-in HTTP response is just supposed to be used 108 * for setting cookies or other HTTP headers. The built PDF document itself 109 * will automatically get written to the response after this method returns. 110 * @param model the model Map 111 * @param stamper the PdfStamper instance that will contain the AcroFields. 112 * You may also customize this PdfStamper instance according to your needs, 113 * e.g. setting the "formFlattening" property. 114 * @param request in case we need locale etc. Shouldn't look at attributes. 115 * @param response in case we need to set cookies. Shouldn't write to it. 116 * @throws Exception any exception that occurred during document building 117 */ 118 protected abstract void mergePdfDocument(Map<String, Object> model, PdfStamper stamper, 119 HttpServletRequest request, HttpServletResponse response) throws Exception; 120 121}