aboutsummaryrefslogtreecommitdiff
path: root/pdf-as-pdfbox-2/src/main/java/at/gv/egiz
diff options
context:
space:
mode:
Diffstat (limited to 'pdf-as-pdfbox-2/src/main/java/at/gv/egiz')
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/positioning/Positioning.java619
-rw-r--r--pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox2/PADESPDFBOXSigner.java1754
2 files changed, 1195 insertions, 1178 deletions
diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/positioning/Positioning.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/positioning/Positioning.java
index 52a865b1..13d1ebe6 100644
--- a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/positioning/Positioning.java
+++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/positioning/Positioning.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
@@ -26,323 +26,336 @@ package at.gv.egiz.pdfas.lib.impl.pdfbox2.positioning;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
-import org.apache.pdfbox.pdmodel.PDPageTree;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
-import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import at.gv.egiz.pdfas.common.exceptions.PdfAsException;
import at.gv.egiz.pdfas.common.settings.ISettings;
+import at.gv.egiz.pdfas.lib.api.IConfigurationConstants;
import at.gv.egiz.pdfas.lib.impl.pdfbox2.utils.PdfBoxUtils;
import at.gv.egiz.pdfas.lib.impl.stamping.IPDFVisualObject;
import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction;
import at.knowcenter.wag.egov.egiz.pdf.TablePos;
import at.knowcenter.wag.egov.egiz.pdfbox2.pdf.PDFUtilities;
+import lombok.extern.slf4j.Slf4j;
/**
* Created with IntelliJ IDEA. User: afitzek Date: 8/29/13 Time: 4:30 PM To
* change this template use File | Settings | File Templates.
*/
+@Slf4j
public class Positioning {
- private static final Logger logger = LoggerFactory
- .getLogger(Positioning.class);
-
- /**
- * The left/right margin.
- */
- public static final float SIGNATURE_MARGIN_HORIZONTAL = 50f;
-
- /**
- * The top/bottom margin.
- */
- public static final float SIGNATURE_MARGIN_VERTICAL = 20f;
-
- /**
- * Evalutates absolute positioning and prepares the PositioningInstruction
- * for placing the table.
- *
- * @param pos
- * The absolute positioning parameter. If null it is sought in
- * the profile definition.
- * @param signature_type
- * The profile definition of the table to be written.
- * @param pdfDataSource
- * The pdf.
- * @param pdf_table
- * The pdf table to be written.
- * @param settings
- * @return Returns the PositioningInformation.
- * @throws PdfAsException
- * F.e.
- */
- public static PositioningInstruction determineTablePositioning(
- TablePos pos, String signature_type, PDDocument pdfDataSource,
- IPDFVisualObject pdf_table, ISettings settings) throws PdfAsException {
- return adjustSignatureTableandCalculatePosition(pdfDataSource,
- pdf_table, pos, settings);
- }
-
- private static PDRectangle rotateBox(PDRectangle cropBox, int rotation) {
- if (rotation != 0) {
- Point2D upSrc = new Point2D.Float();
-
- upSrc.setLocation(cropBox.getUpperRightX(),
- cropBox.getUpperRightY());
-
- Point2D llSrc = new Point2D.Float();
- llSrc.setLocation(cropBox.getLowerLeftX(), cropBox.getLowerLeftY());
- AffineTransform transform = new AffineTransform();
- transform.setToIdentity();
- if (rotation % 360 != 0) {
- transform.setToRotation(Math.toRadians(rotation * -1), llSrc.getX(),
- llSrc.getY());
- }
- Point2D upDst = new Point2D.Float();
- transform.transform(upSrc, upDst);
-
- Point2D llDst = new Point2D.Float();
- transform.transform(llSrc, llDst);
-
- float y1 = (float) upDst.getY();
- float y2 = (float) llDst.getY();
-
- if(y1 > y2) {
- float t = y1;
- y1 = y2;
- y2 = t;
- }
-
- if(y1 < 0) {
- y2 = y2 + -1 * y1;
- y1 = 0;
- }
-
- float x1 = (float) upDst.getX();
- float x2 = (float) llDst.getX();
-
- if(x1 > x2) {
- float t = x1;
- x1 = x2;
- x2 = t;
- }
-
- if(x1 < 0) {
- x2 = x2 + -1 * x1;
- x1 = 0;
- }
-
- cropBox.setUpperRightX(x2);
- cropBox.setUpperRightY(y2);
- cropBox.setLowerLeftY(y1);
- cropBox.setLowerLeftX(x1);
- }
- return cropBox;
- }
-
- /**
- * 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.
- * @param settings
- * @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, ISettings settings) throws PdfAsException {
- List<PDSignatureField> pdSignatureFieldList;
- PdfBoxUtils.checkPDFPermissions(pdfDataSource);
- int counter = 0;
-
- try {
- //count signature fields with signatures
- pdSignatureFieldList = pdfDataSource.getSignatureFields();
- for (PDSignatureField signatureField : pdSignatureFieldList)
- {
- if(signatureField.getSignature()!=null){
- counter++;
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- // get pages of currentdocument
- int doc_pages = pdfDataSource.getNumberOfPages();
- int page = doc_pages;
- boolean make_new_page = pos.isNewPage();
-
- //we cannot add new page if a document is already signed
-
-
- 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;
- }
- }
-
- if(make_new_page && counter!=0) {
- make_new_page = false;
- }
-
- PDPage pdPage = pdfDataSource.getPage(page-1);
-
- PDRectangle cropBox = pdPage.getCropBox();
-
- // fallback to MediaBox if Cropbox not available!
-
- if (cropBox == null) {
- cropBox = pdPage.getCropBox();
- }
-
- if (cropBox == null) {
- cropBox = pdPage.getCropBox();
- }
-
- // getPagedimensions
- // Rectangle psize = reader.getPageSizeWithRotation(page);
- // int page_rotation = reader.getPageRotation(page);
-
- // Integer rotation = pdPage.getRotation();
- // int page_rotation = rotation.intValue();
-
- int rotation = pdPage.getRotation();
-
- logger.debug("Original CropBox: " + cropBox.toString());
-
- cropBox = rotateBox(cropBox, rotation);
-
- logger.debug("Rotated CropBox: " + cropBox.toString());
-
- float page_width = cropBox.getWidth();
- float page_height = cropBox.getHeight();
-
- logger.debug("CropBox width: " + page_width);
- logger.debug("CropBox heigth: " + page_height);
-
- // 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, legacy40);
-
- float pre_page_length = Float.NEGATIVE_INFINITY;
- try {
- pre_page_length = PDFUtilities.getMaxYPosition(pdfDataSource, page-1, pdf_table, SIGNATURE_MARGIN_VERTICAL, footer_line, settings);
- //pre_page_length = PDFUtilities.getFreeTablePosition(pdfDataSource, page-1, pdf_table,SIGNATURE_MARGIN_VERTICAL);
- } catch (IOException e) {
- logger.warn("Could not determine page length, using -INFINITY");
- }
-
- 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) {
- if(counter!=0)
- make_new_page = false;
- else{
- make_new_page = true;
- page++;
- }
- if (!pos.isPauto()) {
- // we have to correct pagenumber
- page = pdfDataSource.getNumberOfPages();
- }
- // 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) {
- if(counter!=0){
- make_new_page = false;
- }
- else{
- make_new_page = true;
- page++;
- }
- if (!pos.isPauto()) {
- // we have to correct pagenumber in case of absolute page and
- // not enough
- // space
- page = pdfDataSource.getNumberOfPages();
- }
- // no text --> SIGNATURE_BORDER
- pos_y = page_height - SIGNATURE_MARGIN_VERTICAL;
- }
- return new PositioningInstruction(make_new_page, page, pos_x, pos_y,
- pos.rotation);
- }
+ /**
+ * 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.
+ * @param settings
+ * @return Returns the PositioningInformation.
+ * @throws PdfAsException F.e.
+ */
+ public static PositioningInstruction determineTablePositioning(
+ TablePos pos, String signature_type, PDDocument pdfDataSource,
+ IPDFVisualObject pdf_table, ISettings settings) throws PdfAsException {
+ return adjustSignatureTableandCalculatePosition(pdfDataSource,
+ pdf_table, pos, settings);
+ }
+
+ /**
+ * 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.
+ * @param settings
+ * @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, ISettings settings) throws PdfAsException {
+ PdfBoxUtils.checkPDFPermissions(pdfDataSource);
+ final long numberOfExistingSignatures = getNumberOfExistingSignatures(pdfDataSource);
+
+ // get pages of currentdocument
+ final 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();
+ if (page > doc_pages) {
+ log.debug("Document is shorter than requested page for signature block. Adding new page ...");
+ make_new_page = true;
+ page = doc_pages;
+
+ }
+ }
+
+ make_new_page = checkIfNewPageIsAllowed(make_new_page, numberOfExistingSignatures, settings);
+
+
+ if(make_new_page && numberOfExistingSignatures!=0) {
+ make_new_page = false;
+
+ }
+
+
+ final PDPage pdPage = pdfDataSource.getPage(page - 1);
+
+ PDRectangle cropBox = pdPage.getCropBox();
+
+ // fallback to MediaBox if Cropbox not available!
+
+ if (cropBox == null) {
+ cropBox = pdPage.getCropBox();
+ }
+
+ if (cropBox == null) {
+ cropBox = pdPage.getCropBox();
+ }
+
+ // getPagedimensions
+ // Rectangle psize = reader.getPageSizeWithRotation(page);
+ // int page_rotation = reader.getPageRotation(page);
+
+ // Integer rotation = pdPage.getRotation();
+ // int page_rotation = rotation.intValue();
+
+ final int rotation = pdPage.getRotation();
+
+ log.debug("Original CropBox: " + cropBox.toString());
+
+ cropBox = rotateBox(cropBox, rotation);
+
+ log.debug("Rotated CropBox: " + cropBox.toString());
+
+ final float page_width = cropBox.getWidth();
+ final float page_height = cropBox.getHeight();
+
+ log.debug("CropBox width: " + page_width);
+ log.debug("CropBox heigth: " + page_height);
+
+ // 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();
+
+ 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 pre_page_length = calculatePrePageLength(pdfDataSource, page, pdf_table, pos.getFooterLine(), settings);
+
+ if (pre_page_length == Float.NEGATIVE_INFINITY) {
+ // we do have an empty page or nothing in area above footerline
+ pos_y = page_height - SIGNATURE_MARGIN_VERTICAL;
+ return buildPostitionInfoOnSubpage(pdfDataSource, make_new_page, page, pos_x, pos_y, pos.rotation,
+ pos.getFooterLine(), table_height, pos, page_height, numberOfExistingSignatures, settings);
+
+ } else {
+ // we do have text take SIGNATURE_MARGIN
+ pos_y = page_height - pre_page_length - SIGNATURE_MARGIN_VERTICAL;
+ return buildPostitionInfoOnSubpage(pdfDataSource, make_new_page, page, pos_x, pos_y, pos.rotation,
+ pos.getFooterLine(), table_height, pos, page_height, numberOfExistingSignatures, settings);
+
+ }
+ }
+
+ private static float calculatePrePageLength(PDDocument pdfDataSource, int page, IPDFVisualObject pdf_table, float footer_line, ISettings settings) {
+ try {
+ return PDFUtilities.getMaxYPosition(pdfDataSource, page - 1, pdf_table,
+ SIGNATURE_MARGIN_VERTICAL, footer_line, settings);
+
+ } catch (final IOException e) {
+ log.warn("Could not determine page length, using -INFINITY");
+ return Float.NEGATIVE_INFINITY;
+
+ }
+ }
+
+ private static boolean isFailOnLessSpaceEnabled(ISettings settings) {
+ String value = settings.getValue(IConfigurationConstants.SIG_BLOCK_LESS_SPACE_STOPPING_WITH_ERROR);
+ return Boolean.valueOf(value);
+
+ }
+
+ private static boolean checkIfNewPageIsAllowed(boolean make_new_page, long numberOfExistingSignatures, ISettings settings) throws PdfAsException {
+ if(make_new_page && numberOfExistingSignatures!=0) {
+ log.info("Signature-block would be need a new page, but new pages are not allowed on already signed documents.");
+ if (isFailOnLessSpaceEnabled(settings)) {
+ throw new PdfAsException("error.pdf.stamp.12");
+
+ } else {
+ log.info("Placing signature-block on last page without free-space checks ... ");
+ return false;
+
+ }
+
+ } else {
+ return make_new_page;
+
+ }
+ }
+
+ private static PositioningInstruction buildPostitionInfoOnSubpage(PDDocument pdfDataSource, boolean make_new_page, int page, float pos_x,
+ float pos_y, float rotation, float footer_line, float table_height, TablePos pos, float page_height,
+ long numberOfExistingSignatures, ISettings settings) throws PdfAsException {
+ if (pos_y - footer_line <= table_height) {
+
+ make_new_page = checkIfNewPageIsAllowed(true, numberOfExistingSignatures, settings);
+ if (make_new_page) {
+ page++;
+
+ }
+
+ if (!pos.isPauto()) {
+ // we have to correct pagenumber
+ page = pdfDataSource.getNumberOfPages();
+
+ }
+ // no text --> SIGNATURE_BORDER
+ pos_y = page_height - SIGNATURE_MARGIN_VERTICAL;
+
+ }
+
+ return new PositioningInstruction(make_new_page, page, pos_x, pos_y, pos.rotation);
+
+ }
+
+ private static long getNumberOfExistingSignatures(PDDocument pdfDataSource) {
+ try {
+ return pdfDataSource.getSignatureFields().stream()
+ .filter(el -> el.getSignature() != null)
+ .count();
+
+ } catch (final IOException e) {
+ log.warn("Can not extract existing Signatures from PDF. Use it as 0", e);
+ return 0;
+
+ }
+ }
+
+ private static PDRectangle rotateBox(PDRectangle cropBox, int rotation) {
+ if (rotation != 0) {
+ final Point2D upSrc = new Point2D.Float();
+
+ upSrc.setLocation(cropBox.getUpperRightX(),
+ cropBox.getUpperRightY());
+
+ final Point2D llSrc = new Point2D.Float();
+ llSrc.setLocation(cropBox.getLowerLeftX(), cropBox.getLowerLeftY());
+ final AffineTransform transform = new AffineTransform();
+ transform.setToIdentity();
+ if (rotation % 360 != 0) {
+ transform.setToRotation(Math.toRadians(rotation * -1), llSrc.getX(),
+ llSrc.getY());
+ }
+ final Point2D upDst = new Point2D.Float();
+ transform.transform(upSrc, upDst);
+
+ final Point2D llDst = new Point2D.Float();
+ transform.transform(llSrc, llDst);
+
+ float y1 = (float) upDst.getY();
+ float y2 = (float) llDst.getY();
+
+ if (y1 > y2) {
+ final float t = y1;
+ y1 = y2;
+ y2 = t;
+ }
+
+ if (y1 < 0) {
+ y2 = y2 + -1 * y1;
+ y1 = 0;
+ }
+
+ float x1 = (float) upDst.getX();
+ float x2 = (float) llDst.getX();
+
+ if (x1 > x2) {
+ final float t = x1;
+ x1 = x2;
+ x2 = t;
+ }
+
+ if (x1 < 0) {
+ x2 = x2 + -1 * x1;
+ x1 = 0;
+ }
+
+ cropBox.setUpperRightX(x2);
+ cropBox.setUpperRightY(y2);
+ cropBox.setLowerLeftY(y1);
+ cropBox.setLowerLeftX(x1);
+ }
+ return cropBox;
+ }
+
}
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 df2a42bb..d780aeec 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
@@ -23,6 +23,58 @@
******************************************************************************/
package at.gv.egiz.pdfas.lib.impl.signing.pdfbox2;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+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;
+
+import javax.activation.DataSource;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSDocument;
+import org.apache.pdfbox.cos.COSInteger;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSString;
+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;
+import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureElement;
+import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureTreeRoot;
+import org.apache.pdfbox.pdmodel.graphics.color.PDOutputIntent;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions;
+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 org.apache.pdfbox.preflight.PreflightDocument;
+import org.apache.pdfbox.preflight.ValidationResult;
+import org.apache.pdfbox.preflight.exception.SyntaxValidationException;
+import org.apache.pdfbox.preflight.exception.ValidationException;
+import org.apache.pdfbox.preflight.parser.PreflightParser;
+import org.apache.pdfbox.rendering.ImageType;
+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;
import at.gv.egiz.pdfas.common.messages.MessageResolver;
@@ -36,7 +88,6 @@ 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.placeholder.SignaturePlaceholderExtractor;
import at.gv.egiz.pdfas.lib.impl.pdfbox2.positioning.Positioning;
import at.gv.egiz.pdfas.lib.impl.pdfbox2.utils.PdfBoxUtils;
import at.gv.egiz.pdfas.lib.impl.placeholder.PlaceholderFilter;
@@ -58,955 +109,908 @@ 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 org.apache.commons.io.IOUtils;
-import org.apache.pdfbox.cos.*;
-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;
-import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureElement;
-import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureTreeRoot;
-import org.apache.pdfbox.pdmodel.graphics.color.PDOutputIntent;
-import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
-import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions;
-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 org.apache.pdfbox.preflight.PreflightDocument;
-import org.apache.pdfbox.preflight.ValidationResult;
-import org.apache.pdfbox.preflight.exception.SyntaxValidationException;
-import org.apache.pdfbox.preflight.exception.ValidationException;
-import org.apache.pdfbox.preflight.parser.PreflightParser;
-import org.apache.pdfbox.rendering.ImageType;
-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 javax.activation.DataSource;
-import java.awt.*;
-import java.awt.image.BufferedImage;
-import java.io.ByteArrayOutputStream;
-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 {
- private static final Logger logger = LoggerFactory.getLogger(PADESPDFBOXSigner.class);
- private boolean isAdobeSigForm = false;
+ private static final Logger logger = LoggerFactory.getLogger(PADESPDFBOXSigner.class);
+ private boolean isAdobeSigForm = false;
- public void signPDF(PDFObject genericPdfObject, RequestedSignature requestedSignature,
- PDFASSignatureInterface genericSigner) throws PdfAsException {
+ @Override
+ public void signPDF(PDFObject genericPdfObject, RequestedSignature requestedSignature,
+ PDFASSignatureInterface genericSigner) throws PdfAsException {
- PDFAsVisualSignatureProperties properties = null;
- List<SignaturePlaceholderData> placeholders;
- List<SignaturePlaceholderData> availablePlaceholders;
- SignaturePlaceholderData signaturePlaceholderData = null;
+ 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);
- }
+ String placeholder_id = "";
+ if (PlaceholderWebConfiguration.getValue(PLACEHOLDER_WEB_ID) != null && !PlaceholderWebConfiguration
+ .getValue(PLACEHOLDER_WEB_ID).equalsIgnoreCase("")) {
+ placeholder_id = PlaceholderWebConfiguration.getValue(PLACEHOLDER_WEB_ID);
+ }
- if (!(genericPdfObject instanceof PDFBOXObject)) {
- // tODO:
- throw new PdfAsException();
- }
+ if (!(genericPdfObject instanceof PDFBOXObject)) {
+ // tODO:
+ throw new PdfAsException();
+ }
- PDFBOXObject pdfObject = (PDFBOXObject) genericPdfObject;
+ final PDFBOXObject pdfObject = (PDFBOXObject) genericPdfObject;
- if (!(genericSigner instanceof PDFASPDFBOXSignatureInterface)) {
- // tODO:
- throw new PdfAsException();
- }
+ if (!(genericSigner instanceof PDFASPDFBOXSignatureInterface)) {
+ // tODO:
+ throw new PdfAsException();
+ }
- PDFASPDFBOXSignatureInterface signer = (PDFASPDFBOXSignatureInterface) genericSigner;
+ final PDFASPDFBOXSignatureInterface signer = (PDFASPDFBOXSignatureInterface) genericSigner;
+
+ String pdfaVersion = null;
+
+ PDDocument doc = null;
+ final SignatureOptions options = new SignatureOptions();
+ COSDocument visualSignatureDocumentGuard = null;
+ 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);
+ 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);
- String pdfaVersion = null;
+// placeholders = SignaturePlaceholderExtractor.getPlaceholders();
+ availablePlaceholders = listAvailablePlaceholders(placeholders, existingSignatureLocations(doc));
- PDDocument doc = null;
- SignatureOptions options = new SignatureOptions();
- COSDocument visualSignatureDocumentGuard = null;
- try {
+ 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());
+ }
- doc = pdfObject.getDocument();
- //if signature already exists dont create new page
- List<PDSignatureField> pdSignatureFieldList = doc.getSignatureFields();
- PDSignature signature;
+ tablePos = signaturePlaceholderData.getTablePos();
+ if (tablePos != null) {
- // 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);
- if (signature == null) {
- // create signature dictionary
- signature = new PDSignature();
- }
- else {
- isAdobeSigForm = true;
- }
+ final SignatureProfileConfiguration signatureProfileConfiguration = pdfObject.getStatus()
+ .getSignatureProfileConfiguration(requestedSignature.getSignatureProfileID());
- signature.setFilter(COSName.getPDFName(signer.getPDFFilter()));
- signature.setSubFilter(COSName.getPDFName(signer.getPDFSubFilter()));
-// SignaturePlaceholderData signaturePlaceholderDataInit =
- placeholders = PlaceholderFilter.checkPlaceholderSignatureLocationList(pdfObject.getStatus(),
- pdfObject.getStatus().getSettings(), placeholder_id);
+ final float minWidth = signatureProfileConfiguration.getMinWidth();
-// 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) {
-
- SignatureProfileConfiguration signatureProfileConfiguration = pdfObject.getStatus()
- .getSignatureProfileConfiguration(requestedSignature.getSignatureProfileID());
-
- 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());
- }
- }
- SignatureProfileSettings signatureProfileSettings = TableFactory
- .createProfile(requestedSignature.getSignatureProfileID(), pdfObject.getStatus().getSettings());
-
- //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);
+ if (minWidth > 0) {
+ if (tablePos.getWidth() < minWidth) {
+ tablePos.width = minWidth;
+ logger.debug("Correcting placeholder with to minimum width {}", minWidth);
}
- ValueResolver resolver = new ValueResolver(requestedSignature, pdfObject.getStatus());
-
- String signerName = resolver.resolve("SIG_SUBJECT", signatureProfileSettings.getValue("SIG_SUBJECT"),
- signatureProfileSettings);
-
- signature.setName(signerName);
- signature.setSignDate(Calendar.getInstance());
- String signerReason = signatureProfileSettings.getSigningReason();
-
- if (signerReason == null) {
- signerReason = "PAdES Signature";
- }
-
- signature.setReason(signerReason);
- logger.debug("Signing reason: " + signerReason);
-
- logger.debug("Signing @ " + signer.getSigningDate().getTime().toString());
- // the signing date, needed for valid signature
- // signature.setSignDate(signer.getSigningDate());
-
- signer.setPDSignature(signature);
-
- int signatureSize = 0x1000;
- try {
- String reservedSignatureSizeString = signatureProfileSettings.getValue(SIG_RESERVED_SIZE);
- if (reservedSignatureSizeString != null) {
- signatureSize = Integer.parseInt(reservedSignatureSizeString);
- }
- logger.debug("Reserving {} bytes for signature", signatureSize);
- } catch (NumberFormatException e) {
- logger.warn("Invalid configuration value: {} should be a number using 0x1000", SIG_RESERVED_SIZE);
- }
- options.setPreferredSignatureSize(signatureSize);
-
- if(signatureProfileSettings.isPDFA() || signatureProfileSettings.isPDFA3()) {
- pdfaVersion = getPDFAVersion(doc);
- signatureProfileSettings.setPDFAVersion(pdfaVersion);
- }
-
- // Is visible Signature
- if (requestedSignature.isVisual()) {
- logger.info("Creating visual siganture block");
-
- SignatureProfileConfiguration signatureProfileConfiguration = pdfObject.getStatus()
- .getSignatureProfileConfiguration(requestedSignature.getSignatureProfileID());
+ }
+ logger.debug("Placeholder Position set to: " + tablePos.toString());
+ }
+ }
+ final SignatureProfileSettings signatureProfileSettings = TableFactory
+ .createProfile(requestedSignature.getSignatureProfileID(), pdfObject.getStatus().getSettings());
+
+ // Check if input document is PDF-A conform
+ if (signatureProfileSettings.isPDFA()) {
+ final DataSource origDoc = pdfObject.getOriginalDocument();
+ final InputStream stream = origDoc.getInputStream();
+ // Run PreflightParser for checking conformity//
+ // runPDFAPreflight(origDoc);
+ }
+ final ValueResolver resolver = new ValueResolver(requestedSignature, pdfObject.getStatus());
+
+ final String signerName = resolver.resolve("SIG_SUBJECT", signatureProfileSettings.getValue(
+ "SIG_SUBJECT"),
+ signatureProfileSettings);
+
+ signature.setName(signerName);
+ signature.setSignDate(Calendar.getInstance());
+ String signerReason = signatureProfileSettings.getSigningReason();
+
+ if (signerReason == null) {
+ signerReason = "PAdES Signature";
+ }
+
+ signature.setReason(signerReason);
+ logger.debug("Signing reason: " + signerReason);
+
+ logger.debug("Signing @ " + signer.getSigningDate().getTime().toString());
+ // the signing date, needed for valid signature
+ // signature.setSignDate(signer.getSigningDate());
+
+ signer.setPDSignature(signature);
+
+ int signatureSize = 0x1000;
+ try {
+ final String reservedSignatureSizeString = signatureProfileSettings.getValue(SIG_RESERVED_SIZE);
+ if (reservedSignatureSizeString != null) {
+ signatureSize = Integer.parseInt(reservedSignatureSizeString);
+ }
+ logger.debug("Reserving {} bytes for signature", signatureSize);
+ } catch (final NumberFormatException e) {
+ logger.warn("Invalid configuration value: {} should be a number using 0x1000", SIG_RESERVED_SIZE);
+ }
+ options.setPreferredSignatureSize(signatureSize);
+
+ if (signatureProfileSettings.isPDFA() || signatureProfileSettings.isPDFA3()) {
+ pdfaVersion = getPDFAVersion(doc);
+ signatureProfileSettings.setPDFAVersion(pdfaVersion);
+ }
+
+ // 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 (tablePos == null) {
- // ================================================================
- // PositioningStage (visual) -> find position or use
- // fixed
- // position
+ // Legacy Modes not supported with pdfbox2 anymore
+// boolean legacy32Position = signatureProfileConfiguration.getLegacy32Positioning();
+// boolean legacy40Position = signatureProfileConfiguration.getLegacy40Positioning();
- String posString = pdfObject.getStatus().getSignParamter().getSignaturePosition();
+ // create Table describtion
+
+ final Table main = TableFactory.createSigTable(signatureProfileSettings, MAIN, pdfObject.getStatus(),
+ requestedSignature);
+
+ final IPDFStamper stamper = StamperFactory.createDefaultStamper(pdfObject.getStatus().getSettings());
+
+ final IPDFVisualObject visualObject = stamper.createVisualPDFObject(pdfObject, main);
+
+ /*
+ * PDDocument originalDocument = PDDocument .load(new
+ * ByteArrayInputStream(pdfObject.getStatus()
+ * .getPdfObject().getOriginalDocument()));
+ */
+
+ final PositioningInstruction positioningInstruction = Positioning.determineTablePositioning(tablePos,
+ "",
+ doc, visualObject, pdfObject.getStatus().getSettings());
+
+ logger.debug("Positioning: {}", positioningInstruction.toString());
+
+ if (!isAdobeSigForm) {
+ if (positioningInstruction.isMakeNewPage()) {
+ final int last = doc.getNumberOfPages() - 1;
+ final PDDocumentCatalog root = doc.getDocumentCatalog();
+ final PDPage lastPage = root.getPages().get(last);
+ root.getPages().getCOSObject().setNeedToBeUpdated(true);
+ final PDPage p = new PDPage(lastPage.getMediaBox());
+ p.setResources(new PDResources());
+ p.setRotation(lastPage.getRotation());
+ doc.addPage(p);
+ }
+
+ // handle rotated page
+ final int targetPageNumber = positioningInstruction.getPage();
+ logger.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());
+
+ final SignaturePositionImpl position = new SignaturePositionImpl();
+ position.setX(positioningInstruction.getX());
+ position.setY(positioningInstruction.getY());
+ position.setPage(positioningInstruction.getPage());
+ position.setHeight(visualObject.getHeight());
+ position.setWidth(visualObject.getWidth());
+ requestedSignature.setSignaturePosition(position);
+ }
- TablePos signaturePos = null;
+ properties = new PDFAsVisualSignatureProperties(pdfObject.getStatus().getSettings(), pdfObject,
+ (PdfBoxVisualObject) visualObject, positioningInstruction, signatureProfileSettings);
+
+ properties.buildSignature();
+
+ /*
+ * ByteArrayOutputStream sigbos = new ByteArrayOutputStream();
+ * sigbos.write(StreamUtils.inputStreamToByteArray (properties
+ * .getVisibleSignature())); sigbos.close();
+ */
+
+ /*
+ * if (signaturePlaceholderData != null) {
+ *
+ * InputStream fis =
+ * PADESPDFBOXSigner.class.getResourceAsStream("/placeholder/empty.jpg");
+ * PDImageXObject img = JPEGFactory.createFromStream(doc, fis);
+ *
+ * img.getCOSObject().setNeedToBeUpdated(true); // PDDocumentCatalog root =
+ * doc.getDocumentCatalog(); // PDPageNode rootPages = root.getPages(); //
+ * List<PDPage> kids = new ArrayList<PDPage>(); // rootPages.getAllKids(kids);
+ * int pageNumber = positioningInstruction.getPage(); PDPage page =
+ * doc.getPages().get(pageNumber - 1);
+ *
+ * logger.info("Placeholder name: " +
+ * signaturePlaceholderData.getPlaceholderName()); COSDictionary
+ * xobjectsDictionary = (COSDictionary) page.getResources().getCOSObject()
+ * .getDictionaryObject(COSName.XOBJECT);
+ *
+ *
+ * xobjectsDictionary.setItem(signaturePlaceholderData.getPlaceholderName(),
+ * img); xobjectsDictionary.setNeedToBeUpdated(true);
+ * page.getResources().getCOSObject().setNeedToBeUpdated(true);
+ * logger.info("Placeholder name: " +
+ * signaturePlaceholderData.getPlaceholderName()); }
+ */
+
+ if (signatureProfileSettings.isPDFA() || signatureProfileSettings.isPDFA3()) {
+ final PDDocumentCatalog root = doc.getDocumentCatalog();
+ final COSBase base = root.getCOSObject().getItem(COSName.OUTPUT_INTENTS);
+
+ InputStream colorProfile = null;
+ // colorProfile = this.getClass().getResourceAsStream("/icm/sRGB.icm");
+ colorProfile = this.getClass().getResourceAsStream("/icm/sRGB Color Space Profile.icm");
+ // Set output intents for PDF/A conformity//
+ try {
+ final PDOutputIntent intent = new PDOutputIntent(doc, colorProfile);
+
+ intent.setInfo("sRGB IEC61966-2.1");
+ intent.setOutputCondition("sRGB IEC61966-2.1");
+ intent.setOutputConditionIdentifier("sRGB IEC61966-2.1");
+ intent.setRegistryName("http://www.color.org");
+ final List<PDOutputIntent> oi = new ArrayList<>();
+ oi.add(intent);
+ root.setOutputIntents(oi);
+ root.getCOSObject().setNeedToBeUpdated(true);
+
+ logger.info("added Output Intent");
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ throw new PdfAsException("Failed to add Output Intent", e);
+ } finally {
+ IOUtils.closeQuietly(colorProfile);
+ }
+ }
+ options.setPage(positioningInstruction.getPage() - 1);
+ options.setVisualSignature(properties.getVisibleSignature());
+ }
- String signaturePosString = signatureProfileConfiguration.getDefaultPositioning();
+ visualSignatureDocumentGuard = options.getVisualSignature();
- if (signaturePosString != null) {
- logger.debug("using signature Positioning: " + signaturePos);
- signaturePos = new TablePos(signaturePosString);
- }
+ doc.addSignature(signature, signer, options);
- logger.debug("using Positioning: " + posString);
+ if (sigFieldName == null) {
+ sigFieldName = "PDF-AS Signatur";
+ }
+ final int count = PdfBoxUtils.countSignatures(doc, sigFieldName);
- if (posString != null) {
- // Merge Signature Position
- tablePos = new TablePos(posString, signaturePos);
- } else {
- // Fallback to signature Position!
- tablePos = signaturePos;
- }
+ sigFieldName = sigFieldName + count;
- if (tablePos == null) {
- // Last Fallback default position
- tablePos = new TablePos();
- }
- }
+ final PDAcroForm acroFormm = doc.getDocumentCatalog().getAcroForm();
- //Legacy Modes not supported with pdfbox2 anymore
-// boolean legacy32Position = signatureProfileConfiguration.getLegacy32Positioning();
-// boolean legacy40Position = signatureProfileConfiguration.getLegacy40Positioning();
+ // PDStructureTreeRoot pdstRoot =
+ // doc.getDocumentCatalog().getStructureTreeRoot();
+ // COSDictionary dic =
+ // doc.getDocumentCatalog().getCOSDictionary();
+ // PDStructureElement el = new PDStructureElement("Widget",
+ // pdstRoot);
- // create Table describtion
-
-
- Table main = TableFactory.createSigTable(signatureProfileSettings, MAIN, pdfObject.getStatus(),
- requestedSignature);
-
- IPDFStamper stamper = StamperFactory.createDefaultStamper(pdfObject.getStatus().getSettings());
-
- IPDFVisualObject visualObject = stamper.createVisualPDFObject(pdfObject, main);
-
- /*
- * PDDocument originalDocument = PDDocument .load(new
- * ByteArrayInputStream(pdfObject.getStatus()
- * .getPdfObject().getOriginalDocument()));
- */
-
- PositioningInstruction positioningInstruction = Positioning.determineTablePositioning(tablePos, "",
- doc, visualObject, pdfObject.getStatus().getSettings());
-
- logger.debug("Positioning: {}", positioningInstruction.toString());
-
- if(!isAdobeSigForm) {
- if (positioningInstruction.isMakeNewPage()) {
- int last = doc.getNumberOfPages() - 1;
- PDDocumentCatalog root = doc.getDocumentCatalog();
- PDPage lastPage = root.getPages().get(last);
- root.getPages().getCOSObject().setNeedToBeUpdated(true);
- PDPage p = new PDPage(lastPage.getMediaBox());
- p.setResources(new PDResources());
- p.setRotation(lastPage.getRotation());
- doc.addPage(p);
- }
-
- // handle rotated page
- int targetPageNumber = positioningInstruction.getPage();
- logger.debug("Target Page: " + targetPageNumber);
- PDPage targetPage = doc.getPages().get(targetPageNumber - 1);
- int rot = targetPage.getRotation();
- logger.debug("Page rotation: " + rot);
- logger.debug("resulting Sign rotation: " + positioningInstruction.getRotation());
-
- SignaturePositionImpl position = new SignaturePositionImpl();
- position.setX(positioningInstruction.getX());
- position.setY(positioningInstruction.getY());
- position.setPage(positioningInstruction.getPage());
- position.setHeight(visualObject.getHeight());
- position.setWidth(visualObject.getWidth());
- requestedSignature.setSignaturePosition(position);
- }
-
- properties = new PDFAsVisualSignatureProperties(pdfObject.getStatus().getSettings(), pdfObject,
- (PdfBoxVisualObject) visualObject, positioningInstruction, signatureProfileSettings);
-
- properties.buildSignature();
-
- /*
- * ByteArrayOutputStream sigbos = new
- * ByteArrayOutputStream();
- * sigbos.write(StreamUtils.inputStreamToByteArray
- * (properties .getVisibleSignature())); sigbos.close();
- */
-
- /*if (signaturePlaceholderData != null) {
-
- InputStream fis = PADESPDFBOXSigner.class.getResourceAsStream("/placeholder/empty.jpg");
- PDImageXObject img = JPEGFactory.createFromStream(doc, fis);
-
- img.getCOSObject().setNeedToBeUpdated(true);
- // PDDocumentCatalog root = doc.getDocumentCatalog();
- // PDPageNode rootPages = root.getPages();
- // List<PDPage> kids = new ArrayList<PDPage>();
- // rootPages.getAllKids(kids);
- int pageNumber = positioningInstruction.getPage();
- PDPage page = doc.getPages().get(pageNumber - 1);
-
- logger.info("Placeholder name: " + signaturePlaceholderData.getPlaceholderName());
- COSDictionary xobjectsDictionary = (COSDictionary) page.getResources().getCOSObject()
- .getDictionaryObject(COSName.XOBJECT);
-
-
- xobjectsDictionary.setItem(signaturePlaceholderData.getPlaceholderName(), img);
- xobjectsDictionary.setNeedToBeUpdated(true);
- page.getResources().getCOSObject().setNeedToBeUpdated(true);
- logger.info("Placeholder name: " + signaturePlaceholderData.getPlaceholderName());
- }*/
-
- if (signatureProfileSettings.isPDFA() || signatureProfileSettings.isPDFA3()) {
- PDDocumentCatalog root = doc.getDocumentCatalog();
- COSBase base = root.getCOSObject().getItem(COSName.OUTPUT_INTENTS);
-
- InputStream colorProfile = null;
- //colorProfile = this.getClass().getResourceAsStream("/icm/sRGB.icm");
- colorProfile = this.getClass().getResourceAsStream("/icm/sRGB Color Space Profile.icm");
- //Set output intents for PDF/A conformity//
- try {
- PDOutputIntent intent = new PDOutputIntent(doc, colorProfile);
-
- intent.setInfo("sRGB IEC61966-2.1");
- intent.setOutputCondition("sRGB IEC61966-2.1");
- intent.setOutputConditionIdentifier("sRGB IEC61966-2.1");
- intent.setRegistryName("http://www.color.org");
- List<PDOutputIntent> oi = new ArrayList<PDOutputIntent>();
- oi.add(intent);
- root.setOutputIntents(oi);
- root.getCOSObject().setNeedToBeUpdated(true);
-
- logger.info("added Output Intent");
- } catch (Throwable e) {
- e.printStackTrace();
- throw new PdfAsException("Failed to add Output Intent", e);
- }
- finally {
- IOUtils.closeQuietly(colorProfile);
- }
- }
- options.setPage(positioningInstruction.getPage()-1);
- options.setVisualSignature(properties.getVisibleSignature());
- }
+ // this is not used for Adobe signature fields
+ if (!isAdobeSigForm) {
+ PDSignatureField signatureField = null;
+ if (acroFormm != null) {
+ @SuppressWarnings("unchecked")
+ final List<PDField> fields = acroFormm.getFields();
- visualSignatureDocumentGuard = options.getVisualSignature();
-
- doc.addSignature(signature, signer, options);
-
- if (sigFieldName == null) {
- sigFieldName = "PDF-AS Signatur";
- }
- int count = PdfBoxUtils.countSignatures(doc, sigFieldName);
-
- sigFieldName = sigFieldName + count;
-
- PDAcroForm acroFormm = doc.getDocumentCatalog().getAcroForm();
-
- // PDStructureTreeRoot pdstRoot =
- // doc.getDocumentCatalog().getStructureTreeRoot();
- // COSDictionary dic =
- // doc.getDocumentCatalog().getCOSDictionary();
- // PDStructureElement el = new PDStructureElement("Widget",
- // pdstRoot);
-
- //this is not used for Adobe signature fields
- if(!isAdobeSigForm) {
- PDSignatureField signatureField = null;
- if (acroFormm != null) {
- @SuppressWarnings("unchecked")
- List<PDField> fields = acroFormm.getFields();
-
- if (fields != null) {
- for (PDField pdField : fields) {
- if (pdField != null) {
- if (pdField instanceof PDSignatureField) {
- PDSignatureField tmpSigField = (PDSignatureField) pdField;
-
- if (tmpSigField.getSignature() != null
- && tmpSigField.getSignature().getCOSObject() != null) {
- if (tmpSigField.getSignature().getCOSObject()
- .equals(signature.getCOSObject())) {
- signatureField = (PDSignatureField) pdField;
-
- }
- }
- }
- }
- }
- } else {
- logger.warn("Failed to name Signature Field! [Cannot find Field list in acroForm!]");
- }
-
- if (signatureField != null) {
- signatureField.setPartialName(sigFieldName);
- }
- if (properties != null) {
- signatureField.setAlternateFieldName(properties.getAlternativeTableCaption());
- } else {
- signatureField.setAlternateFieldName(sigFieldName);
- }
- } else {
- logger.warn("Failed to name Signature Field! [Cannot find acroForm!]");
- }
- }
-
- PDSignatureField signatureField = null;
- PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
- if (acroForm != null) {
- signatureField = (PDSignatureField) acroForm.getField(sigFieldName);
- }
-
- // PDF-UA
- logger.info("Adding pdf/ua content.");
- try {
- PDDocumentCatalog root = doc.getDocumentCatalog();
- PDStructureTreeRoot structureTreeRoot = root.getStructureTreeRoot();
- if (structureTreeRoot != null) {
- logger.info("Tree Root: {}", structureTreeRoot.toString());
- List<Object> kids = structureTreeRoot.getKids();
-
- if (kids == null) {
- logger.info("No kid-elements in structure tree Root, maybe not PDF/UA document");
- }
-
- PDStructureElement docElement = null;
- for (Object k : kids) {
- if (k instanceof PDStructureElement) {
- docElement = (PDStructureElement) k;
- break;
-
- }
- }
-
- 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());
-
- List<Object> l = new ArrayList<Object>();
- l.add(objectDic);
- sigBlock.setKids(l);
- sigBlock.setPage(signatureField.getWidget().getPage());
-
-
- sigBlock.setTitle("Signature Table");
- sigBlock.setParent(docElement);
- docElement.appendKid(sigBlock);
-
- // Create and add Attribute dictionary to mitigate PAC
- // warning
- COSDictionary sigBlockDic = (COSDictionary) sigBlock.getCOSObject();
- COSDictionary sub = new COSDictionary();
-
- sub.setName("O", "Layout");
- sub.setName("Placement", "Block");
- sigBlockDic.setItem(COSName.A, sub);
- sigBlockDic.setNeedToBeUpdated(true);
-
- // Modify number tree
- PDNumberTreeNode ntn = structureTreeRoot.getParentTree();
- if (ntn == null) {
- ntn = new PDNumberTreeNode(objectDic, null);
- logger.info("No number-tree-node found!");
- }
-
- COSArray ntnKids = (COSArray) ntn.getCOSObject().getDictionaryObject(COSName.KIDS);
- COSArray ntnNumbers = (COSArray) ntn.getCOSObject().getDictionaryObject(COSName.NUMS);
-
- 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();
- //limits for exact one entry
- limitsArray.add(COSInteger.get(parentTreeNextKey));
- limitsArray.add(COSInteger.get(parentTreeNextKey));
-
- COSArray numsArray = new COSArray();
- numsArray.add(COSInteger.get(parentTreeNextKey));
- numsArray.add(sigBlock);
-
- pTreeEntry.setItem(COSName.NUMS, numsArray);
- pTreeEntry.setItem(COSName.LIMITS, limitsArray);
-
- PDNumberTreeNode newKidsElement = new PDNumberTreeNode(pTreeEntry, PDNumberTreeNode.class);
-
- ntnKids.add(newKidsElement);
- ntnKids.setNeedToBeUpdated(true);
-
-
- }else if(ntnNumbers != null && ntnKids == null){
-
- int arrindex = ntnNumbers.size();
-
- ntnNumbers.add(arrindex, COSInteger.get(parentTreeNextKey));
- ntnNumbers.add(arrindex + 1, sigBlock.getCOSObject());
-
- ntnNumbers.setNeedToBeUpdated(true);
-
- structureTreeRoot.setParentTree(ntn);
-
- }else if(ntnNumbers == null && ntnKids == null){
- //document is not pdfua conform before signature creation
- throw new PdfAsException("error.pdf.sig.pdfua.1");
- }else{
- //this is not allowed
- throw new PdfAsException("error.pdf.sig.pdfua.1");
- }
-
- // set StructureParent for signature field annotation
- signatureField.getWidget().setStructParent(parentTreeNextKey);
-
- //Increase the next Key value in the structure tree root
- structureTreeRoot.setParentTreeNextKey(parentTreeNextKey+1);
-
- // add the Tabs /S Element for Tabbing through annots
- PDPage p = signatureField.getWidget().getPage();
- p.getCOSObject().setName("Tabs", "S");
- p.getCOSObject().setNeedToBeUpdated(true);
-
- //check alternative signature field name
- if (signatureField != null) {
- if(signatureField.getAlternateFieldName().equals(""))
- signatureField.setAlternateFieldName(sigFieldName);
- }
-
- ntn.getCOSObject().setNeedToBeUpdated(true);
- sigBlock.getCOSObject().setNeedToBeUpdated(true);
- structureTreeRoot.getCOSObject().setNeedToBeUpdated(true);
- objectDic.setNeedToBeUpdated(true);
- docElement.getCOSObject().setNeedToBeUpdated(true);
- }
- } catch (Throwable e) {
- if (signatureProfileSettings.isPDFUA() == true) {
- logger.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");
- }
- }
-
- try {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- synchronized (doc) {
- doc.saveIncremental(bos);
- byte[] outputDocument = bos.toByteArray();
- pdfObject.setSignedDocument(outputDocument);
- }
- /* Check if resulting pdf is PDF-A conform */
- if (signatureProfileSettings.isPDFA()) {
- runPDFAPreflight(new ByteArrayDataSource(pdfObject.getSignedDocument()));
- }
+ if (fields != null) {
+ for (final PDField pdField : fields) {
+ if (pdField != null) {
+ if (pdField instanceof PDSignatureField) {
+ final PDSignatureField tmpSigField = (PDSignatureField) pdField;
- } catch (IOException e1) {
- e1.printStackTrace();
- }
+ if (tmpSigField.getSignature() != null
+ && tmpSigField.getSignature().getCOSObject() != null) {
+ if (tmpSigField.getSignature().getCOSObject()
+ .equals(signature.getCOSObject())) {
+ signatureField = (PDSignatureField) pdField;
- finally {
- if (options != null) {
- if (options.getVisualSignature() != null) {
- options.getVisualSignature().close();
}
+ }
}
+ }
}
+ } else {
+ logger.warn("Failed to name Signature Field! [Cannot find Field list in acroForm!]");
+ }
+
+ if (signatureField != null) {
+ signatureField.setPartialName(sigFieldName);
+ }
+ if (properties != null) {
+ signatureField.setAlternateFieldName(properties.getAlternativeTableCaption());
+ } else {
+ signatureField.setAlternateFieldName(sigFieldName);
+ }
+ } else {
+ logger.warn("Failed to name Signature Field! [Cannot find acroForm!]");
+ }
+ }
+
+ PDSignatureField signatureField = null;
+ final PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
+ if (acroForm != null) {
+ signatureField = (PDSignatureField) acroForm.getField(sigFieldName);
+ }
+
+ // PDF-UA
+ logger.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());
+ final List<Object> kids = structureTreeRoot.getKids();
+
+ if (kids == null) {
+ logger.info("No kid-elements in structure tree Root, maybe not PDF/UA document");
+ }
+
+ PDStructureElement docElement = null;
+ for (final Object k : kids) {
+ if (k instanceof PDStructureElement) {
+ docElement = (PDStructureElement) k;
+ break;
- System.gc();
- } catch (IOException e) {
- logger.warn(MessageResolver.resolveMessage("error.pdf.sig.01"), e);
- throw new PdfAsException("error.pdf.sig.01", e);
- } finally {
- if (doc != null) {
- try {
- doc.close();
- //SignaturePlaceholderExtractor.getPlaceholders().clear();
- } catch (IOException e) {
- logger.debug("Failed to close COS Doc!", e);
- // Ignore
- }
- }
- logger.debug("Signature done!");
- }
- }
-
- private int getParentTreeNextKey(PDStructureTreeRoot structureTreeRoot) throws IOException {
- int nextKey = structureTreeRoot.getParentTreeNextKey();
- if (nextKey < 0) {
- Map<Integer, COSObjectable> 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
- */
- private void runPDFAPreflight(final DataSource signedDocument) throws PdfAsException {
- PreflightDocument document = null;
- ValidationResult result = null;
- try {
- PreflightParser parser = new PreflightParser(signedDocument);
- //
- // parser.parse(Format.PDF_A1B);
- parser.parse();
- document = parser.getPreflightDocument();
- document.validate();
-
- document.close();
- result = document.getResult();
- logger.info("PDF-A Validation Result: " + result.isValid());
-
- if (result.getErrorsList().size() > 0) {
- logger.error("The following validation errors occured for PDF-A validation");
}
+ }
- for (ValidationResult.ValidationError ve : result.getErrorsList()) {
- logger.error("\t" + ve.getErrorCode() + ": " + ve.getDetails());
- }
+ final PDStructureElement sigBlock = new PDStructureElement("Form", docElement);
- if (!result.isValid()) {
- logger.info("The file is not a valid PDF-A document");
- }
+ // create object dictionary and add as child element
+ final COSDictionary objectDic = new COSDictionary();
+ objectDic.setName("Type", "OBJR");
- } catch (SyntaxValidationException e) {
- logger.error("The file is syntactically invalid.", e);
- throw new PdfAsException("Resulting PDF Document is syntactically invalid.");
- } catch (ValidationException e) {
- logger.error("The file is not a valid PDF-A document.", e);
- } catch (IOException e) {
- logger.error("An IOException (" + e.getMessage()
- + ") occurred, while validating the PDF-A conformance", e);
- throw new PdfAsException("Failed validating PDF Document IOException.");
- } catch (RuntimeException e) {
- logger.debug("An RuntimeException (" + e.getMessage()
- + ") occurred, while validating the PDF-A conformance", e);
- throw new PdfAsException("Failed validating PDF Document RuntimeException.");
- } finally {
- if (document != null) {
- IOUtils.closeQuietly(document);
- }
- }
- }
+ objectDic.setItem("Pg", signatureField.getWidget().getPage());
+ objectDic.setItem("Obj", signatureField.getWidget());
- @Override
- public PDFObject buildPDFObject(OperationStatus operationStatus) {
- return new PDFBOXObject(operationStatus);
- }
-
- @Override
- public PDFASSignatureInterface buildSignaturInterface(IPlainSigner signer, SignParameter parameters,
- RequestedSignature requestedSignature) {
- return new PdfboxSignerWrapper(signer, parameters, requestedSignature);
- }
+ final List<Object> l = new ArrayList<>();
+ l.add(objectDic);
+ sigBlock.setKids(l);
+ sigBlock.setPage(signatureField.getWidget().getPage());
- @Override
- public PDFASSignatureExtractor buildBlindSignaturInterface(X509Certificate certificate, String filter, String subfilter, Calendar date) {
- return new SignatureDataExtractor(certificate, filter, subfilter, date);
- }
+ sigBlock.setTitle("Signature Table");
+ sigBlock.setParent(docElement);
+ docElement.appendKid(sigBlock);
- @Override
- public void checkPDFPermissions(PDFObject genericPdfObject) throws PdfAsException {
- if (!(genericPdfObject instanceof PDFBOXObject)) {
- // tODO:
- throw new PdfAsException();
- }
+ // Create and add Attribute dictionary to mitigate PAC
+ // warning
+ final COSDictionary sigBlockDic = sigBlock.getCOSObject();
+ final COSDictionary sub = new COSDictionary();
- PDFBOXObject pdfObject = (PDFBOXObject) genericPdfObject;
- PdfBoxUtils.checkPDFPermissions(pdfObject.getDocument());
- }
+ sub.setName("O", "Layout");
+ sub.setName("Placement", "Block");
+ sigBlockDic.setItem(COSName.A, sub);
+ sigBlockDic.setNeedToBeUpdated(true);
- @Override
- public byte[] rewritePlainSignature(byte[] plainSignature) {
- String signature = new COSString(plainSignature).toHexString();
- byte[] pdfSignature = signature.getBytes();
- return pdfSignature;
- }
+ // Modify number tree
+ PDNumberTreeNode ntn = structureTreeRoot.getParentTree();
+ if (ntn == null) {
+ ntn = new PDNumberTreeNode(objectDic, null);
+ logger.info("No number-tree-node found!");
+ }
- @Override
- public Image generateVisibleSignaturePreview(SignParameter parameter, java.security.cert.X509Certificate cert,
- int resolution, OperationStatus status, RequestedSignature requestedSignature) throws PDFASError {
- try {
+ final COSArray ntnKids = (COSArray) ntn.getCOSObject().getDictionaryObject(COSName.KIDS);
+ final COSArray ntnNumbers = (COSArray) ntn.getCOSObject().getDictionaryObject(COSName.NUMS);
- PDFBOXObject pdfObject = (PDFBOXObject) status.getPdfObject();
- PDDocument origDoc = new PDDocument();
+ final int parentTreeNextKey = getParentTreeNextKey(structureTreeRoot);
- origDoc.addPage(new PDPage(PDRectangle.A4));
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- origDoc.save(baos);
- baos.close();
+ if (ntnNumbers == null && ntnKids != null) {// no number array, so continue with the kids array
+ // create dictionary with limits and nums array
+ final COSDictionary pTreeEntry = new COSDictionary();
+ final COSArray limitsArray = new COSArray();
+ // limits for exact one entry
+ limitsArray.add(COSInteger.get(parentTreeNextKey));
+ limitsArray.add(COSInteger.get(parentTreeNextKey));
- pdfObject.setOriginalDocument(new ByteArrayDataSource(baos.toByteArray()));
+ final COSArray numsArray = new COSArray();
+ numsArray.add(COSInteger.get(parentTreeNextKey));
+ numsArray.add(sigBlock);
- SignatureProfileSettings signatureProfileSettings = TableFactory
- .createProfile(requestedSignature.getSignatureProfileID(), pdfObject.getStatus().getSettings());
+ pTreeEntry.setItem(COSName.NUMS, numsArray);
+ pTreeEntry.setItem(COSName.LIMITS, limitsArray);
- // create Table describtion
- Table main = TableFactory.createSigTable(signatureProfileSettings, MAIN, pdfObject.getStatus(),
- requestedSignature);
+ final PDNumberTreeNode newKidsElement = new PDNumberTreeNode(pTreeEntry, PDNumberTreeNode.class);
- IPDFStamper stamper = StamperFactory.createDefaultStamper(pdfObject.getStatus().getSettings());
+ ntnKids.add(newKidsElement);
+ ntnKids.setNeedToBeUpdated(true);
- IPDFVisualObject visualObject = stamper.createVisualPDFObject(pdfObject, main);
+ } else if (ntnNumbers != null && ntnKids == null) {
- SignatureProfileConfiguration signatureProfileConfiguration = pdfObject.getStatus()
- .getSignatureProfileConfiguration(requestedSignature.getSignatureProfileID());
+ final int arrindex = ntnNumbers.size();
- String signaturePosString = signatureProfileConfiguration.getDefaultPositioning();
- PositioningInstruction positioningInstruction;
- if (signaturePosString != null) {
- positioningInstruction = Positioning.determineTablePositioning(new TablePos(signaturePosString), "",
- origDoc, visualObject, pdfObject.getStatus().getSettings());
- } else {
- positioningInstruction = Positioning.determineTablePositioning(new TablePos(), "", origDoc,
- visualObject, pdfObject.getStatus().getSettings());
- }
+ ntnNumbers.add(arrindex, COSInteger.get(parentTreeNextKey));
+ ntnNumbers.add(arrindex + 1, sigBlock.getCOSObject());
- origDoc.close();
+ ntnNumbers.setNeedToBeUpdated(true);
- SignaturePositionImpl position = new SignaturePositionImpl();
- position.setX(positioningInstruction.getX());
- position.setY(positioningInstruction.getY());
- position.setPage(positioningInstruction.getPage());
- position.setHeight(visualObject.getHeight());
- position.setWidth(visualObject.getWidth());
+ structureTreeRoot.setParentTree(ntn);
- requestedSignature.setSignaturePosition(position);
+ } else if (ntnNumbers == null && ntnKids == null) {
+ // document is not pdfua conform before signature creation
+ throw new PdfAsException("error.pdf.sig.pdfua.1");
+ } else {
+ // this is not allowed
+ throw new PdfAsException("error.pdf.sig.pdfua.1");
+ }
- PDFAsVisualSignatureProperties properties = new PDFAsVisualSignatureProperties(
- pdfObject.getStatus().getSettings(), pdfObject, (PdfBoxVisualObject) visualObject,
- positioningInstruction, signatureProfileSettings);
+ // set StructureParent for signature field annotation
+ signatureField.getWidget().setStructParent(parentTreeNextKey);
- properties.buildSignature();
- PDDocument visualDoc;
- synchronized (PDDocument.class) {
- visualDoc = PDDocument.load(properties.getVisibleSignature());
- }
- // PDPageable pageable = new PDPageable(visualDoc);
+ // Increase the next Key value in the structure tree root
+ structureTreeRoot.setParentTreeNextKey(parentTreeNextKey + 1);
- PDPage firstPage = visualDoc.getDocumentCatalog().getPages().get(0);
+ // add the Tabs /S Element for Tabbing through annots
+ final PDPage p = signatureField.getWidget().getPage();
+ p.getCOSObject().setName("Tabs", "S");
+ p.getCOSObject().setNeedToBeUpdated(true);
- float stdRes = 72;
- float targetRes = resolution;
- float factor = targetRes / stdRes;
+ // check alternative signature field name
+ if (signatureField != null) {
+ if (signatureField.getAlternateFieldName().equals("")) {
+ signatureField.setAlternateFieldName(sigFieldName);
+ }
+ }
+ ntn.getCOSObject().setNeedToBeUpdated(true);
+ sigBlock.getCOSObject().setNeedToBeUpdated(true);
+ structureTreeRoot.getCOSObject().setNeedToBeUpdated(true);
+ objectDic.setNeedToBeUpdated(true);
+ docElement.getCOSObject().setNeedToBeUpdated(true);
+ }
+ } catch (final Throwable e) {
+ if (signatureProfileSettings.isPDFUA() == true) {
+ logger.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");
+ }
+ }
+
+ try {
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ synchronized (doc) {
+ doc.saveIncremental(bos);
+ final byte[] outputDocument = bos.toByteArray();
+ pdfObject.setSignedDocument(outputDocument);
+ }
+ /* Check if resulting pdf is PDF-A conform */
+ if (signatureProfileSettings.isPDFA()) {
+ runPDFAPreflight(new ByteArrayDataSource(pdfObject.getSignedDocument()));
+ }
- int targetPageNumber = 0;//TODO: is this always the case
- PDFRenderer pdfRenderer = new PDFRenderer(visualDoc);
- BufferedImage outputImage = pdfRenderer.renderImageWithDPI(targetPageNumber, targetRes, ImageType.ARGB);
+ } catch (final IOException e1) {
+ e1.printStackTrace();
+ }
- //BufferedImage outputImage = firstPage.convertToImage(BufferedImage.TYPE_4BYTE_ABGR, (int) targetRes);
+ finally {
+ if (options != null) {
+ if (options.getVisualSignature() != null) {
+ options.getVisualSignature().close();
+ }
+ }
+ }
+
+ System.gc();
+ logger.debug("Signature done!");
+
+ } catch (final IOException e) {
+ logger.warn(MessageResolver.resolveMessage("error.pdf.sig.01"), e);
+ throw new PdfAsException("error.pdf.sig.01", e);
+
+ } finally {
+ if (doc != null) {
+ try {
+ doc.close();
+ // SignaturePlaceholderExtractor.getPlaceholders().clear();
+ } catch (final IOException e) {
+ logger.debug("Failed to close COS Doc!", e);
+ // Ignore
+ }
+ }
+ }
+ }
- visualDoc.close();
- pdfRenderer = null;
+ private int getParentTreeNextKey(PDStructureTreeRoot structureTreeRoot) throws IOException {
+ int nextKey = structureTreeRoot.getParentTreeNextKey();
+ if (nextKey < 0) {
+ final Map<Integer, COSObjectable> destNumberTreeAsMap = getNumberTreeAsMap(structureTreeRoot
+ .getParentTree());
+ if (destNumberTreeAsMap.isEmpty()) {
+ nextKey = 0;
- BufferedImage cutOut = new BufferedImage((int) (position.getWidth() * factor),
- (int) (position.getHeight() * factor), BufferedImage.TYPE_4BYTE_ABGR);
+ } else {
+ nextKey = Collections.max(destNumberTreeAsMap.keySet()) + 1;
- Graphics2D graphics = (Graphics2D) cutOut.getGraphics();
+ }
+ }
- graphics.drawImage(outputImage, 0, 0, cutOut.getWidth(), cutOut.getHeight(), (int) (1 * factor),
- (int) (outputImage.getHeight() - ((position.getHeight() + 1) * factor)),
- (int) ((1 + position.getWidth()) * factor), (int) (outputImage.getHeight()
- - ((position.getHeight() + 1) * factor) + (position.getHeight() * factor)),
- null);
- return cutOut;
- } catch (PdfAsException e) {
- logger.warn("PDF-AS Exception", e);
- throw ErrorExtractor.searchPdfAsError(e, status);
- } catch (Throwable e) {
- logger.warn("Unexpected Throwable Exception", e);
- throw ErrorExtractor.searchPdfAsError(e, status);
- }
+ return nextKey;
+ }
+
+ /**
+ * Check via PreFlightParser if PDF-Document is a valid PDFA1
+ *
+ * @param signedDocument: signed Document
+ * @throws PdfAsException
+ */
+ private void runPDFAPreflight(final DataSource signedDocument) throws PdfAsException {
+ PreflightDocument document = null;
+ ValidationResult result = null;
+ try {
+ final PreflightParser parser = new PreflightParser(signedDocument);
+ //
+ // parser.parse(Format.PDF_A1B);
+ parser.parse();
+ document = parser.getPreflightDocument();
+ document.validate();
+
+ document.close();
+ result = document.getResult();
+ logger.info("PDF-A Validation Result: " + result.isValid());
+
+ if (result.getErrorsList().size() > 0) {
+ logger.error("The following validation errors occured for PDF-A validation");
+ }
+
+ for (final ValidationResult.ValidationError ve : result.getErrorsList()) {
+ logger.error("\t" + ve.getErrorCode() + ": " + ve.getDetails());
+ }
+
+ if (!result.isValid()) {
+ logger.info("The file is not a valid PDF-A document");
+ }
+
+ } catch (final SyntaxValidationException e) {
+ logger.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);
+ } catch (final IOException e) {
+ logger.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()
+ + ") occurred, while validating the PDF-A conformance", e);
+ throw new PdfAsException("Failed validating PDF Document RuntimeException.");
+ } finally {
+ if (document != null) {
+ IOUtils.closeQuietly(document);
+ }
+ }
+ }
+
+ @Override
+ public PDFObject buildPDFObject(OperationStatus operationStatus) {
+ return new PDFBOXObject(operationStatus);
+ }
+
+ @Override
+ public PDFASSignatureInterface buildSignaturInterface(IPlainSigner signer, SignParameter parameters,
+ RequestedSignature requestedSignature) {
+ return new PdfboxSignerWrapper(signer, parameters, requestedSignature);
+ }
+
+ @Override
+ public PDFASSignatureExtractor buildBlindSignaturInterface(X509Certificate certificate, String filter,
+ String subfilter, Calendar date) {
+ return new SignatureDataExtractor(certificate, filter, subfilter, date);
+ }
+
+ @Override
+ public void checkPDFPermissions(PDFObject genericPdfObject) throws PdfAsException {
+ if (!(genericPdfObject instanceof PDFBOXObject)) {
+ // tODO:
+ throw new PdfAsException();
}
- private String getPDFAVersion(PDDocument doc) {
- try {
- PDDocumentCatalog cat = doc.getDocumentCatalog();
- PDMetadata metadata = cat.getMetadata();
-
- if (metadata != null) {
- DomXmpParser xmpParser = new DomXmpParser();
- XMPMetadata xmpMetadata = xmpParser.parse(metadata.exportXMPMetadata());
- if (xmpMetadata != null) {
- PDFAIdentificationSchema pdfaIdentificationSchema = xmpMetadata.getPDFIdentificationSchema();
- if (pdfaIdentificationSchema != null) {
- Integer pdfaversion = pdfaIdentificationSchema.getPart();
- String conformance = pdfaIdentificationSchema.getConformance();
- logger.info("Detected PDF/A Version: {} - {}", pdfaversion, conformance);
-
- if (pdfaversion != null) {
- return String.valueOf(pdfaversion);
- }
- }
- }
+ final PDFBOXObject pdfObject = (PDFBOXObject) genericPdfObject;
+ PdfBoxUtils.checkPDFPermissions(pdfObject.getDocument());
+ }
+
+ @Override
+ public byte[] rewritePlainSignature(byte[] plainSignature) {
+ final String signature = new COSString(plainSignature).toHexString();
+ final byte[] pdfSignature = signature.getBytes();
+ return pdfSignature;
+ }
+
+ @Override
+ public Image generateVisibleSignaturePreview(SignParameter parameter,
+ java.security.cert.X509Certificate cert,
+ int resolution, OperationStatus status, RequestedSignature requestedSignature) throws PDFASError {
+ try {
+
+ final PDFBOXObject pdfObject = (PDFBOXObject) status.getPdfObject();
+ final PDDocument origDoc = new PDDocument();
+
+ origDoc.addPage(new PDPage(PDRectangle.A4));
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ origDoc.save(baos);
+ baos.close();
+
+ pdfObject.setOriginalDocument(new ByteArrayDataSource(baos.toByteArray()));
+
+ final SignatureProfileSettings signatureProfileSettings = TableFactory
+ .createProfile(requestedSignature.getSignatureProfileID(), pdfObject.getStatus().getSettings());
+
+ // create Table describtion
+ final Table main = TableFactory.createSigTable(signatureProfileSettings, MAIN, pdfObject.getStatus(),
+ requestedSignature);
+
+ final IPDFStamper stamper = StamperFactory.createDefaultStamper(pdfObject.getStatus().getSettings());
+
+ final IPDFVisualObject visualObject = stamper.createVisualPDFObject(pdfObject, main);
+
+ final SignatureProfileConfiguration signatureProfileConfiguration = pdfObject.getStatus()
+ .getSignatureProfileConfiguration(requestedSignature.getSignatureProfileID());
+
+ final String signaturePosString = signatureProfileConfiguration.getDefaultPositioning();
+ PositioningInstruction positioningInstruction;
+ if (signaturePosString != null) {
+ positioningInstruction = Positioning.determineTablePositioning(new TablePos(signaturePosString), "",
+ origDoc, visualObject, pdfObject.getStatus().getSettings());
+ } else {
+ positioningInstruction = Positioning.determineTablePositioning(new TablePos(), "", origDoc,
+ visualObject, pdfObject.getStatus().getSettings());
+ }
+
+ origDoc.close();
+
+ final SignaturePositionImpl position = new SignaturePositionImpl();
+ position.setX(positioningInstruction.getX());
+ position.setY(positioningInstruction.getY());
+ position.setPage(positioningInstruction.getPage());
+ position.setHeight(visualObject.getHeight());
+ position.setWidth(visualObject.getWidth());
+
+ requestedSignature.setSignaturePosition(position);
+
+ final PDFAsVisualSignatureProperties properties = new PDFAsVisualSignatureProperties(
+ pdfObject.getStatus().getSettings(), pdfObject, (PdfBoxVisualObject) visualObject,
+ positioningInstruction, signatureProfileSettings);
+
+ properties.buildSignature();
+ PDDocument visualDoc;
+ synchronized (PDDocument.class) {
+ visualDoc = PDDocument.load(properties.getVisibleSignature());
+ }
+ // PDPageable pageable = new PDPageable(visualDoc);
+
+ final PDPage firstPage = visualDoc.getDocumentCatalog().getPages().get(0);
+
+ final float stdRes = 72;
+ final float targetRes = resolution;
+ final float factor = targetRes / stdRes;
+
+ final int targetPageNumber = 0;// TODO: is this always the case
+ PDFRenderer pdfRenderer = new PDFRenderer(visualDoc);
+ final BufferedImage outputImage = pdfRenderer.renderImageWithDPI(targetPageNumber, targetRes,
+ ImageType.ARGB);
+
+ visualDoc.close();
+ pdfRenderer = null;
+
+ final BufferedImage cutOut = new BufferedImage((int) (position.getWidth() * factor),
+ (int) (position.getHeight() * factor), BufferedImage.TYPE_4BYTE_ABGR);
+
+ final Graphics2D graphics = (Graphics2D) cutOut.getGraphics();
+
+ graphics.drawImage(outputImage, 0, 0, cutOut.getWidth(), cutOut.getHeight(), (int) (1 * factor),
+ (int) (outputImage.getHeight() - (position.getHeight() + 1) * factor),
+ (int) ((1 + position.getWidth()) * factor), (int) (outputImage.getHeight()
+ - (position.getHeight() + 1) * factor + position.getHeight() * factor),
+ null);
+ return cutOut;
+ } catch (final PdfAsException e) {
+ logger.warn("PDF-AS Exception", e);
+ throw ErrorExtractor.searchPdfAsError(e, status);
+ } catch (final Throwable e) {
+ logger.warn("Unexpected Throwable Exception", e);
+ throw ErrorExtractor.searchPdfAsError(e, status);
+ }
+ }
+
+ private String getPDFAVersion(PDDocument doc) {
+ try {
+ final PDDocumentCatalog cat = doc.getDocumentCatalog();
+ final PDMetadata metadata = cat.getMetadata();
+
+ if (metadata != null) {
+ final DomXmpParser xmpParser = new DomXmpParser();
+ final XMPMetadata xmpMetadata = xmpParser.parse(metadata.exportXMPMetadata());
+ if (xmpMetadata != null) {
+ final PDFAIdentificationSchema pdfaIdentificationSchema = xmpMetadata.getPDFIdentificationSchema();
+ if (pdfaIdentificationSchema != null) {
+ final Integer pdfaversion = pdfaIdentificationSchema.getPart();
+ final String conformance = pdfaIdentificationSchema.getConformance();
+ logger.info("Detected PDF/A Version: {} - {}", pdfaversion, conformance);
+
+ if (pdfaversion != null) {
+ return String.valueOf(pdfaversion);
}
- } catch (Throwable e) {
- logger.warn("Failed to determine PDF/A Version!", e);
+ }
}
- return null;
+ }
+ } catch (final Throwable e) {
+ logger.warn("Failed to determine PDF/A Version!", e);
}
-
- // Find an existing signature.
- private PDSignature findExistingSignature(PDDocument doc, String sigFieldName) {
- PDSignature signature = null;
- PDSignatureField signatureField;
- PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
- if (acroForm != null) {
- signatureField = (PDSignatureField) acroForm.getField(sigFieldName);
- if (signatureField != null) {
- // retrieve signature dictionary
- signature = signatureField.getSignature();
- if (signature == null) {
- signature = new PDSignature();
- signatureField.getCOSObject().setItem(COSName.V, signature);
- }
- else {
- throw new IllegalStateException("The signature field " + sigFieldName + " is already signed.");
- }
- }
- }
- return signature;
- }
-
- private List<String> existingSignatureLocations(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;
- }
-
- //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())) {
- SignaturePlaceholderData spd = placeholders.get(i);
- if (spd.getId() != null) {
- if(result == null) {
- result = spd;
- } else {
- 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;
- }
- }
- }
- }
- }
- }
- }
- return result;
- }
-
-
- //find first placeholder_id
- public List<SignaturePlaceholderData> listAvailablePlaceholders(List<SignaturePlaceholderData> placeholders, List<String> existingPlaceholders) {
- 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();
- if (numbers == null)
- {
- numbers = new LinkedHashMap<>();
+ return null;
+ }
+
+ // Find an existing signature.
+ private PDSignature findExistingSignature(PDDocument doc, String sigFieldName) {
+ PDSignature signature = null;
+ PDSignatureField signatureField;
+ final PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
+ if (acroForm != null) {
+ signatureField = (PDSignatureField) acroForm.getField(sigFieldName);
+ if (signatureField != null) {
+ // retrieve signature dictionary
+ signature = signatureField.getSignature();
+ if (signature == null) {
+ signature = new PDSignature();
+ signatureField.getCOSObject().setItem(COSName.V, signature);
+ } else {
+ throw new IllegalStateException("The signature field " + sigFieldName + " is already signed.");
}
- else
- {
- // must copy because the map is read only
- numbers = new LinkedHashMap<>(numbers);
+ }
+ }
+ 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());
}
- List<PDNumberTreeNode> kids = tree.getKids();
- if (kids != null)
- {
- for (PDNumberTreeNode kid : kids)
- {
- numbers.putAll(getNumberTreeAsMap(kid));
+ }
+ } 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 numbers;
+ }
}
-
-}
+ 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();
+ if (numbers == null) {
+ numbers = new LinkedHashMap<>();
+ } else {
+ // must copy because the map is read only
+ numbers = new LinkedHashMap<>(numbers);
+ }
+ final List<PDNumberTreeNode> kids = tree.getKids();
+ if (kids != null) {
+ for (final PDNumberTreeNode kid : kids) {
+ numbers.putAll(getNumberTreeAsMap(kid));
+ }
+ }
+ return numbers;
+ }
+
+} \ No newline at end of file