aboutsummaryrefslogtreecommitdiff
path: root/pdf-as-pdfbox-2
diff options
context:
space:
mode:
authorThomas <>2023-06-26 21:11:26 +0200
committerThomas <>2023-06-26 21:11:26 +0200
commit8e0ccca7cfd1680e33737e0db8d6e1091661568c (patch)
tree33ae7a462976eecbda355921706412a1ff60131a /pdf-as-pdfbox-2
parent203eaa42d83da699ed9cc6c7e286906b5aeb6d44 (diff)
downloadpdf-as-4-8e0ccca7cfd1680e33737e0db8d6e1091661568c.tar.gz
pdf-as-4-8e0ccca7cfd1680e33737e0db8d6e1091661568c.tar.bz2
pdf-as-4-8e0ccca7cfd1680e33737e0db8d6e1091661568c.zip
refact(placeholder): clean-up, bugfix and optimize QR-Code placeholder detection
Major re-factoring of QR-Code detection with huge clean-up of code.
Diffstat (limited to 'pdf-as-pdfbox-2')
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/PDFBoxPlaceholderExtractor.java28
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignatureFieldsAndPlaceHolderExtractor.java134
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignaturePlaceholderExtractor.java818
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PADESPDFBOXSigner.java352
-rw-r--r--pdf-as-pdfbox-2/src/test/java/at/gv/egiz/pdfas/lib/testpdfbox/PDFBoxPlaceholderExtractorTest.java51
-rw-r--r--pdf-as-pdfbox-2/src/test/java/at/gv/egiz/pdfas/lib/testpdfbox/SignatureFieldsAndPlaceHolderExtractorTest.java21
-rw-r--r--pdf-as-pdfbox-2/src/test/resources/data/platzhalter_en_de_test.pdfbin0 -> 46732 bytes
7 files changed, 636 insertions, 768 deletions
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/PDFBoxPlaceholderExtractor.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/PDFBoxPlaceholderExtractor.java
index 63b006bf..ad874bc0 100644
--- a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/PDFBoxPlaceholderExtractor.java
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/PDFBoxPlaceholderExtractor.java
@@ -1,5 +1,7 @@
package at.gv.egiz.pdfas.lib.impl.pdfbox2.placeholder;
+import java.io.IOException;
+
import at.gv.egiz.pdfas.common.exceptions.PDFIOException;
import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
import at.gv.egiz.pdfas.lib.impl.pdfbox2.PDFBOXObject;
@@ -7,9 +9,6 @@ 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;
-import java.io.IOException;
-import java.util.List;
-
public class PDFBoxPlaceholderExtractor implements PlaceholderExtractor {
@@ -18,27 +17,8 @@ public class PDFBoxPlaceholderExtractor implements PlaceholderExtractor {
if (doc instanceof PDFBOXObject) {
PDFBOXObject object = (PDFBOXObject) doc;
try {
- SignaturePlaceholderExtractor extractor = new SignaturePlaceholderExtractor(placeholderId,
- matchMode, object.getDocument());
- return extractor.extract(object.getDocument(),
- placeholderId, matchMode);
- } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e2) {
- throw new PDFIOException("error.pdf.io.04", e2);
- }
-
- }
- throw new PdfAsException("INVALID STATE");
- }
-
- @Override
- public List<SignaturePlaceholderData> extractList(PDFObject doc, String placeholderId, int matchMode) throws PdfAsException {
- if (doc instanceof PDFBOXObject) {
- PDFBOXObject object = (PDFBOXObject) doc;
- try {
- SignaturePlaceholderExtractor extractor = new SignaturePlaceholderExtractor(placeholderId,
- matchMode, object.getDocument());
- return extractor.extractList(object.getDocument(),
- placeholderId, matchMode);
+ SignaturePlaceholderExtractor extractor = new SignaturePlaceholderExtractor();
+ return extractor.extract(object.getDocument(), placeholderId, matchMode);
} catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e2) {
throw new PDFIOException("error.pdf.io.04", e2);
}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignatureFieldsAndPlaceHolderExtractor.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignatureFieldsAndPlaceHolderExtractor.java
index 609f8254..8e5e5d4e 100644
--- a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignatureFieldsAndPlaceHolderExtractor.java
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignatureFieldsAndPlaceHolderExtractor.java
@@ -1,106 +1,58 @@
package at.gv.egiz.pdfas.lib.impl.pdfbox2.placeholder;
-import at.gv.egiz.pdfas.lib.impl.placeholder.PlaceholderExtractorConstants;
-import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData;
+import java.util.ArrayList;
+import java.util.List;
+
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDField;
import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
+import at.gv.egiz.pdfas.lib.impl.placeholder.PlaceholderExtractorConstants;
+import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData;
public class SignatureFieldsAndPlaceHolderExtractor {
- //Search for empty signature fields
- public static List<String> findEmptySignatureFields(PDDocument doc)
- {
- PDSignature signature;
- List<PDField> signatureField;
- List<String> signatureFieldNames = new ArrayList<>();
- PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
- if (acroForm != null) {
- signatureField = acroForm.getFields();
- for (PDField pdField : signatureField) {
- if(pdField instanceof PDSignatureField && pdField.getPartialName()!=null)
- {
- signature = ((PDSignatureField) pdField).getSignature();
- if(signature == null) signatureFieldNames.add(pdField.getPartialName());
- }
- }
- }
- return signatureFieldNames;
- }
- /*
- Needed by PDF-OVER
- */
-
- /**
- * Returns the next unused signature placeholder
- * @param doc The document to be searched for signature placeholders
- * @return The next unused signature placeholder or null in case there is none
- */
- public static SignaturePlaceholderData getNextUnusedSignaturePlaceHolder(PDDocument doc) {
- try {
- String placeholderId = "1";
- int mode = PlaceholderExtractorConstants.PLACEHOLDER_MATCH_MODE_SORTED;
- SignaturePlaceholderExtractor signaturePlaceholderExtractor = new SignaturePlaceholderExtractor( placeholderId,
- mode, doc);
- List<SignaturePlaceholderData> results = signaturePlaceholderExtractor.extractList(doc, placeholderId,
- mode);
- if (results == null) {
- return null;
- }
- List<String> used = getExistingSignatureLocations(doc);
- //return first not used
- for(SignaturePlaceholderData result : results) {
- if(!used.contains(result.getPlaceholderName()))
- return result;
- }
- return null;
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
-
- public static SignaturePlaceholderData getSignaturePlaceHolder(PDDocument doc, String placeholderId,
- int mode) {
- try {
- SignaturePlaceholderExtractor signaturePlaceholderExtractor = new SignaturePlaceholderExtractor( placeholderId,
- mode, doc);
- return signaturePlaceholderExtractor.extract(doc, placeholderId, mode);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
+ // Search for empty signature fields
+ public static List<String> findEmptySignatureFields(PDDocument doc) {
+ PDSignature signature;
+ List<PDField> signatureField;
+ final List<String> signatureFieldNames = new ArrayList<>();
+ final PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
+ if (acroForm != null) {
+ signatureField = acroForm.getFields();
+ for (final PDField pdField : signatureField) {
+ if (pdField instanceof PDSignatureField && pdField.getPartialName() != null) {
+ signature = ((PDSignatureField) pdField).getSignature();
+ if (signature == null) {
+ signatureFieldNames.add(pdField.getPartialName());
+ }
}
+ }
}
-
- public static List<SignaturePlaceholderData> getSignaturePlaceHolderList(PDDocument doc, String placeholderId, int mode) {
- try {
- SignaturePlaceholderExtractor signaturePlaceholderExtractor = new SignaturePlaceholderExtractor( placeholderId,
- mode, doc);
- return signaturePlaceholderExtractor.extractList(doc, placeholderId, mode);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
-
- public static List<String> getExistingSignatureLocations(PDDocument doc) {
- List<String> existingLocations = new ArrayList<>();
- try {
- List <PDSignature> pdSignatureList = doc.getSignatureDictionaries();
- if(pdSignatureList.size() != 0) {
- for(PDSignature sig : pdSignatureList) {
- existingLocations.add(sig.getLocation());
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- return existingLocations;
+ return signatureFieldNames;
+ }
+ /*
+ * Needed by PDF-OVER
+ */
+
+ /**
+ * Returns the next unused signature placeholder
+ *
+ * @param doc The document to be searched for signature placeholders
+ * @return The next unused signature placeholder or null in case there is none
+ */
+ public static SignaturePlaceholderData getNextUnusedSignaturePlaceHolder(PDDocument doc) {
+ try {
+ final String placeholderId = "1";
+ final int mode = PlaceholderExtractorConstants.PLACEHOLDER_MATCH_MODE_SORTED;
+ final SignaturePlaceholderExtractor signaturePlaceholderExtractor = new SignaturePlaceholderExtractor();
+ return signaturePlaceholderExtractor.extract(doc, placeholderId, mode);
+
+ } catch (final Exception e) {
+ e.printStackTrace();
+ return null;
}
+ }
}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignaturePlaceholderExtractor.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignaturePlaceholderExtractor.java
index b62b660a..99027be0 100644
--- a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignaturePlaceholderExtractor.java
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/placeholder/SignaturePlaceholderExtractor.java
@@ -3,19 +3,19 @@
* 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
@@ -51,30 +51,26 @@ import java.awt.geom.NoninvertibleTransformException;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
-import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;
+import java.util.stream.Collectors;
import org.apache.pdfbox.contentstream.PDFStreamEngine;
import org.apache.pdfbox.contentstream.operator.Operator;
import org.apache.pdfbox.contentstream.operator.OperatorProcessor;
import org.apache.pdfbox.cos.COSBase;
-import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
-import org.apache.pdfbox.pdmodel.font.PDFont;
-import org.apache.pdfbox.pdmodel.font.PDFontFactory;
import org.apache.pdfbox.pdmodel.graphics.PDXObject;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
import org.apache.pdfbox.util.Matrix;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
@@ -91,10 +87,10 @@ import at.gv.egiz.pdfas.common.exceptions.PDFIOException;
import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
import at.gv.egiz.pdfas.common.exceptions.PlaceholderExtractionException;
import at.gv.egiz.pdfas.lib.impl.placeholder.PlaceholderExtractorConstants;
-import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderContext;
import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData;
import at.knowcenter.wag.egov.egiz.pdf.TablePos;
import javassist.bytecode.stackmap.TypeData.ClassName;
+import lombok.extern.slf4j.Slf4j;
/**
* Extract all relevant information from a placeholder image.
@@ -102,423 +98,393 @@ import javassist.bytecode.stackmap.TypeData.ClassName;
* @author exthex
*
*/
-public class SignaturePlaceholderExtractor extends PDFStreamEngine implements PlaceholderExtractorConstants{
- /**
- * The log.
- */
- private static Logger logger = LoggerFactory
- .getLogger(SignaturePlaceholderExtractor.class);
-
- private List<SignaturePlaceholderData> placeholders = new ArrayList<>();
- private int currentPage = 0;
- private PDDocument doc;
-
- protected SignaturePlaceholderExtractor(String placeholderId,
- int placeholderMatchMode, PDDocument doc) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
- super();
-
- final Properties properties = new Properties();
- properties.load(ClassName.class.getClassLoader().getResourceAsStream("placeholder/pdfbox-reader-2.properties"));
-
- Set<Entry<Object, Object>> entries = properties.entrySet();
- for(Entry<Object, Object> entry:entries){
- String processorClassName = (String)entry.getValue();
- Class<?> klass = Class.forName( processorClassName );
- org.apache.pdfbox.contentstream.operator.OperatorProcessor processor =
- (OperatorProcessor) klass.newInstance();
-
- addOperator( processor );
- }
- this.doc = doc;
- }
-
- /**
- * Search the document for placeholder images and possibly included
- * additional info.<br/>
- * Searches only for the first placeholder page after page from top.
- *
- * @return all available info from the first found placeholder.
- * @throws PdfAsException
- * if the document could not be read.
- * @throws PlaceholderExtractionException
- * if STRICT matching mode was requested and no suitable
- * placeholder could be found.
- */
- public SignaturePlaceholderData extract(PDDocument doc,
- String placeholderId, int matchMode) throws PdfAsException {
- SignaturePlaceholderContext.setSignaturePlaceholderData(null);
-// SignaturePlaceholderExtractor extractor;
-// try {
-// extractor = new SignaturePlaceholderExtractor(placeholderId,
-// matchMode, doc);
-// } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e2) {
-// throw new PDFIOException("error.pdf.io.04", e2);
-// }
-
- int pageNr = 0;
- for(PDPage page : doc.getPages()){
- pageNr++;
-
- try {
- setCurrentPage(pageNr);
- if(page.getContents() != null && page.getResources() != null && page.getContentStreams() != null) {
- processPage(page); //TODO: pdfbox2 - right?
-
- }
- SignaturePlaceholderData ret = matchPlaceholderPage(
- placeholders, placeholderId, matchMode);
- if (ret != null) {
- SignaturePlaceholderContext
- .setSignaturePlaceholderData(ret);
- return ret;
- }
- } catch (IOException e1) {
- throw new PDFIOException("error.pdf.io.04", e1);
- } catch(Throwable e) {
- throw new PDFIOException("error.pdf.io.04", e);
- }
- }
- if (placeholders.size() > 0) {
- SignaturePlaceholderData ret = matchPlaceholderDocument(
- 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;
- }
-
- public List<SignaturePlaceholderData> extractList(PDDocument doc,
- String placeholderId, int matchMode) throws PdfAsException {
- SignaturePlaceholderContext.setSignaturePlaceholderData(null);
-
- int pageNr = 0;
- for(PDPage page : doc.getPages()){
- pageNr++;
-
- try {
- setCurrentPage(pageNr);
- if(page.getContents() != null && page.getResources() != null && page.getContentStreams() != null) {
- processPage(page); //TODO: pdfbox2 - right?
-
- }
- SignaturePlaceholderData ret = matchPlaceholderPage(
- placeholders, placeholderId, matchMode);
- if (ret != null) {
- SignaturePlaceholderContext
- .setSignaturePlaceholderData(ret);
- return placeholders;
- }
- } catch (IOException e1) {
- throw new PDFIOException("error.pdf.io.04", e1);
- } catch(Throwable e) {
- throw new PDFIOException("error.pdf.io.04", e);
- }
- }
- if (placeholders.size() > 0) {
- SignaturePlaceholderData ret = matchPlaceholderDocument(
- placeholders, placeholderId, matchMode);
- SignaturePlaceholderContext.setSignaturePlaceholderData(ret);
- return placeholders;
- }
- // no placeholders found, apply strict mode if set
- if (matchMode == PLACEHOLDER_MATCH_MODE_STRICT) {
- throw new PlaceholderExtractionException("error.pdf.stamp.09");
- }
- return null;
- }
-
- private SignaturePlaceholderData matchPlaceholderDocument(
- List<SignaturePlaceholderData> placeholders, String placeholderId,
- int matchMode) throws PlaceholderExtractionException {
-
- if (matchMode == PLACEHOLDER_MATCH_MODE_STRICT)
- throw new PlaceholderExtractionException("error.pdf.stamp.09");
-
- if (placeholders.size() == 0)
- return null;
-
- if (matchMode == PLACEHOLDER_MATCH_MODE_SORTED) {
- // sort all placeholders by the id string if all ids are null do nothing
- SignaturePlaceholderData currentFirstSpd = null;
- for (int i = 0; i < placeholders.size(); i++) {
- SignaturePlaceholderData spd = placeholders.get(i);
- if (spd.getId() != null) {
- if(currentFirstSpd == null) {
- currentFirstSpd = spd;
- logger.debug("Setting new current ID: {}",
- currentFirstSpd.getId());
- } else {
- String currentID = currentFirstSpd.getId();
- String testID = spd.getId();
- logger.debug("Testing placeholder current: {} compare to {}",
- currentID, testID);
- if(testID.compareToIgnoreCase(currentID) < 0) {
- currentFirstSpd = spd;
- logger.debug("Setting new current ID: {}",
- testID);
- }
- }
- }
- }
-
- if(currentFirstSpd != null) {
- logger.info("Running Placeholder sorted mode: using id: {}", currentFirstSpd.getId());
- return currentFirstSpd;
- } else {
- logger.info("Running Placeholder sorted mode: no placeholder with id found, fallback to first placeholder");
- }
- }
-
- for (int i = 0; i < placeholders.size(); i++) {
- SignaturePlaceholderData spd = placeholders.get(i);
- if (spd.getId() == null)
- return spd;
- }
-
- if (matchMode == PLACEHOLDER_MATCH_MODE_LENIENT)
- return placeholders.get(0);
-
- return null;
- }
-
- private SignaturePlaceholderData matchPlaceholderPage(
- List<SignaturePlaceholderData> placeholders, String placeholderId,
- int matchMode) {
-
- if(matchMode == PLACEHOLDER_MATCH_MODE_SORTED)
- return null;
-
- if (placeholders.size() == 0)
- return null;
- for (int i = 0; i < placeholders.size(); i++) {
- SignaturePlaceholderData data = placeholders.get(i);
- if (placeholderId != null && placeholderId.equals(data.getId()))
- return data;
- if (placeholderId == null && data.getId() == null)
- return data;
- }
- return null;
- }
-
-
-
- private void setCurrentPage(int pageNr) {
- this.currentPage = pageNr;
- }
-
- @Override
- protected void processOperator(Operator operator, List<COSBase> arguments)
- throws IOException {
- String operation = operator.getName();
- if (operation.equals("Do")) {
- COSName objectName = (COSName) arguments.get(0);
- PDXObject xobject = (PDXObject) getResources().getXObject(objectName);
- if (xobject instanceof PDImageXObject) {
- try {
- PDImageXObject image = (PDImageXObject) xobject;
- SignaturePlaceholderData data = checkImage(image);
- if (data != null) {
- PDPage page = getCurrentPage();
- Matrix ctm = getGraphicsState()
- .getCurrentTransformationMatrix();
- int pageRotation = page.getRotation();
- pageRotation = pageRotation % 360;
- double rotationInRadians = Math.toRadians(pageRotation);//(page.findRotation() * Math.PI) / 180;
-
- AffineTransform rotation = new AffineTransform();
- rotation.setToRotation(rotationInRadians);
- AffineTransform rotationInverse = rotation
- .createInverse();
- Matrix rotationInverseMatrix = new Matrix();
- rotationInverseMatrix
- .setFromAffineTransform(rotationInverse);
- Matrix rotationMatrix = new Matrix();
- rotationMatrix.setFromAffineTransform(rotation);
-
- Matrix unrotatedCTM = ctm
- .multiply(rotationInverseMatrix);
-
- float x = unrotatedCTM.getXPosition();
- float yPos = unrotatedCTM.getYPosition();
- float yScale = unrotatedCTM.getScaleY();
- float y = yPos + yScale;
- float w = unrotatedCTM.getScaleX();
-
- logger.debug("Page height: {}", page.getCropBox().getHeight());
- logger.debug("Page width: {}", page.getCropBox().getWidth());
-
- if(pageRotation == 90) {
- y = page.getCropBox().getWidth() - (y * (-1));
- } else if(pageRotation == 180) {
- x = page.getCropBox().getWidth() + x;
- y = page.getCropBox().getHeight() - (y * (-1));
- } else if(pageRotation == 270) {
- x = page.getCropBox().getHeight() + x;
- }
-
-
-
-
-
- String posString = "p:" + currentPage + ";x:" + Math.floor(x)
- + ";y:" + Math.ceil(y) + ";w:" + Math.ceil(w);
-
- logger.debug("Found Placeholder at: {}", posString);
- try {
- data.setTablePos(new TablePos(posString));
- data.setPlaceholderName(objectName.getName());
- placeholders.add(data);
- } catch (PdfAsException e) {
- throw new IOException();
- }
- }
- } catch (NoninvertibleTransformException e) {
- throw new IOException(e);
- }
- }
- } else {
- super.processOperator(operator, arguments);
- }
- }
-
- private Map<String, PDFont> fonts;
-
- //TODO: pdfbox2 - was override
- public Map<String, PDFont> getFonts() {
- if (fonts == null) {
- // at least an empty map will be returned
- // TODO we should return null instead of an empty map
- fonts = new HashMap<String, PDFont>();
- if(this.getResources() != null && this.getResources().getCOSObject() != null) {
- COSDictionary fontsDictionary = (COSDictionary) this.getResources().getCOSObject().getDictionaryObject(COSName.FONT);
- if (fontsDictionary == null) {
- // ignore we do not want to set anything, never when creating a signature!!!!!
- //fontsDictionary = new COSDictionary();
- //this.getResources().getCOSDictionary().setItem(COSName.FONT, fontsDictionary);
+@Slf4j
+public class SignaturePlaceholderExtractor extends PDFStreamEngine implements PlaceholderExtractorConstants {
+
+ private final List<SignaturePlaceholderData> placeholders = new ArrayList<>();
+ private int currentPage = 0;
+
+ protected SignaturePlaceholderExtractor() throws IOException, ClassNotFoundException,
+ InstantiationException, IllegalAccessException {
+ super();
+
+ final Properties properties = new Properties();
+ properties.load(ClassName.class.getClassLoader().getResourceAsStream(
+ "placeholder/pdfbox-reader-2.properties"));
+
+ final Set<Entry<Object, Object>> entries = properties.entrySet();
+ for (final Entry<Object, Object> entry : entries) {
+ final String processorClassName = (String) entry.getValue();
+ final Class<?> klass = Class.forName(processorClassName);
+ final org.apache.pdfbox.contentstream.operator.OperatorProcessor processor =
+ (OperatorProcessor) klass.newInstance();
+
+ addOperator(processor);
+
+ }
+ }
+
+ /**
+ * Search the document for placeholder images and possibly included additional
+ * info.<br/>
+ * Searches only for the first placeholder page after page from top.
+ *
+ * @return available info from the first found placeholder.
+ * @throws PdfAsException if the document could not be read.
+ * @throws PlaceholderExtractionException if STRICT matching mode was requested
+ * and no suitable placeholder could be
+ * found.
+ */
+ public SignaturePlaceholderData extract(PDDocument doc,
+ String placeholderId, int matchMode) throws PdfAsException {
+
+ List<String> extistingSignatureNames = existingExistingSignatureNames(doc);
+
+
+ int pageNr = 0;
+ for (final PDPage page : doc.getPages()) {
+ pageNr++;
+
+ try {
+ this.currentPage = pageNr;
+ if (page.getContents() != null && page.getResources() != null && page.getContentStreams() != null) {
+ processPage(page); // TODO: pdfbox2 - right?
+
+ }
+
+ final SignaturePlaceholderData ret = matchPlaceholderPage(
+ removeAlreadyUsePlaceholders(placeholders, extistingSignatureNames), placeholderId, matchMode);
+ if (ret != null) {
+ return ret;
+
+ }
+
+ } catch (final IOException e1) {
+ throw new PDFIOException("error.pdf.io.04", e1);
+
+ } catch (final Throwable e) {
+ throw new PDFIOException("error.pdf.io.04", e);
+
+ }
+ }
+
+ if (placeholders.size() > 0) {
+ final SignaturePlaceholderData ret = matchPlaceholderDocument(
+ removeAlreadyUsePlaceholders(placeholders, extistingSignatureNames), placeholderId, matchMode);
+ 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;
+ }
+
+ @Override
+ protected void processOperator(Operator operator, List<COSBase> arguments)
+ throws IOException {
+ final String operation = operator.getName();
+ if (operation.equals("Do")) {
+ final COSName objectName = (COSName) arguments.get(0);
+ final PDXObject xobject = getResources().getXObject(objectName);
+ if (xobject instanceof PDImageXObject) {
+ try {
+ final PDImageXObject image = (PDImageXObject) xobject;
+ final SignaturePlaceholderData data = checkImage(image);
+ if (data != null) {
+ final PDPage page = getCurrentPage();
+ final Matrix ctm = getGraphicsState()
+ .getCurrentTransformationMatrix();
+ int pageRotation = page.getRotation();
+ pageRotation = pageRotation % 360;
+ final double rotationInRadians = Math.toRadians(pageRotation);// (page.findRotation() * Math.PI) /
+ // 180;
+
+ final AffineTransform rotation = new AffineTransform();
+ rotation.setToRotation(rotationInRadians);
+ final AffineTransform rotationInverse = rotation
+ .createInverse();
+ final Matrix rotationInverseMatrix = new Matrix();
+ rotationInverseMatrix
+ .setFromAffineTransform(rotationInverse);
+ final Matrix rotationMatrix = new Matrix();
+ rotationMatrix.setFromAffineTransform(rotation);
+
+ final Matrix unrotatedCTM = ctm
+ .multiply(rotationInverseMatrix);
+
+ float x = unrotatedCTM.getXPosition();
+ final float yPos = unrotatedCTM.getYPosition();
+ final float yScale = unrotatedCTM.getScaleY();
+ float y = yPos + yScale;
+ final float w = unrotatedCTM.getScaleX();
+
+ log.debug("Page height: {}", page.getCropBox().getHeight());
+ log.debug("Page width: {}", page.getCropBox().getWidth());
+
+ if (pageRotation == 90) {
+ y = page.getCropBox().getWidth() - y * -1;
+ } else if (pageRotation == 180) {
+ x = page.getCropBox().getWidth() + x;
+ y = page.getCropBox().getHeight() - y * -1;
+ } else if (pageRotation == 270) {
+ x = page.getCropBox().getHeight() + x;
}
- else {
- for (COSName fontName : fontsDictionary.keySet()) {
- COSBase font = fontsDictionary.getDictionaryObject(fontName);
- // data-000174.pdf contains a font that is a COSArray, looks to be an error in the
- // PDF, we will just ignore entries that are not dictionaries.
- if (font instanceof COSDictionary) {
- PDFont newFont = null;
- try {
- newFont = PDFontFactory.createFont((COSDictionary) font);
- }
- catch (IOException exception) {
- logger.error("error while creating a font", exception);
- }
- if (newFont != null) {
- fonts.put(fontName.getName(), newFont);
- }
- }
- }
+
+ final String posString = "p:" + currentPage + ";x:" + Math.floor(x)
+ + ";y:" + Math.ceil(y) + ";w:" + Math.ceil(w);
+
+ log.debug("Found Placeholder at: {}", posString);
+ try {
+ data.setTablePos(new TablePos(posString));
+ data.setPlaceholderName(objectName.getName());
+ placeholders.add(data);
+
+ } catch (final PdfAsException e) {
+ throw new IOException();
+
}
+ }
+ } catch (final NoninvertibleTransformException e) {
+ throw new IOException(e);
+ }
+ }
+ } else {
+ super.processOperator(operator, arguments);
+ }
+ }
+
+ private SignaturePlaceholderData matchPlaceholderDocument(
+ List<SignaturePlaceholderData> placeholders, String placeholderId,
+ int matchMode) throws PlaceholderExtractionException {
+
+ if (matchMode == PLACEHOLDER_MATCH_MODE_STRICT) {
+ throw new PlaceholderExtractionException("error.pdf.stamp.09");
+ }
+
+ if (placeholders.size() == 0) {
+ return null;
+ }
+
+ if (matchMode == PLACEHOLDER_MATCH_MODE_SORTED) {
+ // sort all placeholders by the id string if all ids are null do nothing
+ SignaturePlaceholderData currentFirstSpd = null;
+ for (final SignaturePlaceholderData spd : placeholders) {
+ if (spd.getId() != null) {
+ if (currentFirstSpd == null) {
+ currentFirstSpd = spd;
+ log.debug("Setting new current ID: {}",
+ currentFirstSpd.getId());
+ } else {
+ currentFirstSpd = placeHolderIdMatcher(currentFirstSpd, spd);
+
+ }
+ }
+ }
+
+ if (currentFirstSpd != null) {
+ log.info("Running Placeholder sorted mode: using id: {}", currentFirstSpd.getId());
+ return currentFirstSpd;
+
+ } else {
+ log.info(
+ "Running Placeholder sorted mode: no placeholder with id found, fallback to first placeholder");
+ }
+ }
+
+ for (final SignaturePlaceholderData spd : placeholders) {
+ if (spd.getId() == null) {
+ return spd;
+ }
+ }
+
+ if (matchMode == PLACEHOLDER_MATCH_MODE_LENIENT) {
+ return placeholders.get(0);
+ }
+
+ return null;
+ }
+
+ private SignaturePlaceholderData placeHolderIdMatcher(SignaturePlaceholderData currentFirstSpd,
+ SignaturePlaceholderData spd) {
+ try {
+ Integer currentIDInt = Integer.valueOf(currentFirstSpd.getId());
+ Integer testIDInt = Integer.valueOf(spd.getId());
+
+ if (testIDInt < currentIDInt) {
+ log.debug("Setting new current ID: {}", testIDInt);
+ return spd;
+
+ } else {
+ return currentFirstSpd;
+
+ }
+ } catch (NumberFormatException e) {
+ log.trace("Can not compare placeholderId's on integer level. Using String compare ... ");
+ final String currentID = currentFirstSpd.getId();
+ final String testID = spd.getId();
+ log.debug("Testing placeholder current: {} compare to {}",
+ currentID, testID);
+ if (testID.compareToIgnoreCase(currentID) < 0) {
+ log.debug("Setting new current ID: {}",
+ testID);
+ return spd;
+
+ } else {
+ return currentFirstSpd;
+
+ }
+ }
+ }
+
+ private SignaturePlaceholderData matchPlaceholderPage(
+ List<SignaturePlaceholderData> placeholders, String placeholderId,
+ int matchMode) {
+
+ if ((matchMode == PLACEHOLDER_MATCH_MODE_SORTED) || (placeholders.size() == 0)) {
+ return null;
+ }
+
+ for (final SignaturePlaceholderData data : placeholders) {
+ if (placeholderId != null && placeholderId.equals(data.getId())) {
+ return data;
+
+ }
+ if (placeholderId == null && data.getId() == null) {
+ return data;
+
+ }
+ }
+
+ return null;
+ }
+
+ private List<String> existingExistingSignatureNames(PDDocument doc) {
+ final List<String> existingLocations = new ArrayList<>();
+ try {
+ final List<PDSignature> pdSignatureList = doc.getSignatureDictionaries();
+ if (pdSignatureList.size() != 0) {
+ for (final PDSignature sig : pdSignatureList) {
+ existingLocations.add(sig.getLocation());
+ }
+ }
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
+ return existingLocations;
+ }
+
+ private List<SignaturePlaceholderData> removeAlreadyUsePlaceholders(
+ List<SignaturePlaceholderData> placeholders, List<String> existingPlaceholders) {
+ if (placeholders != null) {
+ return placeholders.stream()
+ .filter(el -> !existingPlaceholders.contains(el.getPlaceholderName()))
+ .collect(Collectors.toList());
+
+ } else {
+ return Collections.emptyList();
+
+ }
+ }
+
+ /**
+ * Checks an image if it is a placeholder for a signature.
+ *
+ * @param image
+ * @return
+ * @throws IOException
+ */
+ private SignaturePlaceholderData checkImage(PDImageXObject image)
+ throws IOException {
+ final BufferedImage bimg = image.getImage();
+ 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;
+ }
+
+ final LuminanceSource source = new BufferedImageLuminanceSource(bimg);
+ final BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
+ Result result;
+ final long before = System.currentTimeMillis();
+ try {
+ final Hashtable<DecodeHintType, Object> hints = new Hashtable<>();
+ final Vector<BarcodeFormat> formats = new Vector<>();
+ formats.add(BarcodeFormat.QR_CODE);
+ hints.put(DecodeHintType.POSSIBLE_FORMATS, formats);
+ result = new MultiFormatReader().decode(bitmap, hints);
+
+ final 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)) {
+
+ final String[] data = text.split(";");
+ if (data.length > 1) {
+ for (int i = 1; i < data.length; i++) {
+ final String kvPair = data[i];
+ final 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 (final 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);
}
- return fonts;
- }
-
- /**
- * Checks an image if it is a placeholder for a signature.
- *
- * @param image
- * @return
- * @throws IOException
- */
- private SignaturePlaceholderData checkImage(PDImageXObject image)
- throws IOException {
- BufferedImage bimg = image.getImage();
- if (bimg == null) {
- String type = image.getSuffix();
- if (type != null) {
- type = type.toUpperCase() + " images";
- } else {
- type = "Image type";
- }
- logger.info("Unable to extract image for QRCode analysis. "
- + type
- + " not supported. Add additional JAI Image filters to your classpath. Refer to https://jai.dev.java.net. Skipping image.");
- return null;
- }
- if (bimg.getHeight() < 10 || bimg.getWidth() < 10) {
- logger.debug("Image too small for QRCode. Skipping image.");
- return null;
- }
-
- LuminanceSource source = new BufferedImageLuminanceSource(bimg);
- BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
- Result result;
- long before = System.currentTimeMillis();
- try {
- Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>();
- Vector<BarcodeFormat> formats = new Vector<BarcodeFormat>();
- formats.add(BarcodeFormat.QR_CODE);
- hints.put(DecodeHintType.POSSIBLE_FORMATS, formats);
- result = new MultiFormatReader().decode(bitmap, hints);
-
- String text = result.getText();
- String profile = null;
- String type = null;
- String sigKey = null;
- String id = null;
- if (text != null) {
- if (text.startsWith(QR_PLACEHOLDER_IDENTIFIER)) {
-
- String[] data = text.split(";");
- if (data.length > 1) {
- for (int i = 1; i < data.length; i++) {
- String kvPair = data[i];
- String[] kv = kvPair.split("=");
- if (kv.length != 2) {
- logger.debug("Invalid parameter in placeholder data: "
- + kvPair);
- } else {
- if (kv[0]
- .equalsIgnoreCase(SignaturePlaceholderData.ID_KEY)) {
- id = kv[1];
- } else if (kv[0]
- .equalsIgnoreCase(SignaturePlaceholderData.PROFILE_KEY)) {
- profile = kv[1];
- } else if (kv[0]
- .equalsIgnoreCase(SignaturePlaceholderData.SIG_KEY_KEY)) {
- sigKey = kv[1];
- } else if (kv[0]
- .equalsIgnoreCase(SignaturePlaceholderData.TYPE_KEY)) {
- type = kv[1];
- }
- }
- }
- }
- return new SignaturePlaceholderData(profile, type, sigKey,
- id);
- } else {
- logger.warn("QR-Code found but does not start with \""
- + QR_PLACEHOLDER_IDENTIFIER
- + "\". Ignoring QR placeholder.");
- }
- }
- } catch (ReaderException re) {
- if (logger.isDebugEnabled()) {
- logger.debug("Could not decode - not a placeholder. needed: "
- + (System.currentTimeMillis() - before));
- }
- if (!(re instanceof NotFoundException)) {
- if (logger.isInfoEnabled()) {
- logger.info("Failed to decode image", re);
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- if (logger.isInfoEnabled()) {
- logger.info("Failed to decode image. Probably a zxing bug", e);
- }
- }
- return null;
- }
+ }
+ } catch (final ArrayIndexOutOfBoundsException e) {
+ if (log.isInfoEnabled()) {
+ log.info("Failed to decode image. Probably a zxing bug", e);
+ }
+ }
+ return null;
+ }
}
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PADESPDFBOXSigner.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PADESPDFBOXSigner.java
index b827abe6..e555cb39 100644
--- a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PADESPDFBOXSigner.java
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PADESPDFBOXSigner.java
@@ -70,8 +70,6 @@ import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.xmpbox.XMPMetadata;
import org.apache.xmpbox.schema.PDFAIdentificationSchema;
import org.apache.xmpbox.xml.DomXmpParser;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import at.gv.egiz.pdfas.common.exceptions.PDFASError;
import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
@@ -83,7 +81,6 @@ import at.gv.egiz.pdfas.lib.api.sign.IPlainSigner;
import at.gv.egiz.pdfas.lib.api.sign.SignParameter;
import at.gv.egiz.pdfas.lib.impl.ErrorExtractor;
import at.gv.egiz.pdfas.lib.impl.SignaturePositionImpl;
-import at.gv.egiz.pdfas.lib.impl.configuration.PlaceholderWebConfiguration;
import at.gv.egiz.pdfas.lib.impl.configuration.SignatureProfileConfiguration;
import at.gv.egiz.pdfas.lib.impl.pdfbox2.PDFBOXObject;
import at.gv.egiz.pdfas.lib.impl.pdfbox2.positioning.Positioning;
@@ -107,40 +104,32 @@ import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction;
import at.knowcenter.wag.egov.egiz.pdf.TablePos;
import at.knowcenter.wag.egov.egiz.table.Table;
import iaik.x509.X509Certificate;
+import lombok.extern.slf4j.Slf4j;
+@Slf4j
public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
- private static final Logger logger = LoggerFactory.getLogger(PADESPDFBOXSigner.class);
- private boolean isAdobeSigForm = false;
+
@Override
public void signPDF(PDFObject genericPdfObject, RequestedSignature requestedSignature,
PDFASSignatureInterface genericSigner) throws PdfAsException {
PDFAsVisualSignatureProperties properties = null;
- List<SignaturePlaceholderData> placeholders;
- List<SignaturePlaceholderData> availablePlaceholders;
- SignaturePlaceholderData signaturePlaceholderData = null;
-
- String placeholder_id = "";
-
- if (PlaceholderWebConfiguration.getValue(PLACEHOLDER_WEB_ID) != null && !PlaceholderWebConfiguration
- .getValue(PLACEHOLDER_WEB_ID).equalsIgnoreCase("")) {
- placeholder_id = PlaceholderWebConfiguration.getValue(PLACEHOLDER_WEB_ID);
- }
-
+
+ boolean isAdobeSigForm = false;
+
if (!(genericPdfObject instanceof PDFBOXObject)) {
- // tODO:
- throw new PdfAsException();
+ throw new PdfAsException("PDF to signObject is of wrong type: " + genericPdfObject.getClass().getName());
+
}
- final PDFBOXObject pdfObject = (PDFBOXObject) genericPdfObject;
-
if (!(genericSigner instanceof PDFASPDFBOXSignatureInterface)) {
- // tODO:
- throw new PdfAsException();
+ throw new PdfAsException("PDF signerObject is of wrong type:" + genericSigner.getClass().getName());
+
}
-
+
+ final PDFBOXObject pdfObject = (PDFBOXObject) genericPdfObject;
final PDFASPDFBOXSignatureInterface signer = (PDFASPDFBOXSignatureInterface) genericSigner;
String pdfaVersion = null;
@@ -148,76 +137,42 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
PDDocument doc = null;
SignatureOptions options = new SignatureOptions();
try {
-
doc = pdfObject.getDocument();
- // if signature already exists dont create new page
- final List<PDSignatureField> pdSignatureFieldList = doc.getSignatureFields();
- PDSignature signature;
+
// sign a PDF with an existing empty signature, as created by the
// CreateEmptySignatureForm example.
- String sigFieldName = pdfObject.getStatus().getSettings().getValue(SIGNATURE_FIELD_NAME);
- signature = findExistingSignature(doc, sigFieldName);
+ PDSignature signature = findExistingSignature(doc, getSignatureFieldNameConfig(pdfObject));
if (signature == null) {
// create signature dictionary
signature = new PDSignature();
+
} else {
isAdobeSigForm = true;
+
}
signature.setFilter(COSName.getPDFName(signer.getPDFFilter()));
signature.setSubFilter(COSName.getPDFName(signer.getPDFSubFilter()));
-// SignaturePlaceholderData signaturePlaceholderDataInit =
- placeholders = PlaceholderFilter.checkPlaceholderSignatureLocationList(pdfObject.getStatus(),
- pdfObject.getStatus().getSettings(), placeholder_id);
-
-// placeholders = SignaturePlaceholderExtractor.getPlaceholders();
- availablePlaceholders = listAvailablePlaceholders(placeholders, existingSignatureLocations(doc));
-
- if (placeholder_id.equalsIgnoreCase("")) {
- if (checkAvailablePlaceholders(placeholders, existingSignatureLocations(doc)) != null) {
- placeholder_id = checkAvailablePlaceholders(placeholders, existingSignatureLocations(doc)).getId();
- }
- }
-
- if (availablePlaceholders != null) {
- signaturePlaceholderData = PlaceholderFilter
- .checkPlaceholderSignatureLocation(pdfObject.getStatus(), pdfObject.getStatus().getSettings(),
- placeholder_id);
- }
-
- TablePos tablePos = null;
-
- if (signaturePlaceholderData != null) {
- signature.setLocation(signaturePlaceholderData.getPlaceholderName());
- }
-
- if (signaturePlaceholderData != null) {
- // Placeholder found!
- placeholders.clear();
- logger.info("Placeholder data found.");
- if (signaturePlaceholderData.getProfile() != null) {
- logger.debug("Placeholder Profile set to: " + signaturePlaceholderData.getProfile());
- requestedSignature.setSignatureProfileID(signaturePlaceholderData.getProfile());
- }
-
- tablePos = signaturePlaceholderData.getTablePos();
- if (tablePos != null) {
-
- final SignatureProfileConfiguration signatureProfileConfiguration = pdfObject.getStatus()
- .getSignatureProfileConfiguration(requestedSignature.getSignatureProfileID());
-
- final float minWidth = signatureProfileConfiguration.getMinWidth();
-
- if (minWidth > 0) {
- if (tablePos.getWidth() < minWidth) {
- tablePos.width = minWidth;
- logger.debug("Correcting placeholder with to minimum width {}", minWidth);
- }
- }
- logger.debug("Placeholder Position set to: " + tablePos.toString());
+
+
+ String placeholder_id = pdfObject.getStatus().getSignParamter().getPlaceHolderId();
+
+ SignaturePlaceholderData nextPlaceholderData = PlaceholderFilter.checkPlaceholderSignatureLocation(
+ pdfObject.getStatus(), pdfObject.getStatus().getSettings(), placeholder_id);
+
+ if (nextPlaceholderData != null) {
+ log.info("Placeholder data found.");
+ signature.setLocation(nextPlaceholderData.getPlaceholderName());
+
+ if (nextPlaceholderData.getProfile() != null) {
+ log.debug("Placeholder Profile set to: {}", nextPlaceholderData.getProfile());
+ requestedSignature.setSignatureProfileID(nextPlaceholderData.getProfile());
+
}
}
+
+
final SignatureProfileSettings signatureProfileSettings = TableFactory.createProfile(
requestedSignature.getSignatureProfileID(), pdfObject.getStatus().getSettings());
@@ -243,9 +198,11 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
}
signature.setReason(signerReason);
- logger.debug("Signing reason: " + signerReason);
+ log.debug("Signing reason: " + signerReason);
- logger.debug("Signing @ " + signer.getSigningDate().getTime().toString());
+
+
+ log.debug("Signing @ " + signer.getSigningDate().getTime().toString());
// the signing date, needed for valid signature
// signature.setSignDate(signer.getSigningDate());
@@ -257,9 +214,9 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
if (reservedSignatureSizeString != null) {
signatureSize = Integer.parseInt(reservedSignatureSizeString);
}
- logger.debug("Reserving {} bytes for signature", signatureSize);
+ log.debug("Reserving {} bytes for signature", signatureSize);
} catch (final NumberFormatException e) {
- logger.warn("Invalid configuration value: {} should be a number using 0x1000", SIG_RESERVED_SIZE);
+ log.warn("Invalid configuration value: {} should be a number using 0x1000", SIG_RESERVED_SIZE);
}
options.setPreferredSignatureSize(signatureSize);
@@ -269,44 +226,15 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
}
// Is visible Signature
- if (requestedSignature.isVisual()) {
- logger.info("Creating visual siganture block");
-
- final SignatureProfileConfiguration signatureProfileConfiguration = pdfObject.getStatus()
- .getSignatureProfileConfiguration(requestedSignature.getSignatureProfileID());
-
- if (tablePos == null) {
- // ================================================================
- // PositioningStage (visual) -> find position or use
- // fixed
- // position
-
- final String posString = pdfObject.getStatus().getSignParamter().getSignaturePosition();
-
- TablePos signaturePos = null;
-
- final String signaturePosString = signatureProfileConfiguration.getDefaultPositioning();
-
- if (signaturePosString != null) {
- logger.debug("using signature Positioning: " + signaturePos);
- signaturePos = new TablePos(signaturePosString);
- }
-
- logger.debug("using Positioning: " + posString);
-
- if (posString != null) {
- // Merge Signature Position
- tablePos = new TablePos(posString, signaturePos);
- } else {
- // Fallback to signature Position!
- tablePos = signaturePos;
- }
-
- if (tablePos == null) {
- // Last Fallback default position
- tablePos = new TablePos();
- }
- }
+ if (requestedSignature.isVisual()) {
+ log.info("Creating visual siganture block");
+
+ final SignatureProfileConfiguration signatureProfileConfiguration =
+ pdfObject.getStatus().getSignatureProfileConfiguration(requestedSignature.getSignatureProfileID());
+ TablePos tablePos = prepareTablePosition(nextPlaceholderData, signatureProfileConfiguration,
+ pdfObject.getStatus().getSignParamter().getSignaturePosition());
+
+
// Legacy Modes not supported with pdfbox2 anymore
// boolean legacy32Position = signatureProfileConfiguration.getLegacy32Positioning();
@@ -331,7 +259,7 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
"",
doc, visualObject, pdfObject.getStatus().getSettings(), signatureProfileSettings);
- logger.debug("Positioning: {}", positioningInstruction.toString());
+ log.debug("Positioning: {}", positioningInstruction.toString());
if (!isAdobeSigForm) {
if (positioningInstruction.isMakeNewPage()) {
@@ -347,11 +275,11 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
// handle rotated page
final int targetPageNumber = positioningInstruction.getPage();
- logger.debug("Target Page: " + targetPageNumber);
+ log.debug("Target Page: " + targetPageNumber);
final PDPage targetPage = doc.getPages().get(targetPageNumber - 1);
final int rot = targetPage.getRotation();
- logger.debug("Page rotation: " + rot);
- logger.debug("resulting Sign rotation: " + positioningInstruction.getRotation());
+ log.debug("Page rotation: " + rot);
+ log.debug("resulting Sign rotation: " + positioningInstruction.getRotation());
final SignaturePositionImpl position = new SignaturePositionImpl();
position.setX(positioningInstruction.getX());
@@ -386,7 +314,7 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
* int pageNumber = positioningInstruction.getPage(); PDPage page =
* doc.getPages().get(pageNumber - 1);
*
- * logger.info("Placeholder name: " +
+ * log.info("Placeholder name: " +
* signaturePlaceholderData.getPlaceholderName()); COSDictionary
* xobjectsDictionary = (COSDictionary) page.getResources().getCOSObject()
* .getDictionaryObject(COSName.XOBJECT);
@@ -395,7 +323,7 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
* xobjectsDictionary.setItem(signaturePlaceholderData.getPlaceholderName(),
* img); xobjectsDictionary.setNeedToBeUpdated(true);
* page.getResources().getCOSObject().setNeedToBeUpdated(true);
- * logger.info("Placeholder name: " +
+ * log.info("Placeholder name: " +
* signaturePlaceholderData.getPlaceholderName()); }
*/
@@ -418,7 +346,7 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
root.setOutputIntents(oi);
root.getCOSObject().setNeedToBeUpdated(true);
- logger.info("added Output Intent");
+ log.info("added Output Intent");
} catch (final Throwable e) {
e.printStackTrace();
throw new PdfAsException("Failed to add Output Intent", e);
@@ -432,6 +360,8 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
doc.addSignature(signature, signer, options);
+ String sigFieldName = getSignatureFieldNameConfig(pdfObject);
+
if (sigFieldName == null) {
sigFieldName = "PDF-AS Signatur";
}
@@ -473,7 +403,7 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
}
}
} else {
- logger.warn("Failed to name Signature Field! [Cannot find Field list in acroForm!]");
+ log.warn("Failed to name Signature Field! [Cannot find Field list in acroForm!]");
}
if (signatureField != null) {
@@ -485,7 +415,7 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
signatureField.setAlternateFieldName(sigFieldName);
}
} else {
- logger.warn("Failed to name Signature Field! [Cannot find acroForm!]");
+ log.warn("Failed to name Signature Field! [Cannot find acroForm!]");
}
}
@@ -496,16 +426,16 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
}
// PDF-UA
- logger.info("Adding pdf/ua content.");
+ log.info("Adding pdf/ua content.");
try {
final PDDocumentCatalog root = doc.getDocumentCatalog();
final PDStructureTreeRoot structureTreeRoot = root.getStructureTreeRoot();
if (structureTreeRoot != null) {
- logger.info("Tree Root: {}", structureTreeRoot.toString());
+ log.info("Tree Root: {}", structureTreeRoot.toString());
final List<Object> kids = structureTreeRoot.getKids();
if (kids == null) {
- logger.info("No kid-elements in structure tree Root, maybe not PDF/UA document");
+ log.info("No kid-elements in structure tree Root, maybe not PDF/UA document");
}
PDStructureElement docElement = null;
@@ -549,7 +479,7 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
PDNumberTreeNode ntn = structureTreeRoot.getParentTree();
if (ntn == null) {
ntn = new PDNumberTreeNode(objectDic, null);
- logger.info("No number-tree-node found!");
+ log.info("No number-tree-node found!");
}
final COSArray ntnKids = (COSArray) ntn.getCOSObject().getDictionaryObject(COSName.KIDS);
@@ -622,10 +552,10 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
}
} catch (final Throwable e) {
if (signatureProfileSettings.isPDFUA() == true) {
- logger.error("Could not create PDF-UA conform document!");
+ log.error("Could not create PDF-UA conform document!");
throw new PdfAsException("error.pdf.sig.pdfua.1", e);
} else {
- logger.info("Could not create PDF-UA conform signature");
+ log.info("Could not create PDF-UA conform signature");
}
}
@@ -642,19 +572,19 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
}
} catch (final IOException e1) {
- logger.error("Can not save incremental update", e1);
+ log.error("Can not save incremental update", e1);
}
System.gc();
- logger.debug("Signature done!");
+ log.debug("Signature done!");
} catch (final IOException e) {
- logger.warn(MessageResolver.resolveMessage("error.pdf.sig.01"), e);
+ log.warn(MessageResolver.resolveMessage("error.pdf.sig.01"), e);
throw new PdfAsException("error.pdf.sig.01", e);
} catch (PDFASError e2) {
- logger.warn(e2.getInfo());
+ log.warn(e2.getInfo());
throw new PdfAsException("error.pdf.sig.01", e2);
} finally {
@@ -664,7 +594,7 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
options.getVisualSignature().close();
options.close();
} catch (IOException e) {
- logger.debug("Failed to close VisualSignature!", e);
+ log.debug("Failed to close VisualSignature!", e);
}
}
}
@@ -674,13 +604,60 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
doc.close();
// SignaturePlaceholderExtractor.getPlaceholders().clear();
} catch (final IOException e) {
- logger.debug("Failed to close COS Doc!", e);
+ log.debug("Failed to close COS Doc!", e);
// Ignore
}
}
}
}
+ private TablePos prepareTablePosition(SignaturePlaceholderData nextPlaceholderData,
+ SignatureProfileConfiguration signatureProfileConfiguration, String profilePosParam) throws PdfAsException {
+
+
+ if (nextPlaceholderData != null && nextPlaceholderData.getTablePos() != null) {
+ final float minWidth = signatureProfileConfiguration.getMinWidth();
+ TablePos tablePos = nextPlaceholderData.getTablePos();
+ if (minWidth > 0) {
+ if (tablePos.getWidth() < minWidth) {
+ tablePos.width = minWidth;
+ log.debug("Correcting placeholder with to minimum width {}", minWidth);
+ }
+ }
+ log.debug("Placeholder Position set to: " + tablePos.toString());
+ return tablePos;
+
+ } else {
+ TablePos signaturePos = null;
+ final String signaturePosString = signatureProfileConfiguration.getDefaultPositioning();
+
+ if (signaturePosString != null) {
+ log.debug("using signature Positioning: " + signaturePosString);
+ signaturePos = new TablePos(signaturePosString);
+
+ }
+
+ log.debug("using Positioning: " + profilePosParam);
+
+ if (profilePosParam != null) {
+ // Merge Signature Position
+ return new TablePos(profilePosParam, signaturePos);
+
+ } else if (signaturePos != null){
+ // Fallback to signature Position!
+ return signaturePos;
+
+ } else {
+ return new TablePos();
+
+ }
+ }
+ }
+
+ private String getSignatureFieldNameConfig(PDFBOXObject pdfObject) {
+ return pdfObject.getStatus().getSettings().getValue(SIGNATURE_FIELD_NAME);
+ }
+
private int getParentTreeNextKey(PDStructureTreeRoot structureTreeRoot) throws IOException {
int nextKey = structureTreeRoot.getParentTreeNextKey();
if (nextKey < 0) {
@@ -717,31 +694,31 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
document.close();
result = document.getResult();
- logger.info("PDF-A Validation Result: " + result.isValid());
+ log.info("PDF-A Validation Result: " + result.isValid());
if (result.getErrorsList().size() > 0) {
- logger.error("The following validation errors occured for PDF-A validation");
+ log.error("The following validation errors occured for PDF-A validation");
}
for (final ValidationResult.ValidationError ve : result.getErrorsList()) {
- logger.error("\t" + ve.getErrorCode() + ": " + ve.getDetails());
+ log.error("\t" + ve.getErrorCode() + ": " + ve.getDetails());
}
if (!result.isValid()) {
- logger.info("The file is not a valid PDF-A document");
+ log.info("The file is not a valid PDF-A document");
}
} catch (final SyntaxValidationException e) {
- logger.error("The file is syntactically invalid.", e);
+ log.error("The file is syntactically invalid.", e);
throw new PdfAsException("Resulting PDF Document is syntactically invalid.");
} catch (final ValidationException e) {
- logger.error("The file is not a valid PDF-A document.", e);
+ log.error("The file is not a valid PDF-A document.", e);
} catch (final IOException e) {
- logger.error("An IOException (" + e.getMessage()
+ log.error("An IOException (" + e.getMessage()
+ ") occurred, while validating the PDF-A conformance", e);
throw new PdfAsException("Failed validating PDF Document IOException.");
} catch (final RuntimeException e) {
- logger.debug("An RuntimeException (" + e.getMessage()
+ log.debug("An RuntimeException (" + e.getMessage()
+ ") occurred, while validating the PDF-A conformance", e);
throw new PdfAsException("Failed validating PDF Document RuntimeException.");
} finally {
@@ -876,10 +853,10 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
return cutOut;
} catch (final PdfAsException e) {
- logger.warn("PDF-AS Exception", e);
+ log.warn("PDF-AS Exception", e);
throw ErrorExtractor.searchPdfAsError(e, status);
} catch (final Throwable e) {
- logger.warn("Unexpected Throwable Exception", e);
+ log.warn("Unexpected Throwable Exception", e);
throw ErrorExtractor.searchPdfAsError(e, status);
}
}
@@ -897,7 +874,7 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
if (pdfaIdentificationSchema != null) {
final Integer pdfaversion = pdfaIdentificationSchema.getPart();
final String conformance = pdfaIdentificationSchema.getConformance();
- logger.info("Detected PDF/A Version: {} - {}", pdfaversion, conformance);
+ log.info("Detected PDF/A Version: {} - {}", pdfaversion, conformance);
if (pdfaversion != null) {
return String.valueOf(pdfaversion);
@@ -906,7 +883,7 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
}
}
} catch (final Throwable e) {
- logger.warn("Failed to determine PDF/A Version!", e);
+ log.warn("Failed to determine PDF/A Version!", e);
}
return null;
}
@@ -931,74 +908,7 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants {
}
return signature;
}
-
- private List<String> existingSignatureLocations(PDDocument doc) {
- final List<String> existingLocations = new ArrayList<>();
- try {
- final List<PDSignature> pdSignatureList = doc.getSignatureDictionaries();
- if (pdSignatureList.size() != 0) {
- for (final PDSignature sig : pdSignatureList) {
- existingLocations.add(sig.getLocation());
- }
- }
- } catch (final IOException e) {
- e.printStackTrace();
- }
- return existingLocations;
- }
-
- // find first placeholder_id
- public SignaturePlaceholderData checkAvailablePlaceholders(List<SignaturePlaceholderData> placeholders,
- List<String> existingPlaceholders) {
- SignaturePlaceholderData result = null;
-
- if (placeholders != null) {
- for (int i = 0; i < placeholders.size(); ++i) {
- // take smallest id
- if (!existingPlaceholders.contains(placeholders.get(i).getPlaceholderName())) {
- final SignaturePlaceholderData spd = placeholders.get(i);
- if (spd.getId() != null) {
- if (result == null) {
- result = spd;
- } else {
- try {
- final int currentID = Integer.parseInt(result.getId());
- final int testID = Integer.parseInt(spd.getId());
- if (testID < currentID) {
- result = spd;
- }
- } catch (final Exception e) {
- // fallback to string compare
- final String currentID = result.getId();
- final String testID = spd.getId();
- if (testID.compareToIgnoreCase(currentID) < 0) {
- result = spd;
- }
- }
- }
- }
- }
- }
- }
- return result;
- }
-
- // find first placeholder_id
- public List<SignaturePlaceholderData> listAvailablePlaceholders(List<SignaturePlaceholderData> placeholders,
- List<String> existingPlaceholders) {
- final List<SignaturePlaceholderData> result = new ArrayList<>();
-
- if (placeholders != null) {
- for (int i = 0; i < placeholders.size(); ++i) {
- // take smallest id
- if (!existingPlaceholders.contains(placeholders.get(i).getPlaceholderName())) {
- result.add(placeholders.get(i));
- }
- }
- }
- return result;
- }
-
+
static Map<Integer, COSObjectable> getNumberTreeAsMap(PDNumberTreeNode tree)
throws IOException {
Map<Integer, COSObjectable> numbers = tree.getNumbers();
diff --git a/pdf-as-pdfbox-2/src/test/java/at/gv/egiz/pdfas/lib/testpdfbox/PDFBoxPlaceholderExtractorTest.java b/pdf-as-pdfbox-2/src/test/java/at/gv/egiz/pdfas/lib/testpdfbox/PDFBoxPlaceholderExtractorTest.java
new file mode 100644
index 00000000..fbe3cdea
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/test/java/at/gv/egiz/pdfas/lib/testpdfbox/PDFBoxPlaceholderExtractorTest.java
@@ -0,0 +1,51 @@
+package at.gv.egiz.pdfas.lib.testpdfbox;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.junit.Test;
+
+import at.gv.egiz.pdfas.lib.impl.pdfbox2.placeholder.SignatureFieldsAndPlaceHolderExtractor;
+import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData;
+import lombok.SneakyThrows;
+
+public class PDFBoxPlaceholderExtractorTest {
+
+ @Test
+ @SneakyThrows
+ public void nextPlaceholder() {
+ SignaturePlaceholderData result = getNextSignaturePlaceHolder("/data/platzhalter_en_de_test.pdf");
+ assertEquals("Im48", result.getPlaceholderName());
+
+ }
+
+ @Test
+ @SneakyThrows
+ public void allPlaceHolders() {
+ List<String> listOfPlaceHolders = getPlaceHolders("/data/platzhalter_en_de_test.pdf");
+ assertNotNull(listOfPlaceHolders);
+
+ }
+
+ private static List<String> getPlaceHolders(String filePath) throws IOException {
+ final PDDocument doc = PDDocument.load(PDFBoxPlaceholderExtractorTest.class.getResourceAsStream(
+ filePath));
+ final List<String> results = SignatureFieldsAndPlaceHolderExtractor.findEmptySignatureFields(doc);
+ return results;
+
+ }
+
+ private static SignaturePlaceholderData getNextSignaturePlaceHolder(String filePath) throws IOException {
+ final PDDocument doc = PDDocument.load(PDFBoxPlaceholderExtractorTest.class.getResourceAsStream(
+ filePath));
+ final SignaturePlaceholderData result =
+ SignatureFieldsAndPlaceHolderExtractor.getNextUnusedSignaturePlaceHolder(doc);
+ return result;
+
+ }
+
+}
diff --git a/pdf-as-pdfbox-2/src/test/java/at/gv/egiz/pdfas/lib/testpdfbox/SignatureFieldsAndPlaceHolderExtractorTest.java b/pdf-as-pdfbox-2/src/test/java/at/gv/egiz/pdfas/lib/testpdfbox/SignatureFieldsAndPlaceHolderExtractorTest.java
index 0d85c82b..61a1199d 100644
--- a/pdf-as-pdfbox-2/src/test/java/at/gv/egiz/pdfas/lib/testpdfbox/SignatureFieldsAndPlaceHolderExtractorTest.java
+++ b/pdf-as-pdfbox-2/src/test/java/at/gv/egiz/pdfas/lib/testpdfbox/SignatureFieldsAndPlaceHolderExtractorTest.java
@@ -1,16 +1,17 @@
package at.gv.egiz.pdfas.lib.testpdfbox;
-import at.gv.egiz.pdfas.lib.impl.pdfbox2.placeholder.SignatureFieldsAndPlaceHolderExtractor;
-import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData;
-import org.apache.pdfbox.pdmodel.PDDocument;
-import org.junit.Assert;
-import org.junit.Test;
-
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.junit.Assert;
+import org.junit.Test;
+
+import at.gv.egiz.pdfas.lib.impl.pdfbox2.placeholder.SignatureFieldsAndPlaceHolderExtractor;
+import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData;
+
public class SignatureFieldsAndPlaceHolderExtractorTest {
public String getPath(String resourceName) {
@@ -48,6 +49,14 @@ public class SignatureFieldsAndPlaceHolderExtractorTest {
SignaturePlaceholderData result = getNextSignaturePlaceHolder(getPath("manySignFields.pdf"));
Assert.assertEquals(null,result);
}
+
+ @Test
+ public void firstQrCodeOnUnsignedDoc() {
+ SignaturePlaceholderData result = getNextSignaturePlaceHolder(getPath("new_qr_2-2.pdf"));
+ Assert.assertEquals("Image5",result.getPlaceholderName());
+
+ }
+
@Test
public void subsequentCalls(){
SignaturePlaceholderData result = getNextSignaturePlaceHolder(getPath("new_qr_2_signed_signed_signed.pdf"));
diff --git a/pdf-as-pdfbox-2/src/test/resources/data/platzhalter_en_de_test.pdf b/pdf-as-pdfbox-2/src/test/resources/data/platzhalter_en_de_test.pdf
new file mode 100644
index 00000000..06b9aa0e
--- /dev/null
+++ b/pdf-as-pdfbox-2/src/test/resources/data/platzhalter_en_de_test.pdf
Binary files differ