aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dok/Anwendungsbeschreibung/PDF-AS-3.2-Anwendungsbeschreibung.docbin0 -> 597504 bytes
-rw-r--r--dok/Anwendungsbeschreibung/PDF-AS-3.2-Anwendungsbeschreibung.pdfbin0 -> 1138810 bytes
-rw-r--r--dok/Anwendungsbeschreibung/qr-code-examples.zipbin0 -> 560119 bytes
-rw-r--r--pom.xml10
-rw-r--r--src/main/java/at/gv/egiz/pdfas/commandline/Main.java95
-rw-r--r--src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCode.java2
-rw-r--r--src/main/java/at/gv/egiz/pdfas/exceptions/framework/PlaceholderExtractionException.java56
-rw-r--r--src/main/java/at/gv/egiz/pdfas/placeholder/SignaturePlaceholderContext.java49
-rw-r--r--src/main/java/at/gv/egiz/pdfas/placeholder/SignaturePlaceholderData.java129
-rw-r--r--src/main/java/at/gv/egiz/pdfas/placeholder/SignaturePlaceholderExtractor.java299
-rw-r--r--src/main/java/at/gv/egiz/pdfas/utils/PDFASUtils.java9
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/PdfAS.java42
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinarySignature.java28
-rw-r--r--src/main/resources/at/gv/egiz/pdfas/placeholder/empty.jpgbin0 -> 631 bytes
-rw-r--r--src/main/resources/at/gv/egiz/pdfas/placeholder/pdfbox-reader.properties23
15 files changed, 731 insertions, 11 deletions
diff --git a/dok/Anwendungsbeschreibung/PDF-AS-3.2-Anwendungsbeschreibung.doc b/dok/Anwendungsbeschreibung/PDF-AS-3.2-Anwendungsbeschreibung.doc
new file mode 100644
index 0000000..19f43e1
--- /dev/null
+++ b/dok/Anwendungsbeschreibung/PDF-AS-3.2-Anwendungsbeschreibung.doc
Binary files differ
diff --git a/dok/Anwendungsbeschreibung/PDF-AS-3.2-Anwendungsbeschreibung.pdf b/dok/Anwendungsbeschreibung/PDF-AS-3.2-Anwendungsbeschreibung.pdf
new file mode 100644
index 0000000..fd58ad4
--- /dev/null
+++ b/dok/Anwendungsbeschreibung/PDF-AS-3.2-Anwendungsbeschreibung.pdf
Binary files differ
diff --git a/dok/Anwendungsbeschreibung/qr-code-examples.zip b/dok/Anwendungsbeschreibung/qr-code-examples.zip
new file mode 100644
index 0000000..9a6f454
--- /dev/null
+++ b/dok/Anwendungsbeschreibung/qr-code-examples.zip
Binary files differ
diff --git a/pom.xml b/pom.xml
index ae023aa..09603aa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -360,6 +360,16 @@
<artifactId>ognl</artifactId>
<version>2.6.9</version>
</dependency>
+ <dependency>
+ <groupId>com.google.zxing</groupId>
+ <artifactId>core</artifactId>
+ <version>1.6-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.zxing</groupId>
+ <artifactId>javase</artifactId>
+ <version>1.6-SNAPSHOT</version>
+ </dependency>
</dependencies>
diff --git a/src/main/java/at/gv/egiz/pdfas/commandline/Main.java b/src/main/java/at/gv/egiz/pdfas/commandline/Main.java
index 161dee8..40d0e5a 100644
--- a/src/main/java/at/gv/egiz/pdfas/commandline/Main.java
+++ b/src/main/java/at/gv/egiz/pdfas/commandline/Main.java
@@ -79,6 +79,7 @@ import at.knowcenter.wag.egov.egiz.web.servlets.VerifyServlet;
*/
public abstract class Main
{
+// 23.11.2010 changed by exthex - added parameters for placeholder handling
/**
* Command line parameter setting the application mode sign|verify
*/
@@ -113,6 +114,21 @@ public abstract class Main
* Command line parameter selecting the position of the signature.
*/
protected static final String PARAMETER_POS = "-pos";
+
+ /**
+ * Command line parameter signaling to search the source document for a placeholder for the signature
+ */
+ protected static final String PARAMETER_SEARCH_PLACEHOLDER = "-checkforplaceholder";
+
+ /**
+ * Command line parameter selecting the id of the placeholder to use
+ */
+ protected static final String PARAMETER_PLACEHOLDER_ID = "-placeholder_id";
+
+ /**
+ * Command line parameter selecting the match mode for the placeholder
+ */
+ protected static final String PARAMETER_PLACEHOLDER_MATCH_MODE = "-placeholder_matchmode";
/**
* Command line parameter selecting the signature which is going to be
@@ -158,6 +174,21 @@ public abstract class Main
public static final String VALUE_SIGNATURE_MODE_DETACHED_TEXT = "detachedtextual";
/**
+ * The placeholder match mode STRICT
+ */
+ public static final String VALUE_PLACEHOLDER_MATCH_MODE_STRICT = "strict";
+
+ /**
+ * The placeholder match mode STRICT
+ */
+ public static final String VALUE_PLACEHOLDER_MATCH_MODE_MODERATE = "moderate";
+
+ /**
+ * The placeholder match mode STRICT
+ */
+ public static final String VALUE_PLACEHOLDER_MATCH_MODE_LENIENT = "lenient";
+
+ /**
* The log.
*/
private static final Log logger_ = LogFactory.getLog(Main.class);
@@ -207,6 +238,10 @@ public abstract class Main
String user_name = null;
String user_password = null;
String pos_string = null;
+
+ boolean search_placeholder = false;
+ String placeholderId = null;
+ int placeholderMatchMode = SignParameters.PLACEHOLDER_MATCH_MODE_MODERATE;
int verify_which = -1;
@@ -300,6 +335,44 @@ public abstract class Main
continue;
}
+ if (cur_arg.equals(PARAMETER_SEARCH_PLACEHOLDER))
+ {
+ search_placeholder = true;
+ continue;
+ }
+
+ if (cur_arg.equals(PARAMETER_PLACEHOLDER_ID))
+ {
+ i++;
+ if (i >= args.length)
+ {
+ printNoValue(PARAMETER_PLACEHOLDER_ID);
+ return;
+ }
+ placeholderId = args[i];
+ continue;
+ }
+
+ if (cur_arg.equals(PARAMETER_PLACEHOLDER_MATCH_MODE))
+ {
+ i++;
+ if (i >= args.length)
+ {
+ printNoValue(PARAMETER_PLACEHOLDER_MATCH_MODE);
+ return;
+ }
+ String matchMode = args[i];
+ if (matchMode.equals(VALUE_PLACEHOLDER_MATCH_MODE_LENIENT))
+ placeholderMatchMode = SignParameters.PLACEHOLDER_MATCH_MODE_LENIENT;
+ else if (matchMode.equals(VALUE_PLACEHOLDER_MATCH_MODE_MODERATE))
+ placeholderMatchMode = SignParameters.PLACEHOLDER_MATCH_MODE_MODERATE;
+ else if (matchMode.equals(VALUE_PLACEHOLDER_MATCH_MODE_STRICT))
+ placeholderMatchMode = SignParameters.PLACEHOLDER_MATCH_MODE_STRICT;
+ else
+ printUnrecognizedValue(PARAMETER_PLACEHOLDER_MATCH_MODE, args[i]);
+ continue;
+ }
+
if (cur_arg.equals(PARAMETER_USER_NAME))
{
i++;
@@ -435,7 +508,7 @@ public abstract class Main
output = generateOutputFileNameFromInput(input, signature_mode);
}
- carryOutCommand(mode, signature_mode, connector, signature_type, user_name, user_password, verify_which, input, output, pos_string);
+ carryOutCommand(mode, signature_mode, connector, signature_type, user_name, user_password, verify_which, input, output, pos_string, search_placeholder, placeholderId, placeholderMatchMode);
}
catch (PdfAsException e)
@@ -460,7 +533,7 @@ public abstract class Main
}
protected static void carryOutCommand(final String mode, final String signature_mode, final String connector, final String signature_type, final String user_name, final String user_password,
- final int verify_which, final String input, String output, final String pos_string) throws PdfAsException
+ final int verify_which, final String input, String output, final String pos_string, boolean search_placeholder, String placeholderId, int placeholderMatchMode) throws PdfAsException
{
// File file = new File(input);
//
@@ -481,7 +554,7 @@ public abstract class Main
if (mode.equals(VALUE_MODE_SIGN))
{
- carryOutSign(input, connector, signature_mode, signature_type, pos_string, user_name, user_password, output, messageOutput);
+ carryOutSign(input, connector, signature_mode, signature_type, pos_string, user_name, user_password, output, messageOutput, search_placeholder, placeholderId, placeholderMatchMode);
}
else
{
@@ -491,7 +564,7 @@ public abstract class Main
}
public static void carryOutSign(String input, String connector, String signature_mode, String signature_type, String pos_string, String user_name, String user_password, String output,
- PrintWriter messageOutput) throws PdfAsException
+ PrintWriter messageOutput, boolean search_placeholder, String placeholderId, int placeholderMatchMode) throws PdfAsException
{
messageOutput.println("Signing " + input + "...");
@@ -532,7 +605,7 @@ public abstract class Main
}
try {
- processSign(dataSource, connector, signature_mode, signature_type, pos_string, dataSink);
+ processSign(dataSource, connector, signature_mode, signature_type, pos_string, search_placeholder, placeholderId, placeholderMatchMode, dataSink);
} catch (Exception e) {
// Exception caught in order to delete file based datasink
if (outputFile != null && outputFile.exists())
@@ -626,7 +699,7 @@ public abstract class Main
}
- public static void processSign(DataSource dataSource, String connector, String signature_mode, String signature_type, String pos_string, DataSink dataSink) throws PdfAsException
+ public static void processSign(DataSource dataSource, String connector, String signature_mode, String signature_type, String pos_string, boolean search_placeholder, String placeholderId, int placeholderMatchMode, DataSink dataSink) throws PdfAsException
{
TablePos pos = null;
if (pos_string != null)
@@ -678,6 +751,9 @@ public abstract class Main
sp.setSignatureDevice(connector);
sp.setSignatureProfileId(signature_type);
sp.setSignaturePositioning(posi);
+ sp.setCheckForPlaceholder(search_placeholder);
+ sp.setPlaceholderId(placeholderId);
+ sp.setPlaceholderMatchMode(placeholderMatchMode);
pdfAs.sign(sp);
}
@@ -979,6 +1055,13 @@ public abstract class Main
writer.println(" 'new' ... new page");
writer.println(" intvalue ... pagenumber must be > 0 if p>number of pages in document p-->handled like p:'new'");
writer.println(" f_algo floatvalue ... consider footerline must be >= 0 (only if y_algo is auto and p_algo is not 'new')");
+
+ writer.println(" " + PARAMETER_SEARCH_PLACEHOLDER + " ... [optional] if set, the source document will be scanned for signature placeholder images");
+ writer.println(" " + PARAMETER_PLACEHOLDER_ID + " <id> ... [optional] search for signature placeholder images containing the given id");
+ writer.println(" " + PARAMETER_PLACEHOLDER_MATCH_MODE + " <" + VALUE_PLACEHOLDER_MATCH_MODE_LENIENT + "|" + VALUE_PLACEHOLDER_MATCH_MODE_MODERATE + "|" + VALUE_PLACEHOLDER_MATCH_MODE_STRICT + "> ... [optional] specify the behaviour if no matching placeholder could be found. Default is <moderate>.");
+ writer.println(" " + VALUE_PLACEHOLDER_MATCH_MODE_LENIENT + " ... sign in place of the first found placeholder, regardless if it matches exactly, or at the end of the document if none is found.");
+ writer.println(" " + VALUE_PLACEHOLDER_MATCH_MODE_MODERATE + " ... sign in place of the first found placeholder which has no explicit id set, or at the end of the document if none is found.");
+ writer.println(" " + VALUE_PLACEHOLDER_MATCH_MODE_STRICT + " ... throws a PlaceholderExtractionException.");
writer.println(" OPTIONS for verification:");
writer.println(" " + PARAMETER_VERIFY_WHICH + " <number> ... [optional] zero based number of the signature");
diff --git a/src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCode.java b/src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCode.java
index bf98c85..89c4e07 100644
--- a/src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCode.java
+++ b/src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCode.java
@@ -32,6 +32,8 @@ public final class ErrorCode
public static final int FONT_NOT_FOUND = 230;
public static final int DOCUMENT_IS_PROTECTED = 231;
public static final int INVALID_SIGNATURE_DICTIONARY = 232;
+//23.11.2010 changed by exthex - added error code for failed extraction
+ public static final int SIGNATURE_PLACEHOLDER_EXTRACTION_FAILED = 233;
public static final int INVALID_SIGNATURE_POSITION = 224;
diff --git a/src/main/java/at/gv/egiz/pdfas/exceptions/framework/PlaceholderExtractionException.java b/src/main/java/at/gv/egiz/pdfas/exceptions/framework/PlaceholderExtractionException.java
new file mode 100644
index 0000000..93eda36
--- /dev/null
+++ b/src/main/java/at/gv/egiz/pdfas/exceptions/framework/PlaceholderExtractionException.java
@@ -0,0 +1,56 @@
+package at.gv.egiz.pdfas.exceptions.framework;
+
+import at.gv.egiz.pdfas.exceptions.ErrorCode;
+import at.knowcenter.wag.egov.egiz.exceptions.PresentableException;
+
+/**
+ * Exceptions thrown if STRICT matching mode for placeholder extraction is set and no placeholder could be found.
+ *
+ * @author exthex
+ *
+ */
+public class PlaceholderExtractionException extends PresentableException
+{
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * Constructor.
+ *
+ * @param errorCode the error code
+ * @param message the additional message
+ * @param cause the causing exception
+ *
+ * @see ErrorCode#SIGNATURE_PLACEHOLDER_EXTRACTION_FAILED
+ */
+ public PlaceholderExtractionException(int errorCode, String message, Throwable cause)
+ {
+ super(errorCode, message, cause);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param errorCode the error code
+ * @param message the additional message
+ *
+ * @see ErrorCode#SIGNATURE_PLACEHOLDER_EXTRACTION_FAILED
+ */
+ public PlaceholderExtractionException(int errorCode, String message)
+ {
+ super(errorCode, message);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param errorCode the error code
+ * @param cause the causing exception
+ *
+ * @see ErrorCode#SIGNATURE_PLACEHOLDER_EXTRACTION_FAILED
+ */
+ public PlaceholderExtractionException(int errorCode, Throwable cause)
+ {
+ super(errorCode, cause);
+ }
+
+}
diff --git a/src/main/java/at/gv/egiz/pdfas/placeholder/SignaturePlaceholderContext.java b/src/main/java/at/gv/egiz/pdfas/placeholder/SignaturePlaceholderContext.java
new file mode 100644
index 0000000..e8c157c
--- /dev/null
+++ b/src/main/java/at/gv/egiz/pdfas/placeholder/SignaturePlaceholderContext.java
@@ -0,0 +1,49 @@
+package at.gv.egiz.pdfas.placeholder;
+
+/**
+ * Store and retrieve {@link SignaturePlaceholderData} in/from a thread local context.
+ *
+ * @author exthex
+ *
+ */
+public class SignaturePlaceholderContext {
+
+ private ThreadLocal sigHolder = new ThreadLocal();
+
+ private static SignaturePlaceholderContext instance = new SignaturePlaceholderContext();
+
+ /**
+ * Constructor. Private because this is a singleton.
+ */
+ private SignaturePlaceholderContext() {
+
+ }
+
+ /**
+ * Get the {@link SignaturePlaceholderData} which is currently bound to this thread.
+ * Might be null.
+ *
+ * @return
+ */
+ public static SignaturePlaceholderData getSignaturePlaceholderData(){
+ return (SignaturePlaceholderData)instance.sigHolder.get();
+ }
+
+ /**
+ *
+ * @return true if there is currently a {@link SignaturePlaceholderData} bound to this thread, false otherwise.
+ */
+ public static boolean isSignaturePlaceholderDataSet() {
+ return instance.sigHolder.get() != null;
+ }
+
+ /**
+ * Bind a {@link SignaturePlaceholderData} to this thread.
+ * If the given data is null, the context will be cleared.
+ *
+ * @param data if null, clears the ThreadLocal, else binds the data to the current thread.
+ */
+ public static void setSignaturePlaceholderData(SignaturePlaceholderData data) {
+ instance.sigHolder.set(data);
+ }
+}
diff --git a/src/main/java/at/gv/egiz/pdfas/placeholder/SignaturePlaceholderData.java b/src/main/java/at/gv/egiz/pdfas/placeholder/SignaturePlaceholderData.java
new file mode 100644
index 0000000..754b151
--- /dev/null
+++ b/src/main/java/at/gv/egiz/pdfas/placeholder/SignaturePlaceholderData.java
@@ -0,0 +1,129 @@
+package at.gv.egiz.pdfas.placeholder;
+
+import at.knowcenter.wag.egov.egiz.pdf.TablePos;
+
+/**
+ * This class represents all the data which can be extracted from a placeholder image.
+ *
+ * @author exthex
+ *
+ */
+public class SignaturePlaceholderData {
+
+ public static final String ID_KEY = "id";
+
+ public static final String PROFILE_KEY = "profile";
+
+ public static final String TYPE_KEY = "type";
+
+ public static final String SIG_KEY_KEY = "key";
+
+ private String profile;
+
+ private String type;
+
+ private String key;
+
+ private String id;
+
+ private TablePos tablePos;
+
+ private String placeholderName;
+
+ /**
+ *
+ * @param profile
+ * @param type
+ * @param sigKey
+ * @param id
+ */
+ public SignaturePlaceholderData(String profile, String type, String sigKey, String id) {
+ this.profile = profile;
+ this.type = type;
+ this.key = sigKey;
+ this.id = id;
+ }
+
+ /**
+ * Get the table position for the signature block.<br/>
+ * The table position is created from the page number, the upper left corner and the width of the placeholder image.
+ *
+ * @return
+ */
+ public TablePos getTablePos() {
+ return tablePos;
+ }
+
+ void setTablePos(TablePos tablePos) {
+ this.tablePos = tablePos;
+ }
+
+ /**
+ * The profile name. Might be null if not included in the qr-code.
+ *
+ * @return
+ */
+ public String getProfile() {
+ return profile;
+ }
+
+ void setProfile(String profile) {
+ this.profile = profile;
+ }
+
+ /**
+ * The signature type: "textual" or "binary". Might be null if not included in the qr-code.
+ * @return
+ */
+ public String getType() {
+ return type;
+ }
+
+ void setType(String type) {
+ this.type = type;
+ }
+
+ /**
+ * The key identifier for MOA signature. Might be null if not included in the qr-code.
+ *
+ * @return
+ */
+ public String getKey() {
+ return key;
+ }
+
+ void setKey(String key) {
+ this.key = key;
+ }
+
+ public String toString() {
+ return getClass().toString() + ": profile=" + profile + "; type=" + type + "; sigKey=" + key + "; table pos=" + tablePos;
+ }
+
+ void setPlaceholderName(String name) {
+ this.placeholderName = name;
+ }
+
+ /**
+ * The name of the placeholder image.
+ *
+ * @return
+ */
+ public String getPlaceholderName() {
+ return placeholderName;
+ }
+
+ /**
+ * The id associated with this placeholder.
+ *
+ * @return
+ */
+ public String getId() {
+ return id;
+ }
+
+ void setId(String id) {
+ this.id = id;
+ }
+
+}
diff --git a/src/main/java/at/gv/egiz/pdfas/placeholder/SignaturePlaceholderExtractor.java b/src/main/java/at/gv/egiz/pdfas/placeholder/SignaturePlaceholderExtractor.java
new file mode 100644
index 0000000..50c3281
--- /dev/null
+++ b/src/main/java/at/gv/egiz/pdfas/placeholder/SignaturePlaceholderExtractor.java
@@ -0,0 +1,299 @@
+package at.gv.egiz.pdfas.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.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.api.sign.SignParameters;
+import at.gv.egiz.pdfas.exceptions.ErrorCode;
+import at.gv.egiz.pdfas.exceptions.framework.PlaceholderExtractionException;
+import at.gv.egiz.pdfas.utils.PDFASUtils;
+import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException;
+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);
+
+ private List placeholders = new Vector();
+ private int currentPage = 0;
+
+ private SignaturePlaceholderExtractor(String placeholderId, int placeholderMatchMode) throws IOException {
+ super(ResourceLoader.loadProperties("at/gv/egiz/pdfas/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(InputStream inputStream, String placeholderId, int matchMode)
+ throws PDFDocumentException, PlaceholderExtractionException {
+ SignaturePlaceholderContext.setSignaturePlaceholderData(null);
+ PDDocument doc = null;
+ try
+ {
+ try {
+ doc = PDDocument.load(inputStream);
+ } catch (IOException e) {
+ throw new PDFDocumentException(ErrorCode.DOCUMENT_CANNOT_BE_READ, e);
+ }
+ PDFASUtils.checkDocumentPermissions(doc);
+
+ SignaturePlaceholderExtractor extractor;
+ try
+ {
+ extractor = new SignaturePlaceholderExtractor(placeholderId, matchMode);
+ } catch (IOException e2) {
+ throw new PDFDocumentException(ErrorCode.DOCUMENT_CANNOT_BE_READ, 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 PDFDocumentException(ErrorCode.DOCUMENT_CANNOT_BE_READ, e1);
+ }
+
+ }
+ if (extractor.placeholders.size() > 0){
+ SignaturePlaceholderData ret = matchPlaceholderDocument(extractor.placeholders, placeholderId, matchMode);
+ SignaturePlaceholderContext.setSignaturePlaceholderData(ret);
+ return ret;
+ }
+ return null;
+ }finally{
+ if (doc != null)
+ try {
+ doc.close();
+ } catch (IOException e) {
+ log.debug("Could not close document.", e);
+ }
+ }
+
+ }
+
+ private static SignaturePlaceholderData matchPlaceholderDocument(
+ List placeholders, String placeholderId, int matchMode) throws PlaceholderExtractionException {
+
+ if (matchMode == SignParameters.PLACEHOLDER_MATCH_MODE_STRICT)
+ throw new PlaceholderExtractionException(ErrorCode.SIGNATURE_PLACEHOLDER_EXTRACTION_FAILED, "no suitable placeholder found and STRICT matching mode requested.");
+
+ if (placeholders.size() == 0)
+ return null;
+
+ for (int i = 0; i < placeholders.size(); i++)
+ {
+ SignaturePlaceholderData spd = (SignaturePlaceholderData)placeholders.get(i);
+ if (spd.getId() == null)
+ return spd;
+ }
+
+ if (matchMode == SignParameters.PLACEHOLDER_MATCH_MODE_LENIENT)
+ return (SignaturePlaceholderData)placeholders.get(0);
+
+ return null;
+ }
+
+ private static SignaturePlaceholderData matchPlaceholderPage(List placeholders,
+ String placeholderId, int matchMode) {
+ if (placeholders.size() == 0)
+ return null;
+ for (int i = 0; i < placeholders.size(); i++)
+ {
+ SignaturePlaceholderData data = (SignaturePlaceholderData)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;
+ }
+
+ protected void processOperator( PDFOperator operator, List 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() - 1F;
+ float y = unrotatedCTM.getYPosition() + unrotatedCTM.getYScale() + 1F;
+ float w = unrotatedCTM.getXScale() + 2F;
+
+ String posString = "p:" + currentPage + ";x:" + x + ";y:" + y + ";w:" + w;
+ try
+ {
+ data.setTablePos(new TablePos(posString));
+ data.setPlaceholderName(objectName.getName());
+ placeholders.add(data);
+ } catch (PDFDocumentException 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.getHeight() < 10 || bimg.getWidth() < 10)
+ return null;
+
+ LuminanceSource source = new BufferedImageLuminanceSource(bimg);
+ BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
+ Result result;
+ long before = System.currentTimeMillis();
+ try {
+ Hashtable hints = new Hashtable();
+ Vector formats = new Vector();
+ 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 && text.startsWith("PDF-AS-POS")) {
+ 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);
+ }
+ } 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/src/main/java/at/gv/egiz/pdfas/utils/PDFASUtils.java b/src/main/java/at/gv/egiz/pdfas/utils/PDFASUtils.java
index 9841779..f25e668 100644
--- a/src/main/java/at/gv/egiz/pdfas/utils/PDFASUtils.java
+++ b/src/main/java/at/gv/egiz/pdfas/utils/PDFASUtils.java
@@ -7,6 +7,8 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import org.apache.pdfbox.pdmodel.PDDocument;
+
import at.gv.egiz.pdfas.exceptions.ErrorCode;
import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException;
@@ -16,6 +18,7 @@ import com.lowagie.text.pdf.PdfReader;
* @author tknall
*/
public class PDFASUtils {
+//23.11.2010 changed by exthex - added checkDocumentPermissions(PDDocument doc)
private PDFASUtils() {
}
@@ -56,5 +59,11 @@ public class PDFASUtils {
}
return result;
}
+
+ public static void checkDocumentPermissions(PDDocument doc) throws PDFDocumentException {
+ if (doc.isEncrypted()) {
+ throw new PDFDocumentException(ErrorCode.DOCUMENT_IS_PROTECTED, "Document is encrypted.");
+ }
+ }
}
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/PdfAS.java b/src/main/java/at/knowcenter/wag/egov/egiz/PdfAS.java
index 57868d0..2c2c591 100644
--- a/src/main/java/at/knowcenter/wag/egov/egiz/PdfAS.java
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/PdfAS.java
@@ -30,7 +30,10 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import at.gv.egiz.pdfas.api.analyze.NonTextObjectInfo;
+import at.gv.egiz.pdfas.api.commons.SignatureInformation;
import at.gv.egiz.pdfas.api.timestamp.TimeStamper;
+import at.gv.egiz.pdfas.api.xmldsig.ExtendedSignatureInformation;
+import at.gv.egiz.pdfas.api.xmldsig.XMLDsigData;
import at.gv.egiz.pdfas.commandline.CommandlineConnectorChooser;
import at.gv.egiz.pdfas.exceptions.ErrorCode;
import at.gv.egiz.pdfas.exceptions.framework.CorrectorException;
@@ -95,7 +98,8 @@ import com.lowagie.text.pdf.PdfReader;
*/
public abstract class PdfAS
{
-
+//23.11.2010 changed by exthex - added method: verifyExtendedSignatureHolders(List extended_signature_info, String connectorType, boolean returnHashInputData, Date verificationTime)
+
/**
* The current version of the pdf-as library. This version string is logged on every invocation
* of the api or the web application.
@@ -800,7 +804,7 @@ public abstract class PdfAS
// (holder.signature_object.isTextual() ? "textual" : "binary"));
// logger_.debug(holder.signature_object.toString());
- SignatureResponse result = verify(holder, connectorType, returnHashInputData, verificationTime);
+ SignatureResponse result = verify(holder, connectorType, returnHashInputData, verificationTime, null);
results.add(result);
// logger_.debug();
@@ -815,6 +819,34 @@ public abstract class PdfAS
return results;
}
+ /**
+ *
+ * @param extended_signature_info
+ * @param connectorType
+ * @param returnHashInputData
+ * @param verificationTime
+ * @return
+ * @throws PDFDocumentException
+ * @throws NormalizeException
+ * @throws SignatureException
+ * @throws ConnectorException
+ * @throws ConnectorFactoryException
+ */
+ public static List verifyExtendedSignatureHolders(List extended_signature_info, String connectorType, boolean returnHashInputData, Date verificationTime) throws PDFDocumentException, NormalizeException, SignatureException, ConnectorException, ConnectorFactoryException
+ {
+ List results = new ArrayList();
+ for (int i = 0; i < extended_signature_info.size(); i++)
+ {
+ ExtendedSignatureInformation sigInfo = (ExtendedSignatureInformation) extended_signature_info.get(i);
+ SignatureInformation si = sigInfo.getSignatureInformation();
+ SignatureHolder holder = (SignatureHolder) si.getInternalSignatureInformation();
+
+ SignatureResponse result = verify(holder, connectorType, returnHashInputData, verificationTime, sigInfo.getXmlDsigData());
+ results.add(result);
+ }
+ return results;
+ }
+
public static List verifySignatureHoldersWeb(List signature_holders,
VerifySessionInformation si, String loc_ref) throws PDFDocumentException, NormalizeException, SignatureException, ConnectorException
{
@@ -852,7 +884,7 @@ public abstract class PdfAS
* @throws ConnectorException
* @throws ConnectorFactoryException
*/
- public static SignatureResponse verify(SignatureHolder signature_holder, String connectorType, boolean returnHashInputData, Date verificationTime) throws NormalizeException, PDFDocumentException, SignatureException, ConnectorException, ConnectorFactoryException
+ public static SignatureResponse verify(SignatureHolder signature_holder, String connectorType, boolean returnHashInputData, Date verificationTime, XMLDsigData dsig) throws NormalizeException, PDFDocumentException, SignatureException, ConnectorException, ConnectorFactoryException
{
// String text_to_be_verified = signature_holder.getSignedText();
// logger_.debug("verify text_to_be_verified"+text_to_be_verified);
@@ -894,7 +926,7 @@ public abstract class PdfAS
cp.setVerificationTime(verificationTime);
Connector c = at.gv.egiz.pdfas.framework.ConnectorFactory.createConnector(connectorId, cp);
- return c.doVerify(sd, so);
+ return c.doVerify(sd, so, dsig);
}
public static SignatureResponse verifyWeb(SignatureHolder signature_holder, String connector, String loc_ref) throws NormalizeException, PDFDocumentException, SignatureException, ConnectorException
@@ -933,7 +965,7 @@ public abstract class PdfAS
String profile = so_to_be_verified.getSignatureTypeDefinition().getType();
Connector c = ConnectorChooser.chooseWebConnectorForVerify(connector, so_to_be_verified.getKZ(), so.id, profile, loc_ref);
- return c.doVerify(sd, so);
+ return c.doVerify(sd, so, null);
}
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinarySignature.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinarySignature.java
index 4a80553..c34ee68 100644
--- a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinarySignature.java
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/BinarySignature.java
@@ -21,6 +21,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
@@ -42,6 +43,8 @@ import at.gv.egiz.pdfas.exceptions.pdf.KZSettingNotFoundException;
import at.gv.egiz.pdfas.framework.input.PdfDataSource;
import at.gv.egiz.pdfas.framework.output.DataSink;
import at.gv.egiz.pdfas.framework.signator.SignatorInformation;
+import at.gv.egiz.pdfas.placeholder.SignaturePlaceholderContext;
+import at.gv.egiz.pdfas.placeholder.SignaturePlaceholderData;
import at.gv.egiz.pdfas.utils.PDFASUtils;
import at.knowcenter.wag.egov.egiz.cfg.SettingsReader;
import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException;
@@ -56,13 +59,17 @@ import at.knowcenter.wag.egov.egiz.sig.SignatureTypes;
import at.knowcenter.wag.egov.egiz.tools.CodingHelper;
import at.knowcenter.wag.exactparser.ByteArrayUtils;
+import com.lowagie.text.BadElementException;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
+import com.lowagie.text.Image;
import com.lowagie.text.Rectangle;
+import com.lowagie.text.pdf.BadPdfFormatException;
import com.lowagie.text.pdf.PRStream;
import com.lowagie.text.pdf.PdfArray;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfDictionary;
+import com.lowagie.text.pdf.PdfImage;
import com.lowagie.text.pdf.PdfIndirectObject;
import com.lowagie.text.pdf.PdfIndirectReference;
import com.lowagie.text.pdf.PdfName;
@@ -87,6 +94,8 @@ import com.lowagie.text.pdf.PdfTemplate;
*/
public abstract class BinarySignature
{
+//23.11.2010 changed by exthex - added replacePlaceholder(PdfStamper stamper, int pageNr, String placeholderName) method
+
protected static Log logger = LogFactory.getLog(BinarySignature.class);
/**
* The tolerance area of the line break algorithm.
@@ -826,6 +835,13 @@ public abstract class BinarySignature
{
throw new PDFDocumentException(224, "The provided page (=" + pi.getPage() + ") is out of range.");
}
+
+ if (SignaturePlaceholderContext.isSignaturePlaceholderDataSet() &&
+ SignaturePlaceholderContext.getSignaturePlaceholderData().getPlaceholderName() != null)
+ {
+ replacePlaceholder(stamper, pi.getPage(), SignaturePlaceholderContext.getSignaturePlaceholderData().getPlaceholderName());
+ }
+
PdfContentByte content = stamper.getOverContent(pi.getPage());
// content = StampContent einer PageStamp.
@@ -940,6 +956,18 @@ public abstract class BinarySignature
throw new PresentableException(ErrorCode.CANNOT_WRITE_PDF, e);
}
}
+
+ private static void replacePlaceholder(PdfStamper stamper, int pageNr, String placeholderName) throws BadElementException, MalformedURLException, IOException, BadPdfFormatException {
+ Image img = Image.getInstance(SignaturePlaceholderData.class.getResource("empty.jpg"));
+ PdfImage pImg = new PdfImage(img, "Imwurscht", null);
+ PdfStamperImp stamperImp = (PdfStamperImp)stamper.getWriter();
+ PdfIndirectObject ind = stamperImp.addToBody(pImg);
+
+ PdfDictionary resources = stamper.getReader().getPageN(pageNr).getAsDict(PdfName.RESOURCES);
+ resources.getAsDict(PdfName.XOBJECT).put(new PdfName(placeholderName), ind.getIndirectReference());
+ stamperImp.markUsed(resources);
+ }
+
/**
* Creates the EGIZ Dictionary and adds it to the document.
diff --git a/src/main/resources/at/gv/egiz/pdfas/placeholder/empty.jpg b/src/main/resources/at/gv/egiz/pdfas/placeholder/empty.jpg
new file mode 100644
index 0000000..7913177
--- /dev/null
+++ b/src/main/resources/at/gv/egiz/pdfas/placeholder/empty.jpg
Binary files differ
diff --git a/src/main/resources/at/gv/egiz/pdfas/placeholder/pdfbox-reader.properties b/src/main/resources/at/gv/egiz/pdfas/placeholder/pdfbox-reader.properties
new file mode 100644
index 0000000..a3decc9
--- /dev/null
+++ b/src/main/resources/at/gv/egiz/pdfas/placeholder/pdfbox-reader.properties
@@ -0,0 +1,23 @@
+BT = org.apache.pdfbox.util.operator.BeginText
+cm = org.apache.pdfbox.util.operator.Concatenate
+Do = org.apache.pdfbox.util.operator.Invoke
+ET = org.apache.pdfbox.util.operator.EndText
+gs = org.apache.pdfbox.util.operator.SetGraphicsStateParameters
+q = org.apache.pdfbox.util.operator.GSave
+Q = org.apache.pdfbox.util.operator.GRestore
+T* = org.apache.pdfbox.util.operator.NextLine
+Tc = org.apache.pdfbox.util.operator.SetCharSpacing
+Td = org.apache.pdfbox.util.operator.MoveText
+TD = org.apache.pdfbox.util.operator.MoveTextSetLeading
+Tf = org.apache.pdfbox.util.operator.SetTextFont
+Tj = org.apache.pdfbox.util.operator.ShowText
+TJ = org.apache.pdfbox.util.operator.ShowTextGlyph
+TL = org.apache.pdfbox.util.operator.SetTextLeading
+Tm = org.apache.pdfbox.util.operator.SetMatrix
+Tr = org.apache.pdfbox.util.operator.SetTextRenderingMode
+Ts = org.apache.pdfbox.util.operator.SetTextRise
+Tw = org.apache.pdfbox.util.operator.SetWordSpacing
+Tz = org.apache.pdfbox.util.operator.SetHorizontalTextScaling
+w = org.apache.pdfbox.util.operator.SetLineWidth
+\' = org.apache.pdfbox.util.operator.MoveAndShow
+\" = org.apache.pdfbox.util.operator.SetMoveAndShow \ No newline at end of file