From fc44d4bcad00192f0df8f6086737b9b126094dcd Mon Sep 17 00:00:00 2001 From: Andreas Fitzek Date: Thu, 26 Sep 2013 15:48:43 +0200 Subject: initial code commit --- stamper/stmp-itext/.gitignore | 1 + stamper/stmp-itext/build.gradle | 32 ++ .../at/gv/egiz/pdfas/stmp/itext/ITextStamper.java | 506 +++++++++++++++++++++ .../egiz/pdfas/stmp/itext/ITextVisualObject.java | 61 +++ .../at/gv/egiz/pdfas/stmp/itext/package-info.java | 2 + 5 files changed, 602 insertions(+) create mode 100644 stamper/stmp-itext/.gitignore create mode 100644 stamper/stmp-itext/build.gradle create mode 100644 stamper/stmp-itext/src/main/java/at/gv/egiz/pdfas/stmp/itext/ITextStamper.java create mode 100644 stamper/stmp-itext/src/main/java/at/gv/egiz/pdfas/stmp/itext/ITextVisualObject.java create mode 100644 stamper/stmp-itext/src/main/java/at/gv/egiz/pdfas/stmp/itext/package-info.java (limited to 'stamper/stmp-itext') diff --git a/stamper/stmp-itext/.gitignore b/stamper/stmp-itext/.gitignore new file mode 100644 index 00000000..5e56e040 --- /dev/null +++ b/stamper/stmp-itext/.gitignore @@ -0,0 +1 @@ +/bin diff --git a/stamper/stmp-itext/build.gradle b/stamper/stmp-itext/build.gradle new file mode 100644 index 00000000..1495d8af --- /dev/null +++ b/stamper/stmp-itext/build.gradle @@ -0,0 +1,32 @@ +apply plugin: 'java' +apply plugin: 'eclipse' + +jar { + manifest { + attributes 'Implementation-Title': 'PDF-AS Stamper with itext', 'Implementation-Version': version + } +} + +repositories { + mavenCentral() +} + +dependencies { + compile project (':pdf-as-lib') + compile project (':pdf-as-common') + compile group: 'commons-collections', name: 'commons-collections', version: '3.2' + compile group: 'com.lowagie', name: 'itext', version: '4.2.0' + testCompile group: 'junit', name: 'junit', version: '4.+' +} + +test { + systemProperties 'property': 'value' +} + +uploadArchives { + repositories { + flatDir { + dirs 'repos' + } + } +} diff --git a/stamper/stmp-itext/src/main/java/at/gv/egiz/pdfas/stmp/itext/ITextStamper.java b/stamper/stmp-itext/src/main/java/at/gv/egiz/pdfas/stmp/itext/ITextStamper.java new file mode 100644 index 00000000..5c0fb7df --- /dev/null +++ b/stamper/stmp-itext/src/main/java/at/gv/egiz/pdfas/stmp/itext/ITextStamper.java @@ -0,0 +1,506 @@ +package at.gv.egiz.pdfas.stmp.itext; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.common.settings.ISettings; +import at.gv.egiz.pdfas.lib.impl.stamping.IPDFStamper; +import at.gv.egiz.pdfas.lib.impl.stamping.IPDFVisualObject; +import at.gv.egiz.pdfas.lib.impl.status.PDFObject; +import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction; +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.*; +import com.lowagie.text.pdf.*; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.HashMap; + +public class ITextStamper implements IPDFStamper { + + private static final Logger logger = LoggerFactory.getLogger(ITextStamper.class); + + /** + * The default font definition + */ + private static Font DEFAULT_FONT = new Font(Font.HELVETICA, 8, Font.NORMAL); + + private ISettings settings; + + /** + * 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 PdfAsException + * 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 PdfAsException + { + if (abstractTable == null) + { + PdfAsException pde = new PdfAsException("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); + } + pdf_table.completeRow(); + } + logger.debug("render table:" + abstractTable.getName()); + return pdf_table; + } + + /** + * 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(); + + static { + initStyleMaps(); + } + + /** + * This method initialize the style maps. It maps the style style definitions + * to IText styles. + */ + private static 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)); + } + + /** + * 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) + + if(alignMap_.get(cellStyle.getVAlign()) == null) { + align = -1; + } else { + align = 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); + } + } + + /** + * Creates a custom + * @param fontString + * @return + * @throws PdfAsException + */ + private Font getCellTrueTypeFont(String fontString) throws PdfAsException { + 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 = new Font(fontMap_.get(fontString)); + //Font font = (Font) fontMap_.get(fontString); + + // TODO: implement FONT resources via settings path! + /*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 PdfAsException(e.getMessage()); + //} catch (IOException e) { + // throw new PdfAsException(e.getMessage()); + //} + } + + + /** + * 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; + } + + /** + * 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 PdfAsException + * ErrorCode:220, 221, 222 + * @see com.lowagie.text.pdf.PdfPCell + * @see at.knowcenter.wag.egov.egiz.table.Entry + */ + private PdfPCell renderCell(Entry abstractCell) throws PdfAsException + { + // TODO: read if signature should be PDF/A compatible!! + boolean pdfaValid = false;//PDFASUtils.isPdfAEnabled(sigObject_.getSignatureTypeDefinition().getType()); + + 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 PdfAsException("PDF/A modus requires an embedable true type font"); + } + cell_font = getCellFont(font_string); + + } + //TODO: check and maybe remove ... + // 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."); + //TODO: implement settings .... + img_file = new File(settings.getWorkingDirectory() + File.separator + 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 PdfAsException("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) + { + logger.error("BadElementException:" + e.getMessage()); + PdfAsException pde = new PdfAsException("Unable to create PDF table."); + throw pde; + } + catch (MalformedURLException e) + { + logger.error("MalformedURLException:" + e.getMessage()); + PdfAsException pde = new PdfAsException("Unable to create PDF table."); + throw pde; + } + catch (IOException e) + { + logger.error("Error Code: 222, IOException:" + e.getMessage()); + PdfAsException pde = new PdfAsException("Unable to create PDF table, unable to load image."); + 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; + } + + public IPDFVisualObject createVisualPDFObject(PDFObject pdf, Table table) { + + // TODO: Adapt PDFSignatureObjectIText to render PDFPTable to iTextVisualObject from table + try { + PdfPTable pdfPTable = renderTable(table); + + ITextVisualObject iTextVisualObject = new ITextVisualObject(pdfPTable); + + return iTextVisualObject; + } catch (PdfAsException e) { + e.printStackTrace(); + return null; + } + } + + public byte[] writeVisualObject(IPDFVisualObject visualObject, PositioningInstruction positioningInstruction, + byte[] pdfData) throws PdfAsException { + try { + + ITextVisualObject object = null; + if(visualObject instanceof ITextVisualObject) { + object = (ITextVisualObject)visualObject; + } + + if(object == null) { + //TODO: exception! + return null; + } + + PdfReader reader = new PdfReader(pdfData); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + PdfStamper stamper = new PdfStamper(reader, baos, reader.getPdfVersion(), true); + + int pages = reader.getNumberOfPages(); + int targetPage = positioningInstruction.getPage(); + + // TODO: maybe add new page ... + if(positioningInstruction.isMakeNewPage()) { + Rectangle rect = reader.getPageSize(pages); + stamper.insertPage(pages + 1, new Rectangle(rect)); + targetPage = pages + 1; + } + + if (positioningInstruction.getPage() < 1 || + positioningInstruction.getPage() > stamper.getReader().getNumberOfPages()) + { + throw new PdfAsException("The provided page (=" + + positioningInstruction.getPage() + ") is out of range."); + } + + PdfContentByte content = stamper.getOverContent(targetPage); + + PdfPTable table = object.getTable(); + + table.writeSelectedRows(0, -1, positioningInstruction.getX(), + positioningInstruction.getY(), content); + + stamper.close(); + + baos.close(); + + return baos.toByteArray(); + + } catch (IOException e) { + logger.error(e.getMessage(), e); + } catch (DocumentException e) { + logger.error(e.getMessage(), e); + } + return null; + } + + public void setSettings(ISettings settings) { + this.settings = settings; + } +} \ No newline at end of file diff --git a/stamper/stmp-itext/src/main/java/at/gv/egiz/pdfas/stmp/itext/ITextVisualObject.java b/stamper/stmp-itext/src/main/java/at/gv/egiz/pdfas/stmp/itext/ITextVisualObject.java new file mode 100644 index 00000000..76b8b0fc --- /dev/null +++ b/stamper/stmp-itext/src/main/java/at/gv/egiz/pdfas/stmp/itext/ITextVisualObject.java @@ -0,0 +1,61 @@ +package at.gv.egiz.pdfas.stmp.itext; + +import at.gv.egiz.pdfas.lib.impl.stamping.IPDFVisualObject; +import at.knowcenter.wag.egov.egiz.pdf.Pos; +import com.lowagie.text.pdf.PdfPTable; + +public class ITextVisualObject implements IPDFVisualObject { + + private PdfPTable table; + private float x; + private float y; + private int page; + + public ITextVisualObject(PdfPTable table) { + this.table = table; + } + + public void setWidth(float width) { + table.setTotalWidth(width); + } + + public void fixWidth() { + table.setLockedWidth(true); + } + + public float getHeight() { + return this.table.getTotalHeight(); + } + + public float getWidth() { + return this.table.getTotalWidth(); + } + + public void setXPos(float x) { + this.x = x; + } + + public void setYPos(float y) { + this.y = y; + } + + public float getX() { + return x; + } + + public float getY() { + return y; + } + + public int getPage() { + return page; + } + + public void setPage(int page) { + this.page = page; + } + + public PdfPTable getTable() { + return table; + } +} diff --git a/stamper/stmp-itext/src/main/java/at/gv/egiz/pdfas/stmp/itext/package-info.java b/stamper/stmp-itext/src/main/java/at/gv/egiz/pdfas/stmp/itext/package-info.java new file mode 100644 index 00000000..53f799b4 --- /dev/null +++ b/stamper/stmp-itext/src/main/java/at/gv/egiz/pdfas/stmp/itext/package-info.java @@ -0,0 +1,2 @@ + +package at.gv.egiz.pdfas.stmp.itext; \ No newline at end of file -- cgit v1.2.3