/*
* The Kuali Financial System, a comprehensive financial management system for higher education.
*
* Copyright 2005-2014 The Kuali Foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kuali.kfs.sys;
import java.awt.Color;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Map;
import org.kuali.rice.krad.util.ObjectUtils;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.AcroFields;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfGState;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfStamper;
/**
* This class writes the reports onto a pdf file as per a template provided.
*/
public class PdfFormFillerUtil {
private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PdfFormFillerUtil.class);
private static final SimpleDateFormat FILE_NAME_TIMESTAMP = new SimpleDateFormat("_yyyy-MM-dd_hhmmss");
/**
* This method generates the reports from the template stream provided.
*
* @param template
* @param replacementList
* @throws IOException, DocumentException
*/
public static byte[] populateTemplate(InputStream templateStream, Map<String, String> replacementList) throws IOException, DocumentException {
// --------------------------------------------------
// Validate the parameters
// --------------------------------------------------
if (templateStream == null || replacementList == null) {
throw new IllegalArgumentException("All parameters are required, but one or more were null.");
}
// --------------------------------------------------
// Use iText to build the new PDF
// --------------------------------------------------
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
pdfStampValues(templateStream, outputStream, replacementList);
return outputStream.toByteArray();
}
/**
* This method generates the reports from the template file provided.
*
* @param template
* @param replacementList
* @throws IOException, DocumentException
*/
public static byte[] populateTemplate(File template, Map<String, String> replacementList) throws IOException, DocumentException {
// --------------------------------------------------
// Validate the parameters
// --------------------------------------------------
if (template == null || replacementList == null) {
throw new IllegalArgumentException("All parameters are required, but one or more were null.");
}
// Validate the template file
if (template.exists() == false) {
throw new IOException("The template file '" + template.getAbsolutePath() + "' does not exist.");
}
if (!template.isFile()) {
throw new RuntimeException("The template file '" + template.getAbsolutePath() + "' is not a valid file.");
}
if (!template.canRead()) {
throw new RuntimeException("The template file '" + template.getAbsolutePath() + "' cannot be read.");
}
// --------------------------------------------------
// Use iText to build the new PDF
// --------------------------------------------------
InputStream templateStream = new FileInputStream(template);
return populateTemplate(templateStream, replacementList);
}
/**
* This method stamps the values onto the pdf file from the replacement list
*
* @param strLine
* @param replacementList
* @return
*/
private static boolean validListTagFound(String strLine) {
boolean valid = true;
valid &= strLine.matches("^#[\\S]*$");
return valid;
}
/**
* This Method stamps the values from the map onto the fields in the template provided.
*
* @param templateStream
* @param outputStream
* @param replacementList
* @throws IOException
*/
private static void pdfStampValues(InputStream templateStream, OutputStream outputStream, Map<String, String> replacementList) throws IOException {
try {
// Create a PDF reader for the template
PdfReader pdfReader = new PdfReader(templateStream);
// Create a PDF writer
PdfStamper pdfStamper = new PdfStamper(pdfReader, outputStream);
// Replace the form data with the final values
AcroFields fields = pdfStamper.getAcroFields();
for (Object fieldName : fields.getFields().keySet()) {
// Read the field data
String text = fields.getField(fieldName.toString());
String newText = fields.getField(fieldName.toString());
// Replace the keywords
if (fields.getFieldType(fieldName.toString()) == AcroFields.FIELD_TYPE_TEXT) {
newText = replaceValuesIteratingThroughFile(text, replacementList);
}
else {
if (ObjectUtils.isNotNull(replacementList.get(fieldName.toString()))) {
newText = replacementList.get(fieldName);
}
}
// Populate the field with the final value
fields.setField(fieldName.toString(), newText);
}
// --------------------------------------------------
// Save the new PDF
// --------------------------------------------------
pdfStamper.close();
}
catch (IOException e) {
throw new IOException("IO error processing PDF template", e);
}
catch (DocumentException e) {
throw new IOException("iText error processing PDF template", e);
}
finally {
// --------------------------------------------------
// Close the files
// --------------------------------------------------
templateStream.close();
outputStream.close();
}
}
/**
* This method creates a Final watermark on the input Stream.
*
* @param templateStream
* @param finalmarkText
* @return
* @throws IOException
* @throws DocumentException
*/
public static byte[] createFinalmarkOnFile(byte[] templateStream, String finalmarkText) throws IOException, DocumentException {
// Create a PDF reader for the template
PdfReader pdfReader = new PdfReader(templateStream);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// Create a PDF writer
PdfStamper pdfStamper = new PdfStamper(pdfReader, outputStream);
int n = pdfReader.getNumberOfPages();
int i = 1;
PdfContentByte over;
BaseFont bf;
try {
bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.EMBEDDED);
PdfGState gstate = new PdfGState();
while (i <= n) {
// Watermark under the existing page
Rectangle pageSize = pdfReader.getPageSizeWithRotation(i);
over = pdfStamper.getOverContent(i);
over.beginText();
over.setFontAndSize(bf, 8);
over.setGState(gstate);
over.setColorFill(Color.BLACK);
over.showTextAligned(Element.ALIGN_CENTER, finalmarkText, (pageSize.width() / 2), (pageSize.height() - 10), 0);
over.endText();
i++;
}
pdfStamper.close();
}
catch (DocumentException ex) {
throw new IOException("iText error creating final watermark on PDF", ex);
}
catch (IOException ex) {
throw new IOException("IO error creating final watermark on PDF", ex);
}
return outputStream.toByteArray();
}
/**
* This Method creates a custom watermark on the File.
*
* @param templateStream
* @param watermarkText
* @return
* @throws IOException
* @throws DocumentException
*/
public static byte[] createWatermarkOnFile(byte[] templateStream, String watermarkText) throws IOException, DocumentException {
// Create a PDF reader for the template
PdfReader pdfReader = new PdfReader(templateStream);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// Create a PDF writer
PdfStamper pdfStamper = new PdfStamper(pdfReader, outputStream);
int n = pdfReader.getNumberOfPages();
int i = 1;
PdfContentByte over;
BaseFont bf;
try {
bf = BaseFont.createFont(BaseFont.HELVETICA_BOLD, BaseFont.WINANSI, BaseFont.EMBEDDED);
PdfGState gstate = new PdfGState();
gstate.setFillOpacity(0.5f);
while (i <= n) {
// Watermark under the existing page
Rectangle pageSize = pdfReader.getPageSizeWithRotation(i);
over = pdfStamper.getOverContent(i);
over.beginText();
over.setFontAndSize(bf, 200);
over.setGState(gstate);
over.setColorFill(Color.LIGHT_GRAY);
over.showTextAligned(Element.ALIGN_CENTER, watermarkText, (pageSize.width() / 2), (pageSize.height() / 2), 45);
over.endText();
i++;
}
pdfStamper.close();
}
catch (DocumentException ex) {
throw new IOException("iText error creating watermark on PDF", ex);
}
catch (IOException ex) {
throw new IOException("IO error creating watermark on PDF", ex);
}
return outputStream.toByteArray();
}
/**
* This method splits all the text in the template file and replaces them if they match the keys in the Map.
*
* @param template
* @param replacementList
* @return
*/
private static String replaceValuesIteratingThroughFile(String template, Map<String, String> replacementList) {
StringBuilder buffOriginal = new StringBuilder();
StringBuilder buffNormalized = new StringBuilder();
String[] keys = template.split("[\\s]+");
// Scan for each word
for (String key : keys) {
if (validListTagFound(key)) {
String replacementKey = key.substring(1);
String value = replacementList.get(replacementKey);
if (ObjectUtils.isNotNull(value)) {
buffOriginal.append(value + " ");
}
else {
buffOriginal.append(" ");
}
}
else {
buffOriginal.append(key + " ");
}
}
return buffOriginal.toString();
}
}