From 8ed8e4e931c1cb3d3c814a53a07f73b566c3b719 Mon Sep 17 00:00:00 2001 From: Alexander Marsalek Date: Fri, 2 Jul 2021 14:06:44 +0200 Subject: v4.2.0 --- .../placeholder/PDFBoxPlaceholderExtractor.java | 32 +++++++- .../placeholder/SignaturePlaceholderExtractor.java | 95 ++++++++++++++++------ .../pdfas/lib/impl/pdfbox2/utils/PdfBoxUtils.java | 22 +++-- .../impl/signing/pdfbox2/PADESPDFBOXSigner.java | 94 +++++++++++++++++---- 4 files changed, 194 insertions(+), 49 deletions(-) (limited to 'pdf-as-pdfbox-2/src/main/java/at/gv/egiz') 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 256400a0..63b006bf 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,19 +1,47 @@ package at.gv.egiz.pdfas.lib.impl.pdfbox2.placeholder; +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; 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 { + @Override public SignaturePlaceholderData extract(PDFObject doc, String placeholderId, int matchMode) throws PdfAsException { if (doc instanceof PDFBOXObject) { PDFBOXObject object = (PDFBOXObject) doc; - return SignaturePlaceholderExtractor.extract(object.getDocument(), - placeholderId, matchMode); + 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 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); + } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e2) { + throw new PDFIOException("error.pdf.io.04", e2); + } } throw new PdfAsException("INVALID STATE"); } 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 8a2c1cff..4031d07f 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 @@ -103,11 +103,11 @@ public class SignaturePlaceholderExtractor extends PDFStreamEngine implements Pl private static Logger logger = LoggerFactory .getLogger(SignaturePlaceholderExtractor.class); - private static List placeholders = new ArrayList<>(); + private List placeholders = new ArrayList<>(); private int currentPage = 0; private PDDocument doc; - private SignaturePlaceholderExtractor(String placeholderId, + protected SignaturePlaceholderExtractor(String placeholderId, int placeholderMatchMode, PDDocument doc) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { super(); @@ -126,9 +126,9 @@ public class SignaturePlaceholderExtractor extends PDFStreamEngine implements Pl this.doc = doc; } - public static List getPlaceholders() { - return placeholders; - } +// public static List getPlaceholders() { +// return placeholders; +// } /** @@ -136,38 +136,36 @@ public class SignaturePlaceholderExtractor extends PDFStreamEngine implements Pl * additional info.
* Searches only for the first placeholder page after page from top. * - * @param inputStream * @return all available info from the first found placeholder. - * @throws PDFDocumentException + * @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 static SignaturePlaceholderData extract(PDDocument doc, + 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); - } +// 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 { - extractor.setCurrentPage(pageNr); + setCurrentPage(pageNr); if(page.getContents() != null && page.getResources() != null && page.getContentStreams() != null) { - extractor.processPage(page); //TODO: pdfbox2 - right? + processPage(page); //TODO: pdfbox2 - right? } SignaturePlaceholderData ret = matchPlaceholderPage( - extractor.placeholders, placeholderId, matchMode); + placeholders, placeholderId, matchMode); if (ret != null) { SignaturePlaceholderContext .setSignaturePlaceholderData(ret); @@ -179,9 +177,9 @@ public class SignaturePlaceholderExtractor extends PDFStreamEngine implements Pl throw new PDFIOException("error.pdf.io.04", e); } } - if (extractor.placeholders.size() > 0) { + if (placeholders.size() > 0) { SignaturePlaceholderData ret = matchPlaceholderDocument( - extractor.placeholders, placeholderId, matchMode); + placeholders, placeholderId, matchMode); SignaturePlaceholderContext.setSignaturePlaceholderData(ret); return ret; } @@ -193,7 +191,56 @@ public class SignaturePlaceholderExtractor extends PDFStreamEngine implements Pl return null; } - private static SignaturePlaceholderData matchPlaceholderDocument( + public List extractList(PDDocument doc, + String placeholderId, int matchMode) throws PdfAsException { + SignaturePlaceholderContext.setSignaturePlaceholderData(null); +// List placeholders = new ArrayList<>(); +// 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 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 placeholders, String placeholderId, int matchMode) throws PlaceholderExtractionException { @@ -247,7 +294,7 @@ public class SignaturePlaceholderExtractor extends PDFStreamEngine implements Pl return null; } - private static SignaturePlaceholderData matchPlaceholderPage( + private SignaturePlaceholderData matchPlaceholderPage( List placeholders, String placeholderId, int matchMode) { @@ -266,6 +313,8 @@ public class SignaturePlaceholderExtractor extends PDFStreamEngine implements Pl return null; } + + private void setCurrentPage(int pageNr) { this.currentPage = pageNr; } diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/utils/PdfBoxUtils.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/utils/PdfBoxUtils.java index 1a6499ac..112d9a86 100644 --- a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/utils/PdfBoxUtils.java +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/utils/PdfBoxUtils.java @@ -16,16 +16,26 @@ public class PdfBoxUtils { public static void checkPDFPermissions(PDDocument doc) throws PdfAsValidationException { - AccessPermission accessPermission = doc.getCurrentAccessPermission(); - if (doc.isEncrypted()) { - throw new PdfAsValidationException("error.pdf.sig.12", null); - } + if (doc.isEncrypted() || !accessPermission.isOwnerPermission()) { + //canFillInForm() + //This will tell if the user can fill in interactive form fields (including signature fields) even if + // canModifyAnnotations() returns false. - if (!accessPermission.isOwnerPermission()) { - throw new PdfAsValidationException("error.pdf.sig.12", null); + //canModifyAnnotations() + //This will tell if the user can add or modify text annotations and fill in interactive forms fields and, if + // canModify() returns true, create or modify interactive form fields (including signature fields). + if ((accessPermission.canModify() && accessPermission.canModifyAnnotations()) + || accessPermission.canFillInForm()) { + logger.debug("Document is protected, but Signing is allowed"); + + } else { + throw new PdfAsValidationException("error.pdf.sig.12", null); + + } } + } public static int countSignatures(PDDocument doc, String sigName) { 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 0a32d7a5..3cf0853f 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 @@ -64,6 +64,7 @@ import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocumentCatalog; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.common.COSObjectable; import org.apache.pdfbox.pdmodel.common.PDMetadata; import org.apache.pdfbox.pdmodel.common.PDNumberTreeNode; import org.apache.pdfbox.pdmodel.common.PDRectangle; @@ -96,7 +97,10 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { @@ -157,9 +161,11 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { signature.setFilter(COSName.getPDFName(signer.getPDFFilter())); signature.setSubFilter(COSName.getPDFName(signer.getPDFSubFilter())); - SignaturePlaceholderData signaturePlaceholderDataInit = PlaceholderFilter.checkPlaceholderSignatureLocation(pdfObject.getStatus(), pdfObject.getStatus().getSettings(), placeholder_id); +// SignaturePlaceholderData signaturePlaceholderDataInit = + placeholders =PlaceholderFilter.checkPlaceholderSignatureLocationList(pdfObject.getStatus(), + pdfObject.getStatus().getSettings(), placeholder_id); - placeholders = SignaturePlaceholderExtractor.getPlaceholders(); +// placeholders = SignaturePlaceholderExtractor.getPlaceholders(); availablePlaceholders = listAvailablePlaceholders(placeholders, existingSignatureLocations(doc)); @@ -211,15 +217,16 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { } SignatureProfileSettings signatureProfileSettings = TableFactory .createProfile(requestedSignature.getSignatureProfileID(), pdfObject.getStatus().getSettings()); - //Check if input document is PDF-A conform + + //Check if input document is PDF-A conform if (signatureProfileSettings.isPDFA()) { DataSource origDoc = pdfObject.getOriginalDocument(); InputStream stream = origDoc.getInputStream(); //Run PreflightParser for checking conformity// //runPDFAPreflight(origDoc); } - ValueResolver resolver = new ValueResolver(requestedSignature, pdfObject.getStatus()); + String signerName = resolver.resolve("SIG_SUBJECT", signatureProfileSettings.getValue("SIG_SUBJECT"), signatureProfileSettings); @@ -508,12 +515,13 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { } } - + PDStructureElement sigBlock = new PDStructureElement("Form", docElement); // create object dictionary and add as child element COSDictionary objectDic = new COSDictionary(); objectDic.setName("Type", "OBJR"); + objectDic.setItem("Pg", signatureField.getWidget().getPage()); objectDic.setItem("Obj", signatureField.getWidget()); @@ -539,7 +547,6 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { // Modify number tree PDNumberTreeNode ntn = structureTreeRoot.getParentTree(); - int parentTreeNextKey = structureTreeRoot.getParentTreeNextKey(); if (ntn == null) { ntn = new PDNumberTreeNode(objectDic, null); logger.info("No number-tree-node found!"); @@ -547,9 +554,10 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { COSArray ntnKids = (COSArray) ntn.getCOSObject().getDictionaryObject(COSName.KIDS); COSArray ntnNumbers = (COSArray) ntn.getCOSObject().getDictionaryObject(COSName.NUMS); - - if(ntnNumbers == null && ntnKids != null){//no number array, so continue with the kids array - + + int parentTreeNextKey = getParentTreeNextKey(structureTreeRoot); + + if(ntnNumbers == null && ntnKids != null){//no number array, so continue with the kids array //create dictionary with limits and nums array COSDictionary pTreeEntry = new COSDictionary(); COSArray limitsArray = new COSArray(); @@ -628,7 +636,7 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { byte[] outputDocument = bos.toByteArray(); pdfObject.setSignedDocument(outputDocument); } - /* Check if resulting pdf is PDF-A conform */ + /* Check if resulting pdf is PDF-A conform */ if (signatureProfileSettings.isPDFA()) { runPDFAPreflight(new ByteArrayDataSource(pdfObject.getSignedDocument())); } @@ -653,7 +661,7 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { if (doc != null) { try { doc.close(); - SignaturePlaceholderExtractor.getPlaceholders().clear(); + //SignaturePlaceholderExtractor.getPlaceholders().clear(); } catch (IOException e) { logger.debug("Failed to close COS Doc!", e); // Ignore @@ -663,7 +671,23 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { } } - /** + private int getParentTreeNextKey(PDStructureTreeRoot structureTreeRoot) throws IOException { + int nextKey = structureTreeRoot.getParentTreeNextKey(); + if (nextKey < 0) { + Map destNumberTreeAsMap = getNumberTreeAsMap(structureTreeRoot.getParentTree()); + if (destNumberTreeAsMap.isEmpty()) { + nextKey = 0; + + } else { + nextKey = Collections.max(destNumberTreeAsMap.keySet()) + 1; + + } + } + + return nextKey; + } + + /** * Check via PreFlightParser if PDF-Document is a valid PDFA1 * @param signedDocument: signed Document * @throws PdfAsException @@ -914,16 +938,25 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { if(placeholders!=null) { for(int i = 0; i < placeholders.size(); ++i) { //take smallest id - if(!existingPlaceholders.contains(placeholders.get(i).getPlaceholderName())) { + if(!existingPlaceholders.contains(placeholders.get(i).getPlaceholderName())) { SignaturePlaceholderData spd = placeholders.get(i); if (spd.getId() != null) { if(result == null) { result = spd; } else { - String currentID = result.getId(); - String testID = spd.getId(); - if(testID.compareToIgnoreCase(currentID) < 0) { - result = spd; + try{ + int currentID = Integer.parseInt(result.getId()); + int testID = Integer.parseInt(spd.getId()); + if(testID < currentID) { + result = spd; + } + }catch(Exception e){ + //fallback to string compare + String currentID = result.getId(); + String testID = spd.getId(); + if(testID.compareToIgnoreCase(currentID) < 0) { + result = spd; + } } } } @@ -936,7 +969,7 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { //find first placeholder_id public List listAvailablePlaceholders(List placeholders, List existingPlaceholders) { - List result = null; + List result = new ArrayList<>(); if(placeholders!=null) { for(int i = 0; i < placeholders.size(); ++i) { @@ -948,4 +981,29 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { } return result; } + + static Map getNumberTreeAsMap(PDNumberTreeNode tree) + throws IOException + { + Map numbers = tree.getNumbers(); + if (numbers == null) + { + numbers = new LinkedHashMap<>(); + } + else + { + // must copy because the map is read only + numbers = new LinkedHashMap<>(numbers); + } + List kids = tree.getKids(); + if (kids != null) + { + for (PDNumberTreeNode kid : kids) + { + numbers.putAll(getNumberTreeAsMap(kid)); + } + } + return numbers; + } + } \ No newline at end of file -- cgit v1.2.3