/** * Copyright 2006 by Know-Center, Graz, Austria * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a * joint initiative of the Federal Chancellery Austria and Graz University of * Technology. * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. * * $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: * * * @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); } }