View Javadoc
1   /*
2    * The Kuali Financial System, a comprehensive financial management system for higher education.
3    * 
4    * Copyright 2005-2014 The Kuali Foundation
5    * 
6    * This program is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU Affero General Public License as
8    * published by the Free Software Foundation, either version 3 of the
9    * License, or (at your option) any later version.
10   * 
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU Affero General Public License for more details.
15   * 
16   * You should have received a copy of the GNU Affero General Public License
17   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  package org.kuali.kfs.sys;
20  
21  import java.awt.Color;
22  import java.io.ByteArrayOutputStream;
23  import java.io.File;
24  import java.io.FileInputStream;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.io.OutputStream;
28  import java.text.SimpleDateFormat;
29  import java.util.Map;
30  
31  import org.kuali.rice.krad.util.ObjectUtils;
32  
33  import com.lowagie.text.DocumentException;
34  import com.lowagie.text.Element;
35  import com.lowagie.text.Rectangle;
36  import com.lowagie.text.pdf.AcroFields;
37  import com.lowagie.text.pdf.BaseFont;
38  import com.lowagie.text.pdf.PdfContentByte;
39  import com.lowagie.text.pdf.PdfGState;
40  import com.lowagie.text.pdf.PdfReader;
41  import com.lowagie.text.pdf.PdfStamper;
42  
43  /**
44   * This class writes the reports onto a pdf file as per a template provided.
45   */
46  public class PdfFormFillerUtil {
47  
48      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PdfFormFillerUtil.class);
49      private static final SimpleDateFormat FILE_NAME_TIMESTAMP = new SimpleDateFormat("_yyyy-MM-dd_hhmmss");
50  
51      /**
52       * This method generates the reports from the template stream provided.
53       *
54       * @param template
55       * @param replacementList
56       * @throws IOException, DocumentException
57       */
58      public static byte[] populateTemplate(InputStream templateStream, Map<String, String> replacementList) throws IOException, DocumentException {
59          // --------------------------------------------------
60          // Validate the parameters
61          // --------------------------------------------------
62          if (templateStream == null || replacementList == null) {
63              throw new IllegalArgumentException("All parameters are required, but one or more were null.");
64          }
65  
66          // --------------------------------------------------
67          // Use iText to build the new PDF
68          // --------------------------------------------------
69          ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
70          pdfStampValues(templateStream, outputStream, replacementList);
71          return outputStream.toByteArray();
72      }
73  
74      /**
75       * This method generates the reports from the template file provided.
76       *
77       * @param template
78       * @param replacementList
79       * @throws IOException, DocumentException
80       */
81      public static byte[] populateTemplate(File template, Map<String, String> replacementList) throws IOException, DocumentException {
82       // --------------------------------------------------
83          // Validate the parameters
84          // --------------------------------------------------
85          if (template == null || replacementList == null) {
86              throw new IllegalArgumentException("All parameters are required, but one or more were null.");
87          }
88  
89          // Validate the template file
90          if (template.exists() == false) {
91              throw new IOException("The template file '" + template.getAbsolutePath() + "' does not exist.");
92          }
93          if (!template.isFile()) {
94              throw new RuntimeException("The template file '" + template.getAbsolutePath() + "' is not a valid file.");
95          }
96          if (!template.canRead()) {
97              throw new RuntimeException("The template file '" + template.getAbsolutePath() + "' cannot be read.");
98          }
99  
100         // --------------------------------------------------
101         // Use iText to build the new PDF
102         // --------------------------------------------------
103         InputStream templateStream = new FileInputStream(template);
104         return populateTemplate(templateStream, replacementList);
105     }
106 
107 
108     /**
109      * This method stamps the values onto the pdf file from the replacement list
110      *
111      * @param strLine
112      * @param replacementList
113      * @return
114      */
115     private static boolean validListTagFound(String strLine) {
116         boolean valid = true;
117         valid &= strLine.matches("^#[\\S]*$");
118         return valid;
119     }
120 
121     /**
122      * This Method stamps the values from the map onto the fields in the template provided.
123      *
124      * @param templateStream
125      * @param outputStream
126      * @param replacementList
127      * @throws IOException
128      */
129     private static void pdfStampValues(InputStream templateStream, OutputStream outputStream, Map<String, String> replacementList) throws IOException {
130         try {
131             // Create a PDF reader for the template
132             PdfReader pdfReader = new PdfReader(templateStream);
133 
134             // Create a PDF writer
135             PdfStamper pdfStamper = new PdfStamper(pdfReader, outputStream);
136             // Replace the form data with the final values
137             AcroFields fields = pdfStamper.getAcroFields();
138             for (Object fieldName : fields.getFields().keySet()) {
139                 // Read the field data
140                 String text = fields.getField(fieldName.toString());
141                 String newText = fields.getField(fieldName.toString());
142                 // Replace the keywords
143                 if (fields.getFieldType(fieldName.toString()) == AcroFields.FIELD_TYPE_TEXT) {
144                     newText = replaceValuesIteratingThroughFile(text, replacementList);
145                 }
146                 else {
147                     if (ObjectUtils.isNotNull(replacementList.get(fieldName.toString()))) {
148                         newText = replacementList.get(fieldName);
149                     }
150                 }
151                 // Populate the field with the final value
152                 fields.setField(fieldName.toString(), newText);
153             }
154 
155             // --------------------------------------------------
156             // Save the new PDF
157             // --------------------------------------------------
158             pdfStamper.close();
159 
160         }
161         catch (IOException e) {
162             throw new IOException("IO error processing PDF template", e);
163         }
164         catch (DocumentException e) {
165             throw new IOException("iText error processing PDF template", e);
166         }
167         finally {
168             // --------------------------------------------------
169             // Close the files
170             // --------------------------------------------------
171             templateStream.close();
172             outputStream.close();
173         }
174     }
175 
176     /**
177      * This method creates a Final watermark on the input Stream.
178      *
179      * @param templateStream
180      * @param finalmarkText
181      * @return
182      * @throws IOException
183      * @throws DocumentException
184      */
185     public static byte[] createFinalmarkOnFile(byte[] templateStream, String finalmarkText) throws IOException, DocumentException {
186         // Create a PDF reader for the template
187         PdfReader pdfReader = new PdfReader(templateStream);
188 
189         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
190         // Create a PDF writer
191         PdfStamper pdfStamper = new PdfStamper(pdfReader, outputStream);
192         int n = pdfReader.getNumberOfPages();
193         int i = 1;
194         PdfContentByte over;
195         BaseFont bf;
196         try {
197             bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.EMBEDDED);
198             PdfGState gstate = new PdfGState();
199             while (i <= n) {
200                 // Watermark under the existing page
201                 Rectangle pageSize = pdfReader.getPageSizeWithRotation(i);
202                 over = pdfStamper.getOverContent(i);
203                 over.beginText();
204                 over.setFontAndSize(bf, 8);
205                 over.setGState(gstate);
206                 over.setColorFill(Color.BLACK);
207                 over.showTextAligned(Element.ALIGN_CENTER, finalmarkText, (pageSize.width() / 2), (pageSize.height() - 10), 0);
208                 over.endText();
209                 i++;
210             }
211             pdfStamper.close();
212         }
213         catch (DocumentException ex) {
214             throw new IOException("iText error creating final watermark on PDF", ex);
215         }
216         catch (IOException ex) {
217             throw new IOException("IO error creating final watermark on PDF", ex);
218         }
219         return outputStream.toByteArray();
220     }
221 
222     /**
223      * This Method creates a custom watermark on the File.
224      *
225      * @param templateStream
226      * @param watermarkText
227      * @return
228      * @throws IOException
229      * @throws DocumentException
230      */
231     public static byte[] createWatermarkOnFile(byte[] templateStream, String watermarkText) throws IOException, DocumentException {
232         // Create a PDF reader for the template
233         PdfReader pdfReader = new PdfReader(templateStream);
234 
235         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
236         // Create a PDF writer
237         PdfStamper pdfStamper = new PdfStamper(pdfReader, outputStream);
238         int n = pdfReader.getNumberOfPages();
239         int i = 1;
240         PdfContentByte over;
241         BaseFont bf;
242         try {
243             bf = BaseFont.createFont(BaseFont.HELVETICA_BOLD, BaseFont.WINANSI, BaseFont.EMBEDDED);
244             PdfGState gstate = new PdfGState();
245             gstate.setFillOpacity(0.5f);
246             while (i <= n) {
247                 // Watermark under the existing page
248                 Rectangle pageSize = pdfReader.getPageSizeWithRotation(i);
249                 over = pdfStamper.getOverContent(i);
250                 over.beginText();
251                 over.setFontAndSize(bf, 200);
252                 over.setGState(gstate);
253                 over.setColorFill(Color.LIGHT_GRAY);
254                 over.showTextAligned(Element.ALIGN_CENTER, watermarkText, (pageSize.width() / 2), (pageSize.height() / 2), 45);
255                 over.endText();
256                 i++;
257             }
258             pdfStamper.close();
259         }
260         catch (DocumentException ex) {
261             throw new IOException("iText error creating watermark on PDF", ex);
262         }
263         catch (IOException ex) {
264             throw new IOException("IO error creating watermark on PDF", ex);
265         }
266         return outputStream.toByteArray();
267     }
268 
269     /**
270      * This method splits all the text in the template file and replaces them if they match the keys in the Map.
271      *
272      * @param template
273      * @param replacementList
274      * @return
275      */
276     private static String replaceValuesIteratingThroughFile(String template, Map<String, String> replacementList) {
277         StringBuilder buffOriginal = new StringBuilder();
278         StringBuilder buffNormalized = new StringBuilder();
279 
280         String[] keys = template.split("[\\s]+");
281 
282         // Scan for each word
283         for (String key : keys) {
284             if (validListTagFound(key)) {
285                 String replacementKey = key.substring(1);
286                 String value = replacementList.get(replacementKey);
287                 if (ObjectUtils.isNotNull(value)) {
288                     buffOriginal.append(value + " ");
289                 }
290                 else {
291                     buffOriginal.append(" ");
292                 }
293             }
294             else {
295                 buffOriginal.append(key + " ");
296             }
297         }
298         return buffOriginal.toString();
299     }
300 }