diff options
author | Thomas <> | 2023-01-12 12:58:24 +0100 |
---|---|---|
committer | Thomas <> | 2023-01-12 12:58:24 +0100 |
commit | e0ac5d79c01e458eeb5eb4233f8a0360db878911 (patch) | |
tree | e1760ce016e3f6c8eb0563bff175bf75a90afde0 /pdf-as-pdfbox-2/src/main | |
parent | e69ba716ebae7307645ff9c640967d58ef48b9f4 (diff) | |
download | pdf-as-4-e0ac5d79c01e458eeb5eb4233f8a0360db878911.tar.gz pdf-as-4-e0ac5d79c01e458eeb5eb4233f8a0360db878911.tar.bz2 pdf-as-4-e0ac5d79c01e458eeb5eb4233f8a0360db878911.zip |
feat(signatureblock): optimize processing for signed documents with less space for signature block
Issue #73
add configuration property to stop signing process if
document has less space for new signature block and
new page is not allowed because document is already signed
Diffstat (limited to 'pdf-as-pdfbox-2/src/main')
-rw-r--r-- | pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox2/positioning/Positioning.java | 619 |
1 files changed, 316 insertions, 303 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; + } + } |