aboutsummaryrefslogtreecommitdiff
path: root/pdf-as-pdfbox-2/src/main/java/at/gv/egiz
diff options
context:
space:
mode:
authorChristian Maierhofer <cmaierhofer@iaik.tugraz.at>2016-06-08 08:12:21 +0200
committerAndreas Fitzek <andreas.fitzek@iaik.tugraz.at>2016-08-17 16:45:49 +0200
commitc4e41301d0746ce57044a3aa41375cff3a9f2b5e (patch)
treebdf13d0a0d7fae97215a84286e31ff894bacc979 /pdf-as-pdfbox-2/src/main/java/at/gv/egiz
parenta7945fff6ca14d35ac75039712736dddc6d48e90 (diff)
downloadpdf-as-4-c4e41301d0746ce57044a3aa41375cff3a9f2b5e.tar.gz
pdf-as-4-c4e41301d0746ce57044a3aa41375cff3a9f2b5e.tar.bz2
pdf-as-4-c4e41301d0746ce57044a3aa41375cff3a9f2b5e.zip
initial pdfbox-2 commit
Diffstat (limited to 'pdf-as-pdfbox-2/src/main/java/at/gv/egiz')
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/PDFBOXBackend.java53
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/PDFBOXObject.java70
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/PDFBoxPlaceholderExtractor.java24
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignaturePlaceholderExtractor.java474
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/positioning/Positioning.java308
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/utils/PdfBoxUtils.java73
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PADESPDFBOXSigner.java796
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PDFASPDFBOXExtractorInterface.java7
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PDFASPDFBOXSignatureInterface.java10
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PdfboxSignerWrapper.java96
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/SignatureDataExtractor.java92
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/FontInfoCache.java8
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/IDGenerator.java5
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/ImageObject.java64
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsTemplateCreator.java166
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureBuilder.java659
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureDesigner.java472
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureProperties.java149
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFBoxFont.java299
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFBoxTable.java722
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PdfBoxStamper.java67
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PdfBoxVisualObject.java108
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/StamperFactory.java25
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/TableDrawUtils.java609
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/pdfbox2/PDFBOXVerifier.java158
25 files changed, 5514 insertions, 0 deletions
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/PDFBOXBackend.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/PDFBOXBackend.java
new file mode 100644
index 00000000..1a9424a6
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/PDFBOXBackend.java
@@ -0,0 +1,53 @@
+package at.gv.egiz.pdfas.lib.impl.pdfbox2;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import at.gv.egiz.pdfas.lib.backend.PDFASBackend;
+import at.gv.egiz.pdfas.lib.impl.pdfbox2.placeholder.PDFBoxPlaceholderExtractor;
+import at.gv.egiz.pdfas.lib.impl.placeholder.PlaceholderExtractor;
+import at.gv.egiz.pdfas.lib.impl.signing.IPdfSigner;
+import at.gv.egiz.pdfas.lib.impl.signing.pdfbox2.PADESPDFBOXSigner;
+import at.gv.egiz.pdfas.lib.impl.verify.VerifyBackend;
+import at.gv.egiz.pdfas.lib.impl.verify.pdfbox2.PDFBOXVerifier;
+
+public class PDFBOXBackend implements PDFASBackend {
+
+ private static final String NAME = "PDFBOX_2_BACKEND";
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(PDFBOXBackend.class);
+
+ static {
+ logger.info(" ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
+ logger.info(" + PDFBOX Backend created");
+ logger.info(" + PDFBOX Version used: " + org.apache.pdfbox.util.Version.getVersion());
+ logger.info(" ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public boolean usedAsDefault() {
+ return true;
+ }
+
+ @Override
+ public IPdfSigner getPdfSigner() {
+ return new PADESPDFBOXSigner();
+ }
+
+ @Override
+ public PlaceholderExtractor getPlaceholderExtractor() {
+ return new PDFBoxPlaceholderExtractor();
+ }
+
+ @Override
+ public VerifyBackend getVerifier() {
+ return new PDFBOXVerifier();
+ }
+
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/PDFBOXObject.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/PDFBOXObject.java
new file mode 100644
index 00000000..0eee4cd3
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/PDFBOXObject.java
@@ -0,0 +1,70 @@
+package at.gv.egiz.pdfas.lib.impl.pdfbox2;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.activation.DataSource;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+
+import at.gv.egiz.pdfas.lib.impl.status.OperationStatus;
+import at.gv.egiz.pdfas.lib.impl.status.PDFObject;
+
+public class PDFBOXObject extends PDFObject {
+
+ private PDDocument doc;
+
+ private Map<String, PDFont> fontCache = new HashMap<String, PDFont>();
+
+ public PDFBOXObject(OperationStatus operationStatus) {
+ super(operationStatus);
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ if(doc != null) {
+ doc.close();
+ }
+ }
+
+ public void close() {
+ if(doc != null) {
+ try {
+ doc.close();
+ //System.gc();
+ } catch(Throwable e) {
+ // ignore!
+ }
+ doc = null;
+ }
+ }
+
+ public void setOriginalDocument(DataSource originalDocument) throws IOException {
+ this.originalDocument = originalDocument;
+ if(doc != null) {
+ doc.close();
+ }
+ synchronized(PDDocument.class) {
+ this.doc = PDDocument.load(this.originalDocument.getInputStream());
+ }
+ if(this.doc != null) {
+ this.doc.getDocument().setWarnMissingClose(false);
+ }
+ }
+
+ public PDDocument getDocument() {
+ return this.doc;
+ }
+
+ @Override
+ public String getPDFVersion() {
+ return String.valueOf(getDocument().getDocument().getVersion());
+ }
+
+ public Map<String, PDFont> getFontCache() {
+ return fontCache;
+ }
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/PDFBoxPlaceholderExtractor.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/PDFBoxPlaceholderExtractor.java
new file mode 100644
index 00000000..730a6581
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/PDFBoxPlaceholderExtractor.java
@@ -0,0 +1,24 @@
+package at.gv.egiz.pdfas.lib.impl.pdfbox2.placeholder;
+
+import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
+import at.gv.egiz.pdfas.lib.impl.pdfbox2.PDFBOXObject;
+import at.gv.egiz.pdfas.lib.impl.placeholder.PlaceholderExtractor;
+import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData;
+import at.gv.egiz.pdfas.lib.impl.status.PDFObject;
+
+public class PDFBoxPlaceholderExtractor implements PlaceholderExtractor {
+
+ @Override
+ public SignaturePlaceholderData extract(PDFObject doc,
+ String placeholderId, int matchMode) throws PdfAsException {
+
+ if (doc instanceof PDFBOXObject) {
+ PDFBOXObject object = (PDFBOXObject) doc;
+ return SignaturePlaceholderExtractor.extract(object.getDocument(),
+ placeholderId, matchMode);
+ }
+
+ throw new PdfAsException("INVALID STATE");
+ }
+
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignaturePlaceholderExtractor.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignaturePlaceholderExtractor.java
new file mode 100644
index 00000000..39d66c3c
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignaturePlaceholderExtractor.java
@@ -0,0 +1,474 @@
+/*******************************************************************************
+ * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright>
+ * 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.
+ ******************************************************************************/
+/**
+ * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright>
+ * 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.
+ */
+package at.gv.egiz.pdfas.lib.impl.pdfbox2.placeholder;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import org.apache.pdfbox.contentstream.PDContentStream;
+import org.apache.pdfbox.contentstream.PDFStreamEngine;
+import org.apache.pdfbox.contentstream.operator.Operator;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.common.PDStream;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.font.PDFontFactory;
+import org.apache.pdfbox.pdmodel.graphics.PDXObject;
+import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
+import org.apache.pdfbox.util.Matrix;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import at.gv.egiz.pdfas.common.exceptions.PDFIOException;
+import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
+import at.gv.egiz.pdfas.common.exceptions.PlaceholderExtractionException;
+import at.gv.egiz.pdfas.lib.impl.placeholder.PlaceholderExtractorConstants;
+import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderContext;
+import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData;
+import at.knowcenter.wag.egov.egiz.pdf.TablePos;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.LuminanceSource;
+import com.google.zxing.MultiFormatReader;
+import com.google.zxing.NotFoundException;
+import com.google.zxing.ReaderException;
+import com.google.zxing.Result;
+import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
+import com.google.zxing.common.HybridBinarizer;
+
+/**
+ * Extract all relevant information from a placeholder image.
+ *
+ * @author exthex
+ *
+ */
+public class SignaturePlaceholderExtractor extends PDFStreamEngine implements PlaceholderExtractorConstants{
+ /**
+ * The log.
+ */
+ private static Logger logger = LoggerFactory
+ .getLogger(SignaturePlaceholderExtractor.class);
+
+ private List<SignaturePlaceholderData> placeholders = new Vector<SignaturePlaceholderData>();
+ private int currentPage = 0;
+ private PDDocument doc;
+
+ private SignaturePlaceholderExtractor(String placeholderId,
+ int placeholderMatchMode, PDDocument doc) throws IOException {
+ super();
+ //super(ResourceLoader.loadProperties( //TODO: pdfbox2 - properties need to be set
+ // "placeholder/pdfbox-reader.properties", true));
+ this.doc = doc;
+ }
+
+ /**
+ * Search the document for placeholder images and possibly included
+ * additional info.<br/>
+ * Searches only for the first placeholder page after page from top.
+ *
+ * @param inputStream
+ * @return all available info from the first found placeholder.
+ * @throws PDFDocumentException
+ * if the document could not be read.
+ * @throws PlaceholderExtractionException
+ * if STRICT matching mode was requested and no suitable
+ * placeholder could be found.
+ */
+ public static SignaturePlaceholderData extract(PDDocument doc,
+ String placeholderId, int matchMode) throws PdfAsException {
+ SignaturePlaceholderContext.setSignaturePlaceholderData(null);
+
+ SignaturePlaceholderExtractor extractor;
+ try {
+ extractor = new SignaturePlaceholderExtractor(placeholderId,
+ matchMode, doc);
+ } catch (IOException e2) {
+ throw new PDFIOException("error.pdf.io.04", e2);
+ }
+
+ int pageNr = 0;
+ for(PDPage page : doc.getPages()){
+ pageNr++;
+
+ try {
+ extractor.setCurrentPage(pageNr);
+ if(page.getContents() != null && page.getResources() != null && page.getContentStreams() != null) {
+ extractor.processPage(page); //TODO: pdfbox2 - right?
+
+ }
+ SignaturePlaceholderData ret = matchPlaceholderPage(
+ extractor.placeholders, placeholderId, matchMode);
+ if (ret != null) {
+ SignaturePlaceholderContext
+ .setSignaturePlaceholderData(ret);
+ return ret;
+ }
+ } catch (IOException e1) {
+ throw new PDFIOException("error.pdf.io.04", e1);
+ } catch(Throwable e) {
+ throw new PDFIOException("error.pdf.io.04", e);
+ }
+ }
+ if (extractor.placeholders.size() > 0) {
+ SignaturePlaceholderData ret = matchPlaceholderDocument(
+ extractor.placeholders, placeholderId, matchMode);
+ SignaturePlaceholderContext.setSignaturePlaceholderData(ret);
+ return ret;
+ }
+ // no placeholders found, apply strict mode if set
+ if (matchMode == PLACEHOLDER_MATCH_MODE_STRICT) {
+ throw new PlaceholderExtractionException("error.pdf.stamp.09");
+ }
+
+ return null;
+ }
+
+ private static SignaturePlaceholderData matchPlaceholderDocument(
+ List<SignaturePlaceholderData> placeholders, String placeholderId,
+ int matchMode) throws PlaceholderExtractionException {
+
+ if (matchMode == PLACEHOLDER_MATCH_MODE_STRICT)
+ throw new PlaceholderExtractionException("error.pdf.stamp.09");
+
+ if (placeholders.size() == 0)
+ return null;
+
+ if (matchMode == PLACEHOLDER_MATCH_MODE_SORTED) {
+ // sort all placeholders by the id string if all ids are null do nothing
+ SignaturePlaceholderData currentFirstSpd = null;
+ for (int i = 0; i < placeholders.size(); i++) {
+ SignaturePlaceholderData spd = placeholders.get(i);
+ if (spd.getId() != null) {
+ if(currentFirstSpd == null) {
+ currentFirstSpd = spd;
+ logger.debug("Setting new current ID: {}",
+ currentFirstSpd.getId());
+ } else {
+ String currentID = currentFirstSpd.getId();
+ String testID = spd.getId();
+ logger.debug("Testing placeholder current: {} compare to {}",
+ currentID, testID);
+ if(testID.compareToIgnoreCase(currentID) < 0) {
+ currentFirstSpd = spd;
+ logger.debug("Setting new current ID: {}",
+ testID);
+ }
+ }
+ }
+ }
+
+ if(currentFirstSpd != null) {
+ logger.info("Running Placeholder sorted mode: using id: {}", currentFirstSpd.getId());
+ return currentFirstSpd;
+ } else {
+ logger.info("Running Placeholder sorted mode: no placeholder with id found, fallback to first placeholder");
+ }
+ }
+
+ for (int i = 0; i < placeholders.size(); i++) {
+ SignaturePlaceholderData spd = placeholders.get(i);
+ if (spd.getId() == null)
+ return spd;
+ }
+
+ if (matchMode == PLACEHOLDER_MATCH_MODE_LENIENT)
+ return placeholders.get(0);
+
+ return null;
+ }
+
+ private static SignaturePlaceholderData matchPlaceholderPage(
+ List<SignaturePlaceholderData> placeholders, String placeholderId,
+ int matchMode) {
+
+ if(matchMode == PLACEHOLDER_MATCH_MODE_SORTED)
+ return null;
+
+ if (placeholders.size() == 0)
+ return null;
+ for (int i = 0; i < placeholders.size(); i++) {
+ SignaturePlaceholderData data = placeholders.get(i);
+ if (placeholderId != null && placeholderId.equals(data.getId()))
+ return data;
+ if (placeholderId == null && data.getId() == null)
+ return data;
+ }
+ return null;
+ }
+
+ private void setCurrentPage(int pageNr) {
+ this.currentPage = pageNr;
+ }
+
+ @Override
+ protected void processOperator(Operator operator, List<COSBase> arguments)
+ throws IOException {
+ String operation = operator.getName();
+ if (operation.equals("Do")) {
+ COSName objectName = (COSName) arguments.get(0);
+ PDXObject xobject = (PDXObject) getResources().getXObject(objectName);
+ if (xobject instanceof PDImageXObject) {
+ try {
+ PDImageXObject image = (PDImageXObject) xobject;
+ SignaturePlaceholderData data = checkImage(image);
+ if (data != null) {
+ PDPage page = getCurrentPage();
+ Matrix ctm = getGraphicsState()
+ .getCurrentTransformationMatrix();
+ int pageRotation = page.getRotation();
+ pageRotation = pageRotation % 360;
+ double rotationInRadians = Math.toRadians(pageRotation);//(page.findRotation() * Math.PI) / 180;
+
+ AffineTransform rotation = new AffineTransform();
+ rotation.setToRotation(rotationInRadians);
+ AffineTransform rotationInverse = rotation
+ .createInverse();
+ Matrix rotationInverseMatrix = new Matrix();
+ rotationInverseMatrix
+ .setFromAffineTransform(rotationInverse);
+ Matrix rotationMatrix = new Matrix();
+ rotationMatrix.setFromAffineTransform(rotation);
+
+ Matrix unrotatedCTM = ctm
+ .multiply(rotationInverseMatrix);
+
+ float x = unrotatedCTM.getXPosition();
+ float yPos = unrotatedCTM.getYPosition();
+ float yScale = unrotatedCTM.getScaleY();
+ float y = yPos + yScale;
+ float w = unrotatedCTM.getScaleX();;
+
+ logger.debug("Page height: {}", page.getCropBox().getHeight());
+ logger.debug("Page width: {}", page.getCropBox().getWidth());
+
+ if(pageRotation == 90) {
+ y = page.getCropBox().getWidth() - (y * (-1));
+ } else if(pageRotation == 180) {
+ x = page.getCropBox().getWidth() + x;
+ y = page.getCropBox().getHeight() - (y * (-1));
+ } else if(pageRotation == 270) {
+ x = page.getCropBox().getHeight() + x;
+ }
+
+ String posString = "p:" + currentPage + ";x:" + x
+ + ";y:" + y + ";w:" + w;
+
+ logger.debug("Found Placeholder at: {}", posString);
+ try {
+ data.setTablePos(new TablePos(posString));
+ data.setPlaceholderName(objectName.getName());
+ placeholders.add(data);
+ } catch (PdfAsException e) {
+ throw new IOException();
+ }
+ }
+ } catch (NoninvertibleTransformException e) {
+ throw new IOException(e);
+ }
+ }
+ } else {
+ super.processOperator(operator, arguments);
+ }
+ }
+
+ private Map<String, PDFont> fonts;
+
+ //TODO: pdfbox2 - was override
+ public Map<String, PDFont> getFonts() {
+ if (fonts == null)
+ {
+ // at least an empty map will be returned
+ // TODO we should return null instead of an empty map
+ fonts = new HashMap<String, PDFont>();
+ if(this.getResources() != null && this.getResources().getCOSObject() != null) {
+ COSDictionary fontsDictionary = (COSDictionary) this.getResources().getCOSObject().getDictionaryObject(COSName.FONT);
+ if (fontsDictionary == null)
+ {
+ // ignore we do not want to set anything, never when creating a signature!!!!!
+ //fontsDictionary = new COSDictionary();
+ //this.getResources().getCOSDictionary().setItem(COSName.FONT, fontsDictionary);
+ }
+ else
+ {
+ for (COSName fontName : fontsDictionary.keySet())
+ {
+ COSBase font = fontsDictionary.getDictionaryObject(fontName);
+ // data-000174.pdf contains a font that is a COSArray, looks to be an error in the
+ // PDF, we will just ignore entries that are not dictionaries.
+ if (font instanceof COSDictionary)
+ {
+ PDFont newFont = null;
+ try
+ {
+ newFont = PDFontFactory.createFont((COSDictionary) font);
+ }
+ catch (IOException exception)
+ {
+ logger.error("error while creating a font", exception);
+ }
+ if (newFont != null)
+ {
+ fonts.put(fontName.getName(), newFont);
+ }
+ }
+ }
+ }
+ }
+ }
+ return fonts;
+ }
+
+ /**
+ * Checks an image if it is a placeholder for a signature.
+ *
+ * @param image
+ * @return
+ * @throws IOException
+ */
+ private SignaturePlaceholderData checkImage(PDImageXObject image)
+ throws IOException {
+ BufferedImage bimg = image.getImage();
+ if (bimg == null) {
+ String type = image.getSuffix();
+ if (type != null) {
+ type = type.toUpperCase() + " images";
+ } else {
+ type = "Image type";
+ }
+ logger.info("Unable to extract image for QRCode analysis. "
+ + type
+ + " not supported. Add additional JAI Image filters to your classpath. Refer to https://jai.dev.java.net. Skipping image.");
+ return null;
+ }
+ if (bimg.getHeight() < 10 || bimg.getWidth() < 10) {
+ logger.debug("Image too small for QRCode. Skipping image.");
+ return null;
+ }
+
+ LuminanceSource source = new BufferedImageLuminanceSource(bimg);
+ BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
+ Result result;
+ long before = System.currentTimeMillis();
+ try {
+ Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>();
+ Vector<BarcodeFormat> formats = new Vector<BarcodeFormat>();
+ formats.add(BarcodeFormat.QR_CODE);
+ hints.put(DecodeHintType.POSSIBLE_FORMATS, formats);
+ result = new MultiFormatReader().decode(bitmap, hints);
+
+ String text = result.getText();
+ String profile = null;
+ String type = null;
+ String sigKey = null;
+ String id = null;
+ if (text != null) {
+ if (text.startsWith(QR_PLACEHOLDER_IDENTIFIER)) {
+ String[] data = text.split(";");
+ if (data.length > 1) {
+ for (int i = 1; i < data.length; i++) {
+ String kvPair = data[i];
+ String[] kv = kvPair.split("=");
+ if (kv.length != 2) {
+ logger.debug("Invalid parameter in placeholder data: "
+ + kvPair);
+ } else {
+ if (kv[0]
+ .equalsIgnoreCase(SignaturePlaceholderData.ID_KEY)) {
+ id = kv[1];
+ } else if (kv[0]
+ .equalsIgnoreCase(SignaturePlaceholderData.PROFILE_KEY)) {
+ profile = kv[1];
+ } else if (kv[0]
+ .equalsIgnoreCase(SignaturePlaceholderData.SIG_KEY_KEY)) {
+ sigKey = kv[1];
+ } else if (kv[0]
+ .equalsIgnoreCase(SignaturePlaceholderData.TYPE_KEY)) {
+ type = kv[1];
+ }
+ }
+ }
+ }
+ return new SignaturePlaceholderData(profile, type, sigKey,
+ id);
+ } else {
+ logger.warn("QR-Code found but does not start with \""
+ + QR_PLACEHOLDER_IDENTIFIER
+ + "\". Ignoring QR placeholder.");
+ }
+ }
+ } catch (ReaderException re) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Could not decode - not a placeholder. needed: "
+ + (System.currentTimeMillis() - before));
+ }
+ if (!(re instanceof NotFoundException)) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Failed to decode image", re);
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Failed to decode image. Probably a zxing bug", e);
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/positioning/Positioning.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/positioning/Positioning.java
new file mode 100644
index 00000000..429fe518
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/positioning/Positioning.java
@@ -0,0 +1,308 @@
+/*******************************************************************************
+ * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright>
+ * 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.
+ ******************************************************************************/
+package at.gv.egiz.pdfas.lib.impl.pdfbox2.positioning;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
+import at.gv.egiz.pdfas.lib.impl.pdfbox2.utils.PdfBoxUtils;
+import at.gv.egiz.pdfas.lib.impl.stamping.IPDFVisualObject;
+import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction;
+import at.knowcenter.wag.egov.egiz.pdf.TablePos;
+import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFUtilities;
+
+/**
+ * Created with IntelliJ IDEA. User: afitzek Date: 8/29/13 Time: 4:30 PM To
+ * change this template use File | Settings | File Templates.
+ */
+public class Positioning {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(Positioning.class);
+
+ /**
+ * The left/right margin.
+ */
+ public static final float SIGNATURE_MARGIN_HORIZONTAL = 50f;
+
+ /**
+ * The top/bottom margin.
+ */
+ public static final float SIGNATURE_MARGIN_VERTICAL = 20f;
+
+ /**
+ * Evalutates absolute positioning and prepares the PositioningInstruction
+ * for placing the table.
+ *
+ * @param pos
+ * The absolute positioning parameter. If null it is sought in
+ * the profile definition.
+ * @param signature_type
+ * The profile definition of the table to be written.
+ * @param pdfDataSource
+ * The pdf.
+ * @param pdf_table
+ * The pdf table to be written.
+ * @return Returns the PositioningInformation.
+ * @throws PdfAsException
+ * F.e.
+ */
+ public static PositioningInstruction determineTablePositioning(
+ TablePos pos, String signature_type, PDDocument pdfDataSource,
+ IPDFVisualObject pdf_table, boolean legacy32, boolean legacy40) throws PdfAsException {
+ return adjustSignatureTableandCalculatePosition(pdfDataSource,
+ pdf_table, pos, legacy32, legacy40);
+ }
+
+ private static PDRectangle rotateBox(PDRectangle cropBox, int rotation) {
+ if (rotation != 0) {
+ Point2D upSrc = new Point2D.Float();
+
+ upSrc.setLocation(cropBox.getUpperRightX(),
+ cropBox.getUpperRightY());
+
+ Point2D llSrc = new Point2D.Float();
+ llSrc.setLocation(cropBox.getLowerLeftX(), cropBox.getLowerLeftY());
+ AffineTransform transform = new AffineTransform();
+ transform.setToIdentity();
+ if (rotation % 360 != 0) {
+ transform.setToRotation(Math.toRadians(rotation * -1), llSrc.getX(),
+ llSrc.getY());
+ }
+ Point2D upDst = new Point2D.Float();
+ transform.transform(upSrc, upDst);
+
+ Point2D llDst = new Point2D.Float();
+ transform.transform(llSrc, llDst);
+
+ float y1 = (float) upDst.getY();
+ float y2 = (float) llDst.getY();
+
+ if(y1 > y2) {
+ float t = y1;
+ y1 = y2;
+ y2 = t;
+ }
+
+ if(y1 < 0) {
+ y2 = y2 + -1 * y1;
+ y1 = 0;
+ }
+
+ float x1 = (float) upDst.getX();
+ float x2 = (float) llDst.getX();
+
+ if(x1 > x2) {
+ float t = x1;
+ x1 = x2;
+ x2 = t;
+ }
+
+ if(x1 < 0) {
+ x2 = x2 + -1 * x1;
+ x1 = 0;
+ }
+
+ cropBox.setUpperRightX(x2);
+ cropBox.setUpperRightY(y2);
+ cropBox.setLowerLeftY(y1);
+ cropBox.setLowerLeftX(x1);
+ }
+ return cropBox;
+ }
+
+ /**
+ * Sets the width of the table according to the layout of the document and
+ * calculates the y position where the PDFPTable should be placed.
+ *
+ * @param pdfDataSource
+ * The PDF document.
+ * @param pdf_table
+ * The PDFPTable to be placed.
+ * @return Returns the position where the PDFPTable should be placed.
+ * @throws PdfAsException
+ * F.e.
+ */
+ public static PositioningInstruction adjustSignatureTableandCalculatePosition(
+ final PDDocument pdfDataSource, IPDFVisualObject pdf_table,
+ TablePos pos, boolean legacy32, boolean legacy40) throws PdfAsException {
+
+ PdfBoxUtils.checkPDFPermissions(pdfDataSource);
+ // get pages of currentdocument
+
+ int doc_pages = pdfDataSource.getNumberOfPages();
+ int page = doc_pages;
+ boolean make_new_page = pos.isNewPage();
+ if (!(pos.isNewPage() || pos.isPauto())) {
+ // we should posit signaturtable on this page
+
+ page = pos.getPage();
+ // System.out.println("XXXXPAGE="+page+" doc_pages="+doc_pages);
+ if (page > doc_pages) {
+ make_new_page = true;
+ page = doc_pages;
+ // throw new PDFDocumentException(227, "Page number is to big(="
+ // + page+
+ // ") cannot be parsed.");
+ }
+ }
+
+ PDPage pdPage = pdfDataSource.getPage(page-1);
+
+ PDRectangle cropBox = pdPage.getCropBox();
+
+ // fallback to MediaBox if Cropbox not available!
+
+ if (cropBox == null) {
+ cropBox = pdPage.getCropBox();
+ }
+
+ if (cropBox == null) {
+ cropBox = pdPage.getCropBox();
+ }
+
+ // getPagedimensions
+ // Rectangle psize = reader.getPageSizeWithRotation(page);
+ // int page_rotation = reader.getPageRotation(page);
+
+ // Integer rotation = pdPage.getRotation();
+ // int page_rotation = rotation.intValue();
+
+ int rotation = pdPage.getRotation();
+
+ logger.debug("Original CropBox: " + cropBox.toString());
+
+ cropBox = rotateBox(cropBox, rotation);
+
+ logger.debug("Rotated CropBox: " + cropBox.toString());
+
+ float page_width = cropBox.getWidth();
+ float page_height = cropBox.getHeight();
+
+ logger.debug("CropBox width: " + page_width);
+ logger.debug("CropBox heigth: " + page_height);
+
+ // now we can calculate x-position
+ float pre_pos_x = SIGNATURE_MARGIN_HORIZONTAL;
+ if (!pos.isXauto()) {
+ // we do have absolute x
+ pre_pos_x = pos.getPosX();
+ }
+ // calculate width
+ // center
+ float pre_width = page_width - 2 * pre_pos_x;
+ if (!pos.isWauto()) {
+ // we do have absolute width
+ pre_width = pos.getWidth();
+ if (pos.isXauto()) { // center x
+ pre_pos_x = (page_width - pre_width) / 2;
+ }
+ }
+ final float pos_x = pre_pos_x;
+ final float width = pre_width;
+ // Signatur table dimensions are complete
+ pdf_table.setWidth(width);
+ pdf_table.fixWidth();
+ // pdf_table.setTotalWidth(width);
+ // pdf_table.setLockedWidth(true);
+
+ final float table_height = pdf_table.getHeight();
+ // now check pos_y
+ float pos_y = pos.getPosY();
+
+ // in case an absolute y position is already given OR
+ // if the table is related to an invisible signature
+ // there is no need for further calculations
+ // (fixed adding new page in case of invisible signatures)
+ if (!pos.isYauto() || table_height == 0) {
+ // we do have y-position too --> all parameters but page ok
+ if (make_new_page) {
+ page++;
+ }
+ return new PositioningInstruction(make_new_page, page, pos_x,
+ pos_y, pos.rotation);
+ }
+ // pos_y is auto
+ if (make_new_page) {
+ // ignore footer in new page
+ page++;
+ pos_y = page_height - SIGNATURE_MARGIN_VERTICAL;
+ return new PositioningInstruction(make_new_page, page, pos_x,
+ pos_y, pos.rotation);
+ }
+ // up to here no checks have to be made if Tablesize and Pagesize are
+ // fit
+ // Now we have to getfreespace in page and reguard footerline
+ float footer_line = pos.getFooterLine();
+
+ float pre_page_length = PDFUtilities.calculatePageLength(pdfDataSource,
+ page - 1, page_height - footer_line, /* page_rotation, */
+ legacy32, legacy40);
+
+ if (pre_page_length == Float.NEGATIVE_INFINITY) {
+ // we do have an empty page or nothing in area above footerline
+ pre_page_length = page_height;
+ // no text --> SIGNATURE_BORDER
+ pos_y = page_height - SIGNATURE_MARGIN_VERTICAL;
+ if (pos_y - footer_line <= table_height) {
+ make_new_page = true;
+ if (!pos.isPauto()) {
+ // we have to correct pagenumber
+ page = pdfDataSource.getNumberOfPages();
+ }
+ page++;
+ // no text --> SIGNATURE_BORDER
+ pos_y = page_height - SIGNATURE_MARGIN_VERTICAL;
+ }
+ return new PositioningInstruction(make_new_page, page, pos_x,
+ pos_y, pos.rotation);
+ }
+ final float page_length = pre_page_length;
+ // we do have text take SIGNATURE_MARGIN
+ pos_y = page_height - page_length - SIGNATURE_MARGIN_VERTICAL;
+ if (pos_y - footer_line <= table_height) {
+ make_new_page = true;
+ if (!pos.isPauto()) {
+ // we have to correct pagenumber in case of absolute page and
+ // not enough
+ // space
+ page = pdfDataSource.getNumberOfPages();
+ }
+ page++;
+ // no text --> SIGNATURE_BORDER
+ pos_y = page_height - SIGNATURE_MARGIN_VERTICAL;
+ }
+ return new PositioningInstruction(make_new_page, page, pos_x, pos_y,
+ pos.rotation);
+
+ }
+
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/utils/PdfBoxUtils.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/utils/PdfBoxUtils.java
new file mode 100644
index 00000000..85073568
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/utils/PdfBoxUtils.java
@@ -0,0 +1,73 @@
+package at.gv.egiz.pdfas.lib.impl.pdfbox2.utils;
+
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import at.gv.egiz.pdfas.common.exceptions.PdfAsValidationException;
+
+public class PdfBoxUtils {
+ private static final Logger logger = LoggerFactory
+ .getLogger(PdfBoxUtils.class);
+
+ public static void checkPDFPermissions(PDDocument doc)
+ throws PdfAsValidationException {
+
+ AccessPermission accessPermission = doc.getCurrentAccessPermission();
+ if (doc.isEncrypted()) {
+ throw new PdfAsValidationException("error.pdf.sig.12", null);
+ }
+
+ if (!accessPermission.isOwnerPermission()) {
+ throw new PdfAsValidationException("error.pdf.sig.12", null);
+ }
+
+ }
+
+ public static int countSignatures(PDDocument doc, String sigName) {
+ int count = 0;
+ COSDictionary trailer = doc.getDocument().getTrailer();
+ COSDictionary root = (COSDictionary) trailer
+ .getDictionaryObject(COSName.ROOT);
+ COSDictionary acroForm = (COSDictionary) root
+ .getDictionaryObject(COSName.ACRO_FORM);
+ COSArray fields = (COSArray) acroForm
+ .getDictionaryObject(COSName.FIELDS);
+ for (int i = 0; i < fields.size(); i++) {
+ COSDictionary field = (COSDictionary) fields.getObject(i);
+ String type = field.getNameAsString("FT");
+ if ("Sig".equals(type)) {
+ String name = field.getString(COSName.T);
+ if (name != null) {
+ logger.debug("Found Sig: " + name);
+ try {
+ if (name.startsWith(sigName)) {
+ String numberString = name.replace(sigName, "");
+
+ logger.debug("Found Number: " + numberString);
+
+ int SigIDX = Integer.parseInt(numberString);
+ if (SigIDX > count) {
+ count = SigIDX;
+ }
+ }
+ } catch (Throwable e) {
+ logger.info("Found a different Signature, we do not need to count this.");
+ }
+ }
+ }
+
+ }
+
+ count++;
+
+ logger.debug("Returning sig number: " + count);
+
+ return count;
+ }
+
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PADESPDFBOXSigner.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PADESPDFBOXSigner.java
new file mode 100644
index 00000000..d105174b
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PADESPDFBOXSigner.java
@@ -0,0 +1,796 @@
+/*******************************************************************************
+ * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright>
+ * 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.
+ ******************************************************************************/
+package at.gv.egiz.pdfas.lib.impl.signing.pdfbox2;
+
+import iaik.x509.X509Certificate;
+
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSDocument;
+import org.apache.pdfbox.cos.COSInteger;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSString;
+import org.apache.pdfbox.pdfwriter.COSWriter;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.common.PDNumberTreeNode;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureElement;
+import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureTreeRoot;
+import org.apache.pdfbox.pdmodel.graphics.color.PDOutputIntent;
+import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions;
+import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
+import org.apache.pdfbox.pdmodel.interactive.form.PDField;
+import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
+import org.apache.pdfbox.rendering.ImageType;
+import org.apache.pdfbox.rendering.PDFRenderer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import at.gv.egiz.pdfas.common.exceptions.PDFASError;
+import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
+import at.gv.egiz.pdfas.common.messages.MessageResolver;
+import at.gv.egiz.pdfas.common.settings.SignatureProfileSettings;
+import at.gv.egiz.pdfas.common.utils.StreamUtils;
+import at.gv.egiz.pdfas.common.utils.TempFileHelper;
+import at.gv.egiz.pdfas.lib.api.ByteArrayDataSource;
+import at.gv.egiz.pdfas.lib.api.IConfigurationConstants;
+import at.gv.egiz.pdfas.lib.api.sign.IPlainSigner;
+import at.gv.egiz.pdfas.lib.api.sign.SignParameter;
+import at.gv.egiz.pdfas.lib.impl.ErrorExtractor;
+import at.gv.egiz.pdfas.lib.impl.SignaturePositionImpl;
+import at.gv.egiz.pdfas.lib.impl.configuration.SignatureProfileConfiguration;
+import at.gv.egiz.pdfas.lib.impl.pdfbox2.PDFBOXObject;
+import at.gv.egiz.pdfas.lib.impl.pdfbox2.positioning.Positioning;
+import at.gv.egiz.pdfas.lib.impl.pdfbox2.utils.PdfBoxUtils;
+import at.gv.egiz.pdfas.lib.impl.placeholder.PlaceholderFilter;
+import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData;
+import at.gv.egiz.pdfas.lib.impl.signing.IPdfSigner;
+import at.gv.egiz.pdfas.lib.impl.signing.PDFASSignatureExtractor;
+import at.gv.egiz.pdfas.lib.impl.signing.PDFASSignatureInterface;
+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.stamping.TableFactory;
+import at.gv.egiz.pdfas.lib.impl.stamping.ValueResolver;
+import at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2.PDFAsVisualSignatureProperties;
+import at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2.PdfBoxVisualObject;
+import at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2.StamperFactory;
+import at.gv.egiz.pdfas.lib.impl.status.OperationStatus;
+import at.gv.egiz.pdfas.lib.impl.status.PDFObject;
+import at.gv.egiz.pdfas.lib.impl.status.RequestedSignature;
+import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction;
+import at.knowcenter.wag.egov.egiz.pdf.TablePos;
+import at.knowcenter.wag.egov.egiz.table.Table;
+
+public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
+
+ private static final Logger logger = LoggerFactory.getLogger(PADESPDFBOXSigner.class);
+
+ public void signPDF(PDFObject genericPdfObject, RequestedSignature requestedSignature,
+ PDFASSignatureInterface genericSigner) throws PdfAsException {
+ //String fisTmpFile = null;
+
+ PDFAsVisualSignatureProperties properties = null;
+
+ if (!(genericPdfObject instanceof PDFBOXObject)) {
+ // tODO:
+ throw new PdfAsException();
+ }
+
+ PDFBOXObject pdfObject = (PDFBOXObject) genericPdfObject;
+
+ if (!(genericSigner instanceof PDFASPDFBOXSignatureInterface)) {
+ // tODO:
+ throw new PdfAsException();
+ }
+
+ PDFASPDFBOXSignatureInterface signer = (PDFASPDFBOXSignatureInterface) genericSigner;
+
+ PDDocument doc = null;
+ SignatureOptions options = new SignatureOptions();
+ COSDocument visualSignatureDocumentGuard = null;
+ try {
+
+
+ doc = pdfObject.getDocument();
+
+ SignaturePlaceholderData signaturePlaceholderData = PlaceholderFilter
+ .checkPlaceholderSignature(pdfObject.getStatus(), pdfObject.getStatus().getSettings());
+
+ TablePos tablePos = null;
+
+ if (signaturePlaceholderData != null) {
+ // Placeholder found!
+ logger.info("Placeholder data found.");
+ if (signaturePlaceholderData.getProfile() != null) {
+ logger.debug("Placeholder Profile set to: " + signaturePlaceholderData.getProfile());
+ requestedSignature.setSignatureProfileID(signaturePlaceholderData.getProfile());
+ }
+
+ tablePos = signaturePlaceholderData.getTablePos();
+ if (tablePos != null) {
+
+ SignatureProfileConfiguration signatureProfileConfiguration = pdfObject.getStatus()
+ .getSignatureProfileConfiguration(requestedSignature.getSignatureProfileID());
+
+ float minWidth = signatureProfileConfiguration.getMinWidth();
+
+ if(minWidth > 0) {
+ if (tablePos.getWidth() < minWidth) {
+ tablePos.width = minWidth;
+ logger.debug("Correcting placeholder with to minimum width {}", minWidth);
+ }
+ }
+ logger.debug("Placeholder Position set to: " + tablePos.toString());
+ }
+ }
+
+ PDSignature signature = new PDSignature();
+ signature.setFilter(COSName.getPDFName(signer.getPDFFilter())); // default
+ // filter
+ signature.setSubFilter(COSName.getPDFName(signer.getPDFSubFilter()));
+
+ SignatureProfileSettings signatureProfileSettings = TableFactory
+ .createProfile(requestedSignature.getSignatureProfileID(), pdfObject.getStatus().getSettings());
+
+ ValueResolver resolver = new ValueResolver(requestedSignature, pdfObject.getStatus());
+ String signerName = resolver.resolve("SIG_SUBJECT", signatureProfileSettings.getValue("SIG_SUBJECT"),
+ signatureProfileSettings);
+
+ signature.setName(signerName);
+ signature.setSignDate(Calendar.getInstance());
+ String signerReason = signatureProfileSettings.getSigningReason();
+
+ if (signerReason == null) {
+ signerReason = "PAdES Signature";
+ }
+
+ signature.setReason(signerReason);
+ logger.debug("Signing reason: " + signerReason);
+
+ logger.debug("Signing @ " + signer.getSigningDate().getTime().toString());
+ // the signing date, needed for valid signature
+ // signature.setSignDate(signer.getSigningDate());
+
+ signer.setPDSignature(signature);
+
+ int signatureSize = 0x1000;
+ try {
+ String reservedSignatureSizeString = signatureProfileSettings.getValue(SIG_RESERVED_SIZE);
+ if (reservedSignatureSizeString != null) {
+ signatureSize = Integer.parseInt(reservedSignatureSizeString);
+ }
+ logger.debug("Reserving {} bytes for signature", signatureSize);
+ } catch (NumberFormatException e) {
+ logger.warn("Invalid configuration value: {} should be a number using 0x1000", SIG_RESERVED_SIZE);
+ }
+ options.setPreferredSignatureSize(signatureSize);
+
+ // Is visible Signature
+ if (requestedSignature.isVisual()) {
+ logger.info("Creating visual siganture block");
+
+ SignatureProfileConfiguration signatureProfileConfiguration = pdfObject.getStatus()
+ .getSignatureProfileConfiguration(requestedSignature.getSignatureProfileID());
+
+ if (tablePos == null) {
+ // ================================================================
+ // PositioningStage (visual) -> find position or use
+ // fixed
+ // position
+
+ String posString = pdfObject.getStatus().getSignParamter().getSignaturePosition();
+
+ TablePos signaturePos = null;
+
+ String signaturePosString = signatureProfileConfiguration.getDefaultPositioning();
+
+ if (signaturePosString != null) {
+ logger.debug("using signature Positioning: " + signaturePos);
+ signaturePos = new TablePos(signaturePosString);
+ }
+
+ logger.debug("using Positioning: " + posString);
+
+ if (posString != null) {
+ // Merge Signature Position
+ tablePos = new TablePos(posString, signaturePos);
+ } else {
+ // Fallback to signature Position!
+ tablePos = signaturePos;
+ }
+
+ if (tablePos == null) {
+ // Last Fallback default position
+ tablePos = new TablePos();
+ }
+ }
+
+ boolean legacy32Position = signatureProfileConfiguration.getLegacy32Positioning();
+ boolean legacy40Position = signatureProfileConfiguration.getLegacy40Positioning();
+
+ // create Table describtion
+ Table main = TableFactory.createSigTable(signatureProfileSettings, MAIN, pdfObject.getStatus(),
+ requestedSignature);
+
+ IPDFStamper stamper = StamperFactory.createDefaultStamper(pdfObject.getStatus().getSettings());
+
+ IPDFVisualObject visualObject = stamper.createVisualPDFObject(pdfObject, main);
+
+ /*
+ * PDDocument originalDocument = PDDocument .load(new
+ * ByteArrayInputStream(pdfObject.getStatus()
+ * .getPdfObject().getOriginalDocument()));
+ */
+
+ PositioningInstruction positioningInstruction = Positioning.determineTablePositioning(tablePos, "",
+ doc, visualObject, legacy32Position, legacy40Position);
+
+ logger.debug("Positioning: {}", positioningInstruction.toString());
+
+ if (positioningInstruction.isMakeNewPage()) {
+ int last = doc.getNumberOfPages() - 1;
+ PDDocumentCatalog root = doc.getDocumentCatalog();
+ PDPage lastPage = root.getPages().get(last);
+ root.getPages().getCOSObject().setNeedToBeUpdated(true);
+ PDPage p = new PDPage(lastPage.getMediaBox());
+ p.setResources(new PDResources());
+ p.setRotation(lastPage.getRotation());
+ doc.addPage(p);
+ }
+
+ // handle rotated page
+ int targetPageNumber = positioningInstruction.getPage();
+ logger.debug("Target Page: " + targetPageNumber);
+ PDPage targetPage = doc.getPages().get(targetPageNumber - 1);
+ int rot = targetPage.getRotation();
+ logger.debug("Page rotation: " + rot);
+ // positioningInstruction.setRotation(positioningInstruction.getRotation()
+ // + rot);
+ logger.debug("resulting Sign rotation: " + positioningInstruction.getRotation());
+
+ SignaturePositionImpl position = new SignaturePositionImpl();
+ position.setX(positioningInstruction.getX());
+ position.setY(positioningInstruction.getY());
+ position.setPage(positioningInstruction.getPage());
+ position.setHeight(visualObject.getHeight());
+ position.setWidth(visualObject.getWidth());
+
+ requestedSignature.setSignaturePosition(position);
+
+ properties = new PDFAsVisualSignatureProperties(pdfObject.getStatus().getSettings(), pdfObject,
+ (PdfBoxVisualObject) visualObject, positioningInstruction, signatureProfileSettings);
+
+ properties.buildSignature();
+
+ /*
+ * ByteArrayOutputStream sigbos = new
+ * ByteArrayOutputStream();
+ * sigbos.write(StreamUtils.inputStreamToByteArray
+ * (properties .getVisibleSignature())); sigbos.close();
+ */
+
+ if (signaturePlaceholderData != null) {
+ // Placeholder found!
+ // replace placeholder
+
+ PDImageXObject img = PDImageXObject.createFromFile("/placeholder/empty.jpg", doc);
+
+ img.getCOSObject().setNeedToBeUpdated(true);
+ // PDDocumentCatalog root = doc.getDocumentCatalog();
+ // PDPageNode rootPages = root.getPages();
+ // List<PDPage> kids = new ArrayList<PDPage>();
+ // rootPages.getAllKids(kids);
+ int pageNumber = positioningInstruction.getPage();
+ PDPage page = doc.getPages().get(pageNumber - 1);
+
+ logger.info("Placeholder name: " + signaturePlaceholderData.getPlaceholderName());
+ COSDictionary xobjectsDictionary = (COSDictionary) page.getResources().getCOSObject()
+ .getDictionaryObject(COSName.XOBJECT);
+ xobjectsDictionary.setItem(signaturePlaceholderData.getPlaceholderName(), img);
+ xobjectsDictionary.setNeedToBeUpdated(true);
+ page.getResources().getCOSObject().setNeedToBeUpdated(true);
+ logger.info("Placeholder name: " + signaturePlaceholderData.getPlaceholderName());
+
+ }
+
+ if (signatureProfileSettings.isPDFA()) {
+ PDDocumentCatalog root = doc.getDocumentCatalog();
+ COSBase base = root.getCOSObject().getItem(COSName.OUTPUT_INTENTS);
+ if (base == null) {
+ InputStream colorProfile = null;
+ try {
+ colorProfile = PDDocumentCatalog.class
+ .getResourceAsStream("/icm/sRGB Color Space Profile.icm");
+
+ try {
+ PDOutputIntent oi = new PDOutputIntent(doc, colorProfile);
+ oi.setInfo("sRGB IEC61966-2.1");
+ oi.setOutputCondition("sRGB IEC61966-2.1");
+ oi.setOutputConditionIdentifier("sRGB IEC61966-2.1");
+ oi.setRegistryName("http://www.color.org");
+
+ root.addOutputIntent(oi);
+ root.getCOSObject().setNeedToBeUpdated(true);
+ logger.info("added Output Intent");
+ } catch (Throwable e) {
+ e.printStackTrace();
+ throw new PdfAsException("Failed to add Output Intent", e);
+ }
+ } finally {
+ IOUtils.closeQuietly(colorProfile);
+ }
+ }
+ }
+
+ options.setPage(positioningInstruction.getPage());
+
+ options.setVisualSignature(properties.getVisibleSignature());
+ }
+
+ visualSignatureDocumentGuard = options.getVisualSignature();
+
+ doc.addSignature(signature, signer, options);
+
+ String sigFieldName = signatureProfileSettings.getSignFieldValue();
+
+ if (sigFieldName == null) {
+ sigFieldName = "PDF-AS Signatur";
+ }
+
+ int count = PdfBoxUtils.countSignatures(doc, sigFieldName);
+
+ sigFieldName = sigFieldName + count;
+
+ PDAcroForm acroFormm = doc.getDocumentCatalog().getAcroForm();
+
+ // PDStructureTreeRoot pdstRoot =
+ // doc.getDocumentCatalog().getStructureTreeRoot();
+ // COSDictionary dic =
+ // doc.getDocumentCatalog().getCOSDictionary();
+ // PDStructureElement el = new PDStructureElement("Widget",
+ // pdstRoot);
+
+ PDSignatureField signatureField = null;
+ if (acroFormm != null) {
+ @SuppressWarnings("unchecked")
+ List<PDField> fields = acroFormm.getFields();
+
+ if (fields != null) {
+ for (PDField pdField : fields) {
+ if (pdField != null) {
+ if (pdField instanceof PDSignatureField) {
+ PDSignatureField tmpSigField = (PDSignatureField) pdField;
+
+ if (tmpSigField.getSignature() != null
+ && tmpSigField.getSignature().getCOSObject() != null) {
+ if (tmpSigField.getSignature().getCOSObject()
+ .equals(signature.getCOSObject())) {
+ signatureField = (PDSignatureField) pdField;
+
+ }
+ }
+ }
+ }
+ }
+ } else {
+ logger.warn("Failed to name Signature Field! [Cannot find Field list in acroForm!]");
+ }
+
+ if (signatureField != null) {
+ signatureField.setPartialName(sigFieldName);
+ }
+ if (properties != null) {
+ signatureField.setAlternateFieldName(properties.getAlternativeTableCaption());
+ } else {
+ signatureField.setAlternateFieldName(sigFieldName);
+ }
+ } else {
+ logger.warn("Failed to name Signature Field! [Cannot find acroForm!]");
+ }
+
+ // PDF-UA
+ logger.info("Adding pdf/ua content.");
+ try {
+ if(true)
+ throw new Exception("skip");
+ PDDocumentCatalog root = doc.getDocumentCatalog();
+ PDStructureTreeRoot structureTreeRoot = root.getStructureTreeRoot();
+ if (structureTreeRoot != null) {
+ logger.info("Tree Root: {}", structureTreeRoot.toString());
+ List<Object> kids = structureTreeRoot.getKids();
+
+ if (kids == null) {
+ logger.info("No kid-elements in structure tree Root, maybe not PDF/UA document");
+ }
+
+ PDStructureElement docElement = null;
+ for (Object k : kids) {
+ if (k instanceof PDStructureElement) {
+ docElement = (PDStructureElement) k;
+ break;
+
+ }
+ }
+
+ PDStructureElement sigBlock = new PDStructureElement("Form", docElement);
+
+ // create object dictionary and add as child element
+ COSDictionary objectDic = new COSDictionary();
+ objectDic.setName("Type", "OBJR");
+ objectDic.setItem("Pg", signatureField.getWidget().getPage());
+ objectDic.setItem("Obj", signatureField.getWidget());
+
+ List<Object> l = new ArrayList<Object>();
+ l.add(objectDic);
+ sigBlock.setKids(l);
+ sigBlock.setPage(signatureField.getWidget().getPage());
+
+
+ sigBlock.setTitle("Signature Table");
+ sigBlock.setParent(docElement);
+ docElement.appendKid(sigBlock);
+
+ // Create and add Attribute dictionary to mitigate PAC
+ // warning
+ COSDictionary sigBlockDic = (COSDictionary) sigBlock.getCOSObject();
+ COSDictionary sub = new COSDictionary();
+
+ sub.setName("O", "Layout");
+ sub.setName("Placement", "Block");
+ sigBlockDic.setItem(COSName.A, sub);
+ sigBlockDic.setNeedToBeUpdated(true);
+
+ // Modify number tree
+ PDNumberTreeNode ntn = structureTreeRoot.getParentTree();
+ int parentTreeNextKey = structureTreeRoot.getParentTreeNextKey();
+ if (ntn == null) {
+ ntn = new PDNumberTreeNode(objectDic, null);
+ logger.info("No number-tree-node found!");
+ }
+
+ COSArray ntnKids = (COSArray) ntn.getCOSObject().getDictionaryObject(COSName.KIDS);
+ COSArray ntnNumbers = (COSArray) ntn.getCOSObject().getDictionaryObject(COSName.NUMS);
+
+ if(ntnNumbers == null && ntnKids != null){//no number array, so continue with the kids array
+
+ //create dictionary with limits and nums array
+ COSDictionary pTreeEntry = new COSDictionary();
+ COSArray limitsArray = new COSArray();
+ //limits for exact one entry
+ limitsArray.add(COSInteger.get(parentTreeNextKey));
+ limitsArray.add(COSInteger.get(parentTreeNextKey));
+
+ COSArray numsArray = new COSArray();
+ numsArray.add(COSInteger.get(parentTreeNextKey));
+ numsArray.add(sigBlock);
+
+ pTreeEntry.setItem(COSName.NUMS, numsArray);
+ pTreeEntry.setItem(COSName.LIMITS, limitsArray);
+
+ PDNumberTreeNode newKidsElement = new PDNumberTreeNode(pTreeEntry, PDNumberTreeNode.class);
+
+ ntnKids.add(newKidsElement);
+ ntnKids.setNeedToBeUpdated(true);
+
+
+ }else if(ntnNumbers != null && ntnKids == null){
+
+ int arrindex = ntnNumbers.size();
+
+ ntnNumbers.add(arrindex, COSInteger.get(parentTreeNextKey));
+ ntnNumbers.add(arrindex + 1, sigBlock.getCOSObject());
+
+ ntnNumbers.setNeedToBeUpdated(true);
+
+ structureTreeRoot.setParentTree(ntn);
+
+ }else if(ntnNumbers == null && ntnKids == null){
+ //document is not pdfua conform before signature creation
+ throw new PdfAsException("error.pdf.sig.pdfua.1");
+ }else{
+ //this is not allowed
+ throw new PdfAsException("error.pdf.sig.pdfua.1");
+ }
+
+ // set StructureParent for signature field annotation
+ signatureField.getWidget().setStructParent(parentTreeNextKey);
+
+ //Increase the next Key value in the structure tree root
+ structureTreeRoot.setParentTreeNextKey(parentTreeNextKey+1);
+
+ // add the Tabs /S Element for Tabbing through annots
+ PDPage p = signatureField.getWidget().getPage();
+ p.getCOSObject().setName("Tabs", "S");
+ p.getCOSObject().setNeedToBeUpdated(true);
+
+ //check alternative signature field name
+ if (signatureField != null) {
+ if(signatureField.getAlternateFieldName().equals(""))
+ signatureField.setAlternateFieldName(sigFieldName);
+ }
+
+
+ ntn.getCOSObject().setNeedToBeUpdated(true);
+ sigBlock.getCOSObject().setNeedToBeUpdated(true);
+ structureTreeRoot.getCOSObject().setNeedToBeUpdated(true);
+ objectDic.setNeedToBeUpdated(true);
+ docElement.getCOSObject().setNeedToBeUpdated(true);
+
+ }
+ } catch (Throwable e) {
+ if (signatureProfileSettings.isPDFUA() == true) {
+ logger.error("Could not create PDF-UA conform document!");
+ throw new PdfAsException("error.pdf.sig.pdfua.1", e);
+ } else {
+ logger.info("Could not create PDF-UA conform signature");
+ }
+ }
+
+ if (requestedSignature.isVisual()) {
+
+ // if(requestedSignature.getSignaturePosition().)
+ /*
+ * PDAcroForm acroForm =
+ * doc.getDocumentCatalog().getAcroForm(); if (acroForm !=
+ * null) {
+ *
+ * @SuppressWarnings("unchecked") List<PDField> fields =
+ * acroForm.getFields(); PDSignatureField signatureField =
+ * null;
+ *
+ * if (fields != null) { for (PDField pdField : fields) { if
+ * (pdField instanceof PDSignatureField) { if
+ * (((PDSignatureField) pdField).getSignature()
+ * .getDictionary() .equals(signature.getDictionary())) {
+ * signatureField = (PDSignatureField) pdField; } } } } else
+ * { logger.warn(
+ * "Failed to apply rotation! [Cannot find Field list in acroForm!]"
+ * ); }
+ *
+ * if (signatureField != null) { if
+ * (signatureField.getWidget() != null) { if
+ * (signatureField.getWidget()
+ * .getAppearanceCharacteristics() == null) {
+ * PDAppearanceCharacteristicsDictionary dict = new
+ * PDAppearanceCharacteristicsDictionary( new
+ * COSDictionary()); signatureField.getWidget()
+ * .setAppearanceCharacteristics(dict); }
+ *
+ * if (signatureField.getWidget()
+ * .getAppearanceCharacteristics() != null) {
+ * signatureField.getWidget()
+ * .getAppearanceCharacteristics() .setRotation(90); } } }
+ * else { logger.warn(
+ * "Failed to apply rotation! [Cannot find signature Field!]"
+ * ); } } else { logger.warn(
+ * "Failed to apply rotation! [Cannot find acroForm!]" ); }
+ */
+ }
+
+
+
+
+ try {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+ synchronized (doc) {
+ this.saveIncremental(bos, doc, pdfObject.getOriginalDocument().getInputStream(), signer);
+ pdfObject.setSignedDocument(bos.toByteArray());
+ }
+
+ } finally {
+ if (options != null) {
+ if (options.getVisualSignature() != null) {
+ options.getVisualSignature().close();
+ }
+ }
+ }
+
+
+ System.gc();
+ } catch (IOException e) {
+ logger.warn(MessageResolver.resolveMessage("error.pdf.sig.01"), e);
+ throw new PdfAsException("error.pdf.sig.01", e);
+ } finally {
+ if (doc != null) {
+ try {
+ doc.close();
+ } catch (IOException e) {
+ logger.debug("Failed to close COS Doc!", e);
+ // Ignore
+ }
+ }
+
+ logger.debug("Signature done!");
+
+ }
+ }
+
+ public void saveIncremental(OutputStream outStream, PDDocument doc, InputStream inStream, SignatureInterface signer) throws IOException{
+ COSWriter writer = null;
+ try
+ {
+ writer = new COSWriter(outStream, inStream);
+ writer.write(doc, signer);
+ writer.close();
+ }
+ finally
+ {
+ if (writer != null)
+ {
+ writer.close();
+ }
+ }
+ }
+
+ @Override
+ public PDFObject buildPDFObject(OperationStatus operationStatus) {
+ return new PDFBOXObject(operationStatus);
+ }
+
+ @Override
+ public PDFASSignatureInterface buildSignaturInterface(IPlainSigner signer, SignParameter parameters,
+ RequestedSignature requestedSignature) {
+ return new PdfboxSignerWrapper(signer, parameters, requestedSignature);
+ }
+
+ @Override
+ public PDFASSignatureExtractor buildBlindSignaturInterface(X509Certificate certificate, String filter,
+ String subfilter, Calendar date) {
+ return new SignatureDataExtractor(certificate, filter, subfilter, date);
+ }
+
+ @Override
+ public void checkPDFPermissions(PDFObject genericPdfObject) throws PdfAsException {
+ if (!(genericPdfObject instanceof PDFBOXObject)) {
+ // tODO:
+ throw new PdfAsException();
+ }
+
+ PDFBOXObject pdfObject = (PDFBOXObject) genericPdfObject;
+ PdfBoxUtils.checkPDFPermissions(pdfObject.getDocument());
+ }
+
+ @Override
+ public byte[] rewritePlainSignature(byte[] plainSignature) {
+ String signature = new COSString(plainSignature).toHexString();
+ byte[] pdfSignature = signature.getBytes();
+ return pdfSignature;
+ }
+
+ @Override
+ public Image generateVisibleSignaturePreview(SignParameter parameter, java.security.cert.X509Certificate cert,
+ int resolution, OperationStatus status, RequestedSignature requestedSignature) throws PDFASError {
+ try {
+ PDFBOXObject pdfObject = (PDFBOXObject) status.getPdfObject();
+
+ PDDocument origDoc = new PDDocument();
+ origDoc.addPage(new PDPage(PDRectangle.A4));
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ origDoc.save(baos);
+ baos.close();
+
+ pdfObject.setOriginalDocument(new ByteArrayDataSource(baos.toByteArray()));
+
+ SignatureProfileSettings signatureProfileSettings = TableFactory
+ .createProfile(requestedSignature.getSignatureProfileID(), pdfObject.getStatus().getSettings());
+
+ // create Table describtion
+ Table main = TableFactory.createSigTable(signatureProfileSettings, MAIN, pdfObject.getStatus(),
+ requestedSignature);
+
+ IPDFStamper stamper = StamperFactory.createDefaultStamper(pdfObject.getStatus().getSettings() );
+
+ IPDFVisualObject visualObject = stamper.createVisualPDFObject(pdfObject, main);
+
+ SignatureProfileConfiguration signatureProfileConfiguration = pdfObject.getStatus()
+ .getSignatureProfileConfiguration(requestedSignature.getSignatureProfileID());
+
+ String signaturePosString = signatureProfileConfiguration.getDefaultPositioning();
+ PositioningInstruction positioningInstruction = null;
+ if (signaturePosString != null) {
+ positioningInstruction = Positioning.determineTablePositioning(new TablePos(signaturePosString), "",
+ origDoc, visualObject, false, false);
+ } else {
+ positioningInstruction = Positioning.determineTablePositioning(new TablePos(), "", origDoc,
+ visualObject, false, false);
+ }
+
+ origDoc.close();
+
+ SignaturePositionImpl position = new SignaturePositionImpl();
+ position.setX(positioningInstruction.getX());
+ position.setY(positioningInstruction.getY());
+ position.setPage(positioningInstruction.getPage());
+ position.setHeight(visualObject.getHeight());
+ position.setWidth(visualObject.getWidth());
+
+ requestedSignature.setSignaturePosition(position);
+
+ PDFAsVisualSignatureProperties properties = new PDFAsVisualSignatureProperties(
+ pdfObject.getStatus().getSettings(), pdfObject, (PdfBoxVisualObject) visualObject,
+ positioningInstruction, signatureProfileSettings);
+
+ properties.buildSignature();
+ PDDocument visualDoc;
+ synchronized (PDDocument.class) {
+ visualDoc = PDDocument.load(properties.getVisibleSignature());
+ }
+ // PDPageable pageable = new PDPageable(visualDoc);
+
+ PDPage firstPage = visualDoc.getDocumentCatalog().getPages().get(0);
+
+ float stdRes = 72;
+ float targetRes = resolution;
+ float factor = targetRes / stdRes;
+
+
+ int targetPageNumber = 0;//TODO: is this always the case
+ PDFRenderer pdfRenderer = new PDFRenderer(visualDoc);
+ BufferedImage outputImage = pdfRenderer.renderImageWithDPI(targetPageNumber, targetRes, ImageType.ARGB);
+
+ //BufferedImage outputImage = firstPage.convertToImage(BufferedImage.TYPE_4BYTE_ABGR, (int) targetRes);
+
+ BufferedImage cutOut = new BufferedImage((int) (position.getWidth() * factor),
+ (int) (position.getHeight() * factor), BufferedImage.TYPE_4BYTE_ABGR);
+
+ Graphics2D graphics = (Graphics2D) cutOut.getGraphics();
+
+ graphics.drawImage(outputImage, 0, 0, cutOut.getWidth(), cutOut.getHeight(), (int) (1 * factor),
+ (int) (outputImage.getHeight() - ((position.getHeight() + 1) * factor)),
+ (int) ((1 + position.getWidth()) * factor), (int) (outputImage.getHeight()
+ - ((position.getHeight() + 1) * factor) + (position.getHeight() * factor)),
+ null);
+ return cutOut;
+ } catch (PdfAsException e) {
+ logger.warn("PDF-AS Exception", e);
+ throw ErrorExtractor.searchPdfAsError(e, status);
+ } catch (Throwable e) {
+ logger.warn("Unexpected Throwable Exception", e);
+ throw ErrorExtractor.searchPdfAsError(e, status);
+ }
+ }
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PDFASPDFBOXExtractorInterface.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PDFASPDFBOXExtractorInterface.java
new file mode 100644
index 00000000..c99e7c59
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PDFASPDFBOXExtractorInterface.java
@@ -0,0 +1,7 @@
+package at.gv.egiz.pdfas.lib.impl.signing.pdfbox2;
+
+import at.gv.egiz.pdfas.lib.impl.signing.PDFASSignatureExtractor;
+
+public interface PDFASPDFBOXExtractorInterface extends PDFASSignatureExtractor, PDFASPDFBOXSignatureInterface {
+
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PDFASPDFBOXSignatureInterface.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PDFASPDFBOXSignatureInterface.java
new file mode 100644
index 00000000..cc260ece
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PDFASPDFBOXSignatureInterface.java
@@ -0,0 +1,10 @@
+package at.gv.egiz.pdfas.lib.impl.signing.pdfbox2;
+
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
+
+import at.gv.egiz.pdfas.lib.impl.signing.PDFASSignatureInterface;
+
+public interface PDFASPDFBOXSignatureInterface extends PDFASSignatureInterface, SignatureInterface {
+ public void setPDSignature(PDSignature signature);
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PdfboxSignerWrapper.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PdfboxSignerWrapper.java
new file mode 100644
index 00000000..7aaf1510
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PdfboxSignerWrapper.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright>
+ * 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.
+ ******************************************************************************/
+package at.gv.egiz.pdfas.lib.impl.signing.pdfbox2;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.SignatureException;
+import java.util.Calendar;
+
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+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.utils.PDFUtils;
+import at.gv.egiz.pdfas.common.utils.StreamUtils;
+import at.gv.egiz.pdfas.lib.api.sign.IPlainSigner;
+import at.gv.egiz.pdfas.lib.api.sign.SignParameter;
+import at.gv.egiz.pdfas.lib.impl.status.RequestedSignature;
+
+public class PdfboxSignerWrapper implements PDFASPDFBOXSignatureInterface {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(PdfboxSignerWrapper.class);
+
+ private IPlainSigner signer;
+ private PDSignature signature;
+ private int[] byteRange;
+ private Calendar date;
+ private SignParameter parameters;
+ private RequestedSignature requestedSignature;
+
+ public PdfboxSignerWrapper(IPlainSigner signer, SignParameter parameters, RequestedSignature requestedSignature) {
+ this.signer = signer;
+ this.date = Calendar.getInstance();
+ this.parameters = parameters;
+ this.requestedSignature = requestedSignature;
+ }
+
+ public byte[] sign(InputStream inputStream) throws IOException {
+ byte[] data = StreamUtils.inputStreamToByteArray(inputStream);
+ byteRange = PDFUtils.extractSignatureByteRange(data);
+ int[] byteRange2 = signature.getByteRange();
+ logger.debug("Byte Range 2: " + byteRange2);
+ try {
+ logger.debug("Signing with Pdfbox Wrapper");
+ byte[] signature = signer.sign(data, byteRange, this.parameters, this.requestedSignature);
+
+ return signature;
+ } catch (PdfAsException e) {
+ throw new PdfAsWrappedIOException(e);
+ }
+ }
+
+ public int[] getByteRange() {
+ return byteRange;
+ }
+
+ public String getPDFSubFilter() {
+ return this.signer.getPDFSubFilter();
+ }
+
+ public String getPDFFilter() {
+ return this.signer.getPDFFilter();
+ }
+
+ public void setPDSignature(PDSignature signature) {
+ this.signature = signature;
+ }
+
+ public Calendar getSigningDate() {
+ return this.date;
+ }
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/SignatureDataExtractor.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/SignatureDataExtractor.java
new file mode 100644
index 00000000..78e48e5e
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/SignatureDataExtractor.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright>
+ * 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.
+ ******************************************************************************/
+package at.gv.egiz.pdfas.lib.impl.signing.pdfbox2;
+
+import iaik.x509.X509Certificate;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.SignatureException;
+import java.util.Calendar;
+
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+
+import at.gv.egiz.pdfas.common.utils.StreamUtils;
+
+public class SignatureDataExtractor implements PDFASPDFBOXExtractorInterface {
+
+ protected X509Certificate certificate;
+ protected byte[] signatureData;
+
+ protected String pdfSubFilter;
+ protected String pdfFilter;
+ protected PDSignature signature;
+ protected int[] byteRange;
+ protected Calendar date;
+
+ public SignatureDataExtractor(X509Certificate certificate,
+ String filter, String subfilter, Calendar date) {
+ this.certificate = certificate;
+ this.pdfFilter = filter;
+ this.pdfSubFilter = subfilter;
+ this.date = date;
+ }
+
+ public X509Certificate getCertificate() {
+ return certificate;
+ }
+
+ public String getPDFSubFilter() {
+ return this.pdfSubFilter;
+ }
+
+ public String getPDFFilter() {
+ return this.pdfFilter;
+ }
+
+ public byte[] getSignatureData() {
+ return this.signatureData;
+ }
+
+ public byte[] sign(InputStream content) throws IOException {
+ signatureData = StreamUtils.inputStreamToByteArray(content);
+ byteRange = this.signature.getByteRange();
+ return new byte[] { 0 };
+ }
+
+ public void setPDSignature(PDSignature signature) {
+ this.signature = signature;
+ }
+
+ public int[] getByteRange() {
+ return byteRange;
+ }
+
+ public Calendar getSigningDate() {
+ return this.date;
+ }
+
+
+
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/FontInfoCache.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/FontInfoCache.java
new file mode 100644
index 00000000..c208820e
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/FontInfoCache.java
@@ -0,0 +1,8 @@
+package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2;
+
+public class FontInfoCache {
+ String filename;
+ String fontName;
+ String fontFamily;
+ String fontPath;
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/IDGenerator.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/IDGenerator.java
new file mode 100644
index 00000000..ef898d74
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/IDGenerator.java
@@ -0,0 +1,5 @@
+package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2;
+
+public interface IDGenerator {
+ public String createHashedId(String value);
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/ImageObject.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/ImageObject.java
new file mode 100644
index 00000000..16af5cfb
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/ImageObject.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright>
+ * 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.
+ ******************************************************************************/
+package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2;
+
+import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
+
+
+public class ImageObject {
+ private PDImageXObject image;
+ private float width;
+ private float height;
+
+ public ImageObject(PDImageXObject image, float width, float height) {
+ this.image = image;
+ this.width = width;
+ this.height = height;
+ }
+
+ public PDImageXObject getImage() {
+ return image;
+ }
+
+ public void setImage(PDImageXObject image) {
+ this.image = image;
+ }
+
+ public float getWidth() {
+ return width;
+ }
+
+ 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-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsTemplateCreator.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsTemplateCreator.java
new file mode 100644
index 00000000..b8c15119
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsTemplateCreator.java
@@ -0,0 +1,166 @@
+/*******************************************************************************
+ * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright>
+ * 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.
+ ******************************************************************************/
+package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.common.PDStream;
+import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDFTemplateCreator;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDFTemplateStructure;
+import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
+import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
+
+public class PDFAsTemplateCreator extends PDFTemplateCreator {
+
+ PDFAsVisualSignatureBuilder pdfBuilder;
+ private static final Logger logger = LoggerFactory.getLogger(PDFAsTemplateCreator.class);
+
+ public PDFAsTemplateCreator(PDFAsVisualSignatureBuilder bookBuilder) {
+ super(bookBuilder);
+ this.pdfBuilder = bookBuilder;
+ }
+
+
+ public InputStream buildPDF(PDFAsVisualSignatureDesigner properties, PDDocument originalDocument)
+ throws IOException, PdfAsException {
+ logger.debug("pdf building has been started");
+ PDFTemplateStructure pdfStructure = pdfBuilder.getStructure();
+
+ // we create array of [Text, ImageB, ImageC, ImageI]
+ this.pdfBuilder.createProcSetArray();
+
+ //create page
+ this.pdfBuilder.createPage(properties);
+ PDPage page = pdfStructure.getPage();
+
+ //create template
+ this.pdfBuilder.createTemplate(page);
+ PDDocument template = pdfStructure.getTemplate();
+
+ //create /AcroForm
+ this.pdfBuilder.createAcroForm(template);
+ PDAcroForm acroForm = pdfStructure.getAcroForm();
+
+ // AcroForm contains singature fields
+ this.pdfBuilder.createSignatureField(acroForm);
+ PDSignatureField pdSignatureField = pdfStructure.getSignatureField();
+
+ // create signature
+ this.pdfBuilder.createSignature(pdSignatureField, page, properties.getSignatureFieldName());
+
+ // that is /AcroForm/DR entry
+ this.pdfBuilder.createAcroFormDictionary(acroForm, pdSignatureField);
+
+ // create AffineTransform
+ this.pdfBuilder.createAffineTransform(properties.getAffineTransformParams());
+ //AffineTransform transform = pdfStructure.getAffineTransform();
+
+ // rectangle, formatter, image. /AcroForm/DR/XObject contains that form
+ this.pdfBuilder.createSignatureRectangle(pdSignatureField, properties, properties.getRotation() + properties.getPageRotation());
+ this.pdfBuilder.createFormaterRectangle(properties.getFormaterRectangleParams());
+ PDRectangle formater = pdfStructure.getFormatterRectangle();
+
+ //this.pdfBuilder.createSignatureImage(template, properties.getImageStream());
+
+ // create form stream, form and resource.
+ this.pdfBuilder.createHolderFormStream(template);
+ PDStream holderFormStream = pdfStructure.getHolderFormStream();
+ this.pdfBuilder.createHolderFormResources();
+ PDResources holderFormResources = pdfStructure.getHolderFormResources();
+ this.pdfBuilder.createHolderForm(holderFormResources, holderFormStream, formater);
+
+ // that is /AP entry the appearance dictionary.
+ this.pdfBuilder.createAppearanceDictionary(pdfStructure.getHolderForm(), pdSignatureField,
+ properties.getRotation() + properties.getPageRotation());
+
+ // inner formstream, form and resource (hlder form containts inner form)
+ this.pdfBuilder.createInnerFormStreamPdfAs(template, originalDocument);
+ this.pdfBuilder.createInnerFormResource();
+ PDResources innerFormResource = pdfStructure.getInnerFormResources();
+ this.pdfBuilder.createInnerForm(innerFormResource, pdfStructure.getInnerFormStream(), formater);
+ PDFormXObject innerForm = pdfStructure.getInnerForm();
+
+ // inner form must be in the holder form as we wrote
+ this.pdfBuilder.insertInnerFormToHolerResources(innerForm, holderFormResources);
+
+ // Image form is in this structure: /AcroForm/DR/FRM0/Resources/XObject/n0
+ //this.pdfBuilder.createImageFormStream(template);
+ //PDStream imageFormStream = pdfStructure.getImageFormStream();
+ //this.pdfBuilder.createImageFormResources();
+ //PDResources imageFormResources = pdfStructure.getImageFormResources();
+ //this.pdfBuilder.createImageForm(imageFormResources, innerFormResource, imageFormStream, formater, transform,
+ // pdfStructure.getJpedImage());
+
+ // now inject procSetArray
+ /*this.pdfBuilder.injectProcSetArray(innerForm, page, innerFormResource, imageFormResources, holderFormResources,
+ pdfStructure.getProcSet());*/
+ this.pdfBuilder.injectProcSetArray(innerForm, page, innerFormResource, null, holderFormResources,
+ pdfStructure.getProcSet());
+
+
+ /*String imgFormName = pdfStructure.getImageFormName();
+ String imgName = pdfStructure.getImageName();*/
+ String innerFormName = pdfStructure.getInnerFormName().getName();
+
+ // now create Streams of AP
+ /*this.pdfBuilder.injectAppearanceStreams(holderFormStream, imageFormStream, imageFormStream, imgFormName,
+ imgName, innerFormName, properties);*/
+ this.pdfBuilder.injectAppearanceStreams(holderFormStream, null, null, null,
+ null, innerFormName, properties);
+ this.pdfBuilder.createVisualSignature(template);
+ this.pdfBuilder.createWidgetDictionary(pdSignatureField, holderFormResources);
+
+ ByteArrayInputStream in = null;
+
+ //COSDocument doc = pdfStructure.getVisualSignature();
+ //doc.
+ //in = pdfStructure.getTemplateAppearanceStream();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ template.save(baos);
+ baos.close();
+ in = new ByteArrayInputStream(baos.toByteArray());
+
+
+ logger.debug("stream returning started, size= " + in.available());
+
+ // we must close the document
+ this.pdfBuilder.closeTemplate(template);
+
+ // return result of the stream
+ return in;
+ }
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureBuilder.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureBuilder.java
new file mode 100644
index 00000000..30487ead
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureBuilder.java
@@ -0,0 +1,659 @@
+/*******************************************************************************
+ * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright>
+ * 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.
+ ******************************************************************************/
+package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.io.IOUtils;
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdfparser.PDFParser;
+import org.apache.pdfbox.pdfparser.PDFStreamParser;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.common.PDStream;
+import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
+import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory;
+import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
+import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDVisibleSigBuilder;
+import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
+import org.apache.pdfbox.pdmodel.interactive.form.PDField;
+import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
+import at.gv.egiz.pdfas.common.settings.ISettings;
+import at.gv.egiz.pdfas.common.utils.ImageUtils;
+import at.knowcenter.wag.egov.egiz.table.Entry;
+
+public class PDFAsVisualSignatureBuilder extends PDVisibleSigBuilder implements
+ IDGenerator {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(PDFAsVisualSignatureBuilder.class);
+
+ private PDFAsVisualSignatureProperties properties;
+ private PDFAsVisualSignatureDesigner designer;
+ private ISettings settings;
+ private PDResources innerFormResources;
+ private Map<String, ImageObject> images = new HashMap<String, ImageObject>();
+
+ public PDFAsVisualSignatureBuilder(
+ PDFAsVisualSignatureProperties properties, ISettings settings,
+ PDFAsVisualSignatureDesigner designer) {
+ this.properties = properties;
+ this.settings = settings;
+ this.designer = designer;
+ }
+
+ @Override
+ public void createProcSetArray() {
+ COSArray procSetArr = new COSArray();
+ procSetArr.add(COSName.getPDFName("PDF"));
+ procSetArr.add(COSName.getPDFName("Text"));
+ procSetArr.add(COSName.getPDFName("ImageC"));
+ procSetArr.add(COSName.getPDFName("ImageB"));
+ procSetArr.add(COSName.getPDFName("ImageI"));
+ getStructure().setProcSet(procSetArr);
+ logger.debug("ProcSet array has been created");
+ }
+
+ public void createMyPage(PDFAsVisualSignatureDesigner properties) {
+ PDPage page = properties.getSignaturePage();
+ if (page == null) {
+ page = new PDPage();
+ page.setMediaBox(new PDRectangle(properties.getPageWidth(),
+ properties.getPageHeight()));
+
+ }
+ getStructure().setPage(page);
+ logger.info("PDF page has been created");
+ }
+
+ @Override
+ public void createTemplate(PDPage page) throws IOException {
+ PDDocument template = new PDDocument();
+
+ template.addPage(page);
+ getStructure().setTemplate(template);
+ }
+
+ public String createHashedId(String value) {
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+ md.reset();
+ return Hex.encodeHexString(md.digest(value.getBytes("UTF-8")));
+ } catch (Throwable e) {
+ logger.warn("Failed to generate ID for Image using value", e);
+ return value;
+ }
+ }
+
+ private void readTableResources(PDFBoxTable table, PDDocument template)
+ throws PdfAsException, IOException {
+
+ float[] colsSizes = table.getColsRelativeWith();
+ int max_cols = table.getColCount();
+ float padding = table.getPadding();
+ if (colsSizes == null) {
+ colsSizes = new float[max_cols];
+ // set the column ratio for all columns to 1
+ for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) {
+ colsSizes[cols_idx] = 1;
+ }
+ }
+
+ logger.debug("TOTAL Width: " + table.getWidth());
+
+ 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)
+ * table.getWidth();
+ }
+
+ for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) {
+ logger.debug("Col: " + cols_idx + " : " + colsSizes[cols_idx]);
+ }
+
+ /*
+ * if(!addedFonts.contains(table.getFont().getFont(null))) { PDFont font
+ * = table.getFont().getFont(template); addedFonts.add(font);
+ * innerFormResources.addFont(font); }
+ *
+ * if(!addedFonts.contains(table.getValueFont().getFont(null))) { PDFont
+ * font = table.getValueFont().getFont(template); addedFonts.add(font);
+ * innerFormResources.addFont(font); }
+ */
+
+ for (int i = 0; i < table.getRowCount(); i++) {
+ ArrayList<Entry> row = table.getRow(i);
+ for (int j = 0; j < row.size(); j++) {
+ Entry cell = (Entry) row.get(j);
+ if (cell.getType() == Entry.TYPE_IMAGE) {
+ String img_value = (String) cell.getValue();
+ String img_ref = createHashedId(img_value);
+ if (!images.containsKey(img_ref)) {
+ BufferedImage img = ImageUtils.getImage(img_value,
+ settings);
+
+ float width = colsSizes[j];
+ float height = table.getRowHeights()[i] + padding * 2;
+
+ 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();
+
+ if (table.style != null) {
+ if (table.style.getImageScaleToFit() != null) {
+ iwidth = table.style.getImageScaleToFit()
+ .getWidth();
+ iheight = table.style.getImageScaleToFit()
+ .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));
+
+ logger.debug("Scaling image to: " + iwidth + " x "
+ + iheight);
+
+ if (this.designer.properties
+ .getSignatureProfileSettings().isPDFA()) {
+ img = ImageUtils.removeAlphaChannel(img);
+ } else {
+ if (img.getAlphaRaster() == null
+ && img.getColorModel().hasAlpha()) {
+ img = ImageUtils.removeAlphaChannel(img);
+ }
+ }
+ // img = ImageUtils.convertRGBAToIndexed(img);
+
+ PDImageXObject pdImage = LosslessFactory.createFromImage(template, img);
+
+
+ ImageObject image = new ImageObject(pdImage, iwidth,
+ iheight);
+ images.put(img_ref, image);
+ innerFormResources.add(pdImage, "Im");
+ }
+ } else if (cell.getType() == Entry.TYPE_TABLE) {
+ PDFBoxTable tbl_value = (PDFBoxTable) cell.getValue();
+ readTableResources(tbl_value, template);
+ }
+ }
+ }
+ }
+
+ public void createInnerFormStreamPdfAs(PDDocument template, PDDocument origDoc)
+ throws PdfAsException {
+ try {
+
+ // Hint we have to create all PDXObjectImages before creating the
+ // PDPageContentStream
+ // only PDFbox developers know why ...
+ // if (getStructure().getPage().getResources() != null) {
+ // innerFormResources = getStructure().getPage().getResources();
+ // } else {
+ innerFormResources = new PDResources();
+ getStructure().getPage().setResources(innerFormResources);
+ // }
+ readTableResources(properties.getMainTable(), template);
+
+ PDPageContentStream stream = new PDPageContentStream(template,
+ getStructure().getPage());
+ // stream.setFont(PDType1Font.COURIER, 5);
+ TableDrawUtils.drawTable(getStructure().getPage(), stream, 1, 1,
+ designer.getWidth(), designer.getHeight(),
+ properties.getMainTable(), template, false,
+ innerFormResources, images, settings, this, properties);
+ stream.close();
+ PDStream innterFormStream = new PDStream(template,getStructure().getPage().getContents());
+
+ getStructure().setInnterFormStream(innterFormStream);
+ logger.debug("Strean of another form (inner form - it would be inside holder form) has been created");
+
+ } catch (Throwable e) {
+ logger.warn("Failed to create visual signature block", e);
+ throw new PdfAsException("Failed to create visual signature block",
+ e);
+ }
+ }
+
+ @Override
+ public void injectProcSetArray(PDFormXObject innerForm, PDPage page,
+ PDResources innerFormResources, PDResources imageFormResources,
+ PDResources holderFormResources, COSArray procSet) {
+ innerForm.getResources().getCOSObject()
+ .setItem(COSName.PROC_SET, procSet); //
+ page.getCOSObject().setItem(COSName.PROC_SET, procSet);
+ innerFormResources.getCOSObject()
+ .setItem(COSName.PROC_SET, procSet);
+ /*
+ * imageFormResources.getCOSDictionary() .setItem(COSName.PROC_SET,
+ * procSet);
+ */
+ holderFormResources.getCOSObject().setItem(COSName.PROC_SET,
+ procSet);
+ logger.debug("inserted ProcSet to PDF");
+ }
+
+ public void injectAppearanceStreams(PDStream holderFormStream,
+ PDStream innterFormStream, PDStream imageFormStream,
+ String imageObjectName, String imageName, String innerFormName,
+ PDFAsVisualSignatureDesigner properties) throws IOException {
+
+ // 100 means that document width is 100% via the rectangle. if rectangle
+ // is 500px, images 100% is 500px.
+ // String imgFormComment = "q "+imageWidthSize+ " 0 0 50 0 0 cm /" +
+ // imageName + " Do Q\n" + builder.toString();
+ /*
+ * String imgFormComment = "q " + 100 + " 0 0 50 0 0 cm /" + imageName +
+ * " Do Q\n";
+ */
+ double m00 = getStructure().getAffineTransform().getScaleX();
+ double m10 = getStructure().getAffineTransform().getShearY();
+ double m01 = getStructure().getAffineTransform().getShearX();
+ double m11 = getStructure().getAffineTransform().getScaleY();
+ double m02 = getStructure().getAffineTransform().getTranslateX();
+ double m12 = getStructure().getAffineTransform().getTranslateY();
+
+ String holderFormComment = "q " + m00 + " " + m10 + " " + m01 + " "
+ + m11 + " " + m02 + " " + m12 + " cm /" + innerFormName
+ + " Do Q";
+
+ logger.debug("Holder Form Stream: " + holderFormComment);
+
+ // String innerFormComment = "q 1 0 0 1 0 0 cm /" + imageObjectName +
+ // " Do Q\n";
+ String innerFormComment = IOUtils.toString(getStructure().getInnerFormStream().toByteArray());
+
+ // .getInputStreamAsString();//TODO: pdfbox2 - get the string from the stream
+
+ // logger.debug("Inner Form Stream: " + innerFormComment);
+
+ // appendRawCommands(getStructure().getInnterFormStream().createOutputStream(),
+ // getStructure().getInnterFormStream().getInputStreamAsString());
+
+ appendRawCommands(getStructure().getHolderFormStream()
+ .createOutputStream(), holderFormComment.trim().replace("\n", "").replace("\r", ""));
+ appendRawCommands(getStructure().getInnerFormStream()
+ .createOutputStream(), innerFormComment/*.trim().replace("\n", "").replace("\r", "")*/);
+ // appendRawCommands(getStructure().getImageFormStream().createOutputStream(),
+ // imgFormComment);
+ logger.debug("Injected apereance stream to pdf");
+
+ }
+
+ public void createPage(PDFAsVisualSignatureDesigner properties) {
+ PDPage page = new PDPage();
+ page.setMediaBox(new PDRectangle(properties.getPageWidth(), properties
+ .getPageHeight()));
+ page.setRotation(properties.getPageRotation());
+ getStructure().setPage(page);
+ logger.debug("PDF page has been created");
+ }
+
+ public void createAcroForm(PDDocument template) {
+ PDAcroForm theAcroForm = new PDAcroForm(template);
+ template.getDocumentCatalog().setAcroForm(theAcroForm);
+ getStructure().setAcroForm(theAcroForm);
+ logger.debug("Acro form page has been created");
+ }
+
+ public void createSignatureField(PDAcroForm acroForm) throws IOException {
+ PDSignatureField sf = new PDSignatureField(acroForm);
+ getStructure().setSignatureField(sf);
+ logger.debug("Signature field has been created");
+ }
+
+ public void createSignature(PDSignatureField pdSignatureField, PDPage page,
+ String signatureName) throws IOException {
+ PDSignature pdSignature = new PDSignature();
+ pdSignatureField.setSignature(pdSignature);
+ pdSignatureField.getWidget().setPage(page);
+ page.getAnnotations().add(pdSignatureField.getWidget());
+ pdSignature.setName(signatureName);
+ pdSignature.setByteRange(new int[] { 0, 0, 0, 0 });
+ pdSignature.setContents(new byte[4096]);
+ getStructure().setPdSignature(pdSignature);
+ logger.debug("PDSignatur has been created");
+ }
+
+ public void createAcroFormDictionary(PDAcroForm acroForm,
+ PDSignatureField signatureField) throws IOException {
+ @SuppressWarnings("unchecked")
+ List<PDField> acroFormFields = acroForm.getFields();
+ COSDictionary acroFormDict = acroForm.getCOSObject();
+ acroFormDict.setDirect(true);
+ acroFormDict.setInt(COSName.SIG_FLAGS, 3);
+ acroFormFields.add(signatureField);
+ acroFormDict.setString(COSName.DA, "/sylfaen 0 Tf 0 g");
+ getStructure().setAcroFormFields(acroFormFields);
+ getStructure().setAcroFormDictionary(acroFormDict);
+ logger.debug("AcroForm dictionary has been created");
+ }
+
+ public void createSignatureRectangle(PDSignatureField signatureField,
+ PDFAsVisualSignatureDesigner properties, float degrees)
+ throws IOException {
+
+ PDRectangle rect = new PDRectangle();
+
+ Point2D upSrc = new Point2D.Float();
+ upSrc.setLocation(properties.getxAxis() + properties.getWidth(),
+ properties.getPageHeight() - properties.getyAxis());
+
+ Point2D llSrc = new Point2D.Float();
+ llSrc.setLocation(properties.getxAxis(), properties.getPageHeight()
+ - properties.getyAxis() - properties.getHeight());
+
+ rect.setUpperRightX((float) upSrc.getX());
+ rect.setUpperRightY((float) upSrc.getY());
+ rect.setLowerLeftY((float) llSrc.getY());
+ rect.setLowerLeftX((float) llSrc.getX());
+ logger.debug("orig rectangle of signature has been created: {}",
+ rect.toString());
+
+ AffineTransform transform = new AffineTransform();
+ transform.setToIdentity();
+ if (degrees % 360 != 0) {
+ transform.setToRotation(Math.toRadians(degrees), llSrc.getX(),
+ llSrc.getY());
+ }
+
+ Point2D upDst = new Point2D.Float();
+ transform.transform(upSrc, upDst);
+
+ Point2D llDst = new Point2D.Float();
+ transform.transform(llSrc, llDst);
+
+ float xPos = properties.getxAxis();
+ float yPos = properties.getPageHeight() - properties.getyAxis();
+ logger.debug("POS {} x {}", xPos, yPos);
+ logger.debug("SIZE {} x {}", properties.getWidth(),
+ properties.getHeight());
+ // translate according to page! rotation
+ int pageRotation = properties.getPageRotation();
+ AffineTransform translate = new AffineTransform();
+ switch (pageRotation) {
+ case 90:
+ translate.setToTranslation(
+ properties.getPageHeight()
+ - (properties.getPageHeight() - properties
+ .getyAxis()) - properties.getxAxis()
+ + properties.getHeight(),
+ properties.getxAxis()
+ + properties.getHeight()
+ - (properties.getPageHeight() - properties
+ .getyAxis()));
+ break;
+ case 180:
+ // translate.setToTranslation(properties.getPageWidth() -
+ // properties.getxAxis() - properties.getxAxis(),
+ // properties.getPageHeight() - properties.getyAxis() +
+ // properties.getHeight());
+ translate.setToTranslation(
+ properties.getPageWidth() - 2 * xPos,
+ properties.getPageHeight() - 2
+ * (yPos - properties.getHeight()));
+ break;
+ case 270:
+ translate.setToTranslation(-properties.getHeight() + yPos - xPos,
+ properties.getPageWidth() - (yPos - properties.getHeight())
+ - xPos);
+ break;
+ }
+
+ translate.transform(upDst, upDst);
+ translate.transform(llDst, llDst);
+
+ rect.setUpperRightX((float) upDst.getX());
+ rect.setUpperRightY((float) upDst.getY());
+ rect.setLowerLeftY((float) llDst.getY());
+ rect.setLowerLeftX((float) llDst.getX());
+ logger.debug("rectangle of signature has been created: {}",
+ rect.toString());
+ signatureField.getWidget().setRectangle(rect);
+ getStructure().setSignatureRectangle(rect);
+ logger.debug("rectangle of signature has been created");
+ }
+
+ public void createAffineTransform(float[] params) {
+ AffineTransform transform = new AffineTransform(params[0], params[1],
+ params[2], params[3], params[4], params[5]);
+ // transform.rotate(90);
+ getStructure().setAffineTransform(transform);
+ logger.debug("Matrix has been added");
+ }
+
+ public void createSignatureImage(PDDocument template,
+ InputStream inputStream) throws IOException {
+ PDImageXObject img = JPEGFactory.createFromStream(template, inputStream);
+ getStructure().setImage(img);
+ logger.debug("Visible Signature Image has been created");
+ // pdfStructure.setTemplate(template);
+ inputStream.close();
+
+ }
+
+ public void createFormaterRectangle(float[] params) {
+
+ PDRectangle formrect = new PDRectangle();
+ float[] translated = new float[4];
+ getStructure().getAffineTransform().transform(params, 0, translated, 0,
+ 2);
+
+ formrect.setUpperRightX(translated[0]);
+ formrect.setUpperRightY(translated[1]);
+ formrect.setLowerLeftX(translated[2]);
+ formrect.setLowerLeftY(translated[3]);
+
+ getStructure().setFormatterRectangle(formrect);
+ logger.debug("Formater rectangle has been created");
+
+ }
+
+ public void createHolderFormStream(PDDocument template) {
+ PDStream holderForm = new PDStream(template);
+ getStructure().setHolderFormStream(holderForm);
+ logger.debug("Holder form Stream has been created");
+ }
+
+ public void createHolderFormResources() {
+ PDResources holderFormResources = new PDResources();
+ getStructure().setHolderFormResources(holderFormResources);
+ logger.debug("Holder form resources have been created");
+
+ }
+
+ public void createHolderForm(PDResources holderFormResources,
+ PDStream holderFormStream, PDRectangle formrect) {
+
+ PDFormXObject holderForm = new PDFormXObject(holderFormStream);
+ holderForm.setResources(holderFormResources);
+ holderForm.setBBox(formrect);
+ holderForm.setFormType(1);
+ getStructure().setHolderForm(holderForm);
+ logger.debug("Holder form has been created");
+
+ }
+
+ public void createAppearanceDictionary(PDFormXObject holderForml,
+ PDSignatureField signatureField, float degrees) throws IOException {
+
+ PDAppearanceDictionary appearance = new PDAppearanceDictionary();
+ appearance.getCOSObject().setDirect(true);
+
+ PDAppearanceStream appearanceStream = new PDAppearanceStream(
+ holderForml.getCOSStream());
+ AffineTransform transform = new AffineTransform();
+ transform.setToIdentity();
+ transform.rotate(Math.toRadians(degrees));
+ appearanceStream.setMatrix(transform);
+ appearance.setNormalAppearance(appearanceStream);
+ signatureField.getWidget().setAppearance(appearance);
+
+ getStructure().setAppearanceDictionary(appearance);
+ logger.debug("PDF appereance Dictionary has been created");
+
+ }
+
+ public void createInnerFormResource() {
+ getStructure().setInnerFormResources(innerFormResources);
+ logger.debug("Resources of another form (inner form - it would be inside holder form) have been created");
+ }
+
+ public void createInnerForm(PDResources innerFormResources,
+ PDStream innerFormStream, PDRectangle formrect) {
+ PDFormXObject innerForm = new PDFormXObject(innerFormStream);
+ innerForm.setResources(innerFormResources);
+ innerForm.setBBox(formrect);
+ innerForm.setFormType(1);
+ getStructure().setInnerForm(innerForm);
+ logger.debug("Another form (inner form - it would be inside holder form) have been created");
+
+ }
+
+ public void insertInnerFormToHolerResources(PDFormXObject innerForm,
+ PDResources holderFormResources) {
+ COSName name = holderFormResources.add(innerForm, "FRM");//TODO: pdfbox2 - is this right?
+ getStructure().setInnerFormName(name);
+ logger.debug("Alerady inserted inner form inside holder form");
+ }
+
+ public void createImageFormStream(PDDocument template) {
+ PDStream imageFormStream = new PDStream(template);
+ getStructure().setImageFormStream(imageFormStream);
+ logger.debug("Created image form Stream");
+ }
+
+ public void createImageFormResources() {
+ PDResources imageFormResources = new PDResources();
+ getStructure().setImageFormResources(imageFormResources);
+ logger.debug("Created image form Resources");
+ }
+
+ public void createImageForm(PDResources imageFormResources,
+ PDResources innerFormResource, PDStream imageFormStream,
+ PDRectangle formrect, AffineTransform affineTransform, PDFormXObject img)
+ throws IOException {
+
+ /*
+ * if you need text on the visible signature
+ *
+ * PDFont font = PDTrueTypeFont.loadTTF(this.pdfStructure.getTemplate(),
+ * new File("D:\\arial.ttf")); font.setFontEncoding(new
+ * WinAnsiEncoding());
+ *
+ * Map<String, PDFont> fonts = new HashMap<String, PDFont>();
+ * fonts.put("arial", font);
+ */
+ PDFormXObject imageForm = new PDFormXObject(imageFormStream);
+ imageForm.setBBox(formrect);
+ imageForm.setMatrix(affineTransform);
+ imageForm.setResources(imageFormResources);
+ imageForm.setFormType(1);
+ /*
+ * imageForm.getResources().addFont(font);
+ * imageForm.getResources().setFonts(fonts);
+ */
+
+ imageFormResources.getCOSObject().setDirect(true);
+ COSName imageFormName = innerFormResource.add(imageForm, "n");//TODO: pdfbox2 - is this right?
+ COSName imageName = imageFormResources.add(img, "img");
+ this.getStructure().setImageForm(imageForm);
+ this.getStructure().setImageFormName(imageFormName);
+ this.getStructure().setImageName(imageName);
+ logger.debug("Created image form");
+ }
+
+ public void appendRawCommands(OutputStream os, String commands)
+ throws IOException {
+ os.write(commands.getBytes("UTF-8"));
+ os.close();
+ }
+
+ public void createVisualSignature(PDDocument template) {
+ this.getStructure().setVisualSignature(template.getDocument());
+ logger.debug("Visible signature has been created");
+
+ }
+
+ public void createWidgetDictionary(PDSignatureField signatureField,
+ PDResources holderFormResources) throws IOException {
+
+ COSDictionary widgetDict = signatureField.getWidgets().get(0).getCOSObject();//TODO: pdfbox2 - is this right was getWidget before?
+ widgetDict.setNeedToBeUpdated(true);
+ widgetDict.setItem(COSName.DR, holderFormResources.getCOSObject());
+
+ getStructure().setWidgetDictionary(widgetDict);
+ logger.debug("WidgetDictionary has been crated");
+ }
+
+ public void closeTemplate(PDDocument template) throws IOException {
+ template.close();
+ this.getStructure().getTemplate().close();
+ }
+
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureDesigner.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureDesigner.java
new file mode 100644
index 00000000..33450b56
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureDesigner.java
@@ -0,0 +1,472 @@
+/*******************************************************************************
+ * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright>
+ * 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.
+ ******************************************************************************/
+package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2;
+
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageTree;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+
+public class PDFAsVisualSignatureDesigner {
+
+// private static final Logger logger = LoggerFactory.getLogger(PDFAsVisualSignatureDesigner.class);
+
+ private Float sigImgWidth;
+ private Float sigImgHeight;
+ private float xAxis;
+ private float yAxis;
+ private float pageHeight;
+ private float pageWidth;
+ private InputStream imgageStream;
+ private String signatureFieldName = "sig"; // default
+ private float[] formaterRectangleParams = { 0, 0, 100, 50 }; // default
+ //private float[] AffineTransformParams = { 0, 1, -1, 0, 0, 0 }; // default
+ private float[] AffineTransformParams = { 1, 0, 0, 1, 0, 0 }; // default
+ private float imageSizeInPercents;
+ private PDDocument document = null;
+ private int page = 0;
+ private int pageRotation = 0;
+ private boolean newpage = false;
+ PDFAsVisualSignatureProperties properties;
+
+ /**
+ *
+ * @param doc
+ * - Already created PDDocument of your PDF document
+ * @param imageStream
+ * @param page
+ * @throws IOException
+ * - If we can't read, flush, or can't close stream
+ */
+ public PDFAsVisualSignatureDesigner(PDDocument doc, int page,
+ PDFAsVisualSignatureProperties properties, boolean newpage) throws IOException {
+ this.properties = properties;
+ calculatePageSize(doc, page, newpage);
+ document = doc;
+ this.page = page;
+ this.newpage = newpage;
+ }
+
+ /**
+ * Each page of document can be different sizes.
+ *
+ * @param document
+ * @param page
+ */
+ private void calculatePageSize(PDDocument document, int page, boolean newpage) {
+
+ if (page < 1) {
+ throw new IllegalArgumentException("First page of pdf is 1, not "
+ + page);
+ }
+
+ PDPageTree pages = document.getDocumentCatalog().getPages();
+ if(newpage) {
+ PDPage lastPage = (PDPage) pages.get(pages.getCount()-1);
+ PDRectangle mediaBox = lastPage.getMediaBox();
+ pageRotation = lastPage.getRotation() % 360;
+ if(pageRotation == 90 || pageRotation == 270) {
+ this.pageHeight(mediaBox.getWidth());
+ this.pageWidth = mediaBox.getHeight();
+ } else {
+ this.pageHeight(mediaBox.getHeight());
+ this.pageWidth = mediaBox.getWidth();
+ }
+ } else {
+ PDPage firstPage = (PDPage) pages.get(page - 1);
+ PDRectangle mediaBox = firstPage.getMediaBox();
+ pageRotation = firstPage.getRotation() % 360;
+ if(pageRotation == 90 || pageRotation == 270) {
+ this.pageHeight(mediaBox.getWidth());
+ this.pageWidth = mediaBox.getHeight();
+ } else {
+ this.pageHeight(mediaBox.getHeight());
+ this.pageWidth = mediaBox.getWidth();
+ }
+ }
+ float x = this.pageWidth;
+ float y = 0;
+ this.pageWidth = this.pageWidth + y;
+ float tPercent = (100 * y / (x + y));
+ this.imageSizeInPercents = 100 - tPercent;
+ }
+
+ /**
+ *
+ * @param path
+ * of image location
+ * @return image Stream
+ * @throws IOException
+ */
+ public PDFAsVisualSignatureDesigner signatureImage(String path)
+ throws IOException {
+ InputStream fin = new FileInputStream(path);
+ return signatureImageStream(fin);
+ }
+
+ /**
+ * zoom signature image with some percent.
+ *
+ * @param percent
+ * - x % increase image with x percent.
+ * @return Visible Signature Configuration Object
+ */
+ public PDFAsVisualSignatureDesigner zoom(float percent) {
+ sigImgHeight = sigImgHeight + (sigImgHeight * percent) / 100;
+ sigImgWidth = sigImgWidth + (sigImgWidth * percent) / 100;
+ return this;
+ }
+
+ /**
+ *
+ * @param xAxis
+ * - x coordinate
+ * @param yAxis
+ * - y coordinate
+ * @return Visible Signature Configuration Object
+ */
+ public PDFAsVisualSignatureDesigner coordinates(float x, float y) {
+ xAxis(x);
+ yAxis(y);
+ return this;
+ }
+
+ /**
+ *
+ * @return xAxis - gets x coordinates
+ */
+ public float getxAxis() {
+ return xAxis;
+ }
+
+ /**
+ *
+ * @param xAxis
+ * - x coordinate
+ * @return Visible Signature Configuration Object
+ */
+ public PDFAsVisualSignatureDesigner xAxis(float xAxis) {
+ this.xAxis = xAxis;
+ return this;
+ }
+
+ /**
+ *
+ * @return yAxis
+ */
+ public float getyAxis() {
+ return yAxis;
+ }
+
+ /**
+ *
+ * @param yAxis
+ * @return Visible Signature Configuration Object
+ */
+ public PDFAsVisualSignatureDesigner yAxis(float yAxis) {
+ this.yAxis = yAxis;
+ return this;
+ }
+
+ /**
+ *
+ * @return signature image width
+ */
+ public float getWidth() {
+ return this.properties.getMainTable().getWidth();
+ }
+
+ /**
+ *
+ * @param sets
+ * signature image width
+ * @return Visible Signature Configuration Object
+ */
+ public PDFAsVisualSignatureDesigner width(float signatureImgWidth) {
+ this.sigImgWidth = signatureImgWidth;
+ return this;
+ }
+
+ /**
+ *
+ * @return signature image height
+ */
+ public float getHeight() {
+ return this.properties.getMainTable().getHeight();
+ }
+
+ /**
+ *
+ * @param set
+ * signature image Height
+ * @return Visible Signature Configuration Object
+ */
+ public PDFAsVisualSignatureDesigner height(float signatureImgHeight) {
+ this.sigImgHeight = signatureImgHeight;
+ return this;
+ }
+
+ /**
+ *
+ * @return template height
+ */
+ protected float getTemplateHeight() {
+ return getPageHeight();
+ }
+
+ /**
+ *
+ * @param templateHeight
+ * @return Visible Signature Configuration Object
+ */
+ private PDFAsVisualSignatureDesigner pageHeight(float templateHeight) {
+ this.pageHeight = templateHeight;
+ return this;
+ }
+
+ /**
+ *
+ * @return signature field name
+ */
+ public String getSignatureFieldName() {
+ return signatureFieldName;
+ }
+
+ /**
+ *
+ * @param signatureFieldName
+ * @return Visible Signature Configuration Object
+ */
+ public PDFAsVisualSignatureDesigner signatureFieldName(
+ String signatureFieldName) {
+ this.signatureFieldName = signatureFieldName;
+ return this;
+ }
+
+ /**
+ *
+ * @return image Stream
+ */
+ public InputStream getImageStream() {
+ return imgageStream;
+ }
+
+ /**
+ *
+ * @param imgageStream
+ * - stream of your visible signature image
+ * @return Visible Signature Configuration Object
+ * @throws IOException
+ * - If we can't read, flush, or close stream of image
+ */
+ private PDFAsVisualSignatureDesigner signatureImageStream(
+ InputStream imageStream) throws IOException {
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int len;
+ while ((len = imageStream.read(buffer)) > -1) {
+ baos.write(buffer, 0, len);
+ }
+ baos.flush();
+ baos.close();
+
+ byte[] byteArray = baos.toByteArray();
+ byte[] byteArraySecond = byteArray.clone();
+
+ InputStream inputForBufferedImage = new ByteArrayInputStream(byteArray);
+ InputStream revertInputStream = new ByteArrayInputStream(
+ byteArraySecond);
+
+ if (sigImgHeight == null || sigImgWidth == null) {
+ calcualteImageSize(inputForBufferedImage);
+ }
+
+ this.imgageStream = revertInputStream;
+
+ return this;
+ }
+
+ /**
+ * calculates image width and height. sported formats: all
+ *
+ * @param fis
+ * - input stream of image
+ * @throws IOException
+ * - if can't read input stream
+ */
+ private void calcualteImageSize(InputStream fis) throws IOException {
+
+ BufferedImage bimg = ImageIO.read(fis);
+ int width = bimg.getWidth();
+ int height = bimg.getHeight();
+
+ sigImgHeight = (float) height;
+ sigImgWidth = (float) width;
+
+ }
+
+ /**
+ *
+ * @return Affine Transform parameters of for PDF Matrix
+ */
+ public float[] getAffineTransformParams() {
+ return AffineTransformParams;
+ }
+
+ /**
+ *
+ * @param affineTransformParams
+ * @return Visible Signature Configuration Object
+ */
+ public PDFAsVisualSignatureDesigner affineTransformParams(
+ float[] affineTransformParams) {
+ AffineTransformParams = affineTransformParams;
+ return this;
+ }
+
+ /**
+ *
+ * @return formatter PDRectanle parameters
+ */
+ public float[] getFormaterRectangleParams() {
+ return formaterRectangleParams;
+ }
+
+ /**
+ * sets formatter PDRectangle;
+ *
+ * @param formaterRectangleParams
+ * @return Visible Signature Configuration Object
+ */
+ public PDFAsVisualSignatureDesigner formaterRectangleParams(
+ float[] formaterRectangleParams) {
+ this.formaterRectangleParams = formaterRectangleParams;
+ return this;
+ }
+
+ /**
+ *
+ * @return page width
+ */
+ public float getPageWidth() {
+ return pageWidth;
+ }
+
+ public PDPage getSignaturePage() {
+ if (page < 1) {
+ throw new IllegalArgumentException("First page of pdf is 1, not "
+ + page);
+ }
+ PDPage pdPage = null;
+ PDPageTree pages = document.getDocumentCatalog().getPages();
+ if(newpage) {
+ pdPage = new PDPage();
+ } else {
+ pdPage = (PDPage) pages.get(page - 1);
+ }
+
+ return pdPage;
+ }
+
+ /**
+ *
+ * @param sets
+ * pageWidth
+ * @return Visible Signature Configuration Object
+ */
+ public PDFAsVisualSignatureDesigner pageWidth(float pageWidth) {
+ this.pageWidth = pageWidth;
+ return this;
+ }
+
+ /**
+ *
+ * @return page height
+ */
+ public float getPageHeight() {
+ return pageHeight;
+ }
+
+ /**
+ *
+ * @return page height
+ */
+ public int getPageRotation() {
+ return pageRotation;
+ }
+
+ /**
+ * get image size in percents
+ *
+ * @return
+ */
+ public float getImageSizeInPercents() {
+ return imageSizeInPercents;
+ }
+
+ /**
+ *
+ * @param imageSizeInPercents
+ */
+ public void imageSizeInPercents(float imageSizeInPercents) {
+ this.imageSizeInPercents = imageSizeInPercents;
+ }
+
+ /**
+ * returns visible signature text
+ *
+ * @return
+ */
+ public String getSignatureText() {
+ throw new UnsupportedOperationException(
+ "That method is not yet implemented");
+ }
+
+ /**
+ *
+ * @param signatureText
+ * - adds the text on visible signature
+ * @return
+ */
+ public PDFAsVisualSignatureDesigner signatureText(String signatureText) {
+ throw new UnsupportedOperationException(
+ "That method is not yet implemented");
+ }
+
+ public float getRotation() {
+ return this.properties.getRotation();
+ }
+
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureProperties.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureProperties.java
new file mode 100644
index 00000000..db96767a
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureProperties.java
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright>
+ * 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.
+ ******************************************************************************/
+package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageTree;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDVisibleSigProperties;
+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.gv.egiz.pdfas.common.settings.SignatureProfileSettings;
+import at.gv.egiz.pdfas.lib.impl.pdfbox2.PDFBOXObject;
+import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction;
+
+public class PDFAsVisualSignatureProperties extends PDVisibleSigProperties {
+
+ private static final Logger logger = LoggerFactory.getLogger(PDFAsVisualSignatureProperties.class);
+
+ private ISettings settings;
+
+ private PDFBoxTable main;
+
+ private PDFAsVisualSignatureDesigner designer;
+
+ private float rotationAngle = 0;
+
+ private PDDocument origDoc;
+
+ private SignatureProfileSettings signatureProfileSettings;
+
+ private String alternativeTableCaption="";
+
+ public PDFAsVisualSignatureProperties(ISettings settings, PDFBOXObject object,
+ PdfBoxVisualObject visObj, PositioningInstruction pos, SignatureProfileSettings signatureProfileSettings) {
+ this.settings = settings;
+ this.signatureProfileSettings = signatureProfileSettings;
+ try {
+ main = visObj.getTable();
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ this.rotationAngle = pos.getRotation();
+ try {
+ origDoc = object.getDocument();
+
+ designer = new PDFAsVisualSignatureDesigner(origDoc, pos.getPage(), this, pos.isMakeNewPage());
+ PDPageTree pages = origDoc.getDocumentCatalog().getPages();
+ PDPage page = null;
+ if(pos.isMakeNewPage()) {
+ page = (PDPage) pages.get(pages.getCount()-1);
+ } else {
+ page = (PDPage) pages.get(pos.getPage() - 1);
+ }
+ logger.debug("PAGE width {} HEIGHT {}", designer.getPageWidth(), designer.getPageHeight());
+ logger.debug("POS X {} Y {}", pos.getX(), pos.getY());
+ int rot = page.getRotation();
+ float posy = designer.getPageHeight() - pos.getY();
+ float posx = pos.getX();
+ /*switch (rot) {
+ case 90: // CW
+ posx = designer.getPageHeight() - pos.getY();
+ posy = designer.getPageWidth() - main.getWidth();
+ break;
+ case 180:
+ posy = pos.getY();
+ posx = designer.getPageWidth() - pos.getX();
+ break;
+ case 270: // CCW
+ posx = pos.getY();
+ posy = designer.getPageWidth() - pos.getX();
+ break;
+ }*/
+ logger.debug("ROT {}", rot);
+ logger.debug("COORD X {} Y {}", posx, posy);
+ designer.coordinates(posx, posy);
+ float[] form_rect = new float[] {0,0, main.getWidth() + 2, main.getHeight() + 2};
+ logger.debug("AP Rect: {} {} {} {}", form_rect[0], form_rect[1], form_rect[2], form_rect[3]);
+ designer.formaterRectangleParams(form_rect);
+ //this.setPdVisibleSignature(designer);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void buildSignature() throws IOException {
+ PDFAsVisualSignatureBuilder builder = new PDFAsVisualSignatureBuilder(this, this.settings, designer);
+ PDFAsTemplateCreator creator = new PDFAsTemplateCreator(builder);
+ try {
+ setVisibleSignature(creator.buildPDF(designer, this.origDoc));
+ } catch (PdfAsException e) {
+ throw new PdfAsWrappedIOException(e);
+ }
+ }
+
+ public PDFBoxTable getMainTable() {
+ return main;
+ }
+
+
+ public float getRotation() {
+ return this.rotationAngle;
+ }
+
+ public PDFAsVisualSignatureDesigner getDesigner() {
+ return designer;
+ }
+
+ public SignatureProfileSettings getSignatureProfileSettings() {
+ return signatureProfileSettings;
+ }
+
+ public String getAlternativeTableCaption() {
+ return alternativeTableCaption;
+ }
+
+ public void setAlternativeTableCaption(String alternativeTableCaption) {
+ this.alternativeTableCaption = alternativeTableCaption;
+ }
+
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFBoxFont.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFBoxFont.java
new file mode 100644
index 00000000..8b46d56a
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFBoxFont.java
@@ -0,0 +1,299 @@
+/*******************************************************************************
+ * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright>
+ * 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.
+ ******************************************************************************/
+package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.fontbox.ttf.NameRecord;
+import org.apache.fontbox.ttf.NamingTable;
+import org.apache.fontbox.ttf.TTFParser;
+import org.apache.fontbox.ttf.TrueTypeFont;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSObject;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.font.PDTrueTypeFont;
+import org.apache.pdfbox.pdmodel.font.PDType1Font;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import at.gv.egiz.pdfas.common.settings.ISettings;
+import at.gv.egiz.pdfas.lib.impl.pdfbox2.PDFBOXObject;
+
+public class PDFBoxFont {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(PDFBoxFont.class);
+
+ private static final String HELVETICA = "HELVETICA";
+ private static final String COURIER = "COURIER";
+ private static final String TIMES_ROMAN = "TIMES_ROMAN";
+ private static final String BOLD = "BOLD";
+ private static final String NORMAL = "NORMAL";
+ private static final String ITALIC = "ITALIC";
+ private static final String SEP = ":";
+
+ public static PDFont defaultFont = PDType1Font.HELVETICA;
+ public static float defaultFontSize = 8;
+
+ private static Map<String, PDFont> fontStyleMap = new HashMap<String, PDFont>();
+
+ private static Map<String, FontInfoCache> fontInfoCache = new HashMap<String, FontInfoCache>();
+
+ static {
+ fontStyleMap.put(HELVETICA + SEP + NORMAL, PDType1Font.HELVETICA);
+ fontStyleMap.put(HELVETICA + SEP + BOLD, PDType1Font.HELVETICA_BOLD);
+
+ fontStyleMap.put(COURIER + SEP + NORMAL, PDType1Font.COURIER);
+ fontStyleMap.put(COURIER + SEP + BOLD, PDType1Font.COURIER_BOLD);
+
+ fontStyleMap.put(TIMES_ROMAN + SEP + NORMAL, PDType1Font.TIMES_ROMAN);
+ fontStyleMap.put(TIMES_ROMAN + SEP + BOLD, PDType1Font.TIMES_BOLD);
+ fontStyleMap.put(TIMES_ROMAN + SEP + ITALIC, PDType1Font.TIMES_ITALIC);
+ }
+
+ public static void showBuildinFonts() {
+ Iterator<String> it = fontStyleMap.keySet().iterator();
+ logger.info("Available Fonts:");
+ while (it.hasNext()) {
+ logger.info(it.next());
+ }
+ }
+
+ PDFont font;
+ PDFont cachedfont = null;
+ float fontSize;
+ String fontDesc;
+ String ttfFontDesc;
+ ISettings settings;
+
+ private FontInfoCache getFontInfo(String pathName) {
+ synchronized (fontInfoCache) {
+
+ if (fontInfoCache.containsKey(pathName)) {
+ return fontInfoCache.get(pathName);
+ } else {
+ try {
+ String fontNameToLoad = null;
+ String fontFamilyToLoad = null;
+ InputStream ttfData = new FileInputStream(pathName);
+ try {
+ TrueTypeFont ttf = null;
+ TTFParser parser = new TTFParser();
+ ttf = parser.parse(ttfData);
+ NamingTable naming = ttf.getNaming();
+ List<NameRecord> records = naming.getNameRecords();
+ for (int i = 0; i < records.size(); i++) {
+ NameRecord nr = records.get(i);
+ if (nr.getNameId() == NameRecord.NAME_POSTSCRIPT_NAME) {
+ fontNameToLoad = nr.getString();
+ } else if (nr.getNameId() == NameRecord.NAME_FONT_FAMILY_NAME) {
+ fontFamilyToLoad = nr.getString();
+ }
+ }
+ } finally {
+ ttfData.close();
+ }
+ FontInfoCache fontInfo = new FontInfoCache();
+ fontInfo.filename = pathName;
+ fontInfo.fontFamily = fontFamilyToLoad;
+ fontInfo.fontName = fontNameToLoad;
+ fontInfo.fontPath = pathName;
+ fontInfoCache.put(pathName, fontInfo);
+ return fontInfo;
+ } catch (Throwable e) {
+ logger.warn("Failed to generate FontInfo from file: {}", pathName);
+ }
+ return null;
+ }
+ }
+ }
+
+ private PDFont findCachedFont(PDFBOXObject pdfObject, FontInfoCache fontInfo) {
+ try {
+ if(pdfObject.getFontCache().containsKey(fontInfo.fontPath)) {
+ return pdfObject.getFontCache().get(fontInfo.fontPath);
+ }
+
+ List<COSObject> cosObjects = pdfObject.getDocument().getDocument().getObjectsByType(
+ COSName.FONT);
+
+ //COSName cosFontName = COSName.getPDFName(fontInfo.fontName);
+ //COSName cosFontFamily = COSName.getPDFName(fontInfo.fontFamily);
+
+ Iterator<COSObject> cosObjectIt = cosObjects.iterator();
+
+ while (cosObjectIt.hasNext()) {
+ COSObject cosObject = cosObjectIt.next();
+ COSDictionary baseObject = (COSDictionary) cosObject
+ .getObject();
+ if (baseObject instanceof COSDictionary) {
+ COSDictionary fontDictionary = (COSDictionary) baseObject;
+ COSBase subType = cosObject.getItem(COSName.SUBTYPE);
+ COSDictionary fontDescriptor = (COSDictionary)cosObject.getDictionaryObject(COSName.FONT_DESC);
+ String fontName = fontDescriptor.getNameAsString(COSName.FONT_NAME);
+ String fontFamily = fontDescriptor.getNameAsString(COSName.FONT_FAMILY);
+ logger.debug("Checking Font {} - {}", fontFamily, fontName);
+ if (COSName.TRUE_TYPE.equals(subType)) {
+ if (fontInfo.fontName != null && fontInfo.fontName.equals(fontName) &&
+ fontInfo.fontFamily != null && fontInfo.fontFamily.equals(fontFamily)) {
+ // Found it! :)
+ logger.info("Found Font {}", fontInfo.fontName);
+ return new PDTrueTypeFont(fontDictionary);
+ } else {
+ logger.debug("Font not found: {} is {}",
+ fontInfo.fontName, fontName);
+ }
+ } else {
+ logger.debug("Font not a TTF");
+ }
+ } else {
+ logger.debug("Font not a COSDictionary");
+ }
+ }
+ } catch (Throwable e) {
+ logger.info("Failed to find existing TTF fonts!", e);
+ }
+ return null;
+ }
+
+ private PDFont generateTTF(String fonttype, PDFBOXObject pdfObject)
+ throws IOException {
+ /*boolean cacheNow = true;
+ if (pdfObject == null) {
+ if (this.doc == null) {
+ this.doc = new PDDocument();
+ }
+ doc = this.doc;
+ } else {
+ cacheNow = true;
+ }*/
+ ttfFontDesc = fonttype;
+ String fontName = fonttype.replaceFirst("TTF:", "");
+ String fontPath = this.settings.getWorkingDirectory() + File.separator
+ + "fonts" + File.separator + fontName;
+
+ logger.debug("Font from: \"" + fontPath + "\".");
+
+ if(fontStyleMap.containsKey(fontPath)) {
+ return fontStyleMap.get(fontPath);
+ }
+
+ FontInfoCache fontInfo = getFontInfo(fontPath);
+
+ if(fontInfo != null) {
+
+ PDFont font = findCachedFont(pdfObject, fontInfo);
+
+ if (font != null) {
+ return font;
+ }
+ }
+
+ logger.debug("Instantiating font.");
+
+ //if (cacheNow) {
+ cachedfont = PDTrueTypeFont.loadTTF(pdfObject.getDocument(), new File(fontPath));
+
+ fontStyleMap.put(fontPath, cachedfont);
+ return cachedfont;
+ //} else {
+ // return PDTrueTypeFont.loadTTF(doc, fontPath);
+ //}
+
+ }
+
+ private PDFont generateFont(String fonttype, String fontder,
+ PDFBOXObject pdfObject) throws IOException {
+ if (fonttype.startsWith("TTF:")) {
+ // Load TTF Font
+ return generateTTF(fonttype, pdfObject);
+ } else {
+ if (fontder == null) {
+ fontder = NORMAL;
+ }
+
+ String fontDesc = fonttype + SEP + fontder;
+ PDFont font = fontStyleMap.get(fontDesc);
+ if (font == null) {
+ showBuildinFonts();
+ throw new IOException("Invalid font descriptor");
+ }
+ return font;
+ }
+ }
+
+ private void setFont(String desc, PDFBOXObject pdfObject)
+ throws IOException {
+ String[] fontArr = desc.split(",");
+
+ if (fontArr.length == 3) {
+ font = generateFont(fontArr[0], fontArr[2], pdfObject);
+ fontSize = Float.parseFloat(fontArr[1]);
+ } else if (fontArr.length == 2 && fontArr[0].startsWith("TTF:")) {
+ font = generateFont(fontArr[0], null, pdfObject);
+ fontSize = Float.parseFloat(fontArr[1]);
+ } else {
+ logger.warn(
+ "Using default font because: {} is not a valid font descriptor.",
+ desc);
+ this.font = defaultFont;
+ this.fontSize = defaultFontSize;
+ }
+
+ }
+
+ public PDFBoxFont(String fontDesc, ISettings settings,
+ PDFBOXObject pdfObject) throws IOException {
+ this.settings = settings;
+ this.fontDesc = fontDesc;
+ logger.debug("Creating Font: " + fontDesc);
+ this.setFont(fontDesc, pdfObject);
+ }
+
+ public PDFont getFont(/*PDFBOXObject pdfObject*/) throws IOException {
+ if (cachedfont != null) {
+ return cachedfont;
+ }
+ return font;
+ /*
+ if (font instanceof PDTrueTypeFont && pdfObject != null) {
+ return generateTTF(ttfFontDesc, pdfObject);
+ } else {
+ return font;
+ }*/
+ }
+
+ public float getFontSize() {
+ return fontSize;
+ }
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFBoxTable.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFBoxTable.java
new file mode 100644
index 00000000..415e1665
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFBoxTable.java
@@ -0,0 +1,722 @@
+/*******************************************************************************
+ * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright>
+ * 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.
+ ******************************************************************************/
+package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+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.gv.egiz.pdfas.common.utils.ImageUtils;
+import at.gv.egiz.pdfas.common.utils.StringUtils;
+import at.gv.egiz.pdfas.lib.impl.pdfbox2.PDFBOXObject;
+import at.knowcenter.wag.egov.egiz.table.Entry;
+import at.knowcenter.wag.egov.egiz.table.Style;
+import at.knowcenter.wag.egov.egiz.table.Table;
+
+public class PDFBoxTable {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(PDFBoxTable.class);
+
+ Table table;
+ Style style;
+ PDFBoxFont font;
+ PDFBoxFont valueFont;
+ ISettings settings;
+
+ float padding;
+ int positionX = 0;
+ int positionY = 0;
+ float tableWidth;
+ float tableHeight;
+ Color bgColor;
+
+ boolean[] addPadding;
+ float[] rowHeights;
+ float[] colWidths;
+
+ PDDocument originalDoc;
+
+ PDFBOXObject pdfBoxObject;
+
+ private void normalizeContent(Table abstractTable) throws PdfAsException {
+ try {
+ int rows = abstractTable.getRows().size();
+ for (int i = 0; i < rows; i++) {
+ ArrayList<Entry> row = this.table.getRows().get(i);
+ for (int j = 0; j < row.size(); j++) {
+ Entry cell = (Entry) row.get(j);
+
+ switch (cell.getType()) {
+ case Entry.TYPE_CAPTION:
+ case Entry.TYPE_VALUE:
+ String value = (String) cell.getValue();
+ cell.setValue(StringUtils
+ .convertStringToPDFFormat(value));
+ break;
+ }
+ }
+ }
+ } catch (UnsupportedEncodingException e) {
+ throw new PdfAsException("Unsupported Encoding", e);
+ }
+ }
+
+ private void initializeStyle(Table abstractTable, PDFBoxTable parent,
+ PDFBOXObject pdfBoxObject) throws IOException {
+ this.table = abstractTable;
+ try {
+ normalizeContent(abstractTable);
+ } catch (PdfAsException e) {
+ throw new PdfAsWrappedIOException(e);
+ }
+
+ if (parent != null) {
+ style = Style.doInherit(abstractTable.getStyle(), parent.style);
+ } else {
+ style = abstractTable.getStyle();
+ }
+
+ if (style == null) {
+ throw new IOException("Failed to determine Table style, for table "
+ + abstractTable.getName());
+ }
+
+ String fontString = style.getFont();
+
+ String vfontString = style.getValueFont();
+
+ if (parent != null && style == parent.style) {
+ font = parent.getFont();
+
+ valueFont = parent.getValueFont();
+ } else {
+ if (fontString == null && parent != null && parent.style != null) {
+ fontString = parent.style.getFont();
+ } else if (fontString == null) {
+ throw new IOException(
+ "Failed to determine Table font style, for table "
+ + abstractTable.getName());
+ }
+
+ font = new PDFBoxFont(fontString, settings, pdfBoxObject);
+
+ if (vfontString == null && parent != null && parent.style != null) {
+ vfontString = parent.style.getValueFont();
+ } else if (fontString == null) {
+ throw new IOException(
+ "Failed to determine value Table font style, for table "
+ + abstractTable.getName());
+ }
+
+ valueFont = new PDFBoxFont(vfontString, settings, pdfBoxObject);
+ }
+ padding = style.getPadding();
+
+ bgColor = style.getBgColor();
+ }
+
+ public PDFBoxTable(Table abstractTable, PDFBoxTable parent, float fixSize,
+ ISettings settings, PDFBOXObject pdfBoxObject) throws IOException,
+ PdfAsException {
+ this.settings = settings;
+ this.pdfBoxObject = pdfBoxObject;
+ this.originalDoc = pdfBoxObject.getDocument();
+ initializeStyle(abstractTable, parent, pdfBoxObject);
+ float[] relativSizes = abstractTable.getColsRelativeWith();
+ if (relativSizes != null) {
+ colWidths = new float[relativSizes.length];
+ float totalrel = 0;
+
+ for (int i = 0; i < relativSizes.length; i++) {
+ totalrel += relativSizes[i];
+ }
+
+ float unit = (fixSize / totalrel);
+
+ for (int i = 0; i < relativSizes.length; i++) {
+
+ colWidths[i] = unit * relativSizes[i];
+ }
+ } else {
+ colWidths = new float[abstractTable.getMaxCols()];
+ float totalrel = abstractTable.getMaxCols();
+ float unit = (fixSize / totalrel);
+ for (int i = 0; i < colWidths.length; i++) {
+
+ colWidths[i] = unit;
+ }
+ }
+ calculateHeightsBasedOnWidths();
+
+ logger.debug("Generating Table with fixed With {} got width {}",
+ fixSize, getWidth());
+ }
+
+ public PDFBoxTable(Table abstractTable, PDFBoxTable parent,
+ ISettings settings, PDFBOXObject pdfBoxObject) throws IOException,
+ PdfAsException {
+ this.settings = settings;
+ this.pdfBoxObject = pdfBoxObject;
+ this.originalDoc = pdfBoxObject.getDocument();
+ initializeStyle(abstractTable, parent, pdfBoxObject);
+ this.calculateWidthHeight();
+ }
+
+ private void calculateHeightsBasedOnWidths() throws IOException,
+ PdfAsException {
+ int rows = this.table.getRows().size();
+ rowHeights = new float[rows];
+ addPadding = new boolean[rows];
+
+ for (int i = 0; i < rows; i++) {
+ rowHeights[i] = 0;
+ }
+
+ for (int i = 0; i < rows; i++) {
+ ArrayList<Entry> row = this.table.getRows().get(i);
+ for (int j = 0; j < row.size(); j++) {
+ Entry cell = (Entry) row.get(j);
+
+ float colWidth = 0;// colWidths[j];
+
+ int colsleft = cell.getColSpan();
+
+ if (j + colsleft > colWidths.length) {
+ throw new IOException(
+ "Configuration is wrong. Cannot determine column width!");
+ }
+
+ for (int k = 0; k < colsleft; k++) {
+ colWidth = colWidth + colWidths[j + k];
+ }
+
+ float cellheight = getCellHeight(cell, colWidth);
+
+ if (rowHeights[i] < cellheight) {
+ rowHeights[i] = cellheight;
+ }
+
+ logger.debug("ROW: {} COL: {} Width: {} Height: {}", i, j,
+ colWidth, cellheight);
+
+ int span = cell.getColSpan() - 1;
+ j += span;
+ }
+ }
+
+ calcTotals();
+ }
+
+ private void calculateWidthHeight() throws IOException, PdfAsException {
+ int cols = this.table.getMaxCols();
+ colWidths = new float[cols];
+
+ for (int i = 0; i < cols; i++) {
+ colWidths[i] = 0;
+ }
+
+ int rows = this.table.getRows().size();
+ rowHeights = new float[rows];
+
+ for (int i = 0; i < rows; i++) {
+ rowHeights[i] = 0;
+ }
+
+ for (int i = 0; i < rows; i++) {
+ ArrayList<Entry> row = this.table.getRows().get(i);
+ for (int j = 0; j < row.size(); j++) {
+ Entry cell = (Entry) row.get(j);
+ float cellWidth = getCellWidth(cell);
+
+ if (colWidths[j] < cellWidth) {
+ colWidths[j] = cellWidth;
+ }
+
+ float cellheight = getCellHeight(cell);
+
+ if (rowHeights[i] < cellheight) {
+ rowHeights[i] = cellheight;
+ }
+
+ logger.debug("ROW: {} COL: {} Width: {} Height: {}", i, j,
+ cellWidth, cellheight);
+
+ int span = cell.getColSpan() - 1;
+ j += span;
+ }
+ }
+
+ calcTotals();
+ }
+
+ private void calcTotals() {
+
+ this.tableHeight = 0;
+
+ for (int i = 0; i < rowHeights.length; i++) {
+ this.tableHeight += rowHeights[i];
+ }
+
+ // Post Process heights for inner Tables ...
+ for (int i = 0; i < rowHeights.length; i++) {
+ ArrayList<Entry> 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];
+ }
+ }
+
+ private float getCellWidth(Entry cell) throws IOException, PdfAsException {
+ boolean isValue = true;
+ switch (cell.getType()) {
+ case Entry.TYPE_CAPTION:
+ isValue = false;
+ case Entry.TYPE_VALUE:
+ PDFont c = null;
+ float fontSize;
+ String string = (String) cell.getValue();
+ if (isValue) {
+ c = valueFont.getFont();//null
+ fontSize = valueFont.getFontSize();
+ } else {
+ c = font.getFont();//null
+ fontSize = font.getFontSize();
+ }
+ if (string == null) {
+ string = "";
+ cell.setValue(string);
+ }
+ if (string.contains("\n")) {
+ float maxWidth = 0;
+ String[] lines = string.split("\n");
+ for (int i = 0; i < lines.length; i++) {
+ float w = c.getStringWidth(lines[i]) / 1000 * fontSize;
+ if (maxWidth < w) {
+ maxWidth = w;
+ }
+ }
+ return maxWidth;
+ } else {
+ return c.getStringWidth(string) / 1000 * fontSize;
+ }
+ case Entry.TYPE_IMAGE:
+ if (style != null && style.getImageScaleToFit() != null) {
+ return style.getImageScaleToFit().getWidth();
+ }
+ return 80.f;
+ case Entry.TYPE_TABLE:
+ PDFBoxTable pdfBoxTable = null;
+ if (cell.getValue() instanceof Table) {
+ pdfBoxTable = new PDFBoxTable((Table) cell.getValue(), this,
+ this.settings, pdfBoxObject);
+ cell.setValue(pdfBoxTable);
+ } else if (cell.getValue() instanceof PDFBoxTable) {
+ pdfBoxTable = (PDFBoxTable) cell.getValue();
+ } else {
+ throw new IOException("Failed to build PDFBox Table");
+ }
+ return pdfBoxTable.getWidth();
+ default:
+ logger.warn("Invalid Cell Entry Type: " + cell.getType());
+ }
+ return 0;
+ }
+
+ private String concatLines(String[] lines) {
+ String v = "";
+ for (int i = 0; i < lines.length; i++) {
+ v += lines[i];
+ if (i + 1 < lines.length) {
+ v += "\n";
+ }
+ }
+ return v;
+ }
+
+ private String[] breakString(String value, float maxwidth, PDFont font,
+ float fontSize) throws IOException {
+ String[] words = value.split(" ");
+ List<String> lines = new ArrayList<String>();
+ String cLineValue = "";
+ for (int i = 0; i < words.length; i++) {
+ String word = words[i];
+ String[] lineBreaks = word.split("\n");
+ if (lineBreaks.length > 1) {
+ for (int j = 0; j < lineBreaks.length; j++) {
+ String subword = lineBreaks[j];
+ // if (cLine + subword.length() > maxline) {
+ if (j == 0 && word.startsWith("\n")) {
+ lines.add(cLineValue.trim());
+ cLineValue = "";
+ } else if (j != 0) {
+ lines.add(cLineValue.trim());
+ cLineValue = "";
+ }
+ // }
+ String tmpLine = cLineValue + subword;
+ float size = font.getStringWidth(tmpLine) / 1000.0f
+ * fontSize;
+ if (size > maxwidth && cLineValue.length() != 0) {
+ lines.add(cLineValue.trim());
+ cLineValue = "";
+ }
+ cLineValue += subword + " ";
+ }
+ } else {
+ String tmpLine = cLineValue + word;
+ float size = font.getStringWidth(tmpLine) / 1000.0f * fontSize;
+ if (size > maxwidth && cLineValue.length() != 0) {
+ lines.add(cLineValue.trim());
+ cLineValue = "";
+ }
+ cLineValue += word + " ";
+ }
+ }
+ lines.add(cLineValue.trim());
+ return lines.toArray(new String[0]);
+ }
+
+ private String[] breakString(String value, int maxline) {
+ String[] words = value.split(" ");
+ List<String> lines = new ArrayList<String>();
+ int cLine = 0;
+ String cLineValue = "";
+ for (int i = 0; i < words.length; i++) {
+ String word = words[i];
+ String[] lineBreaks = word.split("\n");
+ if (lineBreaks.length > 1) {
+ for (int j = 0; j < lineBreaks.length; j++) {
+ String subword = lineBreaks[j];
+ // if (cLine + subword.length() > maxline) {
+ lines.add(cLineValue.trim());
+ cLineValue = "";
+ cLine = 0;
+ // }
+ cLineValue += subword + " ";
+ cLine += subword.length();
+ }
+ } else {
+ if (cLine + word.length() > maxline && cLineValue.length() != 0) {
+ lines.add(cLineValue.trim());
+ cLineValue = "";
+ cLine = 0;
+ }
+ cLineValue += word + " ";
+ cLine += word.length();
+ }
+ }
+ lines.add(cLineValue.trim());
+ return lines.toArray(new String[0]);
+ }
+
+ // private String[] breakString(String value, PDFont f, float maxwidth)
+ // throws IOException {
+ // String[] words = value.split(" ");
+ // List<String> lines = new ArrayList<String>();
+ // int cLine = 0;
+ // String cLineValue = "";
+ // for (int i = 0; i < words.length; i++) {
+ // String word = words[i];
+ // String[] lineBreaks = word.split("\n");
+ // if (lineBreaks.length > 1) {
+ // for (int j = 0; j < lineBreaks.length; j++) {
+ // String subword = lineBreaks[j];
+ // // if (cLine + subword.length() > maxline) {
+ // lines.add(cLineValue.trim());
+ // cLineValue = "";
+ // cLine = 0;
+ // // }
+ // cLineValue += subword + " ";
+ // cLine += subword.length();
+ // }
+ // } else {
+ // if (f.getStringWidth(cLineValue + word) > maxwidth && cLineValue.length()
+ // != 0) {
+ // lines.add(cLineValue.trim());
+ // cLineValue = "";
+ // cLine = 0;
+ // }
+ // cLineValue += word + " ";
+ // cLine += word.length();
+ // }
+ // }
+ // lines.add(cLineValue.trim());
+ // 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.getHeight(linebytes[j]) / 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,
+ PdfAsException {
+ boolean isValue = true;
+ switch (cell.getType()) {
+ case Entry.TYPE_CAPTION:
+ isValue = false;
+ case Entry.TYPE_VALUE:
+ PDFont c = null;
+ float fontSize;
+ String string = (String) cell.getValue();
+ if (isValue) {
+ c = valueFont.getFont();//null
+ fontSize = valueFont.getFontSize();
+ } else {
+ c = font.getFont();//null
+ fontSize = font.getFontSize();
+ }
+
+ String[] lines = breakString(string, (width - padding * 2.0f), c,
+ fontSize);
+ cell.setValue(concatLines(lines));
+ float[] heights = getStringHeights(lines, c, fontSize);
+ return fontSize * heights.length + padding * 2;
+ case Entry.TYPE_IMAGE:
+ String imageFile = (String) cell.getValue();
+ if (style != null && style.getImageScaleToFit() != null) {
+ // if (style.getImageScaleToFit().getHeight() < width) {
+ return style.getImageScaleToFit().getHeight() + padding * 2;
+ // }
+ }
+ Dimension dim = ImageUtils.getImageDimensions(imageFile, settings);
+ float wfactor = (float) ((width - padding * 2.0f) / dim.getWidth());
+ float scaleFactor = wfactor;
+ float iheight = (float) Math
+ .floor((double) (scaleFactor * dim.getHeight()));
+ //if (dim.getHeight() > 80.0f) {
+ // return width + padding * 2;
+ //}
+ return (float) iheight + padding * 2;
+ case Entry.TYPE_TABLE:
+ PDFBoxTable pdfBoxTable = null;
+ if (cell.getValue() instanceof Table) {
+ pdfBoxTable = new PDFBoxTable((Table) cell.getValue(), this,
+ width, this.settings, this.pdfBoxObject);
+ 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,
+ this.settings, this.pdfBoxObject);
+ cell.setValue(pdfBoxTable);
+ } else {
+ throw new IOException("Failed to build PDFBox Table");
+ }
+ return pdfBoxTable.getHeight();
+ default:
+ logger.warn("Invalid Cell Entry Type: " + cell.getType());
+ }
+ return 0;
+ }
+
+ private float getCellHeight(Entry cell) throws IOException, PdfAsException {
+ boolean isValue = true;
+ switch (cell.getType()) {
+ case Entry.TYPE_CAPTION:
+ isValue = false;
+ case Entry.TYPE_VALUE:
+ PDFont c = null;
+ float fontSize;
+ String string = (String) cell.getValue();
+ if (isValue) {
+ c = valueFont.getFont();//null
+ fontSize = valueFont.getFontSize();
+ } else {
+ c = font.getFont();//null
+ fontSize = font.getFontSize();
+ }
+
+ if (string.contains("\n")) {
+ String[] lines = string.split("\n");
+
+ return fontSize * lines.length + padding * 2;
+ } else {
+ return fontSize + padding * 2;
+ }
+ case Entry.TYPE_IMAGE:
+ String imageFile = (String) cell.getValue();
+ if (style != null && style.getImageScaleToFit() != null) {
+ return style.getImageScaleToFit().getHeight() + padding * 2;
+ }
+ Dimension dim = ImageUtils.getImageDimensions(imageFile, settings);
+ if (dim.getHeight() > 80.0f) {
+ return 80.0f + padding * 2;
+ }
+ return (float) dim.getHeight() + padding * 2;
+
+ case Entry.TYPE_TABLE:
+ PDFBoxTable pdfBoxTable = null;
+ if (cell.getValue() instanceof Table) {
+ pdfBoxTable = new PDFBoxTable((Table) cell.getValue(), this,
+ this.settings, pdfBoxObject);
+ cell.setValue(pdfBoxTable);
+ } else if (cell.getValue() instanceof PDFBoxTable) {
+ pdfBoxTable = (PDFBoxTable) cell.getValue();
+ } else {
+ throw new IOException("Failed to build PDFBox Table");
+ }
+ return pdfBoxTable.getHeight();
+ default:
+ logger.warn("Invalid Cell Entry Type: " + cell.getType());
+ }
+ return 0;
+ }
+
+ public int getX() {
+ return this.positionX;
+ }
+
+ public int getY() {
+ return this.positionY;
+ }
+
+ public float getWidth() {
+ return tableWidth;
+ }
+
+ 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;
+ }
+
+ public int getRowCount() {
+ return this.table.getRows().size();
+ }
+
+ public int getColCount() {
+ return this.table.getMaxCols();// .getColsRelativeWith().length;
+ }
+
+ public float[] getColsRelativeWith() {
+ return this.table.getColsRelativeWith();
+ }
+
+ public float getPadding() {
+ return this.padding;
+ }
+
+ public void dumpTable() {
+ logger.debug("=====================================================================");
+ logger.debug("Information about: " + this.table.getName());
+ logger.debug("\tDimensions: {} x {} (W x H)", this.tableWidth,
+ this.tableHeight);
+ logger.debug("\tPadding: {}", padding);
+ logger.debug("\t================================");
+ logger.debug("\tRow Heights:");
+ for (int i = 0; i < rowHeights.length; i++) {
+ logger.debug("\t[{}] : {}", i, this.rowHeights[i]);
+ }
+ logger.debug("\t================================");
+ logger.debug("\tCol Widths:");
+ for (int i = 0; i < colWidths.length; i++) {
+ logger.debug("\t[{}] : {}", i, this.colWidths[i]);
+ }
+ logger.debug("\t================================");
+ logger.debug("\tTable:");
+ logger.debug("\t" + this.table.toString());
+ logger.debug("=====================================================================");
+ }
+
+ public Table getOrigTable() {
+ return this.table;
+ }
+
+ public ArrayList<Entry> getRow(int i) {
+ return this.table.getRows().get(i);
+ }
+
+ public PDFBoxFont getFont() {
+ return font;
+ }
+
+ public PDFBoxFont getValueFont() {
+ return valueFont;
+ }
+
+ public Color getBGColor() {
+ return this.bgColor;
+ }
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PdfBoxStamper.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PdfBoxStamper.java
new file mode 100644
index 00000000..f89d53c5
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PdfBoxStamper.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright>
+ * 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.
+ ******************************************************************************/
+package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2;
+
+import java.io.IOException;
+
+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.gv.egiz.pdfas.lib.impl.pdfbox2.PDFBOXObject;
+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.Table;
+
+public class PdfBoxStamper implements IPDFStamper {
+
+// private static final Logger logger = LoggerFactory.getLogger(PdfBoxStamper.class);
+
+// private PDFTemplateBuilder pdfBuilder;
+
+ public PdfBoxStamper() {
+// this.pdfBuilder = new PDVisibleSigBuilder();
+ }
+
+ public IPDFVisualObject createVisualPDFObject(PDFObject pdf, Table table) throws IOException {
+ try {
+ PDFBOXObject pdfboxObject = (PDFBOXObject)pdf;
+ return new PdfBoxVisualObject(table, pdf.getStatus().getSettings(), pdfboxObject);
+ } catch (PdfAsException e) {
+ throw new PdfAsWrappedIOException(e);
+ }
+ }
+
+ public byte[] writeVisualObject(IPDFVisualObject visualObject,
+ PositioningInstruction positioningInstruction, byte[] pdfData,
+ String placeholderName) throws PdfAsException {
+ return null;
+ }
+
+ public void setSettings(ISettings settings) {
+ // not needed currently
+ }
+
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PdfBoxVisualObject.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PdfBoxVisualObject.java
new file mode 100644
index 00000000..3a4c3957
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PdfBoxVisualObject.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright>
+ * 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.
+ ******************************************************************************/
+package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2;
+
+import java.io.IOException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
+import at.gv.egiz.pdfas.common.settings.ISettings;
+import at.gv.egiz.pdfas.lib.impl.pdfbox2.PDFBOXObject;
+import at.gv.egiz.pdfas.lib.impl.stamping.IPDFVisualObject;
+import at.knowcenter.wag.egov.egiz.table.Table;
+
+public class PdfBoxVisualObject implements IPDFVisualObject {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(PdfBoxVisualObject.class);
+
+ private Table abstractTable;
+ private PDFBoxTable table;
+ private float width;
+ private float x;
+ private float y;
+ private int page;
+ private ISettings settings;
+ private PDFBOXObject pdfBoxObject;
+
+ public PdfBoxVisualObject(Table table, ISettings settings, PDFBOXObject pdfBoxObject)
+ throws IOException, PdfAsException {
+ this.abstractTable = table;
+ this.pdfBoxObject = pdfBoxObject;
+ this.table = new PDFBoxTable(table, null, settings, pdfBoxObject);
+ this.settings = settings;
+ }
+
+ public void setWidth(float width) {
+ this.width = width;
+ }
+
+ public void fixWidth() {
+ try {
+ table = new PDFBoxTable(abstractTable, null, this.width, settings, this.pdfBoxObject);
+ } catch (IOException e) {
+ logger.warn("Failed to fix width of Table!", e);
+ } catch (PdfAsException e) {
+ logger.warn("Failed to fix width of Table!", e);
+ }
+ }
+
+ public float getHeight() {
+ return table.getHeight();
+ }
+
+ public float getWidth() {
+ return table.getWidth();
+ }
+
+ 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 PDFBoxTable getTable() {
+ return this.table;
+ }
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/StamperFactory.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/StamperFactory.java
new file mode 100644
index 00000000..90561740
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/StamperFactory.java
@@ -0,0 +1,25 @@
+package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2;
+
+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;
+
+public class StamperFactory {
+
+ //public static final String DEFAULT_STAMPER_CLASS = "at.gv.egiz.pdfas.stmp.itext.ITextStamper";
+ public static final String DEFAULT_STAMPER_CLASS = "at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2.PdfBoxStamper";
+
+ public static IPDFStamper createDefaultStamper(ISettings settings) throws PdfAsException {
+ try {
+ Class<?> cls = Class.forName(DEFAULT_STAMPER_CLASS);
+ Object st = cls.newInstance();
+ if (!(st instanceof IPDFStamper))
+ throw new ClassCastException();
+ IPDFStamper stamper = (IPDFStamper) st;
+ stamper.setSettings(settings);
+ return stamper;
+ } catch (Throwable e) {
+ throw new PdfAsException("error.pdf.stamp.10", e);
+ }
+ }
+} \ No newline at end of file
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/TableDrawUtils.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/TableDrawUtils.java
new file mode 100644
index 00000000..aa2a397d
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/TableDrawUtils.java
@@ -0,0 +1,609 @@
+/*******************************************************************************
+ * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright>
+ * 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.
+ ******************************************************************************/
+package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
+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<String, ImageObject> images, ISettings settings, IDGenerator generator, PDFAsVisualSignatureProperties properties)
+ 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);
+//append strings
+ drawContent(page, contentStream, x, y, width, height, abstractTable,
+ doc, subtable, formResources, images, settings, generator, properties);
+ }
+
+ 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<String, ImageObject> images, ISettings settings, IDGenerator generator, PDFAsVisualSignatureProperties properties)
+ throws PdfAsException {
+
+ float contentx = x;
+ float contenty = y + height;
+ float padding = abstractTable.getPadding();
+ float[] colsSizes = getColSizes(abstractTable);
+ StringBuilder alternateTableCaption = new StringBuilder();
+ for (int i = 0; i < abstractTable.getRowCount(); i++) {
+ ArrayList<Entry> row = abstractTable.getRow(i);
+ for (int j = 0; j < row.size(); j++) {
+ Entry cell = (Entry) row.get(j);
+
+ // Cell only contains default values so table style is the primary style
+ Style inherit_style = Style.doInherit(abstractTable.style, cell.getStyle());
+ cell.setStyle(inherit_style);
+
+ float colWidth = 0;//colWidths[j];
+
+ int colsleft = cell.getColSpan();
+
+ if (j + colsleft > colsSizes.length) {
+ throw new PdfAsException(
+ "Configuration is wrong. Cannot determine column width!");
+ }
+
+ for (int k = 0; k < colsleft; k++) {
+ colWidth = colWidth + colsSizes[j + k];
+ }
+
+ drawDebugPadding(contentStream, contentx, contenty, padding,
+ colWidth, abstractTable.getRowHeights()[i], settings);
+
+ switch (cell.getType()) {
+ case Entry.TYPE_CAPTION:
+ drawCaption(page, contentStream, contentx, contenty,
+ colWidth, abstractTable.getRowHeights()[i],
+ padding, abstractTable, doc, cell, formResources, settings);
+ addToAlternateTableCaption(cell, alternateTableCaption);
+ break;
+ case Entry.TYPE_VALUE:
+ drawValue(page, contentStream, contentx, contenty,
+ colWidth, abstractTable.getRowHeights()[i],
+ padding, abstractTable, doc, cell, formResources, settings);
+ addToAlternateTableCaption(cell, alternateTableCaption);
+ break;
+ case Entry.TYPE_IMAGE:
+ drawImage(page, contentStream, contentx, contenty,
+ colWidth, abstractTable.getRowHeights()[i],
+ padding, abstractTable, doc, cell, formResources,
+ images, settings, generator);
+ 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], colWidth,
+ abstractTable.getRowHeights()[i], tbl_value, doc,
+ true, formResources, images, settings, generator,properties);
+ break;
+ default:
+ logger.warn("Unknown Cell entry type: " + cell.getType());
+ break;
+ }
+
+ // Move content pointer
+ contentx += colWidth;
+
+ int span = cell.getColSpan() - 1;
+ j += span;
+ }
+
+ // Move content pointer
+ contenty -= abstractTable.getRowHeights()[i];
+ contentx = x;
+ }
+ properties.setAlternativeTableCaption(alternateTableCaption.toString());
+ }
+
+ 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;
+ }
+ }
+ float ascent = textFont.getFontDescriptor().getAscent();
+ float descent = textFont.getFontDescriptor().getDescent();
+
+ ascent = ascent / 1000.0f * fontSize;
+ descent = descent / 1000.0f * fontSize;
+
+ //ty = ty + (descent * (-1));
+
+ logger.debug("Text tx {} ty {} maxWidth {} textHeight {}", tx, ty,
+ maxWidth, textHeight);
+ logger.debug("Text ASCENT {} DESCENT {}", ascent, descent);
+
+ logger.debug("Text TRANSFORMED ASCENT {} DESCENT {}", ascent, descent);
+
+ drawDebugLineString(contentStream, tx, ty, maxWidth, textHeight, descent, settings);
+
+ contentStream.beginText();
+
+
+ if (formResources.getFont(COSName.getPDFName(textFont.getName())) != null) {
+ 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 + (descent * (-1))) + " as "
+ + cell.getType());
+ contentStream.moveTextPositionByAmount(tx, (ty - fontSize + (descent * (-1))));
+
+ 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.warn("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.warn("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.warn("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<String, ImageObject> images,
+ ISettings settings, IDGenerator generator) throws PdfAsException {
+ try {
+ float innerHeight = height;
+ float innerWidth = width;
+
+ String img_ref = generator.createHashedId((String) cell.getValue());
+ if (!images.containsKey(img_ref)) {
+ logger.warn("Image not prepared! : " + img_ref);
+ throw new PdfAsException("Image not prepared! : " + img_ref);
+ }
+ ImageObject image = images.get(img_ref);
+ PDImageXObject 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.warn("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.debug("Table Col Sizes SUM {} Table Width {}", sum,
+ abstractTable.getWidth());
+ logger.debug("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<Entry> 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 drawDebugLineString(PDPageContentStream contentStream,
+ float x, float y, float width, float height, float descent, 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.MAGENTA);
+ contentStream.drawLine(x, y + (descent * (-1)) - height, x + width, y + (descent * (-1)) - 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<java.util.Map.Entry<String, PDFont>> it = resources.getfon
+// .entrySet().iterator();
+ Iterator<COSName> it = resources.getFontNames().iterator();
+ while (it.hasNext()) {
+ COSName entry = it.next();
+ if (entry.getName().equals(font.getName())) {
+ return entry.getName();
+ }
+ }
+ return "";
+ }
+
+ private static void addToAlternateTableCaption(Entry cell, StringBuilder alternateTableCaption){
+ alternateTableCaption.append(cell.getValue());
+ alternateTableCaption.append("\n");//better for screen reader
+ }
+
+
+}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/pdfbox2/PDFBOXVerifier.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/pdfbox2/PDFBOXVerifier.java
new file mode 100644
index 00000000..270e9e28
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/pdfbox2/PDFBOXVerifier.java
@@ -0,0 +1,158 @@
+package at.gv.egiz.pdfas.lib.impl.verify.pdfbox2;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSString;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import at.gv.egiz.pdfas.common.exceptions.PDFASError;
+import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
+import at.gv.egiz.pdfas.common.settings.ISettings;
+import at.gv.egiz.pdfas.lib.api.verify.VerifyParameter;
+import at.gv.egiz.pdfas.lib.api.verify.VerifyResult;
+import at.gv.egiz.pdfas.lib.impl.ErrorExtractor;
+import at.gv.egiz.pdfas.lib.impl.verify.IVerifier;
+import at.gv.egiz.pdfas.lib.impl.verify.IVerifyFilter;
+import at.gv.egiz.pdfas.lib.impl.verify.VerifierDispatcher;
+import at.gv.egiz.pdfas.lib.impl.verify.VerifyBackend;
+
+public class PDFBOXVerifier implements VerifyBackend {
+
+ private static final Logger logger = LoggerFactory.getLogger(PDFBOXVerifier.class);
+
+ @Override
+ public List<VerifyResult> verify(VerifyParameter parameter) throws PDFASError {
+ int signatureToVerify = parameter.getWhichSignature();
+ int currentSignature = 0;
+ PDDocument doc = null;
+ try {
+ List<VerifyResult> result = new ArrayList<VerifyResult>();
+ ISettings settings = (ISettings) parameter.getConfiguration();
+ VerifierDispatcher verifier = new VerifierDispatcher(settings);
+ doc = PDDocument.load(parameter.getDataSource().getInputStream());
+
+ COSDictionary trailer = doc.getDocument().getTrailer();
+ if (trailer == null) {
+ // No signatures ...
+ return result;
+ }
+ COSDictionary root = (COSDictionary) trailer.getDictionaryObject(COSName.ROOT);
+ if (root == null) {
+ // No signatures ...
+ return result;
+ }
+ COSDictionary acroForm = (COSDictionary) root.getDictionaryObject(COSName.ACRO_FORM);
+ if (acroForm == null) {
+ // No signatures ...
+ return result;
+ }
+ COSArray fields = (COSArray) acroForm.getDictionaryObject(COSName.FIELDS);
+ if (fields == null) {
+ // No signatures ...
+ return result;
+ }
+
+ int lastSig = -1;
+ for (int i = 0; i < fields.size(); i++) {
+ COSDictionary field = (COSDictionary) fields.getObject(i);
+ String type = field.getNameAsString("FT");
+ if ("Sig".equals(type)) {
+ lastSig = i;
+ }
+ }
+
+ byte[] inputData = IOUtils.toByteArray(parameter.getDataSource().getInputStream());
+
+ for (int i = 0; i < fields.size(); i++) {
+ COSDictionary field = (COSDictionary) fields.getObject(i);
+ String type = field.getNameAsString("FT");
+ if ("Sig".equals(type)) {
+ boolean verifyThis = true;
+
+ if (signatureToVerify >= 0) {
+ // verify only specific siganture!
+ verifyThis = signatureToVerify == currentSignature;
+ }
+
+ if (signatureToVerify == -2) {
+ verifyThis = i == lastSig;
+ }
+
+ if (verifyThis) {
+ logger.trace("Found Signature: ");
+ COSBase base = field.getDictionaryObject("V");
+ COSDictionary dict = (COSDictionary) base;
+
+ logger.debug("Signer: " + dict.getNameAsString("Name"));
+ logger.debug("SubFilter: " + dict.getNameAsString("SubFilter"));
+ logger.debug("Filter: " + dict.getNameAsString("Filter"));
+ logger.debug("Modified: " + dict.getNameAsString("M"));
+ COSArray byteRange = (COSArray) dict.getDictionaryObject("ByteRange");
+
+ StringBuilder sb = new StringBuilder();
+ int[] bytes = new int[byteRange.size()];
+ for (int j = 0; j < byteRange.size(); j++) {
+ bytes[j] = byteRange.getInt(j);
+ sb.append(" " + bytes[j]);
+ }
+
+ logger.debug("ByteRange" + sb.toString());
+
+ COSString content = (COSString) dict.getDictionaryObject("Contents");
+
+ ByteArrayOutputStream contentData = new ByteArrayOutputStream();
+ for (int j = 0; j < bytes.length; j = j + 2) {
+ int offset = bytes[j];
+ int length = bytes[j + 1];
+
+ contentData.write(inputData, offset, length);
+ }
+ contentData.close();
+
+ IVerifyFilter verifyFilter = verifier.getVerifier(dict.getNameAsString("Filter"),
+ dict.getNameAsString("SubFilter"));
+
+ IVerifier lvlVerifier = verifier.getVerifierByLevel(parameter.getSignatureVerificationLevel());
+ synchronized (lvlVerifier) {
+ lvlVerifier.setConfiguration(parameter.getConfiguration());
+ if (verifyFilter != null) {
+ List<VerifyResult> results = verifyFilter.verify(contentData.toByteArray(),
+ content.getBytes(), parameter.getVerificationTime(), bytes, lvlVerifier);
+ if (results != null && !results.isEmpty()) {
+ result.addAll(results);
+ }
+ }
+ }
+ }
+ currentSignature++;
+ }
+ }
+ return result;
+ } catch (IOException e) {
+ logger.warn("Failed to verify document", e);
+ throw ErrorExtractor.searchPdfAsError(e, null);
+ } catch (PdfAsException e) {
+ logger.warn("Failed to verify document", e);
+ throw ErrorExtractor.searchPdfAsError(e, null);
+ } finally {
+ if (doc != null) {
+ try {
+ doc.close();
+ } catch (IOException e) {
+ logger.info("Failed to close doc");
+ }
+ }
+ }
+ }
+
+}