aboutsummaryrefslogtreecommitdiff
path: root/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib
diff options
context:
space:
mode:
authorAndreas Fitzek <andreas.fitzek@iaik.tugraz.at>2014-10-16 13:32:35 +0200
committerAndreas Fitzek <andreas.fitzek@iaik.tugraz.at>2014-10-16 13:32:35 +0200
commitc3e2b817615c9c2bbaf056e5547cd3f95f8905ea (patch)
tree6a7946f9ccead542eec2e5d0213fad2ea63f3973 /pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib
parent63784f692a50606f34702706761edac052fdac60 (diff)
downloadpdf-as-4-c3e2b817615c9c2bbaf056e5547cd3f95f8905ea.tar.gz
pdf-as-4-c3e2b817615c9c2bbaf056e5547cd3f95f8905ea.tar.bz2
pdf-as-4-c3e2b817615c9c2bbaf056e5547cd3f95f8905ea.zip
added PDFBOX specifc library
Diffstat (limited to 'pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib')
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/PDFBOXBackend.java40
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/PDFBOXObject.java59
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/placeholder/PDFBoxPlaceholderExtractor.java24
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/placeholder/SignaturePlaceholderExtractor.java371
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/positioning/Positioning.java237
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/utils/PdfBoxUtils.java73
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PADESPDFBOXSigner.java626
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PDFASPDFBOXExtractorInterface.java7
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PDFASPDFBOXSignatureInterface.java10
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PdfboxSignerWrapper.java96
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/SignatureDataExtractor.java94
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/ImageObject.java63
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsTemplateCreator.java170
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureBuilder.java994
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureDesigner.java450
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureProperties.java99
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxFont.java169
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxTable.java691
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PdfBoxStamper.java60
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PdfBoxVisualObject.java97
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/TableDrawUtils.java564
-rw-r--r--pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/pdfbox/PDFBOXVerifier.java172
22 files changed, 5166 insertions, 0 deletions
diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/PDFBOXBackend.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/PDFBOXBackend.java
new file mode 100644
index 00000000..ba1e0088
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/PDFBOXBackend.java
@@ -0,0 +1,40 @@
+package at.gv.egiz.pdfas.lib.impl.pdfbox;
+
+import at.gv.egiz.pdfas.lib.backend.PDFASBackend;
+import at.gv.egiz.pdfas.lib.impl.pdfbox.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.pdfbox.PADESPDFBOXSigner;
+import at.gv.egiz.pdfas.lib.impl.verify.VerifyBackend;
+import at.gv.egiz.pdfas.lib.impl.verify.pdfbox.PDFBOXVerifier;
+
+public class PDFBOXBackend implements PDFASBackend {
+
+ private static final String NAME = "PDFBOX_BACKEND";
+
+ @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/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/PDFBOXObject.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/PDFBOXObject.java
new file mode 100644
index 00000000..f80df075
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/PDFBOXObject.java
@@ -0,0 +1,59 @@
+package at.gv.egiz.pdfas.lib.impl.pdfbox;
+
+import java.io.IOException;
+
+import javax.activation.DataSource;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+
+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;
+
+ 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();
+ }
+ 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());
+ }
+}
diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/placeholder/PDFBoxPlaceholderExtractor.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/placeholder/PDFBoxPlaceholderExtractor.java
new file mode 100644
index 00000000..18099b23
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/placeholder/PDFBoxPlaceholderExtractor.java
@@ -0,0 +1,24 @@
+package at.gv.egiz.pdfas.lib.impl.pdfbox.placeholder;
+
+import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
+import at.gv.egiz.pdfas.lib.impl.pdfbox.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/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/placeholder/SignaturePlaceholderExtractor.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/placeholder/SignaturePlaceholderExtractor.java
new file mode 100644
index 00000000..fe1c0ee7
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/placeholder/SignaturePlaceholderExtractor.java
@@ -0,0 +1,371 @@
+/*******************************************************************************
+ * <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.pdfbox.placeholder;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.exceptions.WrappedIOException;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObject;
+import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage;
+import org.apache.pdfbox.util.Matrix;
+import org.apache.pdfbox.util.PDFOperator;
+import org.apache.pdfbox.util.PDFStreamEngine;
+import org.apache.pdfbox.util.ResourceLoader;
+
+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.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 {
+ /**
+ * The log.
+ */
+ private static Log log = LogFactory
+ .getLog(SignaturePlaceholderExtractor.class);
+
+ public static final String QR_PLACEHOLDER_IDENTIFIER = "PDF-AS-POS";
+ public static final int PLACEHOLDER_MATCH_MODE_STRICT = 0;
+ public static final int PLACEHOLDER_MATCH_MODE_MODERATE = 1;
+ public static final int PLACEHOLDER_MATCH_MODE_LENIENT = 2;
+
+ private List<SignaturePlaceholderData> placeholders = new Vector<SignaturePlaceholderData>();
+ private int currentPage = 0;
+
+ private SignaturePlaceholderExtractor(String placeholderId,
+ int placeholderMatchMode) throws IOException {
+ super(ResourceLoader.loadProperties(
+ "placeholder/pdfbox-reader.properties", true));
+ }
+
+ /**
+ * 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);
+ } catch (IOException e2) {
+ throw new PDFIOException("error.pdf.io.04", e2);
+ }
+ List<?> pages = doc.getDocumentCatalog().getAllPages();
+ Iterator<?> iter = pages.iterator();
+ int pageNr = 0;
+ while (iter.hasNext()) {
+ pageNr++;
+ PDPage page = (PDPage) iter.next();
+ try {
+ extractor.setCurrentPage(pageNr);
+ extractor.processStream(page, page.findResources(), page
+ .getContents().getStream());
+ 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);
+ }
+
+ }
+ 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;
+
+ 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 (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(PDFOperator operator, List<COSBase> arguments)
+ throws IOException {
+ String operation = operator.getOperation();
+ if (operation.equals("Do")) {
+ COSName objectName = (COSName) arguments.get(0);
+ Map<?, ?> xobjects = getResources().getXObjects();
+ PDXObject xobject = (PDXObject) xobjects.get(objectName.getName());
+ if (xobject instanceof PDXObjectImage) {
+ try {
+ PDXObjectImage image = (PDXObjectImage) xobject;
+ SignaturePlaceholderData data = checkImage(image);
+ if (data != null) {
+ PDPage page = getCurrentPage();
+ Matrix ctm = getGraphicsState()
+ .getCurrentTransformationMatrix();
+ double rotationInRadians = (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 y = unrotatedCTM.getYPosition()
+ + unrotatedCTM.getYScale();
+ float w = unrotatedCTM.getXScale();
+
+ String posString = "p:" + currentPage + ";x:" + x
+ + ";y:" + y + ";w:" + w;
+ try {
+ data.setTablePos(new TablePos(posString));
+ data.setPlaceholderName(objectName.getName());
+ placeholders.add(data);
+ } catch (PdfAsException e) {
+ throw new WrappedIOException(e);
+ }
+ }
+ } catch (NoninvertibleTransformException e) {
+ throw new WrappedIOException(e);
+ }
+ }
+ } else {
+ super.processOperator(operator, arguments);
+ }
+ }
+
+ /**
+ * Checks an image if it is a placeholder for a signature.
+ *
+ * @param image
+ * @return
+ * @throws IOException
+ */
+ private SignaturePlaceholderData checkImage(PDXObjectImage image)
+ throws IOException {
+ BufferedImage bimg = image.getRGBImage();
+ if (bimg == null) {
+ String type = image.getSuffix();
+ if (type != null) {
+ type = type.toUpperCase() + " images";
+ } else {
+ type = "Image type";
+ }
+ log.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) {
+ log.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, Vector<BarcodeFormat>> hints = new Hashtable<DecodeHintType, Vector<BarcodeFormat>>();
+ 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) {
+ log.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 {
+ log.warn("QR-Code found but does not start with \""
+ + QR_PLACEHOLDER_IDENTIFIER
+ + "\". Ignoring QR placeholder.");
+ }
+ }
+ } catch (ReaderException re) {
+ if (log.isDebugEnabled()) {
+ log.debug("Could not decode - not a placeholder. needed: "
+ + (System.currentTimeMillis() - before));
+ }
+ if (!(re instanceof NotFoundException)) {
+ if (log.isInfoEnabled()) {
+ log.info("Failed to decode image", re);
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ if (log.isInfoEnabled()) {
+ log.info("Failed to decode image. Probably a zxing bug", e);
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/positioning/Positioning.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/positioning/Positioning.java
new file mode 100644
index 00000000..4efa2148
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/positioning/Positioning.java
@@ -0,0 +1,237 @@
+/*******************************************************************************
+ * <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.pdfbox.positioning;
+
+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.pdfbox.utils.PdfBoxUtils;
+import at.gv.egiz.pdfas.lib.impl.stamping.IPDFVisualObject;
+import at.knowcenter.wag.egov.egiz.pdf.PDFUtilities;
+import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction;
+import at.knowcenter.wag.egov.egiz.pdf.TablePos;
+
+/**
+ * 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) throws PdfAsException {
+ return adjustSignatureTableandCalculatePosition(pdfDataSource,
+ pdf_table, pos, legacy32);
+ }
+
+ /**
+ * 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) 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 = (PDPage) pdfDataSource.getDocumentCatalog()
+ .getAllPages().get(page - 1);
+ PDRectangle cropBox = pdPage.getCropBox();
+
+ // fallback to MediaBox if Cropbox not available!
+
+ if (cropBox == null) {
+ cropBox = pdPage.findCropBox();
+ }
+
+ if (cropBox == null) {
+ cropBox = pdPage.findMediaBox();
+ }
+
+ // getPagedimensions
+ // Rectangle psize = reader.getPageSizeWithRotation(page);
+ // int page_rotation = reader.getPageRotation(page);
+
+ // Integer rotation = pdPage.getRotation();
+ // int page_rotation = rotation.intValue();
+
+ float page_width = cropBox.getWidth();
+ float page_height = cropBox.getHeight();
+
+ // 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);
+
+ 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/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/utils/PdfBoxUtils.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/utils/PdfBoxUtils.java
new file mode 100644
index 00000000..01501f97
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/utils/PdfBoxUtils.java
@@ -0,0 +1,73 @@
+package at.gv.egiz.pdfas.lib.impl.pdfbox.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/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PADESPDFBOXSigner.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PADESPDFBOXSigner.java
new file mode 100644
index 00000000..ee9da479
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PADESPDFBOXSigner.java
@@ -0,0 +1,626 @@
+/*******************************************************************************
+ * <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.pdfbox;
+
+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.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+
+import org.apache.commons.io.IOUtils;
+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.exceptions.COSVisitorException;
+import org.apache.pdfbox.exceptions.SignatureException;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDDocumentCatalog;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageNode;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.graphics.color.PDOutputIntent;
+import org.apache.pdfbox.pdmodel.graphics.xobject.PDJpeg;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+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.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.pdfbox.PDFBOXObject;
+import at.gv.egiz.pdfas.lib.impl.pdfbox.positioning.Positioning;
+import at.gv.egiz.pdfas.lib.impl.pdfbox.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.StamperFactory;
+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.pdfbox.PDFAsVisualSignatureProperties;
+import at.gv.egiz.pdfas.lib.impl.stamping.pdfbox.PdfBoxVisualObject;
+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;
+
+ if (!(genericPdfObject instanceof PDFBOXObject)) {
+ // tODO:
+ throw new PdfAsException();
+ }
+
+ PDFBOXObject pdfObject = (PDFBOXObject) genericPdfObject;
+
+ if (!(genericSigner instanceof PDFASPDFBOXSignatureInterface)) {
+ // tODO:
+ throw new PdfAsException();
+ }
+
+ PDFASPDFBOXSignatureInterface signer = (PDFASPDFBOXSignatureInterface) genericSigner;
+
+ TempFileHelper helper = pdfObject.getStatus().getTempFileHelper();
+ PDDocument doc = null;
+ try {
+ fisTmpFile = helper.getStaticFilename();
+
+ // write to temporary file
+ FileOutputStream fos = new FileOutputStream(new File(fisTmpFile));
+ IOUtils.copy(pdfObject.getOriginalDocument().getInputStream(), fos);
+
+ FileInputStream fis = new FileInputStream(new File(fisTmpFile));
+
+ doc = pdfObject.getDocument();
+
+ 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);
+ SignatureOptions options = new SignatureOptions();
+
+ // Is visible Signature
+ if (requestedSignature.isVisual()) {
+ logger.info("Creating visual siganture block");
+
+ SignatureProfileConfiguration signatureProfileConfiguration = pdfObject
+ .getStatus().getSignatureProfileConfiguration(
+ requestedSignature.getSignatureProfileID());
+
+ SignaturePlaceholderData signaturePlaceholderData = PlaceholderFilter
+ .checkPlaceholderSignature(pdfObject.getStatus(),
+ pdfObject.getStatus().getSettings());
+
+ TablePos tablePos = null;
+
+ if (signaturePlaceholderData != null) {
+ // Placeholder found!
+
+ if (signaturePlaceholderData.getProfile() != null) {
+ requestedSignature
+ .setSignatureProfileID(signaturePlaceholderData
+ .getProfile());
+ }
+
+ tablePos = signaturePlaceholderData.getTablePos();
+ }
+
+ 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();
+
+ // 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);
+
+ 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);
+
+ properties.buildSignature();
+
+ /*
+ * ByteArrayOutputStream sigbos = new ByteArrayOutputStream();
+ * sigbos.write(StreamUtils.inputStreamToByteArray(properties
+ * .getVisibleSignature())); sigbos.close();
+ */
+
+ if (signaturePlaceholderData != null) {
+ // Placeholder found!
+ // replace placeholder
+ InputStream is = PADESPDFBOXSigner.class
+ .getResourceAsStream("/placeholder/empty.jpg");
+ PDJpeg img = new PDJpeg(doc, is);
+ img.getCOSObject().setNeedToBeUpdate(true);
+
+ PDDocumentCatalog root = doc.getDocumentCatalog();
+ PDPageNode rootPages = root.getPages();
+ List<PDPage> kids = new ArrayList<PDPage>();
+ rootPages.getAllKids(kids);
+ int pageNumber = positioningInstruction.getPage();
+ rootPages.getAllKids(kids);
+ PDPage page = kids.get(pageNumber);
+
+ logger.info("Placeholder name: "
+ + signaturePlaceholderData.getPlaceholderName());
+ COSDictionary xobjectsDictionary = (COSDictionary) page
+ .findResources().getCOSDictionary()
+ .getDictionaryObject(COSName.XOBJECT);
+ xobjectsDictionary.setItem(
+ signaturePlaceholderData.getPlaceholderName(), img);
+ xobjectsDictionary.setNeedToBeUpdate(true);
+ page.findResources().getCOSObject().setNeedToBeUpdate(true);
+ logger.info("Placeholder name: "
+ + signaturePlaceholderData.getPlaceholderName());
+ }
+
+ if (positioningInstruction.isMakeNewPage()) {
+ int last = doc.getNumberOfPages() - 1;
+ PDDocumentCatalog root = doc.getDocumentCatalog();
+ PDPageNode rootPages = root.getPages();
+ List<PDPage> kids = new ArrayList<PDPage>();
+ rootPages.getAllKids(kids);
+ PDPage lastPage = kids.get(last);
+ rootPages.getCOSObject().setNeedToBeUpdate(true);
+ PDPage p = new PDPage(lastPage.findMediaBox());
+ p.setResources(new PDResources());
+
+ doc.addPage(p);
+ }
+
+ if (signatureProfileSettings.isPDFA()) {
+ PDDocumentCatalog root = doc.getDocumentCatalog();
+ COSBase base = root.getCOSDictionary().getItem(
+ COSName.OUTPUT_INTENTS);
+ if (base == null) {
+ InputStream 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().setNeedToBeUpdate(true);
+ logger.info("added Output Intent");
+ } catch (Throwable e) {
+ e.printStackTrace();
+ throw new PdfAsException(
+ "Failed to add Output Intent", e);
+ }
+ }
+ }
+
+ // if (signatureProfileSettings.isPDFA()) { // Check for PDF-UA
+ // PDDocumentCatalog root = doc.getDocumentCatalog();
+ // PDStructureTreeRoot treeRoot = root.getStructureTreeRoot();
+ // if (treeRoot != null) { // Handle as PDF-UA
+ // logger.info("Tree Root: {}", treeRoot.toString());
+ // PDStructureElement docElement = PDFBoxTaggingUtils
+ // .getDocumentElement(treeRoot);
+ // PDStructureElement sigBlock = new PDStructureElement(
+ // "Table", docElement);
+ // root.getCOSObject().setNeedToBeUpdate(true);
+ // docElement.getCOSObject().setNeedToBeUpdate(true);
+ // treeRoot.getCOSObject().setNeedToBeUpdate(true);
+ // sigBlock.setTitle("Signature Table");
+ // }
+ // }
+
+ options.setPreferedSignatureSize(0x1000);
+ options.setPage(positioningInstruction.getPage());
+ options.setVisualSignature(properties.getVisibleSignature());
+ }
+
+ 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();
+ if (acroFormm != null) {
+ @SuppressWarnings("unchecked")
+ List<PDField> fields = acroFormm.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 name Signature Field! [Cannot find Field list in acroForm!]");
+ }
+
+ if (signatureField != null) {
+ signatureField.setPartialName(sigFieldName);
+ }
+ } else {
+ logger.warn("Failed to name Signature Field! [Cannot find acroForm!]");
+ }
+
+ 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!]"
+ * ); }
+ */
+ }
+
+ // pdfbox patched (FIS -> IS)
+ doc.saveIncremental(fis, fos);
+ fis.close();
+ fos.flush();
+ fos.close();
+ fos = null;
+
+ fis = new FileInputStream(new File(fisTmpFile));
+
+ // write to resulting output stream
+ // ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ // bos.write();
+ // bos.close();
+
+ pdfObject
+ .setSignedDocument(StreamUtils.inputStreamToByteArray(fis));
+ fis.close();
+ fis = null;
+ System.gc();
+
+ helper.deleteFile(fisTmpFile);
+
+ } catch (IOException e) {
+ logger.error(MessageResolver.resolveMessage("error.pdf.sig.01"), e);
+ throw new PdfAsException("error.pdf.sig.01", e);
+ } catch (SignatureException e) {
+ logger.error(MessageResolver.resolveMessage("error.pdf.sig.01"), e);
+ throw new PdfAsException("error.pdf.sig.01", e);
+ } catch (COSVisitorException e) {
+ logger.error(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!");
+
+ }
+ }
+
+ @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).getHexString();
+ 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(PDPage.PAGE_SIZE_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);
+ } else {
+ positioningInstruction = Positioning.determineTablePositioning(
+ new TablePos(), "", origDoc, visualObject, 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);
+
+ properties.buildSignature();
+ PDDocument visualDoc = PDDocument.load(properties
+ .getVisibleSignature());
+ // PDPageable pageable = new PDPageable(visualDoc);
+ List<PDPage> pages = new ArrayList<PDPage>();
+ visualDoc.getDocumentCatalog().getPages().getAllKids(pages);
+
+ PDPage firstPage = pages.get(0);
+
+ float stdRes = 72;
+ float targetRes = resolution;
+ float factor = targetRes / stdRes;
+
+ 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.error("PDF-AS Exception", e);
+ throw ErrorExtractor.searchPdfAsError(e);
+ } catch (Throwable e) {
+ logger.error("Throwable Exception", e);
+ throw ErrorExtractor.searchPdfAsError(e);
+ }
+ }
+}
diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PDFASPDFBOXExtractorInterface.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PDFASPDFBOXExtractorInterface.java
new file mode 100644
index 00000000..2a2ac4b1
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PDFASPDFBOXExtractorInterface.java
@@ -0,0 +1,7 @@
+package at.gv.egiz.pdfas.lib.impl.signing.pdfbox;
+
+import at.gv.egiz.pdfas.lib.impl.signing.PDFASSignatureExtractor;
+
+public interface PDFASPDFBOXExtractorInterface extends PDFASSignatureExtractor, PDFASPDFBOXSignatureInterface {
+
+}
diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PDFASPDFBOXSignatureInterface.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PDFASPDFBOXSignatureInterface.java
new file mode 100644
index 00000000..54eaaf54
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PDFASPDFBOXSignatureInterface.java
@@ -0,0 +1,10 @@
+package at.gv.egiz.pdfas.lib.impl.signing.pdfbox;
+
+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/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PdfboxSignerWrapper.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PdfboxSignerWrapper.java
new file mode 100644
index 00000000..cad7536e
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/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.pdfbox;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Calendar;
+
+import org.apache.pdfbox.exceptions.SignatureException;
+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 SignatureException,
+ 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/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/SignatureDataExtractor.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/SignatureDataExtractor.java
new file mode 100644
index 00000000..5e3d1085
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/SignatureDataExtractor.java
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * <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.pdfbox;
+
+import iaik.x509.X509Certificate;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Calendar;
+
+import org.apache.pdfbox.exceptions.SignatureException;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+
+import at.gv.egiz.pdfas.common.utils.StreamUtils;
+import at.gv.egiz.pdfas.lib.impl.signing.PDFASSignatureInterface;
+
+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 SignatureException,
+ 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/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/ImageObject.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/ImageObject.java
new file mode 100644
index 00000000..f8ec2073
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/ImageObject.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * <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.pdfbox;
+
+import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage;
+
+public class ImageObject {
+ private PDXObjectImage image;
+ private float width;
+ private float height;
+
+ public ImageObject(PDXObjectImage image, float width, float height) {
+ this.image = image;
+ this.width = width;
+ this.height = height;
+ }
+
+ public PDXObjectImage getImage() {
+ return image;
+ }
+
+ public void setImage(PDXObjectImage 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/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsTemplateCreator.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsTemplateCreator.java
new file mode 100644
index 00000000..feacc52d
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsTemplateCreator.java
@@ -0,0 +1,170 @@
+/*******************************************************************************
+ * <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.pdfbox;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.pdfbox.exceptions.COSVisitorException;
+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.xobject.PDXObjectForm;
+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)
+ 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());
+ this.pdfBuilder.createFormaterRectangle(properties.getFormaterRectangleParams());
+ PDRectangle formater = pdfStructure.getFormaterRectangle();
+
+ //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());
+
+ // inner formstream, form and resource (hlder form containts inner form)
+ this.pdfBuilder.createInnerFormStreamPdfAs(template);
+ this.pdfBuilder.createInnerFormResource();
+ PDResources innerFormResource = pdfStructure.getInnerFormResources();
+ this.pdfBuilder.createInnerForm(innerFormResource, pdfStructure.getInnterFormStream(), formater);
+ PDXObjectForm 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();
+
+ // 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;
+ try
+ {
+ //COSDocument doc = pdfStructure.getVisualSignature();
+ //doc.
+ //in = pdfStructure.getTemplateAppearanceStream();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ template.save(baos);
+ baos.close();
+ in = new ByteArrayInputStream(baos.toByteArray());
+ }
+ catch (COSVisitorException e)
+ {
+ logger.error("COSVisitorException: can't get apereance stream ", e);
+ }
+ logger.debug("stream returning started, size= " + in.available());
+
+ // we must close the document
+ template.close();
+
+ // return result of the stream
+ return in;
+ }
+}
diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureBuilder.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureBuilder.java
new file mode 100644
index 00000000..e3ee19f6
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureBuilder.java
@@ -0,0 +1,994 @@
+/*******************************************************************************
+ * <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.pdfbox;
+
+import java.awt.Color;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.imageio.ImageIO;
+
+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.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.edit.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.font.PDType1Font;
+import org.apache.pdfbox.pdmodel.graphics.xobject.PDJpeg;
+import org.apache.pdfbox.pdmodel.graphics.xobject.PDPixelMap;
+import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectForm;
+import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage;
+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;
+import at.knowcenter.wag.egov.egiz.table.Style;
+
+public class PDFAsVisualSignatureBuilder extends PDVisibleSigBuilder {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(PDFAsVisualSignatureBuilder.class);
+
+ private void drawDebugLine(PDPageContentStream contentStream, float x,
+ float y, float width, float height) {
+ try {
+ contentStream.setStrokingColor(Color.RED);
+ contentStream.drawLine(x, y, x + width, y);
+ contentStream.setStrokingColor(Color.BLUE);
+ contentStream.drawLine(x, y, x, y - height);
+ contentStream.setStrokingColor(Color.GREEN);
+ contentStream.drawLine(x + width, y, x + width, y - height);
+ contentStream.setStrokingColor(Color.ORANGE);
+ contentStream.drawLine(x, y - height, x + width, y - height);
+
+ contentStream.setStrokingColor(Color.BLACK);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void drawDebugPadding(PDPageContentStream contentStream, float x,
+ float y, float padding, float width, float height) {
+ try {
+ contentStream.setStrokingColor(Color.RED);
+ contentStream.drawLine(x, y, x + padding, y - padding);
+ contentStream.drawLine(x + width, y, x + width - padding, y
+ - padding);
+ contentStream.drawLine(x + width, y - height, x + width - padding,
+ y - height + padding);
+ contentStream.drawLine(x, y - height, x + padding, y - height
+ + padding);
+ contentStream.setStrokingColor(Color.BLACK);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void drawTable(PDPage page, PDPageContentStream contentStream,
+ float x, float y, float width, float height,
+ PDFBoxTable abstractTable, PDDocument doc, boolean subtable)
+ throws IOException, PdfAsException {
+
+ final int rows = abstractTable.getRowCount();
+ final int cols = abstractTable.getColCount();
+ float[] colsSizes = abstractTable.getColsRelativeWith();
+ int max_cols = abstractTable.getColCount();
+ float padding = abstractTable.getPadding();
+ float fontSize = PDFBoxFont.defaultFontSize;
+ PDFont textFont = PDFBoxFont.defaultFont;
+ 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("Drawing Table:");
+ abstractTable.dumpTable();
+
+ if (abstractTable.getBGColor() != null) {
+ contentStream.setNonStrokingColor(abstractTable.getBGColor());
+ contentStream.fillRect(x, y, abstractTable.getWidth(),
+ abstractTable.getHeight());
+ contentStream.setNonStrokingColor(Color.BLACK);
+ }
+ 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();
+ }
+
+ for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) {
+ logger.debug("Col: " + cols_idx + " : " + colsSizes[cols_idx]);
+ }
+
+ float border = abstractTable.style.getBorder();
+ contentStream.setLineWidth(border);
+
+ float tableHeight = abstractTable.getHeight();
+ float tableWidth = abstractTable.getWidth();
+ final float colWidth = tableWidth / (float) cols;
+
+ // draw if boarder > 0
+ if (border != 0) {
+
+ float nexty = y + tableHeight;
+ float lasty = nexty;
+ for (int i = 0; i < rows; i++) {
+ ArrayList<Entry> row = abstractTable.getRow(i);
+ // Draw row border!
+ logger.debug("ROW LINE: {} {} {} {}", x, nexty, x + width,
+ nexty);
+ contentStream.drawLine(x, nexty, x + width, nexty);
+ lasty = nexty;
+ if (i < abstractTable.getRowHeights().length) {
+ // nexty -= abstractTable.getRowHeights()[i] + padding * 2;
+ nexty -= abstractTable.getRowHeights()[i];
+ }
+
+ // if (subtable && i + 1 ==
+ // abstractTable.getRowHeights().length) {
+ // nexty -= padding;
+ // }
+
+ float nextx = x;
+ float ypos = y;
+ float yheight = y + abstractTable.getHeight();
+ if (subtable) {
+ // ypos -= padding;
+ yheight = y + abstractTable.getHeight();
+ }
+
+ for (int j = 0; j < row.size(); j++) {
+ Entry cell = (Entry) row.get(j);
+
+ if (subtable && j == cols) {
+ continue;
+ }
+ logger.debug("COL LINE: {} {} {} {}", nextx, ypos, nextx,
+ yheight);
+ contentStream.drawLine(nextx, lasty, nextx, nexty);
+ for (int k = 0; k < cell.getColSpan(); k++) {
+ if (k + j < colsSizes.length) {
+ nextx += (colsSizes != null) ? colsSizes[k + j]
+ : colWidth;
+ }
+ }
+ }
+ if (!subtable) {
+ contentStream.drawLine(nextx, lasty, nextx, nexty);
+ }
+ }
+
+ contentStream.drawLine(x, nexty, x + tableWidth, nexty);
+
+ }
+
+ float textx = x;
+ float texty = y + tableHeight;
+ 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);
+
+ Style inherit_style = Style.doInherit(cell.getStyle(),
+ abstractTable.style);
+ cell.setStyle(inherit_style);
+
+ // if(subtable) {
+ drawDebugPadding(contentStream, textx, texty, padding,
+ ((colsSizes != null) ? colsSizes[j] : colWidth),
+ abstractTable.getRowHeights()[i]);
+ // }
+ // if(true) {
+ // textx += (colsSizes != null) ? colsSizes[j] : colWidth;
+ // continue;
+ // }
+
+ if (cell.getType() == Entry.TYPE_CAPTION
+ || cell.getType() == Entry.TYPE_VALUE) {
+
+ if (cell.getType() == Entry.TYPE_CAPTION) {
+ textFont = abstractTable.getFont().getFont(doc);
+ fontSize = abstractTable.getFont().getFontSize();
+ } else if (cell.getType() == Entry.TYPE_VALUE) {
+ textFont = abstractTable.getValueFont().getFont(doc);
+ fontSize = abstractTable.getValueFont().getFontSize();
+ }
+
+ String text = (String) cell.getValue();
+
+ // COSName name = COSName.getPDFName("ANDI_TAG!");
+ // contentStream.beginMarkedContentSequence(COSName.ALT,
+ // name);
+ String fontName = textFont.equals(PDType1Font.COURIER) ? "COURIER"
+ : "HELVETICA";
+
+ float fheight = textFont.getFontDescriptor().getCapHeight()
+ / 1000 * fontSize;
+
+ String[] tlines = text.split("\n");
+ float textHeight = fontSize * tlines.length;
+
+ Style cellStyle = cell.getStyle();
+ String valign = null;
+ String halign = null;
+
+ if (cell.getType() == Entry.TYPE_CAPTION
+ && cellStyle != null) {
+ valign = cellStyle.getVAlign();
+ halign = cellStyle.getHAlign();
+ } else if (cell.getType() == Entry.TYPE_VALUE
+ && cellStyle != null) {
+ valign = cellStyle.getValueVAlign();
+ halign = cellStyle.getValueHAlign();
+ }
+ float ty = texty - padding;
+ float tx = textx + padding;
+ if (Style.BOTTOM.equals(valign)) {
+ float bottom_offset = abstractTable.getRowHeights()[i]
+ - textHeight;
+ ty -= bottom_offset;
+ } else if (Style.MIDDLE.equals(valign)) {
+ float bottom_offset = abstractTable.getRowHeights()[i]
+ - textHeight;
+ bottom_offset = bottom_offset / 2.0f;
+ ty -= bottom_offset;
+ }
+
+ float columnWidth = (colsSizes != null) ? colsSizes[j]
+ : colWidth;
+ float maxWidth = 0;
+ for (int k = 0; k < tlines.length; k++) {
+ float lineWidth;
+ if (textFont instanceof PDType1Font) {
+ lineWidth = textFont.getStringWidth(tlines[k])
+ / 1000.0f * fontSize;
+ } 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 = columnWidth - maxWidth - 2 * padding;
+ if (offset > 0) {
+ offset = offset / 2.0f;
+ tx += offset;
+ }
+ } else if (Style.RIGHT.equals(halign)) {
+ float offset = columnWidth - maxWidth - 2 * padding;
+ if (offset > 0) {
+ tx += offset;
+ }
+ }
+
+ drawDebugLine(contentStream, tx, ty, maxWidth, textHeight);
+
+ contentStream.beginText();
+
+ if (innerFormResources.getFonts().containsValue(textFont)) {
+ String fontID = getFontID(textFont);
+ logger.debug("Using Font: " + fontID);
+ contentStream.appendRawCommands("/" + fontID + " "
+ + fontSize + " Tf\n");
+ } else {
+ contentStream.setFont(textFont, fontSize);
+ }
+
+ logger.debug("Writing: " + tx + " : " + (ty - fheight)
+ + " = " + text + " as " + cell.getType() + " w "
+ + fontName);
+ contentStream.moveTextPositionByAmount(tx, (ty - fheight));
+
+ if (text.contains("\n")) {
+ String[] lines = text.split("\n");
+ contentStream.appendRawCommands(fontSize + " TL\n");
+ for (int k = 0; k < lines.length; k++) {
+ contentStream.drawString(lines[k]);
+ if (k < lines.length - 1) {
+ contentStream.appendRawCommands("T*\n");
+ }
+ }
+ } else {
+ contentStream.drawString(text);
+ }
+ contentStream.endText();
+ // contentStream.endMarkedContentSequence();
+ } else if (cell.getType() == Entry.TYPE_IMAGE) {
+ String img_ref = (String) cell.getValue();
+ if (!images.containsKey(img_ref)) {
+ logger.error("Image not prepared! : " + img_ref);
+ throw new PdfAsException("Image not prepared! : "
+ + img_ref);
+ }
+ ImageObject image = images.get(img_ref);
+ PDXObjectImage pdImage = image.getImage();
+
+ float imgx = textx + padding;
+ float hoffset = ((colsSizes != null) ? colsSizes[j]
+ : colWidth) - image.getWidth();
+ if (cell.getStyle().getImageVAlign() != null
+ && cell.getStyle().getImageVAlign()
+ .equals(Style.CENTER)) {
+ hoffset = hoffset / 2.0f;
+ imgx += hoffset;
+ } else if (cell.getStyle().getImageHAlign() != null
+ && cell.getStyle().getImageHAlign()
+ .equals(Style.RIGHT)) {
+ imgx += hoffset;
+ }
+
+ float imgy = texty - padding;
+ float voffset = abstractTable.getRowHeights()[i]
+ - image.getHeight();
+ if (cell.getStyle().getImageVAlign() != null
+ && cell.getStyle().getImageVAlign()
+ .equals(Style.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());
+
+ logger.debug("Image: " + imgx + " : "
+ + (imgy - image.getHeight()));
+ contentStream.drawXObject(pdImage, imgx,
+ imgy - image.getHeight(), image.getWidth(),
+ image.getHeight());
+ // contentStream.endMarkedContentSequence();
+
+ } else if (cell.getType() == Entry.TYPE_TABLE) {
+
+ float tableY = texty - abstractTable.getRowHeights()[i];
+ float tableX = textx;
+ // texty = texty - padding;
+ PDFBoxTable tbl_value = (PDFBoxTable) cell.getValue();
+
+ Style inherit_styletab = Style.doInherit(
+ abstractTable.style, cell.getStyle());
+ tbl_value.table.setStyle(inherit_styletab);
+
+ logger.debug("Table: " + tableX + " : " + tableY);
+ // logger.debug("Table height: " + );
+ TableDrawUtils.drawTable(page, contentStream, tableX,
+ tableY, (colsSizes != null) ? colsSizes[j]
+ : colWidth,
+ abstractTable.getRowHeights()[i], tbl_value, doc,
+ true, innerFormResources, images, settings);
+ }
+ textx += (colsSizes != null) ? colsSizes[j] : colWidth;
+ }
+ // if (i + 1 < abstractTable.getRowHeights().length) {
+ logger.debug("Row {} from {} - {} - {} = {}", i, texty,
+ abstractTable.getRowHeights()[i], padding * 2, texty
+ - (abstractTable.getRowHeights()[i]));
+ texty -= abstractTable.getRowHeights()[i];
+ // texty = texty - abstractTable.getRowHeights()[i + 1] - padding
+ // * 2;
+ // texty = texty - abstractTable.getRowHeights()[i] - padding
+ // * 2;
+ // }
+ textx = x;
+ }
+ }
+
+ private void drawTable2(PDPage page, PDPageContentStream contentStream,
+ float x, float y, float width, float height,
+ PDFBoxTable abstractTable, PDDocument doc, boolean subtable) {
+
+ }
+
+ private PDFAsVisualSignatureProperties properties;
+ private PDFAsVisualSignatureDesigner designer;
+ private ISettings settings;
+ private PDResources innerFormResources;
+ private Map<String, ImageObject> images = new HashMap<String, ImageObject>();
+
+ private String getFontID(PDFont font) {
+ Iterator<java.util.Map.Entry<String, PDFont>> it = innerFormResources
+ .getFonts().entrySet().iterator();
+ while (it.hasNext()) {
+ java.util.Map.Entry<String, PDFont> entry = it.next();
+ if (entry.getValue().equals(font)) {
+ return entry.getKey();
+ }
+ }
+ return "";
+ }
+
+ 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);
+ }
+
+ 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_ref = (String) cell.getValue();
+ if (!images.containsKey(img_ref)) {
+ File img_file = new File(img_ref);
+ if (!img_file.isAbsolute()) {
+ logger.debug("Image file declaration is relative. Prepending path of resources directory.");
+ logger.debug("Image Location: "
+ + settings.getWorkingDirectory()
+ + File.separator + img_ref);
+ img_file = new File(settings.getWorkingDirectory()
+ + File.separator + img_ref);
+ } else {
+ logger.debug("Image file declaration is absolute. Skipping file relocation.");
+ }
+
+ if (!img_file.exists()) {
+ logger.debug("Image file \""
+ + img_file.getCanonicalPath()
+ + "\" doesn't exist.");
+ throw new PdfAsException("error.pdf.stamp.04");
+ }
+
+ BufferedImage img = null;
+ try {
+ img = ImageIO.read(img_file);
+ } catch (IOException e) {
+ throw new PdfAsException("error.pdf.stamp.04", e);
+ }
+
+ 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 (img.getAlphaRaster() == null
+ && img.getColorModel().hasAlpha()) {
+ img = ImageUtils.removeAlphaChannel(img);
+ }
+
+ PDXObjectImage pdImage = new PDPixelMap(template, img);
+
+ ImageObject image = new ImageObject(pdImage, iwidth,
+ iheight);
+ images.put(img_ref, image);
+ innerFormResources.addXObject(pdImage, "Im");
+ }
+ } else if (cell.getType() == Entry.TYPE_TABLE) {
+ PDFBoxTable tbl_value = (PDFBoxTable) cell.getValue();
+ readTableResources(tbl_value, template);
+ }
+ }
+ }
+ }
+
+ public void createInnerFormStreamPdfAs(PDDocument template) 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);
+ stream.close();
+ PDStream innterFormStream = 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.error("Failed to create visual signature block", e);
+ throw new PdfAsException("Failed to create visual signature block", e);
+ }
+ }
+
+ @Override
+ public void injectProcSetArray(PDXObjectForm innerForm, PDPage page,
+ PDResources innerFormResources, PDResources imageFormResources,
+ PDResources holderFormResources, COSArray procSet) {
+ innerForm.getResources().getCOSDictionary()
+ .setItem(COSName.PROC_SET, procSet); //
+ page.getCOSDictionary().setItem(COSName.PROC_SET, procSet);
+ innerFormResources.getCOSDictionary()
+ .setItem(COSName.PROC_SET, procSet);
+ /*
+ * imageFormResources.getCOSDictionary() .setItem(COSName.PROC_SET,
+ * procSet);
+ */
+ holderFormResources.getCOSDictionary().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 \n";
+
+ logger.debug("Holder Form Stream: " + holderFormComment);
+
+ // String innerFormComment = "q 1 0 0 1 0 0 cm /" + imageObjectName +
+ // " Do Q\n";
+ String innerFormComment = getStructure().getInnterFormStream()
+ .getInputStreamAsString();
+
+ //logger.debug("Inner Form Stream: " + innerFormComment);
+
+ // appendRawCommands(getStructure().getInnterFormStream().createOutputStream(),
+ // getStructure().getInnterFormStream().getInputStreamAsString());
+
+ appendRawCommands(getStructure().getHolderFormStream()
+ .createOutputStream(), holderFormComment);
+ appendRawCommands(getStructure().getInnterFormStream()
+ .createOutputStream(), innerFormComment);
+ // 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()));
+ 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.getDictionary();
+ 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());
+ 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);
+
+ 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 {
+ PDJpeg img = new PDJpeg(template, inputStream);
+ getStructure().setJpedImage(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().setFormaterRectangle(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) {
+
+ PDXObjectForm holderForm = new PDXObjectForm(holderFormStream);
+ holderForm.setResources(holderFormResources);
+ holderForm.setBBox(formrect);
+ holderForm.setFormType(1);
+ getStructure().setHolderForm(holderForm);
+ logger.debug("Holder form has been created");
+
+ }
+
+ public void createAppearanceDictionary(PDXObjectForm 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) {
+ PDXObjectForm innerForm = new PDXObjectForm(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(PDXObjectForm innerForm,
+ PDResources holderFormResources) {
+ String name = holderFormResources.addXObject(innerForm, "FRM");
+ 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, PDJpeg 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);
+ */
+ PDXObjectForm imageForm = new PDXObjectForm(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);
+ String imageFormName = innerFormResource.addXObject(imageForm, "n");
+ String imageName = imageFormResources.addXObject(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.getWidget().getDictionary();
+ widgetDict.setNeedToBeUpdate(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/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureDesigner.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureDesigner.java
new file mode 100644
index 00000000..17b02d9d
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureDesigner.java
@@ -0,0 +1,450 @@
+/*******************************************************************************
+ * <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.pdfbox;
+
+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.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 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);
+ }
+
+ List<?> pages = document.getDocumentCatalog().getAllPages();
+ if(newpage) {
+ PDPage lastPage = (PDPage) pages.get(pages.size()-1);
+ PDRectangle mediaBox = lastPage.findMediaBox();
+ this.pageHeight(mediaBox.getHeight());
+ this.pageWidth = mediaBox.getWidth();
+ } else {
+ PDPage firstPage = (PDPage) pages.get(page - 1);
+ PDRectangle mediaBox = firstPage.findMediaBox();
+ 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;
+ List<?> pages = document.getDocumentCatalog().getAllPages();
+ 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;
+ }
+
+ /**
+ * 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/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureProperties.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureProperties.java
new file mode 100644
index 00000000..cb1dfc38
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureProperties.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * <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.pdfbox;
+
+import java.io.IOException;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+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.lib.impl.pdfbox.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;
+
+ public PDFAsVisualSignatureProperties(ISettings settings, PDFBOXObject object,
+ PdfBoxVisualObject visObj, PositioningInstruction pos) {
+ this.settings = settings;
+ try {
+ main = visObj.getTable();
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ this.rotationAngle = pos.getRotation();
+ try {
+ PDDocument origDoc = object.getDocument();
+
+ designer = new PDFAsVisualSignatureDesigner(origDoc, pos.getPage(), this, pos.isMakeNewPage());
+ float posy = designer.getPageHeight() - pos.getY();
+ designer.coordinates(pos.getX(), 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));
+ } catch (PdfAsException e) {
+ throw new PdfAsWrappedIOException(e);
+ }
+ }
+
+ public PDFBoxTable getMainTable() {
+ return main;
+ }
+
+
+ public float getRotation() {
+ return this.rotationAngle;
+ }
+
+ public PDFAsVisualSignatureDesigner getDesigner() {
+ return designer;
+ }
+
+}
diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxFont.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxFont.java
new file mode 100644
index 00000000..8fcca9b7
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxFont.java
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * <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.pdfbox;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+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;
+
+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>();
+
+ 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;
+ PDDocument doc;
+ ISettings settings;
+
+ private PDFont generateTTF(String fonttype, PDDocument doc) throws IOException {
+ boolean cacheNow = false;
+ if(doc == null) {
+ if(this.doc == null) {
+ this.doc = new PDDocument();
+ }
+ doc = this.doc;
+ } else {
+ cacheNow = true;
+ }
+ ttfFontDesc = fonttype;
+ String fontName = fonttype.replaceFirst("TTF:", "");
+
+ logger.debug("Instantiating font.");
+ String fontPath = this.settings.getWorkingDirectory() + File.separator + "fonts" + File.separator + fontName;
+ logger.debug("Instantiating \"" + fontPath + "\".");
+
+ if(cacheNow) {
+ cachedfont = PDTrueTypeFont.loadTTF(doc, fontPath);
+ return cachedfont;
+ } else {
+ return PDTrueTypeFont.loadTTF(doc, fontPath);
+ }
+ }
+
+ private PDFont generateFont(String fonttype, String fontder) throws IOException {
+ if(fonttype.startsWith("TTF:")) {
+ // Load TTF Font
+ return generateTTF(fonttype, null);
+ } 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) throws IOException {
+ String[] fontArr = desc.split(",");
+
+ if(fontArr.length == 3) {
+ font = generateFont(fontArr[0], fontArr[2]);
+ fontSize = Float.parseFloat(fontArr[1]);
+ } else if(fontArr.length == 2 && fontArr[0].startsWith("TTF:")) {
+ font = generateFont(fontArr[0], null);
+ 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) throws IOException {
+ this.settings = settings;
+ this.fontDesc = fontDesc;
+ logger.debug("Creating Font: " + fontDesc);
+ this.setFont(fontDesc);
+ }
+
+ public PDFont getFont(PDDocument doc) throws IOException {
+ if(cachedfont != null) {
+ return cachedfont;
+ }
+ if(font instanceof PDTrueTypeFont && doc != null) {
+ return generateTTF(ttfFontDesc, doc);
+ } else {
+ return font;
+ }
+ }
+
+ public float getFontSize() {
+ return fontSize;
+ }
+}
diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxTable.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxTable.java
new file mode 100644
index 00000000..e84bd498
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFBoxTable.java
@@ -0,0 +1,691 @@
+/*******************************************************************************
+ * <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.pdfbox;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.fontbox.ttf.TrueTypeFont;
+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.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.StringUtils;
+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;
+
+ 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)
+ 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);
+
+ 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);
+ }
+ padding = style.getPadding();
+
+ bgColor = style.getBgColor();
+ }
+
+ public PDFBoxTable(Table abstractTable, PDFBoxTable parent, float fixSize,
+ ISettings settings) throws IOException {
+ this.settings = settings;
+ initializeStyle(abstractTable, parent);
+ 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) throws IOException {
+ this.settings = settings;
+ initializeStyle(abstractTable, parent);
+ this.calculateWidthHeight();
+ }
+
+ private void calculateHeightsBasedOnWidths() throws IOException {
+ int rows = this.table.getRows().size();
+ rowHeights = new float[rows];
+ addPadding = new boolean[rows];
+
+ for (int i = 0; i < rows; i++) {
+ rowHeights[i] = 0;
+ }
+
+ 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 {
+ 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 {
+ 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);
+ 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.getFontHeight(linebytes, j, 1) / 1000 * fontSize;
+ if(he > maxLineHeight) {
+ maxLineHeight = he;
+ }
+ }
+ } catch (UnsupportedEncodingException e) {
+ logger.warn("failed to determine String height", e);
+ maxLineHeight = c.getFontDescriptor().getCapHeight() / 1000 * fontSize;
+ } catch (IOException e) {
+ logger.warn("failed to determine String height", e);
+ maxLineHeight = c.getFontDescriptor().getCapHeight() / 1000 * fontSize;
+ }
+
+ heights[i] = maxLineHeight;
+ }
+
+ return heights;
+ }
+
+ private float getCellHeight(Entry cell, float width) throws IOException {
+ boolean isValue = true;
+ switch (cell.getType()) {
+ 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:
+ if (style != null && style.getImageScaleToFit() != null) {
+ //if (style.getImageScaleToFit().getHeight() < width) {
+ return style.getImageScaleToFit().getHeight();
+ //}
+ }
+ return width;
+ case Entry.TYPE_TABLE:
+ PDFBoxTable pdfBoxTable = null;
+ if (cell.getValue() instanceof Table) {
+ pdfBoxTable = new PDFBoxTable((Table) cell.getValue(), this,
+ width, this.settings);
+ cell.setValue(pdfBoxTable);
+ } else if (cell.getValue() instanceof PDFBoxTable) {
+ // recreate here beacuse of fixed width!
+ pdfBoxTable = (PDFBoxTable) cell.getValue();
+ pdfBoxTable = new PDFBoxTable(pdfBoxTable.table, this, width,
+ this.settings);
+ 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 {
+ 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:
+ if (style != null && style.getImageScaleToFit() != null) {
+ return style.getImageScaleToFit().getHeight();
+ }
+ return 80.f;
+ case Entry.TYPE_TABLE:
+ PDFBoxTable pdfBoxTable = null;
+ if (cell.getValue() instanceof Table) {
+ pdfBoxTable = new PDFBoxTable((Table) cell.getValue(), this,
+ this.settings);
+ 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/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PdfBoxStamper.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PdfBoxStamper.java
new file mode 100644
index 00000000..c60c4283
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PdfBoxStamper.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * <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.pdfbox;
+
+import java.io.IOException;
+
+import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
+import at.gv.egiz.pdfas.common.settings.ISettings;
+import at.gv.egiz.pdfas.lib.impl.stamping.IPDFStamper;
+import at.gv.egiz.pdfas.lib.impl.stamping.IPDFVisualObject;
+import at.gv.egiz.pdfas.lib.impl.status.PDFObject;
+import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction;
+import at.knowcenter.wag.egov.egiz.table.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 {
+ return new PdfBoxVisualObject(table, pdf.getStatus().getSettings());
+ }
+
+ 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/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PdfBoxVisualObject.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PdfBoxVisualObject.java
new file mode 100644
index 00000000..c7623cf9
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PdfBoxVisualObject.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * <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.pdfbox;
+
+import java.io.IOException;
+
+import at.gv.egiz.pdfas.common.settings.ISettings;
+import at.gv.egiz.pdfas.lib.impl.stamping.IPDFVisualObject;
+import at.knowcenter.wag.egov.egiz.table.Table;
+
+public class PdfBoxVisualObject implements IPDFVisualObject {
+
+ private Table abstractTable;
+ private PDFBoxTable table;
+ private float width;
+ private float x;
+ private float y;
+ private int page;
+ private ISettings settings;
+
+ public PdfBoxVisualObject(Table table, ISettings settings)
+ throws IOException {
+ this.abstractTable = table;
+ this.table = new PDFBoxTable(table, null, settings);
+ this.settings = settings;
+ }
+
+ public void setWidth(float width) {
+ this.width = width;
+ }
+
+ public void fixWidth() {
+ try {
+ table = new PDFBoxTable(abstractTable, null, this.width, settings);
+ } catch (IOException e) {
+ // should not occur
+ e.printStackTrace();
+ }
+ }
+
+ 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/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/TableDrawUtils.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/TableDrawUtils.java
new file mode 100644
index 00000000..88eb798a
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/TableDrawUtils.java
@@ -0,0 +1,564 @@
+/*******************************************************************************
+ * <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.pdfbox;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.font.PDFont;
+import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
+import at.gv.egiz.pdfas.common.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)
+ throws PdfAsException {
+
+ logger.debug("Drawing Table: X {} Y {} WIDTH {} HEIGHT {} \n{}", x, y,
+ width, height, abstractTable.getOrigTable().toString());
+
+ abstractTable.getOrigTable().setWidth(width);
+
+ drawTableBackground(page, contentStream, x, y, width, height,
+ abstractTable, settings);
+
+ drawBorder(page, contentStream, x, y, width, height, abstractTable,
+ doc, subtable, settings);
+
+ drawContent(page, contentStream, x, y, width, height, abstractTable,
+ doc, subtable, formResources, images, settings);
+ }
+
+ public static void drawContent(PDPage page,
+ PDPageContentStream contentStream, float x, float y, float width,
+ float height, PDFBoxTable abstractTable, PDDocument doc,
+ boolean subtable, PDResources formResources,
+ Map<String, ImageObject> images, ISettings settings)
+ throws PdfAsException {
+
+ float contentx = x;
+ float contenty = y + height;
+ float padding = abstractTable.getPadding();
+ float[] colsSizes = getColSizes(abstractTable);
+
+ for (int i = 0; i < abstractTable.getRowCount(); i++) {
+ ArrayList<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);
+ break;
+ case Entry.TYPE_VALUE:
+ drawValue(page, contentStream, contentx, contenty,
+ colWidth, abstractTable.getRowHeights()[i],
+ padding, abstractTable, doc, cell, formResources, settings);
+ break;
+ case Entry.TYPE_IMAGE:
+ drawImage(page, contentStream, contentx, contenty,
+ colWidth, abstractTable.getRowHeights()[i],
+ padding, abstractTable, doc, cell, formResources,
+ images, settings);
+ break;
+ case Entry.TYPE_TABLE:
+
+ PDFBoxTable tbl_value = (PDFBoxTable) cell.getValue();
+
+ Style inherit_styletab = Style.doInherit(
+ abstractTable.style, cell.getStyle());
+ tbl_value.table.setStyle(inherit_styletab);
+
+ drawTable(page, contentStream, contentx, contenty
+ - abstractTable.getRowHeights()[i], colWidth,
+ abstractTable.getRowHeights()[i], tbl_value, doc,
+ true, formResources, images, settings);
+ break;
+ default:
+ logger.warn("Unknown Cell entry type: " + cell.getType());
+ break;
+ }
+
+ // Move content pointer
+ contentx += colWidth;
+
+ int span = cell.getColSpan() - 1;
+ j += span;
+ }
+
+ // Move content pointer
+ contenty -= abstractTable.getRowHeights()[i];
+ contentx = x;
+ }
+ }
+
+ private static void drawString(PDPage page,
+ PDPageContentStream contentStream, float contentx, float contenty,
+ float width, float height, float padding,
+ PDFBoxTable abstractTable, PDDocument doc, Entry cell,
+ float fontSize, float textHeight, String valign, String halign,
+ String[] tlines, PDFont textFont, PDResources formResources,
+ ISettings settings) throws PdfAsException {
+ try {
+ float ty = contenty - padding;
+ float tx = contentx + padding;
+ float innerHeight = height - (2 * padding);
+ float innerWidth = width - (2 * padding);
+ if (Style.BOTTOM.equals(valign)) {
+ float bottom_offset = innerHeight - textHeight;
+ ty -= bottom_offset;
+ } else if (Style.MIDDLE.equals(valign)) {
+ float bottom_offset = innerHeight - textHeight;
+ bottom_offset = bottom_offset / 2.0f;
+ ty -= bottom_offset;
+ }
+
+ // calculate the max with of the text content
+ float maxWidth = 0;
+ for (int k = 0; k < tlines.length; k++) {
+ float lineWidth;
+ // if (textFont instanceof PDType1Font) {
+ lineWidth = textFont.getStringWidth(tlines[k]) / 1000.0f
+ * fontSize;
+ /*
+ * } else { float fwidth = textFont
+ * .getStringWidth("abcdefghijklmnopqrstuvwxyz ") / 1000.0f *
+ * fontSize; fwidth = fwidth / (float)
+ * "abcdefghijklmnopqrstuvwxyz" .length(); lineWidth =
+ * tlines[k].length() * fwidth; }
+ */
+ if (maxWidth < lineWidth) {
+ maxWidth = lineWidth;
+ }
+ }
+
+ if (Style.CENTER.equals(halign)) {
+ float offset = innerWidth - maxWidth;
+ if (offset > 0) {
+ offset = offset / 2.0f;
+ tx += offset;
+ }
+ } else if (Style.RIGHT.equals(halign)) {
+ float offset = innerWidth - maxWidth;
+ if (offset > 0) {
+ tx += offset;
+ }
+ }
+
+ logger.debug("Text tx {} ty {} maxWidth {} textHeight {}", tx, ty,
+ maxWidth, textHeight);
+
+ drawDebugLine(contentStream, tx, ty, maxWidth, textHeight, settings);
+
+ contentStream.beginText();
+
+ if (formResources.getFonts().containsValue(textFont)) {
+ String fontID = getFontID(textFont, formResources);
+ logger.debug("Using Font: " + fontID);
+ contentStream.appendRawCommands("/" + fontID + " " + fontSize
+ + " Tf\n");
+ } else {
+ contentStream.setFont(textFont, fontSize);
+ }
+
+ logger.debug("Writing: " + tx + " : " + (ty - fontSize) + " as "
+ + cell.getType());
+ contentStream.moveTextPositionByAmount(tx, (ty - fontSize));
+
+ contentStream.appendRawCommands(fontSize + " TL\n");
+ for (int k = 0; k < tlines.length; k++) {
+ contentStream.drawString(tlines[k]);
+ if (k < tlines.length - 1) {
+ contentStream.appendRawCommands("T*\n");
+ }
+ }
+
+ contentStream.endText();
+
+ } catch (IOException e) {
+ logger.error("IO Exception", e);
+ throw new PdfAsException("Error", e);
+ }
+ }
+
+ public static void drawCaption(PDPage page,
+ PDPageContentStream contentStream, float contentx, float contenty,
+ float width, float height, float padding,
+ PDFBoxTable abstractTable, PDDocument doc, Entry cell,
+ PDResources formResources, ISettings settings)
+ throws PdfAsException {
+
+ logger.debug("Drawing Caption @ X: {} Y: {}", contentx, contenty);
+
+ try {
+ float fontSize = PDFBoxFont.defaultFontSize;
+ PDFont textFont = PDFBoxFont.defaultFont;
+
+ textFont = abstractTable.getFont().getFont(doc);
+ fontSize = abstractTable.getFont().getFontSize();
+
+ // get the cell Text
+ String text = (String) cell.getValue();
+ String[] tlines = text.split("\n");
+ float textHeight = fontSize * tlines.length;
+
+ Style cellStyle = cell.getStyle();
+ String valign = cellStyle.getVAlign();
+ String halign = cellStyle.getHAlign();
+
+ drawString(page, contentStream, contentx, contenty, width, height,
+ padding, abstractTable, doc, cell, fontSize, textHeight,
+ valign, halign, tlines, textFont, formResources, settings);
+ } catch (IOException e) {
+ logger.error("IO Exception", e);
+ throw new PdfAsException("Error", e);
+ }
+ }
+
+ public static void drawValue(PDPage page,
+ PDPageContentStream contentStream, float contentx, float contenty,
+ float width, float height, float padding,
+ PDFBoxTable abstractTable, PDDocument doc, Entry cell,
+ PDResources formResources, ISettings settings)
+ throws PdfAsException {
+
+ logger.debug("Drawing Value @ X: {} Y: {}", contentx, contenty);
+
+ try {
+ float fontSize = PDFBoxFont.defaultFontSize;
+ PDFont textFont = PDFBoxFont.defaultFont;
+
+ textFont = abstractTable.getValueFont().getFont(doc);
+ fontSize = abstractTable.getValueFont().getFontSize();
+
+ // get the cell Text
+ String text = (String) cell.getValue();
+ String[] tlines = text.split("\n");
+ float textHeight = fontSize * tlines.length;
+
+ Style cellStyle = cell.getStyle();
+ String valign = cellStyle.getValueVAlign();
+ String halign = cellStyle.getValueHAlign();
+
+ drawString(page, contentStream, contentx, contenty, width, height,
+ padding, abstractTable, doc, cell, fontSize, textHeight,
+ valign, halign, tlines, textFont, formResources, settings);
+ } catch (IOException e) {
+ logger.error("IO Exception", e);
+ throw new PdfAsException("Error", e);
+ }
+ }
+
+ public static void drawImage(PDPage page,
+ PDPageContentStream contentStream, float contentx, float contenty,
+ float width, float height, float padding,
+ PDFBoxTable abstractTable, PDDocument doc, Entry cell,
+ PDResources formResources, Map<String, ImageObject> images,
+ ISettings settings) throws PdfAsException {
+ try {
+ float innerHeight = height;
+ float innerWidth = width;
+
+ String img_ref = (String) cell.getValue();
+ if (!images.containsKey(img_ref)) {
+ logger.error("Image not prepared! : " + img_ref);
+ throw new PdfAsException("Image not prepared! : " + img_ref);
+ }
+ ImageObject image = images.get(img_ref);
+ PDXObjectImage pdImage = image.getImage();
+
+ float imgx = contentx;
+ float hoffset = innerWidth - image.getWidth();
+ if (cell.getStyle().getImageHAlign() != null
+ && cell.getStyle().getImageHAlign().equals(Style.LEFT)) {
+ hoffset = hoffset / 2.0f;
+ imgx += hoffset;
+ } else if (cell.getStyle().getImageHAlign() != null
+ && cell.getStyle().getImageHAlign().equals(Style.RIGHT)) {
+ imgx += hoffset;
+ } else {
+ hoffset = hoffset / 2.0f;
+ imgx += hoffset;
+ }
+
+ float imgy = contenty;
+ float voffset = innerHeight - image.getHeight();
+ if (cell.getStyle().getImageVAlign() != null
+ && cell.getStyle().getImageVAlign().equals(Style.MIDDLE)) {
+ voffset = voffset / 2.0f;
+ imgy -= voffset;
+ } else if (cell.getStyle().getImageVAlign() != null
+ && cell.getStyle().getImageVAlign().equals(Style.BOTTOM)) {
+ imgy -= voffset;
+ }
+
+ drawDebugLine(contentStream, imgx, imgy, image.getWidth(),
+ image.getHeight(), settings);
+
+ // logger.debug("Image: " + imgx + " : " + (imgy -
+ // image.getHeight()));
+ contentStream.drawXObject(pdImage, imgx, imgy - image.getHeight(),
+ image.getWidth(), image.getHeight());
+ } catch (IOException e) {
+ logger.error("IO Exception", e);
+ throw new PdfAsException("Error", e);
+ }
+
+ }
+
+ public static float[] getColSizes(PDFBoxTable abstractTable) {
+ float[] origcolsSizes = abstractTable.getColsRelativeWith();
+ int max_cols = abstractTable.getColCount();
+ float[] colsSizes = new float[max_cols];
+ if (origcolsSizes == null) {
+ // set the column ratio for all columns to 1
+ for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) {
+ colsSizes[cols_idx] = 1;
+ }
+ } else {
+ // set the column ratio for all columns to 1
+ for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) {
+ colsSizes[cols_idx] = origcolsSizes[cols_idx];
+ }
+ }
+
+ // adapt
+ float total = 0;
+
+ for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) {
+ total += colsSizes[cols_idx];
+ }
+
+ for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) {
+ colsSizes[cols_idx] = (colsSizes[cols_idx] / total)
+ * abstractTable.getWidth();
+ }
+
+ float sum = 0;
+
+ for (int cols_idx = 0; cols_idx < colsSizes.length; cols_idx++) {
+ sum += colsSizes[cols_idx];
+ }
+
+ logger.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 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.getFonts()
+ .entrySet().iterator();
+ while (it.hasNext()) {
+ java.util.Map.Entry<String, PDFont> entry = it.next();
+ if (entry.getValue().equals(font)) {
+ return entry.getKey();
+ }
+ }
+ return "";
+ }
+
+}
diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/pdfbox/PDFBOXVerifier.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/pdfbox/PDFBOXVerifier.java
new file mode 100644
index 00000000..037dd5d8
--- /dev/null
+++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/pdfbox/PDFBOXVerifier.java
@@ -0,0 +1,172 @@
+package at.gv.egiz.pdfas.lib.impl.verify.pdfbox;
+
+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());
+ 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.error("Failed to verify document", e);
+ throw ErrorExtractor.searchPdfAsError(e);
+ } catch (PdfAsException e) {
+ logger.error("Failed to verify document", e);
+ throw ErrorExtractor.searchPdfAsError(e);
+ } finally {
+ if (doc != null) {
+ try {
+ doc.close();
+ } catch (IOException e) {
+ logger.info("Failed to close doc");
+ }
+ }
+ }
+ }
+
+}