From 88486a007fa05986b09d319e62e266ce0db5b38a Mon Sep 17 00:00:00 2001 From: Andreas Fitzek Date: Tue, 1 Jul 2014 09:36:09 +0200 Subject: Signatureblock generation refactored --- .../egiz/pdfas/lib/impl/stamping/TableFactory.java | 16 +- .../lib/impl/stamping/pdfbox/ImageObject.java | 27 +- .../pdfbox/PDFAsVisualSignatureBuilder.java | 345 ++++++++------ .../lib/impl/stamping/pdfbox/PDFBoxTable.java | 103 ++-- .../lib/impl/stamping/pdfbox/TableDrawUtils.java | 527 +++++++++++++++++++++ .../at/knowcenter/wag/egov/egiz/table/Style.java | 2 +- 6 files changed, 827 insertions(+), 193 deletions(-) create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/TableDrawUtils.java (limited to 'pdf-as-lib') diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/TableFactory.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/TableFactory.java index 0ff1dd4e..838d9d5a 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/TableFactory.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/TableFactory.java @@ -198,11 +198,25 @@ public class TableFactory implements IProfileConstants { resolver.resolve(key, value, profile), key); if (entry != null) { - entry.setColSpan(2); + //entry.setColSpan(2); entry.setStyle(defaultValueStyle_); row.add(entry); } } + if (TYPE_CAPTION.equals(type)) + { + // add a single value entry + ValueResolver resolver = new ValueResolver(certProvider, operationStatus); + String value = profile.getCaption(key); + Entry entry = new Entry(Entry.TYPE_CAPTION, + resolver.resolve(key, value, profile), key); + if (entry != null) + { + //entry.setColSpan(2); + entry.setStyle(defaultCaptionStyle_); + row.add(entry); + } + } if ((TYPE_VALUE + TYPE_CAPTION).equals(type) || (TYPE_CAPTION + TYPE_VALUE).equals(type)) { // add a caption value pair diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/ImageObject.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/ImageObject.java index db057a2b..25659468 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/ImageObject.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/ImageObject.java @@ -4,11 +4,13 @@ import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage; public class ImageObject { private PDXObjectImage image; - private float size; + private float width; + private float height; - public ImageObject(PDXObjectImage image, float size) { + public ImageObject(PDXObjectImage image, float width, float height) { this.image = image; - this.size = size; + this.width = width; + this.height = height; } public PDXObjectImage getImage() { @@ -19,13 +21,20 @@ public class ImageObject { this.image = image; } - public float getSize() { - return size; + public float getWidth() { + return width; } - public void setSize(float size) { - this.size = size; + public void setWidth(float width) { + this.width = width; } - - + + public float getHeight() { + return height; + } + + public void setHeight(float height) { + this.height = height; + } + } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureBuilder.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureBuilder.java index cd3fd716..f364221b 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureBuilder.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureBuilder.java @@ -51,9 +51,42 @@ public class PDFAsVisualSignatureBuilder extends PDVisibleSigBuilder { private static final Logger logger = LoggerFactory .getLogger(PDFAsVisualSignatureBuilder.class); + private void drawDebugLine(PDPageContentStream contentStream, float x, + float y, float width, float height) { + try { + contentStream.setStrokingColor(Color.RED); + contentStream.drawLine(x, y, x+width, y); + contentStream.setStrokingColor(Color.BLUE); + contentStream.drawLine(x, y, x, y-height); + contentStream.setStrokingColor(Color.GREEN); + contentStream.drawLine(x+width, y, x+width, y-height); + contentStream.setStrokingColor(Color.ORANGE); + contentStream.drawLine(x, y-height, x+width, y-height); + + contentStream.setStrokingColor(Color.BLACK); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + private void drawDebugPadding(PDPageContentStream contentStream, float x, + float y, float padding, float width, float height) { + try { + contentStream.setStrokingColor(Color.RED); + contentStream.drawLine(x, y, x+padding, y-padding); + contentStream.drawLine(x+width, y, x+width-padding, y-padding); + contentStream.drawLine(x+width, y-height, x+width-padding, y-height+padding); + contentStream.drawLine(x, y-height, x+padding, y-height+padding); + contentStream.setStrokingColor(Color.BLACK); + } catch (Throwable e) { + e.printStackTrace(); + } + } + private void drawTable(PDPage page, PDPageContentStream contentStream, - float x, float y, float width, float height, PDFBoxTable abstractTable, PDDocument doc, - boolean subtable) throws IOException, PdfAsException { + float x, float y, float width, float height, + PDFBoxTable abstractTable, PDDocument doc, boolean subtable) + throws IOException, PdfAsException { final int rows = abstractTable.getRowCount(); final int cols = abstractTable.getColCount(); @@ -114,88 +147,66 @@ public class PDFAsVisualSignatureBuilder extends PDVisibleSigBuilder { contentStream.drawLine(x, nexty, x + width, nexty); lasty = nexty; if (i < abstractTable.getRowHeights().length) { - nexty -= abstractTable.getRowHeights()[i] + padding * 2; + //nexty -= abstractTable.getRowHeights()[i] + padding * 2; + nexty -= abstractTable.getRowHeights()[i]; } - - if (subtable && i + 1 == abstractTable.getRowHeights().length) { - nexty -= padding; - } - + + //if (subtable && i + 1 == abstractTable.getRowHeights().length) { + // nexty -= padding; + //} + float nextx = x; float ypos = y; float yheight = y + abstractTable.getHeight(); if (subtable) { - ypos -= padding; + //ypos -= padding; yheight = y + abstractTable.getHeight(); } - + for (int j = 0; j < row.size(); j++) { Entry cell = (Entry) row.get(j); - + if (subtable && j == cols) { continue; } logger.debug("COL LINE: {} {} {} {}", nextx, ypos, nextx, yheight); contentStream.drawLine(nextx, lasty, nextx, nexty); - for(int k = 0; k < cell.getColSpan(); k++) { + for (int k = 0; k < cell.getColSpan(); k++) { if (k + j < colsSizes.length) { - nextx += (colsSizes != null) ? colsSizes[k + j] : colWidth; + nextx += (colsSizes != null) ? colsSizes[k + j] + : colWidth; } } } - if(!subtable) { + if (!subtable) { contentStream.drawLine(nextx, lasty, nextx, nexty); } } - + contentStream.drawLine(x, nexty, x + tableWidth, nexty); - /* - // draw the rows - float nexty = y + tableHeight; - for (int i = 0; i <= rows; i++) { - logger.debug("ROW LINE: {} {} {} {}", x, nexty, x + tableWidth, - nexty); - contentStream.drawLine(x, nexty, x + tableWidth, nexty); - if (i < abstractTable.getRowHeights().length) { - nexty -= abstractTable.getRowHeights()[i] + padding * 2; - } - if (subtable && i + 1 == abstractTable.getRowHeights().length) { - nexty -= padding; - } - } - - // draw the columns - float nextx = x; - float ypos = y; - float yheight = y + abstractTable.getHeight(); - if (subtable) { - ypos -= padding; - yheight = y + abstractTable.getHeight(); - } - for (int i = 0; i <= cols; i++) { - if (subtable && i == cols) { - continue; - } - logger.debug("COL LINE: {} {} {} {}", nextx, ypos, nextx, - yheight); - contentStream.drawLine(nextx, ypos, nextx, yheight); - if (i < colsSizes.length) { - nextx += (colsSizes != null) ? colsSizes[i] : colWidth; - } - }*/ } - float textx = x + padding; + float textx = x; float texty = y + tableHeight; for (int i = 0; i < abstractTable.getRowCount(); i++) { ArrayList row = abstractTable.getRow(i); for (int j = 0; j < row.size(); j++) { Entry cell = (Entry) row.get(j); - - Style inherit_style = Style.doInherit(abstractTable.style, cell.getStyle()); - cell.setStyle(inherit_style); + + Style inherit_style = Style.doInherit(cell.getStyle(), abstractTable.style); + cell.setStyle(inherit_style); + + //if(subtable) { + drawDebugPadding(contentStream, textx, texty, padding, + ((colsSizes != null) ? colsSizes[j] : colWidth), + abstractTable.getRowHeights()[i]); + //} + //if(true) { + // textx += (colsSizes != null) ? colsSizes[j] : colWidth; + // continue; + //} if (cell.getType() == Entry.TYPE_CAPTION || cell.getType() == Entry.TYPE_VALUE) { @@ -208,94 +219,98 @@ public class PDFAsVisualSignatureBuilder extends PDVisibleSigBuilder { fontSize = abstractTable.getValueFont().getFontSize(); } + String text = (String) cell.getValue(); - float ttexty = texty - fontSize - padding * 0.5f; + // COSName name = COSName.getPDFName("ANDI_TAG!"); // contentStream.beginMarkedContentSequence(COSName.ALT, // name); String fontName = textFont.equals(PDType1Font.COURIER) ? "COURIER" : "HELVETICA"; - contentStream.beginText(); - - if (innerFormResources.getFonts().containsValue(textFont)) { - String fontID = getFontID(textFont); - logger.debug("Using Font: " + fontID); - contentStream.appendRawCommands("/" + fontID + " " - + fontSize + " Tf\n"); - } else { - contentStream.setFont(textFont, fontSize); - } - - // TODO: use halign and valgin + float fheight = textFont.getFontDescriptor().getCapHeight() / 1000 * fontSize; - float fheight = textFont.getFontDescriptor().getFontBoundingBox() - .getHeight() - / 1000 * fontSize; - String[] tlines = text.split("\n"); - float textHeight = fheight * tlines.length; - + float textHeight = fontSize * tlines.length; + Style cellStyle = cell.getStyle(); String valign = null; String halign = null; - - if (cell.getType() == Entry.TYPE_CAPTION && cellStyle != null) { + + if (cell.getType() == Entry.TYPE_CAPTION + && cellStyle != null) { valign = cellStyle.getVAlign(); halign = cellStyle.getHAlign(); - } else if (cell.getType() == Entry.TYPE_VALUE && cellStyle != null) { + } else if (cell.getType() == Entry.TYPE_VALUE + && cellStyle != null) { valign = cellStyle.getValueVAlign(); halign = cellStyle.getValueHAlign(); } - float ty = ttexty; - if(Style.BOTTOM.equals(valign)) { - float bottom_offset = abstractTable.getRowHeights()[i] - textHeight; + float ty = texty - padding; + float tx = textx+padding; + if (Style.BOTTOM.equals(valign)) { + float bottom_offset = abstractTable.getRowHeights()[i] + - textHeight; ty -= bottom_offset; - } else if(Style.MIDDLE.equals(valign)) { - float bottom_offset = abstractTable.getRowHeights()[i] - textHeight; + } else if (Style.MIDDLE.equals(valign)) { + float bottom_offset = abstractTable.getRowHeights()[i] + - textHeight; bottom_offset = bottom_offset / 2.0f; ty -= bottom_offset; } - - float columnWidth = (colsSizes != null) ? colsSizes[j] : colWidth; + + float columnWidth = (colsSizes != null) ? colsSizes[j] + : colWidth; float maxWidth = 0; for (int k = 0; k < tlines.length; k++) { float lineWidth; if (textFont instanceof PDType1Font) { - lineWidth = textFont.getStringWidth(tlines[k]) / 1000.0f * fontSize; - //fwidth = textFont.getFontDescriptor().getFontBoundingBox().getWidth() - // / 1000.0f * fontSize; + lineWidth = textFont.getStringWidth(tlines[k]) + / 1000.0f * fontSize; } else { - float fwidth = textFont.getStringWidth("abcdefghijklmnopqrstuvwxyz ") / 1000.0f * fontSize; - fwidth = fwidth / (float)"abcdefghijklmnopqrstuvwxyz".length(); + float fwidth = textFont + .getStringWidth("abcdefghijklmnopqrstuvwxyz ") + / 1000.0f * fontSize; + fwidth = fwidth + / (float) "abcdefghijklmnopqrstuvwxyz" + .length(); lineWidth = tlines[k].length() * fwidth; } - - - - //float w = textFont.getStringWidth(tlines[k]) / 1000 * fontSize; + if (maxWidth < lineWidth) { maxWidth = lineWidth; } } - float tx = textx; - if(Style.CENTER.equals(halign)) { - float offset = columnWidth - maxWidth - 2 * padding; - if(offset > 0) { + if (Style.CENTER.equals(halign)) { + float offset = columnWidth - maxWidth - 2 * padding; + if (offset > 0) { offset = offset / 2.0f; tx += offset; } - } else if(Style.RIGHT.equals(halign)) { - float offset = columnWidth - maxWidth - 2 * padding; - if(offset > 0) { + } else if (Style.RIGHT.equals(halign)) { + float offset = columnWidth - maxWidth - 2 * padding; + if (offset > 0) { tx += offset; } } + + drawDebugLine(contentStream, tx, ty, maxWidth, textHeight); + + contentStream.beginText(); + + if (innerFormResources.getFonts().containsValue(textFont)) { + String fontID = getFontID(textFont); + logger.debug("Using Font: " + fontID); + contentStream.appendRawCommands("/" + fontID + " " + + fontSize + " Tf\n"); + } else { + contentStream.setFont(textFont, fontSize); + } - logger.debug("Writing: " + tx + " : " + ty + " = " - + text + " as " + cell.getType() + " w " + fontName); - contentStream.moveTextPositionByAmount(tx, ty); + logger.debug("Writing: " + tx + " : " + (ty-fheight) + " = " + text + + " as " + cell.getType() + " w " + fontName); + contentStream.moveTextPositionByAmount(tx, (ty-fheight)); if (text.contains("\n")) { String[] lines = text.split("\n"); @@ -320,69 +335,83 @@ public class PDFAsVisualSignatureBuilder extends PDVisibleSigBuilder { } ImageObject image = images.get(img_ref); PDXObjectImage pdImage = image.getImage(); - // text = "Row :" + i + "COL: " + j; - // COSName name = COSName.getPDFName("ANDI_TAG!"); - // contentStream.beginMarkedContentSequence(COSName.ALT, - // name); - - // TODO: use HAlign - float imgy = texty; + float imgx = textx + padding; + float hoffset = ((colsSizes != null) ? colsSizes[j] + : colWidth) + - image.getWidth(); + if (cell.getStyle().getImageVAlign() != null + && cell.getStyle().getImageVAlign() + .equals(Style.CENTER)) { + hoffset = hoffset / 2.0f; + imgx += hoffset; + } else if (cell.getStyle().getImageHAlign() != null + && cell.getStyle().getImageHAlign() + .equals(Style.RIGHT)) { + imgx += hoffset; + } + + float imgy = texty - padding; + float voffset = abstractTable.getRowHeights()[i] + - image.getHeight(); if (cell.getStyle().getImageVAlign() != null && cell.getStyle().getImageVAlign() - .equals(Style.TOP)) { - imgy = texty - padding - image.getSize(); + .equals(Style.MIDDLE)) { + voffset = voffset / 2.0f; + imgy -= voffset; } else if (cell.getStyle().getImageVAlign() != null && cell.getStyle().getImageVAlign() .equals(Style.BOTTOM)) { - // Should allready be at bottom ... - imgy = texty - abstractTable.getRowHeights()[i] - + padding; - } else { - // default to middle - imgy = texty - padding - - abstractTable.getRowHeights()[i] / 2; - imgy = imgy - image.getSize() / 2; - } - logger.debug("Image: " + textx + " : " + imgy); - contentStream.drawXObject(pdImage, textx, imgy, - image.getSize(), image.getSize()); + imgy -= voffset; + } + + drawDebugLine(contentStream, imgx, imgy, image.getWidth(), image.getHeight()); + + logger.debug("Image: " + imgx + " : " + (imgy - image.getHeight())); + contentStream.drawXObject(pdImage, imgx, imgy - image.getHeight(), + image.getWidth(), image.getHeight()); // contentStream.endMarkedContentSequence(); } else if (cell.getType() == Entry.TYPE_TABLE) { - - float tableY = texty - abstractTable.getRowHeights()[i] - - padding; + + float tableY = texty - abstractTable.getRowHeights()[i]; float tableX = textx; // texty = texty - padding; - tableX = textx - padding; PDFBoxTable tbl_value = (PDFBoxTable) cell.getValue(); - - Style inherit_styletab = Style.doInherit(abstractTable.style, cell.getStyle()); + + Style inherit_styletab = Style.doInherit( + abstractTable.style, cell.getStyle()); tbl_value.table.setStyle(inherit_styletab); - + logger.debug("Table: " + tableX + " : " + tableY); - drawTable(page, contentStream, tableX, tableY, - (colsSizes != null) ? colsSizes[j] : colWidth, - abstractTable.getRowHeights()[i] + padding * 2, - tbl_value, doc, true); + //logger.debug("Table height: " + ); + TableDrawUtils.drawTable(page, contentStream, tableX, tableY, + (colsSizes != null) ? colsSizes[j] : colWidth, + abstractTable.getRowHeights()[i], + tbl_value, doc, true, innerFormResources, images, settings); } textx += (colsSizes != null) ? colsSizes[j] : colWidth; } // if (i + 1 < abstractTable.getRowHeights().length) { logger.debug("Row {} from {} - {} - {} = {}", i, texty, abstractTable.getRowHeights()[i], padding * 2, texty - - (abstractTable.getRowHeights()[i] + padding * 2)); - texty -= abstractTable.getRowHeights()[i] + padding * 2; + - (abstractTable.getRowHeights()[i])); + texty -= abstractTable.getRowHeights()[i]; // texty = texty - abstractTable.getRowHeights()[i + 1] - padding // * 2; // texty = texty - abstractTable.getRowHeights()[i] - padding // * 2; // } - textx = x + padding; + textx = x; } } + private void drawTable2(PDPage page, PDPageContentStream contentStream, + float x, float y, float width, float height, + PDFBoxTable abstractTable, PDDocument doc, boolean subtable) { + + } + private PDFAsVisualSignatureProperties properties; private PDFAsVisualSignatureDesigner designer; private ISettings settings; @@ -402,7 +431,7 @@ public class PDFAsVisualSignatureBuilder extends PDVisibleSigBuilder { } public PDFAsVisualSignatureBuilder( - PDFAsVisualSignatureProperties properties, ISettings settings, + PDFAsVisualSignatureProperties properties, ISettings settings, PDFAsVisualSignatureDesigner designer) { this.properties = properties; this.settings = settings; @@ -504,26 +533,46 @@ public class PDFAsVisualSignatureBuilder extends PDVisibleSigBuilder { } float width = colsSizes[j]; + float height = table.getRowHeights()[i] + padding * 2; - float size = (int) Math.floor((double) width); - size -= 2 * padding; - logger.debug("Scaling image to: " + size); + float iwidth = (int) Math.floor((double) width); + iwidth -= 2 * padding; + + float iheight = (int) Math.floor((double) height); + iheight -= 2 * padding; + + float origWidth = (float) img.getWidth(); + float origHeight = (float) img.getHeight(); + + float wfactor = iwidth / origWidth; + float hfactor = iheight / origHeight; + float scaleFactor = wfactor; + if(hfactor < wfactor) { + scaleFactor = hfactor; + } + + iwidth = (float) Math.floor((double)(scaleFactor * origWidth)); + iheight = (float) Math.floor((double)(scaleFactor * origHeight)); if (table.style != null) { if (table.style.getImageScaleToFit() != null) { - size = table.style.getImageScaleToFit() + iwidth = table.style.getImageScaleToFit() .getWidth(); + iheight = table.style.getImageScaleToFit() + .getHeight(); } } - if(img.getAlphaRaster() == null && img.getColorModel().hasAlpha()) { + logger.debug("Scaling image to: " + iwidth + " x " + iheight); + + if (img.getAlphaRaster() == null + && img.getColorModel().hasAlpha()) { img = ImageUtils.removeAlphaChannel(img); } PDXObjectImage pdImage = new PDJpeg(template, img); - - ImageObject image = new ImageObject(pdImage, size); + ImageObject image = new ImageObject(pdImage, iwidth, iheight); images.put(img_ref, image); innerFormResources.addXObject(pdImage, "Im"); } @@ -538,7 +587,7 @@ public class PDFAsVisualSignatureBuilder extends PDVisibleSigBuilder { @Override public void createInnerFormStream(PDDocument template) { try { - + // Hint we have to create all PDXObjectImages before creating the // PDPageContentStream // only PDFbox developers know why ... @@ -549,9 +598,9 @@ public class PDFAsVisualSignatureBuilder extends PDVisibleSigBuilder { PDPageContentStream stream = new PDPageContentStream(template, getStructure().getPage()); // stream.setFont(PDType1Font.COURIER, 5); - drawTable(getStructure().getPage(), stream, 1, 1, + TableDrawUtils.drawTable(getStructure().getPage(), stream, 1, 1, designer.getWidth(), designer.getHeight(), - properties.getMainTable(), template, false); + properties.getMainTable(), template, false, innerFormResources, images, settings); stream.close(); PDStream innterFormStream = getStructure().getPage().getContents(); getStructure().setInnterFormStream(innterFormStream); @@ -608,7 +657,7 @@ public class PDFAsVisualSignatureBuilder extends PDVisibleSigBuilder { String innerFormComment = getStructure().getInnterFormStream() .getInputStreamAsString(); - logger.debug("Inner Form Stream: " + innerFormComment); + //logger.debug("Inner Form Stream: " + innerFormComment); // appendRawCommands(getStructure().getInnterFormStream().createOutputStream(), // getStructure().getInnterFormStream().getInputStreamAsString()); diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxTable.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxTable.java index 01cb031d..24331652 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxTable.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxTable.java @@ -39,6 +39,7 @@ public class PDFBoxTable { float tableHeight; Color bgColor; + boolean[] addPadding; float[] rowHeights; float[] colWidths; @@ -148,6 +149,8 @@ public class PDFBoxTable { } } calculateHeightsBasedOnWidths(); + + logger.debug("Generating Table with fixed With {} got width {}", fixSize, getWidth()); } public PDFBoxTable(Table abstractTable, PDFBoxTable parent, @@ -160,6 +163,7 @@ public class PDFBoxTable { private void calculateHeightsBasedOnWidths() throws IOException { int rows = this.table.getRows().size(); rowHeights = new float[rows]; + addPadding = new boolean[rows]; for (int i = 0; i < rows; i++) { rowHeights[i] = 0; @@ -247,13 +251,28 @@ public class PDFBoxTable { this.tableHeight = 0; for (int i = 0; i < rowHeights.length; i++) { - this.tableHeight += rowHeights[i] + padding * 2; + this.tableHeight += rowHeights[i]; } + // Post Process heights for inner Tables ... + for (int i = 0; i < rowHeights.length; i++) { + ArrayList row = this.table.getRows().get(i); + for (int j = 0; j < row.size(); j++) { + Entry cell = (Entry) row.get(j); + if(cell.getType() == Entry.TYPE_TABLE) { + PDFBoxTable tbl = (PDFBoxTable)cell.getValue(); + if(rowHeights[i] != tbl.getHeight()) + { + tbl.setHeight(rowHeights[i]); + } + } + } + } + this.tableWidth = 0; for (int i = 0; i < colWidths.length; i++) { - this.tableWidth += colWidths[i] + padding * 2; + this.tableWidth += colWidths[i]; } } @@ -309,8 +328,6 @@ public class PDFBoxTable { return pdfBoxTable.getWidth(); default: logger.warn("Invalid Cell Entry Type: " + cell.getType()); - // return font.getFontDescriptor().getFontBoundingBox().getHeight() - // / 1000 * fontSize; } return 0; } @@ -437,6 +454,32 @@ public class PDFBoxTable { // return lines.toArray(new String[0]); // } + private float[] getStringHeights(String[] lines, PDFont c, float fontSize) { + float[] heights = new float[lines.length]; + for (int i = 0; i < lines.length; i++) { + float maxLineHeight = 0; + try { + byte[] linebytes = StringUtils.applyWinAnsiEncoding(lines[i]); + for(int j = 0; j < linebytes.length; j++) { + float he = c.getFontHeight(linebytes, j, 1) / 1000 * fontSize; + if(he > maxLineHeight) { + maxLineHeight = he; + } + } + } catch (UnsupportedEncodingException e) { + logger.warn("failed to determine String height", e); + maxLineHeight = c.getFontDescriptor().getCapHeight() / 1000 * fontSize; + } catch (IOException e) { + logger.warn("failed to determine String height", e); + maxLineHeight = c.getFontDescriptor().getCapHeight() / 1000 * fontSize; + } + + heights[i] = maxLineHeight; + } + + return heights; + } + private float getCellHeight(Entry cell, float width) throws IOException { boolean isValue = true; switch (cell.getType()) { @@ -453,51 +496,35 @@ public class PDFBoxTable { c = font.getFont(null); fontSize = font.getFontSize(); } - /* - * float fwidth; if (c instanceof PDType1Font) { fwidth = - * c.getFontDescriptor().getFontBoundingBox().getWidth() / 1000.0f * - * fontSize * 0.9f; } else if (c instanceof PDTrueTypeFont) { - * PDTrueTypeFont t = (PDTrueTypeFont)c; fwidth = - * t.getAverageFontWidth() / 1000.0f * fontSize; } else { fwidth = - * c.getStringWidth("abcdefghijklmnopqrstuvwxyz ") / 1000.0f * - * fontSize; fwidth = fwidth / - * (float)"abcdefghijklmnopqrstuvwxyz".length(); } - * - * logger.debug("Font Width: {}", fwidth); int maxcharcount = (int) - * ((width - padding * 2) / fwidth) - 1; - * logger.debug("Max {} chars per line!", maxcharcount); - */ - float fheight = c.getFontDescriptor().getFontBoundingBox() - .getHeight() - / 1000 * fontSize * 0.9f; String[] lines = breakString(string, (width - padding * 2.0f), c, fontSize); cell.setValue(concatLines(lines)); - return fheight * lines.length;// - padding; + float[] heights = getStringHeights(lines, c, fontSize); + return fontSize * heights.length + padding * 2; case Entry.TYPE_IMAGE: if (style != null && style.getImageScaleToFit() != null) { - if (style.getImageScaleToFit().getHeight() < width) { + //if (style.getImageScaleToFit().getHeight() < width) { return style.getImageScaleToFit().getHeight(); - } + //} } return width; case Entry.TYPE_TABLE: PDFBoxTable pdfBoxTable = null; if (cell.getValue() instanceof Table) { pdfBoxTable = new PDFBoxTable((Table) cell.getValue(), this, - width - padding, this.settings); + width, this.settings); cell.setValue(pdfBoxTable); } else if (cell.getValue() instanceof PDFBoxTable) { // recreate here beacuse of fixed width! pdfBoxTable = (PDFBoxTable) cell.getValue(); - pdfBoxTable = new PDFBoxTable(pdfBoxTable.table, this, width - - padding, this.settings); + pdfBoxTable = new PDFBoxTable(pdfBoxTable.table, this, width, + this.settings); cell.setValue(pdfBoxTable); } else { throw new IOException("Failed to build PDFBox Table"); } - return pdfBoxTable.getHeight() - padding; + return pdfBoxTable.getHeight(); default: logger.warn("Invalid Cell Entry Type: " + cell.getType()); } @@ -520,15 +547,13 @@ public class PDFBoxTable { c = font.getFont(null); fontSize = font.getFontSize(); } - float fheight = c.getFontDescriptor().getFontBoundingBox() - .getHeight() - / 1000 * fontSize; + if (string.contains("\n")) { String[] lines = string.split("\n"); - return fheight * lines.length; + return fontSize * lines.length + padding * 2; } else { - return fheight; + return fontSize + padding * 2; } case Entry.TYPE_IMAGE: if (style != null && style.getImageScaleToFit() != null) { @@ -568,6 +593,16 @@ public class PDFBoxTable { public float getHeight() { return tableHeight; } + + public void setHeight(float height) { + float diff = height - this.getHeight(); + if(diff > 0) { + this.rowHeights[rowHeights.length - 1] += diff; + calcTotals(); + } else { + logger.warn("Table cannot be this small!"); + } + } public float[] getRowHeights() { return rowHeights; @@ -578,7 +613,7 @@ public class PDFBoxTable { } public int getColCount() { - return this.table.getMaxCols();//.getColsRelativeWith().length; + return this.table.getMaxCols();// .getColsRelativeWith().length; } public float[] getColsRelativeWith() { diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/TableDrawUtils.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/TableDrawUtils.java new file mode 100644 index 00000000..26aef4cb --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/TableDrawUtils.java @@ -0,0 +1,527 @@ +package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox; + +import java.awt.Color; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.edit.PDPageContentStream; +import org.apache.pdfbox.pdmodel.font.PDFont; +import org.apache.pdfbox.pdmodel.font.PDType1Font; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.common.exceptions.PdfAsWrappedIOException; +import at.gv.egiz.pdfas.common.settings.ISettings; +import at.knowcenter.wag.egov.egiz.table.Entry; +import at.knowcenter.wag.egov.egiz.table.Style; + +public class TableDrawUtils { + + private static final Logger logger = LoggerFactory + .getLogger(TableDrawUtils.class); + + public static final String TABLE_DEBUG = "debug.table"; + + public static void drawTable(PDPage page, + PDPageContentStream contentStream, float x, float y, float width, + float height, PDFBoxTable abstractTable, PDDocument doc, + boolean subtable, PDResources formResources, + Map images, ISettings settings) + throws PdfAsException { + + logger.debug("Drawing Table: X {} Y {} WIDTH {} HEIGHT {} \n{}", x, y, + width, height, abstractTable.getOrigTable().toString()); + + abstractTable.getOrigTable().setWidth(width); + + drawTableBackground(page, contentStream, x, y, width, height, + abstractTable, settings); + + drawBorder(page, contentStream, x, y, width, height, abstractTable, + doc, subtable, settings); + + drawContent(page, contentStream, x, y, width, height, abstractTable, + doc, subtable, formResources, images, settings); + } + + public static void drawContent(PDPage page, + PDPageContentStream contentStream, float x, float y, float width, + float height, PDFBoxTable abstractTable, PDDocument doc, + boolean subtable, PDResources formResources, + Map images, ISettings settings) + throws PdfAsException { + + float contentx = x; + float contenty = y + height; + float padding = abstractTable.getPadding(); + float[] colsSizes = getColSizes(abstractTable); + + for (int i = 0; i < abstractTable.getRowCount(); i++) { + ArrayList row = abstractTable.getRow(i); + for (int j = 0; j < row.size(); j++) { + Entry cell = (Entry) row.get(j); + + Style inherit_style = Style.doInherit(cell.getStyle(), + abstractTable.style); + cell.setStyle(inherit_style); + + drawDebugPadding(contentStream, contentx, contenty, padding, + colsSizes[j], abstractTable.getRowHeights()[i], settings); + + switch (cell.getType()) { + case Entry.TYPE_CAPTION: + drawCaption(page, contentStream, contentx, contenty, + colsSizes[j], abstractTable.getRowHeights()[i], + padding, abstractTable, doc, cell, formResources, settings); + break; + case Entry.TYPE_VALUE: + drawValue(page, contentStream, contentx, contenty, + colsSizes[j], abstractTable.getRowHeights()[i], + padding, abstractTable, doc, cell, formResources, settings); + break; + case Entry.TYPE_IMAGE: + drawImage(page, contentStream, contentx, contenty, + colsSizes[j], abstractTable.getRowHeights()[i], + padding, abstractTable, doc, cell, formResources, + images, settings); + break; + case Entry.TYPE_TABLE: + + PDFBoxTable tbl_value = (PDFBoxTable) cell.getValue(); + + Style inherit_styletab = Style.doInherit( + abstractTable.style, cell.getStyle()); + tbl_value.table.setStyle(inherit_styletab); + + drawTable(page, contentStream, contentx, contenty + - abstractTable.getRowHeights()[i], colsSizes[j], + abstractTable.getRowHeights()[i], tbl_value, doc, + true, formResources, images, settings); + break; + default: + logger.warn("Unknown Cell entry type: " + cell.getType()); + break; + } + + // Move content pointer + contentx += colsSizes[j]; + } + + // Move content pointer + contenty -= abstractTable.getRowHeights()[i]; + contentx = x; + } + } + + private static void drawString(PDPage page, + PDPageContentStream contentStream, float contentx, float contenty, + float width, float height, float padding, + PDFBoxTable abstractTable, PDDocument doc, Entry cell, + float fontSize, float textHeight, String valign, String halign, + String[] tlines, PDFont textFont, PDResources formResources, + ISettings settings) throws PdfAsException { + try { + float ty = contenty - padding; + float tx = contentx + padding; + float innerHeight = height - (2 * padding); + float innerWidth = width - (2 * padding); + if (Style.BOTTOM.equals(valign)) { + float bottom_offset = innerHeight - textHeight; + ty -= bottom_offset; + } else if (Style.MIDDLE.equals(valign)) { + float bottom_offset = innerHeight - textHeight; + bottom_offset = bottom_offset / 2.0f; + ty -= bottom_offset; + } + + // calculate the max with of the text content + float maxWidth = 0; + for (int k = 0; k < tlines.length; k++) { + float lineWidth; + // if (textFont instanceof PDType1Font) { + lineWidth = textFont.getStringWidth(tlines[k]) / 1000.0f + * fontSize; + /* + * } else { float fwidth = textFont + * .getStringWidth("abcdefghijklmnopqrstuvwxyz ") / 1000.0f * + * fontSize; fwidth = fwidth / (float) + * "abcdefghijklmnopqrstuvwxyz" .length(); lineWidth = + * tlines[k].length() * fwidth; } + */ + if (maxWidth < lineWidth) { + maxWidth = lineWidth; + } + } + + if (Style.CENTER.equals(halign)) { + float offset = innerWidth - maxWidth; + if (offset > 0) { + offset = offset / 2.0f; + tx += offset; + } + } else if (Style.RIGHT.equals(halign)) { + float offset = innerWidth - maxWidth; + if (offset > 0) { + tx += offset; + } + } + + logger.debug("Text tx {} ty {} maxWidth {} textHeight {}", tx, ty, + maxWidth, textHeight); + + drawDebugLine(contentStream, tx, ty, maxWidth, textHeight, settings); + + contentStream.beginText(); + + if (formResources.getFonts().containsValue(textFont)) { + String fontID = getFontID(textFont, formResources); + logger.debug("Using Font: " + fontID); + contentStream.appendRawCommands("/" + fontID + " " + fontSize + + " Tf\n"); + } else { + contentStream.setFont(textFont, fontSize); + } + + logger.debug("Writing: " + tx + " : " + (ty - fontSize) + " as " + + cell.getType()); + contentStream.moveTextPositionByAmount(tx, (ty - fontSize)); + + contentStream.appendRawCommands(fontSize + " TL\n"); + for (int k = 0; k < tlines.length; k++) { + contentStream.drawString(tlines[k]); + if (k < tlines.length - 1) { + contentStream.appendRawCommands("T*\n"); + } + } + + contentStream.endText(); + + } catch (IOException e) { + logger.error("IO Exception", e); + throw new PdfAsException("Error", e); + } + } + + public static void drawCaption(PDPage page, + PDPageContentStream contentStream, float contentx, float contenty, + float width, float height, float padding, + PDFBoxTable abstractTable, PDDocument doc, Entry cell, + PDResources formResources, ISettings settings) + throws PdfAsException { + + logger.debug("Drawing Caption @ X: {} Y: {}", contentx, contenty); + + try { + float fontSize = PDFBoxFont.defaultFontSize; + PDFont textFont = PDFBoxFont.defaultFont; + + textFont = abstractTable.getFont().getFont(doc); + fontSize = abstractTable.getFont().getFontSize(); + + // get the cell Text + String text = (String) cell.getValue(); + String[] tlines = text.split("\n"); + float textHeight = fontSize * tlines.length; + + Style cellStyle = cell.getStyle(); + String valign = cellStyle.getVAlign(); + String halign = cellStyle.getHAlign(); + + drawString(page, contentStream, contentx, contenty, width, height, + padding, abstractTable, doc, cell, fontSize, textHeight, + valign, halign, tlines, textFont, formResources, settings); + } catch (IOException e) { + logger.error("IO Exception", e); + throw new PdfAsException("Error", e); + } + } + + public static void drawValue(PDPage page, + PDPageContentStream contentStream, float contentx, float contenty, + float width, float height, float padding, + PDFBoxTable abstractTable, PDDocument doc, Entry cell, + PDResources formResources, ISettings settings) + throws PdfAsException { + + logger.debug("Drawing Value @ X: {} Y: {}", contentx, contenty); + + try { + float fontSize = PDFBoxFont.defaultFontSize; + PDFont textFont = PDFBoxFont.defaultFont; + + textFont = abstractTable.getValueFont().getFont(doc); + fontSize = abstractTable.getValueFont().getFontSize(); + + // get the cell Text + String text = (String) cell.getValue(); + String[] tlines = text.split("\n"); + float textHeight = fontSize * tlines.length; + + Style cellStyle = cell.getStyle(); + String valign = cellStyle.getValueVAlign(); + String halign = cellStyle.getValueHAlign(); + + drawString(page, contentStream, contentx, contenty, width, height, + padding, abstractTable, doc, cell, fontSize, textHeight, + valign, halign, tlines, textFont, formResources, settings); + } catch (IOException e) { + logger.error("IO Exception", e); + throw new PdfAsException("Error", e); + } + } + + public static void drawImage(PDPage page, + PDPageContentStream contentStream, float contentx, float contenty, + float width, float height, float padding, + PDFBoxTable abstractTable, PDDocument doc, Entry cell, + PDResources formResources, Map images, + ISettings settings) throws PdfAsException { + try { + float innerHeight = height; + float innerWidth = width; + + String img_ref = (String) cell.getValue(); + if (!images.containsKey(img_ref)) { + logger.error("Image not prepared! : " + img_ref); + throw new PdfAsException("Image not prepared! : " + img_ref); + } + ImageObject image = images.get(img_ref); + PDXObjectImage pdImage = image.getImage(); + + float imgx = contentx; + float hoffset = innerWidth - image.getWidth(); + if (cell.getStyle().getImageHAlign() != null + && cell.getStyle().getImageHAlign().equals(Style.LEFT)) { + hoffset = hoffset / 2.0f; + imgx += hoffset; + } else if (cell.getStyle().getImageHAlign() != null + && cell.getStyle().getImageHAlign().equals(Style.RIGHT)) { + imgx += hoffset; + } else { + hoffset = hoffset / 2.0f; + imgx += hoffset; + } + + float imgy = contenty; + float voffset = innerHeight - image.getHeight(); + if (cell.getStyle().getImageVAlign() != null + && cell.getStyle().getImageVAlign().equals(Style.MIDDLE)) { + voffset = voffset / 2.0f; + imgy -= voffset; + } else if (cell.getStyle().getImageVAlign() != null + && cell.getStyle().getImageVAlign().equals(Style.BOTTOM)) { + imgy -= voffset; + } + + drawDebugLine(contentStream, imgx, imgy, image.getWidth(), + image.getHeight(), settings); + + // logger.debug("Image: " + imgx + " : " + (imgy - + // image.getHeight())); + contentStream.drawXObject(pdImage, imgx, imgy - image.getHeight(), + image.getWidth(), image.getHeight()); + } catch (IOException e) { + logger.error("IO Exception", e); + throw new PdfAsException("Error", e); + } + + } + + public static float[] getColSizes(PDFBoxTable abstractTable) { + float[] origcolsSizes = abstractTable.getColsRelativeWith(); + int max_cols = abstractTable.getColCount(); + float[] colsSizes = new float[max_cols]; + if (origcolsSizes == null) { + // set the column ratio for all columns to 1 + for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) { + colsSizes[cols_idx] = 1; + } + } else { + // set the column ratio for all columns to 1 + for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) { + colsSizes[cols_idx] = origcolsSizes[cols_idx]; + } + } + + // adapt + float total = 0; + + for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) { + total += colsSizes[cols_idx]; + } + + for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) { + colsSizes[cols_idx] = (colsSizes[cols_idx] / total) + * abstractTable.getWidth(); + } + + float sum = 0; + + for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) { + sum += colsSizes[cols_idx]; + } + + logger.info("Table Col Sizes SUM {} Table Width {}", sum, + abstractTable.getWidth()); + logger.info("Table Table Height {}", abstractTable.getHeight()); + + return colsSizes; + } + + public static void drawBorder(PDPage page, + PDPageContentStream contentStream, float x, float y, float width, + float height, PDFBoxTable abstractTable, PDDocument doc, + boolean subtable, ISettings settings) throws PdfAsException { + try { + + logger.debug("Drawing Table borders for " + + abstractTable.getOrigTable().getName()); + + final int rows = abstractTable.getRowCount(); + float border = abstractTable.style.getBorder(); + float[] colsSizes = getColSizes(abstractTable); + + if (border > 0) { + contentStream.setLineWidth(border); + + float x_from = x; + float x_to = x + width; + float y_from = y + height; + float y_to = y + height; + + // draw first line + logger.debug("ROW LINE: {} {} {} {}", x_from, y_from, x_to, + y_from); + contentStream.drawLine(x, y_from, x_to, y_from); + + // Draw all row borders + for (int i = 0; i < rows; i++) { + y_from -= abstractTable.getRowHeights()[i]; + + // Draw row border! + logger.debug("ROW LINE: {} {} {} {}", x_from, y_from, x_to, + y_from); + contentStream.drawLine(x, y_from, x_to, y_from); + + } + + // reset y for "line feed" + y_from = y + height; + y_to = y_from - abstractTable.getRowHeights()[0]; + + // Draw all column borders + for (int i = 0; i < rows; i++) { + ArrayList row = abstractTable.getRow(i); + + // reset x for "line feed" + x_from = x; + + // draw first line + logger.debug("COL LINE: {} {} {} {}", x_from, y_from, + x_from, y_to); + + contentStream.drawLine(x_from, y_from, x_from, y_to); + + for (int j = 0; j < row.size(); j++) { + Entry cell = (Entry) row.get(j); + + for (int k = 0; k < cell.getColSpan(); k++) { + if (k + j < colsSizes.length) { + x_from += colsSizes[k + j]; + } + } + logger.debug("COL LINE: {} {} {} {}", x_from, y_from, + x_from, y_to); + contentStream.drawLine(x_from, y_from, x_from, y_to); + } + + if (i + 1 < rows) { + y_from = y_to; + y_to = y_from - abstractTable.getRowHeights()[i + 1]; + } + } + + } + } catch (Throwable e) { + logger.warn("drawing table borders", e); + throw new PdfAsException("drawing table borders", e); + } + } + + public static void drawTableBackground(PDPage page, + PDPageContentStream contentStream, float x, float y, float width, + float height, PDFBoxTable abstractTable, ISettings settings) + throws PdfAsException { + try { + if (abstractTable.getBGColor() != null) { + contentStream.setNonStrokingColor(abstractTable.getBGColor()); + contentStream.fillRect(x, y, abstractTable.getWidth(), + abstractTable.getHeight()); + contentStream.setNonStrokingColor(Color.BLACK); + } + } catch (Throwable e) { + logger.warn("drawing table borders", e); + throw new PdfAsException("drawing table borders", e); + } + } + + private static void drawDebugLine(PDPageContentStream contentStream, + float x, float y, float width, float height, ISettings settings) { + if ("true".equals(settings.getValue(TABLE_DEBUG))) { + try { + contentStream.setStrokingColor(Color.RED); + contentStream.drawLine(x, y, x + width, y); + contentStream.setStrokingColor(Color.BLUE); + contentStream.drawLine(x, y, x, y - height); + contentStream.setStrokingColor(Color.GREEN); + contentStream.drawLine(x + width, y, x + width, y - height); + contentStream.setStrokingColor(Color.ORANGE); + contentStream.drawLine(x, y - height, x + width, y - height); + + contentStream.setStrokingColor(Color.BLACK); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + private static void drawDebugPadding(PDPageContentStream contentStream, + float x, float y, float padding, float width, float height, + ISettings settings) { + if ("true".equals(settings.getValue(TABLE_DEBUG))) { + try { + contentStream.setStrokingColor(Color.RED); + contentStream.drawLine(x, y, x + padding, y - padding); + contentStream.drawLine(x + width, y, x + width - padding, y + - padding); + contentStream.drawLine(x + width, y - height, x + width + - padding, y - height + padding); + contentStream.drawLine(x, y - height, x + padding, y - height + + padding); + contentStream.setStrokingColor(Color.BLACK); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + private static String getFontID(PDFont font, PDResources resources) { + Iterator> it = resources.getFonts() + .entrySet().iterator(); + while (it.hasNext()) { + java.util.Map.Entry entry = it.next(); + if (entry.getValue().equals(font)) { + return entry.getKey(); + } + } + return ""; + } + +} diff --git a/pdf-as-lib/src/main/java/at/knowcenter/wag/egov/egiz/table/Style.java b/pdf-as-lib/src/main/java/at/knowcenter/wag/egov/egiz/table/Style.java index 50be815d..2dded8ae 100644 --- a/pdf-as-lib/src/main/java/at/knowcenter/wag/egov/egiz/table/Style.java +++ b/pdf-as-lib/src/main/java/at/knowcenter/wag/egov/egiz/table/Style.java @@ -579,7 +579,7 @@ public class Style implements Serializable { } float width = Float.parseFloat(dimensions[0]); - float height = Float.parseFloat(dimensions[0]); + float height = Float.parseFloat(dimensions[1]); return new ImageScaleToFit(width, height); } -- cgit v1.2.3