aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Fitzek <andreas.fitzek@iaik.tugraz.at>2014-04-10 16:59:08 +0200
committerAndreas Fitzek <andreas.fitzek@iaik.tugraz.at>2014-04-10 16:59:08 +0200
commitb4b272b6af9d1f3c51011a407cdc29f64b812865 (patch)
tree284dcfd429c6586e97f96827afaa5e93deabd01b
parentf891dca529c9dc199114ae4f0857d28812315b11 (diff)
downloadpdf-as-4-b4b272b6af9d1f3c51011a407cdc29f64b812865.tar.gz
pdf-as-4-b4b272b6af9d1f3c51011a407cdc29f64b812865.tar.bz2
pdf-as-4-b4b272b6af9d1f3c51011a407cdc29f64b812865.zip
Memory optimizations, added Version to web
-rw-r--r--pdf-as-cli/src/main/java/at/gv/egiz/pdfas/cli/Main.java4
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java54
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/PlaceholderFilter.java4
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/SignaturePlaceholderExtractor.java547
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/positioning/Positioning.java390
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PADESPDFBOXSigner.java9
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureProperties.java3
-rw-r--r--pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/status/PDFObject.java24
-rw-r--r--pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java8
-rw-r--r--pdf-as-web/src/main/webapp/index.jsp3
10 files changed, 532 insertions, 514 deletions
diff --git a/pdf-as-cli/src/main/java/at/gv/egiz/pdfas/cli/Main.java b/pdf-as-cli/src/main/java/at/gv/egiz/pdfas/cli/Main.java
index 34e384ad..dcd94b77 100644
--- a/pdf-as-cli/src/main/java/at/gv/egiz/pdfas/cli/Main.java
+++ b/pdf-as-cli/src/main/java/at/gv/egiz/pdfas/cli/Main.java
@@ -28,6 +28,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Iterator;
import java.util.List;
+import java.util.Scanner;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
@@ -183,6 +184,8 @@ public class Main {
public static void main(String[] args) {
// create the command line parser
+
+
CommandLineParser parser = new GnuParser();
ModeOfOperation mode = ModeOfOperation.INVALID;
try {
@@ -218,7 +221,6 @@ public class Main {
} else if (mode == ModeOfOperation.VERIFY) {
perform_verify(cli);
}
-
} catch (ParseException e) {
System.err.println("Invalid arguments: " + e.getMessage());
usage();
diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java
index 7946f966..25e57188 100644
--- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java
+++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java
@@ -95,22 +95,23 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants {
}
}
- if(parameter.getDataSource() == null || parameter.getDataSource().getByteData() == null) {
+ if (parameter.getDataSource() == null
+ || parameter.getDataSource().getByteData() == null) {
throw new PdfAsValidationException("error.pdf.sig.10", null);
}
-
- if(parameter.getOutput() == null) {
+
+ if (parameter.getOutput() == null) {
throw new PdfAsValidationException("error.pdf.sig.11", null);
}
-
- try {
- PDDocument doc = PDDocument.load(new ByteArrayInputStream(parameter.getDataSource().getByteData()));
- PDFUtils.checkPDFPermissions(doc);
- doc.close();
- } catch(IOException e) {
- throw new PdfAsValidationException("error.pdf.sig.12", null, e);
- }
-
+
+ /*
+ * try { PDDocument doc = PDDocument.load(new
+ * ByteArrayInputStream(parameter.getDataSource().getByteData()));
+ * PDFUtils.checkPDFPermissions(doc); doc.close(); } catch(IOException
+ * e) { throw new PdfAsValidationException("error.pdf.sig.12", null, e);
+ * }
+ */
+
// TODO: verify Sign Parameter
}
@@ -120,8 +121,9 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants {
if (!(parameter.getConfiguration() instanceof ISettings)) {
throw new PdfAsSettingsException("Invalid settings object!");
}
-
- if(parameter.getDataSource() == null || parameter.getDataSource().getByteData() == null) {
+
+ if (parameter.getDataSource() == null
+ || parameter.getDataSource().getByteData() == null) {
throw new PdfAsValidationException("error.pdf.verify.01", null);
}
@@ -142,8 +144,16 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants {
ISettings settings = (ISettings) parameter.getConfiguration();
OperationStatus status = new OperationStatus(settings, parameter);
- //PlaceholderConfiguration placeholderConfiguration = status
- // .getPlaceholderConfiguration();
+
+ // set Original PDF Document Data
+ status.getPdfObject().setOriginalDocument(
+ parameter.getDataSource().getByteData());
+
+ PDDocument doc = status.getPdfObject().getDocument();
+ PDFUtils.checkPDFPermissions(doc);
+
+ // PlaceholderConfiguration placeholderConfiguration = status
+ // .getPlaceholderConfiguration();
RequestedSignature requestedSignature = new RequestedSignature(
status);
@@ -164,11 +174,7 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants {
// status
// .getSignatureProfileConfiguration(signatureProfileID);
- // set Original PDF Document Data
- status.getPdfObject().setOriginalDocument(
- parameter.getDataSource().getByteData());
-
- //this.stampPdf(status);
+ // this.stampPdf(status);
// Create signature
IPdfSigner signer = PdfSignerFactory.createPdfSigner();
@@ -272,7 +278,7 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants {
if (verifyFilter != null) {
List<VerifyResult> results = verifyFilter.verify(
contentData.toByteArray(),
- content.getBytes(),
+ content.getBytes(),
parameter.getVerificationTime(), bytes);
if (results != null && !results.isEmpty()) {
result.addAll(results);
@@ -308,7 +314,7 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants {
throws PdfAsException {
verifySignParameter(parameter);
-
+
StatusRequestImpl request = new StatusRequestImpl();
try {
@@ -355,7 +361,7 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants {
status.getSignParamter().getDataSource().getByteData());
// STAMPER!
- //stampPdf(status);
+ // stampPdf(status);
request.setNeedCertificate(false);
status.setSigningDate(Calendar.getInstance());
diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/PlaceholderFilter.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/PlaceholderFilter.java
index a4d2c7aa..8e0171fb 100644
--- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/PlaceholderFilter.java
+++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/PlaceholderFilter.java
@@ -1,6 +1,5 @@
package at.gv.egiz.pdfas.lib.impl.placeholder;
-import java.io.ByteArrayInputStream;
import java.io.IOException;
import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
@@ -16,8 +15,7 @@ public class PlaceholderFilter implements IConfigurationConstants {
if (status.getPlaceholderConfiguration().isGlobalPlaceholderEnabled()) {
SignaturePlaceholderData signaturePlaceholderData = SignaturePlaceholderExtractor
- .extract(new ByteArrayInputStream(status.getPdfObject()
- .getOriginalDocument()), null, 1);
+ .extract(status.getPdfObject().getDocument(), null, 1);
return signaturePlaceholderData;
/*
diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/SignaturePlaceholderExtractor.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/SignaturePlaceholderExtractor.java
index 816ca04f..93162bd3 100644
--- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/SignaturePlaceholderExtractor.java
+++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/placeholder/SignaturePlaceholderExtractor.java
@@ -89,8 +89,6 @@ import com.google.zxing.common.HybridBinarizer;
//////
-
-
/**
* Extract all relevant information from a placeholder image.
*
@@ -98,279 +96,276 @@ import com.google.zxing.common.HybridBinarizer;
*
*/
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(InputStream inputStream, String placeholderId, int matchMode)
- throws PdfAsException {
- SignaturePlaceholderContext.setSignaturePlaceholderData(null);
- PDDocument doc = null;
- try
- {
- try {
- doc = PDDocument.load(inputStream);
- } catch (IOException e) {
- throw new PDFIOException("error.pdf.io.04", e);
- }
- 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;
- } finally {
- if (doc != null)
- try {
- doc.close();
- } catch (IOException e) {
- log.debug("Could not close document.", e);
- }
- }
-
- }
-
- 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;
- }
+ /**
+ * 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-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/positioning/Positioning.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/positioning/Positioning.java
index c8c2f99f..b6405c2d 100644
--- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/positioning/Positioning.java
+++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/positioning/Positioning.java
@@ -34,210 +34,196 @@ import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
/**
- * Created with IntelliJ IDEA.
- * User: afitzek
- * Date: 8/29/13
- * Time: 4:30 PM
- * To change this template use File | Settings | File Templates.
+ * 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 {
- /**
- * 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
- {
-
- try {
- PDFUtils.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();
-
- if(cropBox == null) {
- cropBox = pdPage.findCropBox();
- }
-
-
- if(cropBox == null) {
- cropBox = pdPage.findMediaBox();
- }
-
- //TODO: fallback to MediaBox if Cropbox not available!
-
- // 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);
- } finally {
- if (pdfDataSource != null) {
- try {
- pdfDataSource.close();
- } catch (Exception e) {
- }
- }
- }
- }
+ /**
+ * 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 {
+
+ PDFUtils.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();
+
+ if (cropBox == null) {
+ cropBox = pdPage.findCropBox();
+ }
+
+ if (cropBox == null) {
+ cropBox = pdPage.findMediaBox();
+ }
+
+ // TODO: fallback to MediaBox if Cropbox not available!
+
+ // 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-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PADESPDFBOXSigner.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PADESPDFBOXSigner.java
index c04dca59..43d501f1 100644
--- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PADESPDFBOXSigner.java
+++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PADESPDFBOXSigner.java
@@ -99,8 +99,7 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
FileInputStream fis = new FileInputStream(new File(fisTmpFile));
- PDDocument doc = PDDocument.load(new ByteArrayInputStream(pdfObject
- .getOriginalDocument()));
+ PDDocument doc = pdfObject.getDocument();
PDSignature signature = new PDSignature();
signature.setFilter(COSName.getPDFName(signer.getPDFFilter())); // default
@@ -198,13 +197,13 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
IPDFVisualObject visualObject = stamper.createVisualPDFObject(
pdfObject, main);
- PDDocument originalDocument = PDDocument
+ /*PDDocument originalDocument = PDDocument
.load(new ByteArrayInputStream(pdfObject.getStatus()
- .getPdfObject().getOriginalDocument()));
+ .getPdfObject().getOriginalDocument()));*/
PositioningInstruction positioningInstruction = Positioning
.determineTablePositioning(tablePos, "",
- originalDocument, visualObject,
+ doc, visualObject,
legacy32Position);
SignaturePositionImpl position = new SignaturePositionImpl();
diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureProperties.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureProperties.java
index 05cce46d..6b1edf13 100644
--- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureProperties.java
+++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox/PDFAsVisualSignatureProperties.java
@@ -32,8 +32,7 @@ public class PDFAsVisualSignatureProperties extends PDVisibleSigProperties {
e.printStackTrace();
}
try {
- PDDocument origDoc = PDDocument.load(new ByteArrayInputStream(
- object.getOriginalDocument()));
+ PDDocument origDoc = object.getDocument();
designer = new PDFAsVisualSignatureDesigner(origDoc, pos.getPage(), this, pos.isMakeNewPage());
float posy = designer.getPageHeight() - pos.getY();
diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/status/PDFObject.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/status/PDFObject.java
index b402d0d2..f8ca3567 100644
--- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/status/PDFObject.java
+++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/status/PDFObject.java
@@ -23,25 +23,47 @@
******************************************************************************/
package at.gv.egiz.pdfas.lib.impl.status;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+
public class PDFObject {
private OperationStatus status;
+ private PDDocument doc;
private byte[] originalDocument;
private byte[] signedDocument;
public PDFObject(OperationStatus operationStatus) {
this.status = operationStatus;
}
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ if(doc != null) {
+ doc.close();
+ }
+ }
public byte[] getOriginalDocument() {
return originalDocument;
}
- public void setOriginalDocument(byte[] originalDocument) {
+ public void setOriginalDocument(byte[] originalDocument) throws IOException {
this.originalDocument = originalDocument;
+ if(doc != null) {
+ doc.close();
+ }
+ this.doc = PDDocument.load(new ByteArrayInputStream(this.originalDocument));
}
+ public PDDocument getDocument() {
+ return this.doc;
+ }
+
public byte[] getSignedDocument() {
return signedDocument;
}
diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java
index 47243a43..f9d3e720 100644
--- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java
+++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java
@@ -809,4 +809,12 @@ public class PdfAsHelper {
}
return false;
}
+
+ public static String getVersion() {
+ return PdfAsFactory.getVersion();
+ }
+
+ public static String getSCMRevision() {
+ return PdfAsFactory.getSCMRevision();
+ }
}
diff --git a/pdf-as-web/src/main/webapp/index.jsp b/pdf-as-web/src/main/webapp/index.jsp
index 82458f77..42f2a168 100644
--- a/pdf-as-web/src/main/webapp/index.jsp
+++ b/pdf-as-web/src/main/webapp/index.jsp
@@ -1,4 +1,5 @@
<%@page import="at.gv.egiz.pdfas.web.config.WebConfiguration"%>
+<%@page import="at.gv.egiz.pdfas.web.helper.PdfAsHelper"%>
<html>
<head>
<title>PDF-Signatur</title>
@@ -60,5 +61,7 @@
}
%>
</form>
+
+ <p><small>Version: <%= PdfAsHelper.getVersion() %> - <%= PdfAsHelper.getSCMRevision() %></small></p>
</body>
</html> \ No newline at end of file