aboutsummaryrefslogtreecommitdiff
path: root/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox
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/impl/pdfbox
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/impl/pdfbox')
-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
6 files changed, 804 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;
+ }
+
+}