diff options
Diffstat (limited to 'pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox')
6 files changed, 804 insertions, 0 deletions
diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/PDFBOXBackend.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/PDFBOXBackend.java new file mode 100644 index 00000000..ba1e0088 --- /dev/null +++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/PDFBOXBackend.java @@ -0,0 +1,40 @@ +package at.gv.egiz.pdfas.lib.impl.pdfbox; + +import at.gv.egiz.pdfas.lib.backend.PDFASBackend; +import at.gv.egiz.pdfas.lib.impl.pdfbox.placeholder.PDFBoxPlaceholderExtractor; +import at.gv.egiz.pdfas.lib.impl.placeholder.PlaceholderExtractor; +import at.gv.egiz.pdfas.lib.impl.signing.IPdfSigner; +import at.gv.egiz.pdfas.lib.impl.signing.pdfbox.PADESPDFBOXSigner; +import at.gv.egiz.pdfas.lib.impl.verify.VerifyBackend; +import at.gv.egiz.pdfas.lib.impl.verify.pdfbox.PDFBOXVerifier; + +public class PDFBOXBackend implements PDFASBackend { + + private static final String NAME = "PDFBOX_BACKEND"; + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean usedAsDefault() { + return true; + } + + @Override + public IPdfSigner getPdfSigner() { + return new PADESPDFBOXSigner(); + } + + @Override + public PlaceholderExtractor getPlaceholderExtractor() { + return new PDFBoxPlaceholderExtractor(); + } + + @Override + public VerifyBackend getVerifier() { + return new PDFBOXVerifier(); + } + +} diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/PDFBOXObject.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/PDFBOXObject.java new file mode 100644 index 00000000..f80df075 --- /dev/null +++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/PDFBOXObject.java @@ -0,0 +1,59 @@ +package at.gv.egiz.pdfas.lib.impl.pdfbox; + +import java.io.IOException; + +import javax.activation.DataSource; + +import org.apache.pdfbox.pdmodel.PDDocument; + +import at.gv.egiz.pdfas.lib.impl.status.OperationStatus; +import at.gv.egiz.pdfas.lib.impl.status.PDFObject; + +public class PDFBOXObject extends PDFObject { + + private PDDocument doc; + + public PDFBOXObject(OperationStatus operationStatus) { + super(operationStatus); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + if(doc != null) { + doc.close(); + } + } + + public void close() { + if(doc != null) { + try { + doc.close(); + //System.gc(); + } catch(Throwable e) { + // ignore! + } + doc = null; + } + } + + public void setOriginalDocument(DataSource originalDocument) throws IOException { + this.originalDocument = originalDocument; + if(doc != null) { + doc.close(); + } + this.doc = PDDocument.load(this.originalDocument.getInputStream()); + if(this.doc != null) { + this.doc.getDocument().setWarnMissingClose(false); + } + } + + public PDDocument getDocument() { + return this.doc; + } + + @Override + public String getPDFVersion() { + return String.valueOf(getDocument().getDocument().getVersion()); + } +} diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/placeholder/PDFBoxPlaceholderExtractor.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/placeholder/PDFBoxPlaceholderExtractor.java new file mode 100644 index 00000000..18099b23 --- /dev/null +++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/placeholder/PDFBoxPlaceholderExtractor.java @@ -0,0 +1,24 @@ +package at.gv.egiz.pdfas.lib.impl.pdfbox.placeholder; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.lib.impl.pdfbox.PDFBOXObject; +import at.gv.egiz.pdfas.lib.impl.placeholder.PlaceholderExtractor; +import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData; +import at.gv.egiz.pdfas.lib.impl.status.PDFObject; + +public class PDFBoxPlaceholderExtractor implements PlaceholderExtractor { + + @Override + public SignaturePlaceholderData extract(PDFObject doc, + String placeholderId, int matchMode) throws PdfAsException { + + if (doc instanceof PDFBOXObject) { + PDFBOXObject object = (PDFBOXObject) doc; + return SignaturePlaceholderExtractor.extract(object.getDocument(), + placeholderId, matchMode); + } + + throw new PdfAsException("INVALID STATE"); + } + +} diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/placeholder/SignaturePlaceholderExtractor.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/placeholder/SignaturePlaceholderExtractor.java new file mode 100644 index 00000000..fe1c0ee7 --- /dev/null +++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/placeholder/SignaturePlaceholderExtractor.java @@ -0,0 +1,371 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * 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 + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +/** + * <copyright> Copyright 2006 by Know-Center, Graz, Austria </copyright> + * 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 + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.gv.egiz.pdfas.lib.impl.pdfbox.placeholder; + +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.exceptions.WrappedIOException; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObject; +import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectImage; +import org.apache.pdfbox.util.Matrix; +import org.apache.pdfbox.util.PDFOperator; +import org.apache.pdfbox.util.PDFStreamEngine; +import org.apache.pdfbox.util.ResourceLoader; + +import at.gv.egiz.pdfas.common.exceptions.PDFIOException; +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.common.exceptions.PlaceholderExtractionException; +import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderContext; +import at.gv.egiz.pdfas.lib.impl.placeholder.SignaturePlaceholderData; +import at.knowcenter.wag.egov.egiz.pdf.TablePos; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.BinaryBitmap; +import com.google.zxing.DecodeHintType; +import com.google.zxing.LuminanceSource; +import com.google.zxing.MultiFormatReader; +import com.google.zxing.NotFoundException; +import com.google.zxing.ReaderException; +import com.google.zxing.Result; +import com.google.zxing.client.j2se.BufferedImageLuminanceSource; +import com.google.zxing.common.HybridBinarizer; + +/** + * Extract all relevant information from a placeholder image. + * + * @author exthex + * + */ +public class SignaturePlaceholderExtractor extends PDFStreamEngine { + /** + * The log. + */ + private static Log log = LogFactory + .getLog(SignaturePlaceholderExtractor.class); + + public static final String QR_PLACEHOLDER_IDENTIFIER = "PDF-AS-POS"; + public static final int PLACEHOLDER_MATCH_MODE_STRICT = 0; + public static final int PLACEHOLDER_MATCH_MODE_MODERATE = 1; + public static final int PLACEHOLDER_MATCH_MODE_LENIENT = 2; + + private List<SignaturePlaceholderData> placeholders = new Vector<SignaturePlaceholderData>(); + private int currentPage = 0; + + private SignaturePlaceholderExtractor(String placeholderId, + int placeholderMatchMode) throws IOException { + super(ResourceLoader.loadProperties( + "placeholder/pdfbox-reader.properties", true)); + } + + /** + * Search the document for placeholder images and possibly included + * additional info.<br/> + * Searches only for the first placeholder page after page from top. + * + * @param inputStream + * @return all available info from the first found placeholder. + * @throws PDFDocumentException + * if the document could not be read. + * @throws PlaceholderExtractionException + * if STRICT matching mode was requested and no suitable + * placeholder could be found. + */ + public static SignaturePlaceholderData extract(PDDocument doc, + String placeholderId, int matchMode) throws PdfAsException { + SignaturePlaceholderContext.setSignaturePlaceholderData(null); + + SignaturePlaceholderExtractor extractor; + try { + extractor = new SignaturePlaceholderExtractor(placeholderId, + matchMode); + } catch (IOException e2) { + throw new PDFIOException("error.pdf.io.04", e2); + } + List<?> pages = doc.getDocumentCatalog().getAllPages(); + Iterator<?> iter = pages.iterator(); + int pageNr = 0; + while (iter.hasNext()) { + pageNr++; + PDPage page = (PDPage) iter.next(); + try { + extractor.setCurrentPage(pageNr); + extractor.processStream(page, page.findResources(), page + .getContents().getStream()); + SignaturePlaceholderData ret = matchPlaceholderPage( + extractor.placeholders, placeholderId, matchMode); + if (ret != null) { + SignaturePlaceholderContext + .setSignaturePlaceholderData(ret); + return ret; + } + } catch (IOException e1) { + throw new PDFIOException("error.pdf.io.04", e1); + } + + } + if (extractor.placeholders.size() > 0) { + SignaturePlaceholderData ret = matchPlaceholderDocument( + extractor.placeholders, placeholderId, matchMode); + SignaturePlaceholderContext.setSignaturePlaceholderData(ret); + return ret; + } + // no placeholders found, apply strict mode if set + if (matchMode == PLACEHOLDER_MATCH_MODE_STRICT) { + throw new PlaceholderExtractionException("error.pdf.stamp.09"); + } + + return null; + } + + private static SignaturePlaceholderData matchPlaceholderDocument( + List<SignaturePlaceholderData> placeholders, String placeholderId, + int matchMode) throws PlaceholderExtractionException { + + if (matchMode == PLACEHOLDER_MATCH_MODE_STRICT) + throw new PlaceholderExtractionException("error.pdf.stamp.09"); + + if (placeholders.size() == 0) + return null; + + for (int i = 0; i < placeholders.size(); i++) { + SignaturePlaceholderData spd = placeholders.get(i); + if (spd.getId() == null) + return spd; + } + + if (matchMode == PLACEHOLDER_MATCH_MODE_LENIENT) + return placeholders.get(0); + + return null; + } + + private static SignaturePlaceholderData matchPlaceholderPage( + List<SignaturePlaceholderData> placeholders, String placeholderId, + int matchMode) { + if (placeholders.size() == 0) + return null; + for (int i = 0; i < placeholders.size(); i++) { + SignaturePlaceholderData data = placeholders.get(i); + if (placeholderId != null && placeholderId.equals(data.getId())) + return data; + if (placeholderId == null && data.getId() == null) + return data; + } + return null; + } + + private void setCurrentPage(int pageNr) { + this.currentPage = pageNr; + } + + @Override + protected void processOperator(PDFOperator operator, List<COSBase> arguments) + throws IOException { + String operation = operator.getOperation(); + if (operation.equals("Do")) { + COSName objectName = (COSName) arguments.get(0); + Map<?, ?> xobjects = getResources().getXObjects(); + PDXObject xobject = (PDXObject) xobjects.get(objectName.getName()); + if (xobject instanceof PDXObjectImage) { + try { + PDXObjectImage image = (PDXObjectImage) xobject; + SignaturePlaceholderData data = checkImage(image); + if (data != null) { + PDPage page = getCurrentPage(); + Matrix ctm = getGraphicsState() + .getCurrentTransformationMatrix(); + double rotationInRadians = (page.findRotation() * Math.PI) / 180; + + AffineTransform rotation = new AffineTransform(); + rotation.setToRotation(rotationInRadians); + AffineTransform rotationInverse = rotation + .createInverse(); + Matrix rotationInverseMatrix = new Matrix(); + rotationInverseMatrix + .setFromAffineTransform(rotationInverse); + Matrix rotationMatrix = new Matrix(); + rotationMatrix.setFromAffineTransform(rotation); + + Matrix unrotatedCTM = ctm + .multiply(rotationInverseMatrix); + + float x = unrotatedCTM.getXPosition(); + float y = unrotatedCTM.getYPosition() + + unrotatedCTM.getYScale(); + float w = unrotatedCTM.getXScale(); + + String posString = "p:" + currentPage + ";x:" + x + + ";y:" + y + ";w:" + w; + try { + data.setTablePos(new TablePos(posString)); + data.setPlaceholderName(objectName.getName()); + placeholders.add(data); + } catch (PdfAsException e) { + throw new WrappedIOException(e); + } + } + } catch (NoninvertibleTransformException e) { + throw new WrappedIOException(e); + } + } + } else { + super.processOperator(operator, arguments); + } + } + + /** + * Checks an image if it is a placeholder for a signature. + * + * @param image + * @return + * @throws IOException + */ + private SignaturePlaceholderData checkImage(PDXObjectImage image) + throws IOException { + BufferedImage bimg = image.getRGBImage(); + if (bimg == null) { + String type = image.getSuffix(); + if (type != null) { + type = type.toUpperCase() + " images"; + } else { + type = "Image type"; + } + log.info("Unable to extract image for QRCode analysis. " + + type + + " not supported. Add additional JAI Image filters to your classpath. Refer to https://jai.dev.java.net. Skipping image."); + return null; + } + if (bimg.getHeight() < 10 || bimg.getWidth() < 10) { + log.debug("Image too small for QRCode. Skipping image."); + return null; + } + + LuminanceSource source = new BufferedImageLuminanceSource(bimg); + BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); + Result result; + long before = System.currentTimeMillis(); + try { + Hashtable<DecodeHintType, Vector<BarcodeFormat>> hints = new Hashtable<DecodeHintType, Vector<BarcodeFormat>>(); + Vector<BarcodeFormat> formats = new Vector<BarcodeFormat>(); + formats.add(BarcodeFormat.QR_CODE); + hints.put(DecodeHintType.POSSIBLE_FORMATS, formats); + result = new MultiFormatReader().decode(bitmap, hints); + + String text = result.getText(); + String profile = null; + String type = null; + String sigKey = null; + String id = null; + if (text != null) { + if (text.startsWith(QR_PLACEHOLDER_IDENTIFIER)) { + String[] data = text.split(";"); + if (data.length > 1) { + for (int i = 1; i < data.length; i++) { + String kvPair = data[i]; + String[] kv = kvPair.split("="); + if (kv.length != 2) { + log.debug("Invalid parameter in placeholder data: " + + kvPair); + } else { + if (kv[0] + .equalsIgnoreCase(SignaturePlaceholderData.ID_KEY)) { + id = kv[1]; + } else if (kv[0] + .equalsIgnoreCase(SignaturePlaceholderData.PROFILE_KEY)) { + profile = kv[1]; + } else if (kv[0] + .equalsIgnoreCase(SignaturePlaceholderData.SIG_KEY_KEY)) { + sigKey = kv[1]; + } else if (kv[0] + .equalsIgnoreCase(SignaturePlaceholderData.TYPE_KEY)) { + type = kv[1]; + } + } + } + } + return new SignaturePlaceholderData(profile, type, sigKey, + id); + } else { + log.warn("QR-Code found but does not start with \"" + + QR_PLACEHOLDER_IDENTIFIER + + "\". Ignoring QR placeholder."); + } + } + } catch (ReaderException re) { + if (log.isDebugEnabled()) { + log.debug("Could not decode - not a placeholder. needed: " + + (System.currentTimeMillis() - before)); + } + if (!(re instanceof NotFoundException)) { + if (log.isInfoEnabled()) { + log.info("Failed to decode image", re); + } + } + } catch (ArrayIndexOutOfBoundsException e) { + if (log.isInfoEnabled()) { + log.info("Failed to decode image. Probably a zxing bug", e); + } + } + return null; + } + +} diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/positioning/Positioning.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/positioning/Positioning.java new file mode 100644 index 00000000..4efa2148 --- /dev/null +++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/positioning/Positioning.java @@ -0,0 +1,237 @@ +/******************************************************************************* + * <copyright> Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria </copyright> + * 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 + * that you distribute must include a readable copy of the "NOTICE" text file. + ******************************************************************************/ +package at.gv.egiz.pdfas.lib.impl.pdfbox.positioning; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.common.PDRectangle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.lib.impl.pdfbox.utils.PdfBoxUtils; +import at.gv.egiz.pdfas.lib.impl.stamping.IPDFVisualObject; +import at.knowcenter.wag.egov.egiz.pdf.PDFUtilities; +import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction; +import at.knowcenter.wag.egov.egiz.pdf.TablePos; + +/** + * Created with IntelliJ IDEA. User: afitzek Date: 8/29/13 Time: 4:30 PM To + * change this template use File | Settings | File Templates. + */ +public class Positioning { + + 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. + * @return Returns the PositioningInformation. + * @throws PdfAsException + * F.e. + */ + public static PositioningInstruction determineTablePositioning( + TablePos pos, String signature_type, PDDocument pdfDataSource, + IPDFVisualObject pdf_table, boolean legacy32) throws PdfAsException { + return adjustSignatureTableandCalculatePosition(pdfDataSource, + pdf_table, pos, legacy32); + } + + /** + * Sets the width of the table according to the layout of the document and + * calculates the y position where the PDFPTable should be placed. + * + * @param pdfDataSource + * The PDF document. + * @param pdf_table + * The PDFPTable to be placed. + * @return Returns the position where the PDFPTable should be placed. + * @throws PdfAsException + * F.e. + */ + public static PositioningInstruction adjustSignatureTableandCalculatePosition( + final PDDocument pdfDataSource, IPDFVisualObject pdf_table, + TablePos pos, boolean legacy32) throws PdfAsException { + + PdfBoxUtils.checkPDFPermissions(pdfDataSource); + // get pages of currentdocument + + int doc_pages = pdfDataSource.getNumberOfPages(); + int page = doc_pages; + boolean make_new_page = pos.isNewPage(); + if (!(pos.isNewPage() || pos.isPauto())) { + // we should posit signaturtable on this page + + page = pos.getPage(); + // System.out.println("XXXXPAGE="+page+" doc_pages="+doc_pages); + if (page > doc_pages) { + make_new_page = true; + page = doc_pages; + // throw new PDFDocumentException(227, "Page number is to big(=" + // + page+ + // ") cannot be parsed."); + } + } + + PDPage pdPage = (PDPage) pdfDataSource.getDocumentCatalog() + .getAllPages().get(page - 1); + PDRectangle cropBox = pdPage.getCropBox(); + + // fallback to MediaBox if Cropbox not available! + + if (cropBox == null) { + cropBox = pdPage.findCropBox(); + } + + if (cropBox == null) { + cropBox = pdPage.findMediaBox(); + } + + // getPagedimensions + // Rectangle psize = reader.getPageSizeWithRotation(page); + // int page_rotation = reader.getPageRotation(page); + + // Integer rotation = pdPage.getRotation(); + // int page_rotation = rotation.intValue(); + + float page_width = cropBox.getWidth(); + float page_height = cropBox.getHeight(); + + // now we can calculate x-position + float pre_pos_x = SIGNATURE_MARGIN_HORIZONTAL; + if (!pos.isXauto()) { + // we do have absolute x + pre_pos_x = pos.getPosX(); + } + // calculate width + // center + float pre_width = page_width - 2 * pre_pos_x; + if (!pos.isWauto()) { + // we do have absolute width + pre_width = pos.getWidth(); + if (pos.isXauto()) { // center x + pre_pos_x = (page_width - pre_width) / 2; + } + } + final float pos_x = pre_pos_x; + final float width = pre_width; + // Signatur table dimensions are complete + pdf_table.setWidth(width); + pdf_table.fixWidth(); + // pdf_table.setTotalWidth(width); + // pdf_table.setLockedWidth(true); + + final float table_height = pdf_table.getHeight(); + // now check pos_y + float pos_y = pos.getPosY(); + + // in case an absolute y position is already given OR + // if the table is related to an invisible signature + // there is no need for further calculations + // (fixed adding new page in case of invisible signatures) + if (!pos.isYauto() || table_height == 0) { + // we do have y-position too --> all parameters but page ok + if (make_new_page) { + page++; + } + return new PositioningInstruction(make_new_page, page, pos_x, + pos_y, pos.rotation); + } + // pos_y is auto + if (make_new_page) { + // ignore footer in new page + page++; + pos_y = page_height - SIGNATURE_MARGIN_VERTICAL; + return new PositioningInstruction(make_new_page, page, pos_x, + pos_y, pos.rotation); + } + // up to here no checks have to be made if Tablesize and Pagesize are + // fit + // Now we have to getfreespace in page and reguard footerline + float footer_line = pos.getFooterLine(); + + float pre_page_length = PDFUtilities.calculatePageLength(pdfDataSource, + page - 1, page_height - footer_line, /* page_rotation, */ + legacy32); + + if (pre_page_length == Float.NEGATIVE_INFINITY) { + // we do have an empty page or nothing in area above footerline + pre_page_length = page_height; + // no text --> SIGNATURE_BORDER + pos_y = page_height - SIGNATURE_MARGIN_VERTICAL; + if (pos_y - footer_line <= table_height) { + make_new_page = true; + if (!pos.isPauto()) { + // we have to correct pagenumber + page = pdfDataSource.getNumberOfPages(); + } + page++; + // no text --> SIGNATURE_BORDER + pos_y = page_height - SIGNATURE_MARGIN_VERTICAL; + } + return new PositioningInstruction(make_new_page, page, pos_x, + pos_y, pos.rotation); + } + final float page_length = pre_page_length; + // we do have text take SIGNATURE_MARGIN + pos_y = page_height - page_length - SIGNATURE_MARGIN_VERTICAL; + if (pos_y - footer_line <= table_height) { + make_new_page = true; + if (!pos.isPauto()) { + // we have to correct pagenumber in case of absolute page and + // not enough + // space + page = pdfDataSource.getNumberOfPages(); + } + page++; + // no text --> SIGNATURE_BORDER + pos_y = page_height - SIGNATURE_MARGIN_VERTICAL; + } + return new PositioningInstruction(make_new_page, page, pos_x, pos_y, + pos.rotation); + + } + +} diff --git a/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/utils/PdfBoxUtils.java b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/utils/PdfBoxUtils.java new file mode 100644 index 00000000..01501f97 --- /dev/null +++ b/pdf-as-pdfbox/src/main/java/at/gv/egiz/pdfas/lib/impl/pdfbox/utils/PdfBoxUtils.java @@ -0,0 +1,73 @@ +package at.gv.egiz.pdfas.lib.impl.pdfbox.utils; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.encryption.AccessPermission; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsValidationException; + +public class PdfBoxUtils { + private static final Logger logger = LoggerFactory + .getLogger(PdfBoxUtils.class); + + public static void checkPDFPermissions(PDDocument doc) + throws PdfAsValidationException { + + AccessPermission accessPermission = doc.getCurrentAccessPermission(); + if (doc.isEncrypted()) { + throw new PdfAsValidationException("error.pdf.sig.12", null); + } + + if (!accessPermission.isOwnerPermission()) { + throw new PdfAsValidationException("error.pdf.sig.12", null); + } + + } + + public static int countSignatures(PDDocument doc, String sigName) { + int count = 0; + COSDictionary trailer = doc.getDocument().getTrailer(); + COSDictionary root = (COSDictionary) trailer + .getDictionaryObject(COSName.ROOT); + COSDictionary acroForm = (COSDictionary) root + .getDictionaryObject(COSName.ACRO_FORM); + COSArray fields = (COSArray) acroForm + .getDictionaryObject(COSName.FIELDS); + for (int i = 0; i < fields.size(); i++) { + COSDictionary field = (COSDictionary) fields.getObject(i); + String type = field.getNameAsString("FT"); + if ("Sig".equals(type)) { + String name = field.getString(COSName.T); + if (name != null) { + logger.debug("Found Sig: " + name); + try { + if (name.startsWith(sigName)) { + String numberString = name.replace(sigName, ""); + + logger.debug("Found Number: " + numberString); + + int SigIDX = Integer.parseInt(numberString); + if (SigIDX > count) { + count = SigIDX; + } + } + } catch (Throwable e) { + logger.info("Found a different Signature, we do not need to count this."); + } + } + } + + } + + count++; + + logger.debug("Returning sig number: " + count); + + return count; + } + +} |