/*
* Copyright (c) 2006 by Know-Center, Graz, Austria
*
* This software is the confidential and proprietary information of Know-Center,
* Graz, Austria. You shall not disclose such Confidential Information and shall
* use it only in accordance with the terms of the license agreement you entered
* into with Know-Center.
*
* KNOW-CENTER MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
* THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
* NON-INFRINGEMENT. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
* LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES.
*
* $Id: PDFSignatureObjectIText.java,v 1.5 2006/10/31 08:09:33 wprinz Exp $
*/
package at.knowcenter.wag.egov.egiz.pdf;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import at.gv.egiz.pdfas.exceptions.ErrorCode;
import at.knowcenter.wag.egov.egiz.cfg.ConfigLogger;
import at.knowcenter.wag.egov.egiz.cfg.SettingsReader;
import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException;
import at.knowcenter.wag.egov.egiz.exceptions.SettingsException;
import at.knowcenter.wag.egov.egiz.sig.SignatureObject;
import at.knowcenter.wag.egov.egiz.table.Entry;
import at.knowcenter.wag.egov.egiz.table.Style;
import at.knowcenter.wag.egov.egiz.table.Table;
import com.lowagie.text.BadElementException;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.Image;
import com.lowagie.text.Phrase;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.SubsetLocal;
/**
* This class is the IText implementation of the PDFSignatureObject interface.
* The class takes an abstract definition of a signature object and convert them
* into a pdf table that is used to sign a pdf document.
*
* @author wlackner
* @see at.knowcenter.wag.egov.egiz.sig.SignatureObject
* @see at.knowcenter.wag.egov.egiz.table.Table
* @see at.knowcenter.wag.egov.egiz.table.Entry
* @see at.knowcenter.wag.egov.egiz.table.Style
* @see com.lowagie.text.pdf.PdfPTable
* @see at.knowcenter.wag.egov.egiz.cfg.SettingsReader
*/
public class PDFSignatureObjectIText implements PDFSignatureObject
{
// 03.11.2010 changed by exthex to allow setting separate hAlign and vAlign for value and non-value cells.
// If no value for valuevalign or valuehalign is set the values from valign and halign are taken.
// Also fixed a minor bug which prevented proper style inheritment (Bug Nr. #534).
// 04.11.2010 changed by exthex - allow setting separate hAlign and vAlign for image cells analog to value cells.
private static final String SIG_PDFA1_B_VALID = "SIG_PDFA1B_VALID";
/**
* The default font definition
*/
private static Font DEFAULT_FONT = new Font(Font.HELVETICA, 8, Font.NORMAL);
/**
* The abstract signature object
*/
private SignatureObject sigObject_ = null;
/**
* The IText pdf table object
*/
private PdfPTable pdfSigObject_ = null;
/**
* The SettingsReader instance
*/
private SettingsReader settings_ = null;
/**
* The logger definition.
*/
private static final Logger logger_ = ConfigLogger.getLogger(PDFSignatureObjectIText.class);
/**
* Map the style align definitions to IText's align statements
*/
private static HashMap alignMap_ = new HashMap();
/**
* Map the font definitions to IText's font statements
*/
private static HashMap fontMap_ = new HashMap();
/**
* The empty constructor. It loads the ui definitions from signature tables
* and init the align map.
*
* @throws PDFDocumentException
*/
public PDFSignatureObjectIText() throws PDFDocumentException
{
loadSettings();
initStyleMaps();
}
/**
* load the class settings
*
* @throws PDFDocumentException
* @see SettingsReader
*/
private void loadSettings() throws PDFDocumentException
{
if (settings_ == null)
{
try
{
settings_ = SettingsReader.getInstance();
}
catch (SettingsException e)
{
String log_message = "Can not load pdf signature settings. Cause:\n" + e.getMessage();
logger_.error(log_message);
throw new PDFDocumentException(101, log_message, e);
}
}
}
/**
* This method initialize the style maps. It maps the style style definitions
* to IText styles.
*/
private void initStyleMaps()
{
alignMap_.put(Style.TOP, new Integer(Element.ALIGN_TOP));
alignMap_.put(Style.MIDDLE, new Integer(Element.ALIGN_MIDDLE));
alignMap_.put(Style.BOTTOM, new Integer(Element.ALIGN_BOTTOM));
alignMap_.put(Style.LEFT, new Integer(Element.ALIGN_LEFT));
alignMap_.put(Style.CENTER, new Integer(Element.ALIGN_CENTER));
alignMap_.put(Style.RIGHT, new Integer(Element.ALIGN_RIGHT));
fontMap_.put(Style.HELVETICA, new Integer(Font.HELVETICA));
fontMap_.put(Style.TIMES_ROMAN, new Integer(Font.TIMES_ROMAN));
fontMap_.put(Style.COURIER, new Integer(Font.COURIER));
fontMap_.put(Style.NORMAL, new Integer(Font.NORMAL));
fontMap_.put(Style.BOLD, new Integer(Font.BOLD));
fontMap_.put(Style.ITALIC, new Integer(Font.ITALIC));
fontMap_.put(Style.BOLDITALIC, new Integer(Font.BOLDITALIC));
fontMap_.put(Style.UNDERLINE, new Integer(Font.UNDERLINE));
fontMap_.put(Style.STRIKETHRU, new Integer(Font.STRIKETHRU));
}
/**
* Set the abstract signature definition.
*
* @param signatorObject
* the abstract signator object
* @see at.knowcenter.wag.egov.egiz.pdf.PDFSignatureObject#setSignatorObject(at.knowcenter.wag.egov.egiz.sig.SignatureObject)
*/
public void setSignatorObject(SignatureObject signatorObject)
{
sigObject_ = signatorObject;
}
/**
* This method maps the table cell definitions to the pdfCell element.
*
* @param pdfCell
* the pdf cell to be styled
* @param cellStyle
* the abstract style definition
* @param type
* type of the cell to render - the appropriate style will be set
* @see com.lowagie.text.pdf.PdfPCell
* @see at.knowcenter.wag.egov.egiz.table.Style
*/
private void setCellStyle(PdfPCell pdfCell, Style cellStyle, int type)
{
if (cellStyle != null)
{
if (cellStyle.getBgColor() != null)
{
pdfCell.setBackgroundColor(cellStyle.getBgColor());
}
pdfCell.setPadding(cellStyle.getPadding());
//exthex - fix for not exactly vertically centered text
pdfCell.setUseAscender(true);
if (cellStyle.getBorder() > 0)
{
pdfCell.setBorderWidth(cellStyle.getBorder());
}
else
{
pdfCell.setBorder(0);
}
int align = -1;
if (type == Entry.TYPE_VALUE && cellStyle.getValueVAlign() != null)
align = ((Integer) alignMap_.get(cellStyle.getValueVAlign())).intValue();
//Note: to change the default valign of images to those of values, change the if construct below
else if (type == Entry.TYPE_IMAGE && cellStyle.getImageVAlign() != null)
align = ((Integer) alignMap_.get(cellStyle.getImageVAlign())).intValue();
else if (cellStyle.getVAlign() != null)
align = ((Integer) alignMap_.get(cellStyle.getVAlign())).intValue();
if (align != -1)
pdfCell.setVerticalAlignment(align);
align = -1;
if (type == Entry.TYPE_VALUE && cellStyle.getValueHAlign() != null)
align = ((Integer) alignMap_.get(cellStyle.getValueHAlign())).intValue();
//Note: to change the default halign of images to those of values, change the if construct below
else if (type == Entry.TYPE_IMAGE && cellStyle.getImageHAlign() != null)
align = ((Integer) alignMap_.get(cellStyle.getImageHAlign())).intValue();
else if (cellStyle.getHAlign() != null)
align = ((Integer) alignMap_.get(cellStyle.getHAlign())).intValue();
if (align != -1)
pdfCell.setHorizontalAlignment(align);
}
}
/**
* This method maps the cell font definition to the iText Font Object
*
* @param fontString
* @return the corresponding iText Font Object
* @see com.lowagie.text.Font
*/
private Font getCellFont(String fontString)
{
Font font = DEFAULT_FONT;
if (fontString == null)
{
return font;
}
Object cache_font = fontMap_.get(fontString);
if (cache_font != null)
{
return (Font) cache_font;
}
String[] font_arr = fontString.split(",");
if (font_arr.length != 3)
{
return font;
}
Object font_face = fontMap_.get(font_arr[0]);
if (font_face == null)
{
return font;
}
Object font_weight = fontMap_.get(font_arr[2]);
if (font_weight == null)
{
return font;
}
int face = ((Integer) font_face).intValue();
float height = Float.parseFloat(font_arr[1]);
int weight = ((Integer) font_weight).intValue();
font = new Font(face, height, weight);
fontMap_.put(fontString, font);
return font;
}
/**
* Creates a custom
* @param fontString
* @return
* @throws PDFDocumentException
*/
private Font getCellTrueTypeFont(String fontString) throws PDFDocumentException {
float fontSize=8;
String fontName = fontString.replaceFirst("TTF:", "");
String[] split = fontName.split(",");
if(split.length>1)
{
fontName = split[0].trim();
try
{
fontSize = Float.parseFloat(split[1].trim());
}catch (NumberFormatException e)
{
logger_.error("Unable to parse fontsize:"+fontString);
}
}
logger_.debug("TrueType Font detected:"+fontName +" ("+fontSize+")");
try {
Font font = (Font) fontMap_.get(fontString);
if (font == null) {
logger_.debug("Font \"" + fontString + "\" not in cache. Instantiating font.");
String fontPath = SettingsReader.RESOURCES_PATH + "fonts" + File.separator + fontName;
logger_.debug("Instantiating \"" + fontPath + "\".");
font = new Font(BaseFont.createFont(fontPath, BaseFont.WINANSI, true), fontSize);
fontMap_.put(fontString, font);
}
return font;
} catch (DocumentException e) {
throw new PDFDocumentException(ErrorCode.FONT_NOT_FOUND, e.getMessage());
} catch (IOException e) {
throw new PDFDocumentException(ErrorCode.FONT_NOT_FOUND, e.getMessage());
}
}
/**
* This method visualize an abstract table cell into a corresponding pdf table
* cell. The new pdf table cell is redered and get the style information from
* the abstract cell. Following types can be rendered:
*
* - text statements
* - images
* - tables
*
*
* @param abstractCell
* the abstract cell definition
* @return the new redererd pdf table cell
* @throws PDFDocumentException
* ErrorCode:220, 221, 222
* @see com.lowagie.text.pdf.PdfPCell
* @see at.knowcenter.wag.egov.egiz.table.Entry
*/
private PdfPCell renderCell(Entry abstractCell) throws PDFDocumentException
{
boolean pdfaValid =false;
try
{
String profileid = sigObject_.getSignatureTypeDefinition().getType();
String pdfa = SettingsReader.getInstance().getSetting("sig_obj." +profileid+".key."+SIG_PDFA1_B_VALID, "default."+SIG_PDFA1_B_VALID, "false");
pdfaValid= "true".equalsIgnoreCase(pdfa);
// exthex test
//SubsetLocal.set(!pdfaValid);
// boolean forceSubset = true; // get this from config, default to false
//String
//SubsetLocal.set(true); // exthex
logger_.trace("Sign PDF/A compliant:"+pdfa);
} catch (SettingsException e1)
{
logger_.error(e1);
}
PdfPCell pdf_cell = null;
Style cell_style = abstractCell.getStyle();
boolean isValue = true;
switch (abstractCell.getType())
{
case Entry.TYPE_CAPTION:
isValue = false;
case Entry.TYPE_VALUE:
String text = (String) abstractCell.getValue();
if (text == null)
{
text = "";
}
String font_string = cell_style.getFont();
if (abstractCell.getType() == Entry.TYPE_VALUE && cell_style.getValueFont() != null)
{
font_string = cell_style.getValueFont();
}
logger_.trace("using cell font: "+font_string);
Font cell_font;
if(font_string.startsWith("TTF:"))
{
cell_font = getCellTrueTypeFont(font_string);
}
else
{
if (pdfaValid) {
throw new PDFDocumentException(ErrorCode.NO_EMBEDABLE_TTF_CONFIGURED_FOR_PDFA, "PDF/A modus requires an embedable true type font");
}
cell_font = getCellFont(font_string);
}
// exthex
if (pdfaValid && abstractCell.getType() == Entry.TYPE_VALUE) {
SubsetLocal.addNonSubsetFont(cell_font.getBaseFont());
}
Phrase text_phrase = new Phrase(text, cell_font);
pdf_cell = new PdfPCell(text_phrase);
setCellStyle(pdf_cell, cell_style, (isValue?Entry.TYPE_VALUE:Entry.TYPE_CAPTION));
break;
case Entry.TYPE_IMAGE:
try
{
String img_ref = (String) abstractCell.getValue();
// fixed by tknall start
File img_file = new File(img_ref);
if (!img_file.isAbsolute()) {
logger_.debug("Image file declaration is relative. Prepending path of resources directory.");
img_file = new File(SettingsReader.relocateFile(img_ref));
} else {
logger_.debug("Image file declaration is absolute. Skipping file relocation.");
}
// String img_location = SettingsReader.relocateFile(img_ref);
// File img_file = new File (img_location);
if (!img_file.exists())
{
logger_.debug("Image file \"" + img_file.getCanonicalPath() + "\" doesn't exist.");
throw new PDFDocumentException(220, "Image file \"" + img_file.getCanonicalPath() + "\" doesn't exist.");
}
Image image = Image.getInstance(img_file.getCanonicalPath());
logger_.debug("Using image file \"" + img_file.getCanonicalPath() + "\".");
image.scaleToFit(80.0f, 80.0f);
boolean fit = true;
Style.ImageScaleToFit istf = cell_style.getImageScaleToFit();
if (istf != null)
{
image.scaleToFit(istf.getWidth(), istf.getHeight());
fit = false;
}
pdf_cell = new PdfPCell(image, fit);
setCellStyle(pdf_cell, cell_style, Entry.TYPE_IMAGE);
}
catch (BadElementException e)
{
if (logger_.isEnabledFor(Level.ERROR))
{
logger_.error("BadElementException:" + e.getMessage());
}
PDFDocumentException pde = new PDFDocumentException(220, "PDF table can not created");
throw pde;
}
catch (MalformedURLException e)
{
if (logger_.isEnabledFor(Level.ERROR))
{
logger_.error("MalformedURLException:" + e.getMessage());
}
PDFDocumentException pde = new PDFDocumentException(221, "PDF table can not created");
throw pde;
}
catch (IOException e)
{
if (logger_.isEnabledFor(Level.ERROR))
{
logger_.error("Error Code: 222, IOException:" + e.getMessage());
}
PDFDocumentException pde = new PDFDocumentException(222, "PDF table can not created: Image can not loaded");
throw pde;
}
break;
case Entry.TYPE_TABLE:
Table table = (Table) abstractCell.getValue();
// inherit the style from the parent table
Style inherit_style = Style.doInherit(table.getStyle(), cell_style);
table.setStyle(inherit_style);
PdfPTable pdf_table = renderTable(table);
pdf_cell = new PdfPCell(pdf_table);
// The default new PdfPCell has a default border of 15.
// For blocks without border and subtables this results
// in a border to be drawn around the cell.
// ==> no border on default
pdf_cell.setBorder(0);
break;
}
return pdf_cell;
}
/**
* This method visualize an abstract table into a corresponding pdf table. The
* new pdf table is redered and get the style information from the abstract
* cell.
*
* @param abstractTable
* the abstract table definition
* @return the new redererd pdf table cell
* @throws PDFDocumentException
* ErrorCode:220, 221, 222, 223
* @see com.lowagie.text.pdf.PdfPTable
* @see at.knowcenter.wag.egov.egiz.table.Table
*/
private PdfPTable renderTable(Table abstractTable) throws PDFDocumentException
{
if (abstractTable == null)
{
PDFDocumentException pde = new PDFDocumentException(223, "Table is not defined.");
throw pde;
}
PdfPTable pdf_table = null;
float[] cols = abstractTable.getColsRelativeWith();
int max_cols = abstractTable.getMaxCols();
if (cols == null)
{
cols = new float[max_cols];
// set the column ratio for all columns to 1
for (int cols_idx = 0; cols_idx < cols.length; cols_idx++)
{
cols[cols_idx] = 1;
}
}
pdf_table = new PdfPTable(cols);
pdf_table.setWidthPercentage(abstractTable.getWidth());
Style table_style = abstractTable.getStyle();
setCellStyle(pdf_table.getDefaultCell(), table_style, Entry.TYPE_TABLE);
ArrayList rows = abstractTable.getRows();
for (int row_idx = 0; row_idx < rows.size(); row_idx++)
{
ArrayList row = (ArrayList) rows.get(row_idx);
// logger_.debug("## Row:" + row_idx + " ## of table:" +
// abstractTable.getName());
for (int entry_idx = 0; entry_idx < row.size(); entry_idx++)
{
Entry cell = (Entry) row.get(entry_idx);
// 03.11.2010 changed by exthex - swapped the two params, was probably a bug
Style inherit_style = Style.doInherit(table_style, cell.getStyle());
cell.setStyle(inherit_style);
// logger_.debug(cell.toString());
PdfPCell pdf_cell = renderCell(cell);
if (cell.getColSpan() > 1)
{
pdf_cell.setColspan(cell.getColSpan());
}
if (cell.isNoWrap())
{
pdf_cell.setNoWrap(true);
}
// System.err.println("valign:" + pdf_cell.getVerticalAlignment() + "
// halign:" +
// pdf_cell.getHorizontalAlignment());
pdf_table.addCell(pdf_cell);
}
}
// logger_.debug("render table:" + abstractTable.getName());
return pdf_table;
}
/**
* This method creates the pdf table object. It takes the abstract table
* definition from the signature object and render the abstract table.
*
* @param sigObject
* the signature object, the base for the abstract table definition
* @return R
* @throws PDFDocumentException
* ErrorCode:220, 221, 222, 223
*/
private PdfPTable createPDFSignatureObject(SignatureObject sigObject) throws PDFDocumentException
{
SubsetLocal.clear();
Table table = sigObject.getAbstractTable();
PdfPTable pdf_table = renderTable(table);
return pdf_table;
}
/*
* This method search for the table definitions in the settings file an init
* @param sigObject
*/
/*
* private void initTableSettings(SignatureObject sigObject) { String sig_type =
* sigObject.getSignationType(); String table_key = SignatureObject.SIG_OBJ +
* sig_type + ".table."; ArrayList main_rows = settings_.getKeys(table_key +
* "main"); }
*/
/**
* Converts the current abstract signature object in a pdf signature object
* implementation
*
* @return the converted pdf signature object
* @see at.knowcenter.wag.egov.egiz.pdf.PDFSignatureObject#getSignatureObject()
*/
public Object getSignatureObject() throws PDFDocumentException
{
if (pdfSigObject_ == null)
{
pdfSigObject_ = (PdfPTable) getSignatureObject(sigObject_);
}
return pdfSigObject_;
}
/**
* Converts a abstract signature object in a pdf signature object
* implementation
*
* @param sigObject
* the abstract signatorObject to convert
* @return the converted pdf signature object
* @throws PDFDocumentException
* @see at.knowcenter.wag.egov.egiz.pdf.PDFSignatureObject#getSignatureObject(at.knowcenter.wag.egov.egiz.sig.SignatureObject)
*/
public Object getSignatureObject(SignatureObject sigObject) throws PDFDocumentException
{
// initTableSettings(sigObject);
return createPDFSignatureObject(sigObject);
}
}