From 65eb7054679d63a4f519bd31eb37805666e6fef8 Mon Sep 17 00:00:00 2001 From: Andreas Fitzek Date: Mon, 12 Dec 2016 07:50:25 +0100 Subject: added PDF/A-3b support, and PDF/A version detection --- pdf-as-pdfbox-2/build.gradle | 1 + .../impl/signing/pdfbox2/PADESPDFBOXSigner.java | 40 +++++++++++++++++- .../stamping/pdfbox2/PDFAsTemplateCreator.java | 37 ++++++++++++++--- .../pdfbox2/PDFAsVisualSignatureBuilder.java | 48 +++++++++++++++++----- .../pdfbox2/PDFAsVisualSignatureProperties.java | 2 +- .../lib/impl/stamping/pdfbox2/PDFBoxFont.java | 15 ++++++- .../lib/impl/stamping/pdfbox2/TableDrawUtils.java | 16 ++++++-- 7 files changed, 134 insertions(+), 25 deletions(-) (limited to 'pdf-as-pdfbox-2') diff --git a/pdf-as-pdfbox-2/build.gradle b/pdf-as-pdfbox-2/build.gradle index 970ca038..c5d7d41e 100644 --- a/pdf-as-pdfbox-2/build.gradle +++ b/pdf-as-pdfbox-2/build.gradle @@ -16,6 +16,7 @@ jar { repositories { + mavenLocal() mavenCentral() } 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 8965ce0e..901e47db 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 @@ -48,6 +48,7 @@ import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocumentCatalog; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.common.PDMetadata; import org.apache.pdfbox.pdmodel.common.PDNumberTreeNode; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureElement; @@ -68,6 +69,9 @@ 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; @@ -132,6 +136,8 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { PDFASPDFBOXSignatureInterface signer = (PDFASPDFBOXSignatureInterface) genericSigner; + String pdfaVersion = null; + PDDocument doc = null; SignatureOptions options = new SignatureOptions(); COSDocument visualSignatureDocumentGuard = null; @@ -221,6 +227,11 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { } 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"); @@ -352,7 +363,7 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { } - if (signatureProfileSettings.isPDFA()) { + if (signatureProfileSettings.isPDFA() || signatureProfileSettings.isPDFA3()) { PDDocumentCatalog root = doc.getDocumentCatalog(); COSBase base = root.getCOSObject().getItem(COSName.OUTPUT_INTENTS); if (base == null) { @@ -807,4 +818,31 @@ public class PADESPDFBOXSigner implements IPdfSigner, IConfigurationConstants { throw ErrorExtractor.searchPdfAsError(e, status); } } + + 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); + } + } + } + } + } catch (Throwable e) { + logger.warn("Failed to determine PDF/A Version!", e); + } + return null; + } } diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsTemplateCreator.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsTemplateCreator.java index b8c15119..b07e6ed5 100644 --- a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsTemplateCreator.java +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsTemplateCreator.java @@ -23,11 +23,10 @@ ******************************************************************************/ package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; +import at.gv.egiz.pdfas.common.settings.SignatureProfileSettings; +import at.gv.egiz.pdfas.lib.impl.stamping.TableFactory; import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; @@ -143,7 +142,9 @@ public class PDFAsTemplateCreator extends PDFTemplateCreator { null, innerFormName, properties); this.pdfBuilder.createVisualSignature(template); this.pdfBuilder.createWidgetDictionary(pdSignatureField, holderFormResources); - + + + ByteArrayInputStream in = null; //COSDocument doc = pdfStructure.getVisualSignature(); @@ -152,7 +153,31 @@ public class PDFAsTemplateCreator extends PDFTemplateCreator { ByteArrayOutputStream baos = new ByteArrayOutputStream(); template.save(baos); baos.close(); - in = new ByteArrayInputStream(baos.toByteArray()); + + SignatureProfileSettings signatureProfileSettings = + this.pdfBuilder.signatureProfileSettings; + + boolean requirePDFA3 = signatureProfileSettings.isPDFA3(); + + if(requirePDFA3) { + + //FileOutputStream fos = new FileOutputStream("/tmp/signature.pdf"); + //fos.write(baos.toByteArray()); + //fos.close(); + + PDDocument cidSetRemoved = PDDocument.load(baos.toByteArray()); + try { + this.pdfBuilder.removeCidSet(cidSetRemoved); + baos.reset(); + baos = new ByteArrayOutputStream(); + cidSetRemoved.save(baos); + baos.close(); + } finally { + cidSetRemoved.close(); + } + } + + in = new ByteArrayInputStream(baos.toByteArray()); logger.debug("stream returning started, size= " + in.available()); diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureBuilder.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureBuilder.java index a33a46e7..d283c3e3 100644 --- a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureBuilder.java +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureBuilder.java @@ -30,22 +30,15 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.MessageDigest; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +import at.gv.egiz.pdfas.common.settings.SignatureProfileSettings; import org.apache.commons.codec.binary.Hex; import org.apache.commons.io.IOUtils; import org.apache.pdfbox.cos.COSArray; import org.apache.pdfbox.cos.COSDictionary; import org.apache.pdfbox.cos.COSName; -import org.apache.pdfbox.pdfparser.PDFParser; -import org.apache.pdfbox.pdfparser.PDFStreamParser; -import org.apache.pdfbox.pdmodel.PDDocument; -import org.apache.pdfbox.pdmodel.PDPage; -import org.apache.pdfbox.pdmodel.PDPageContentStream; -import org.apache.pdfbox.pdmodel.PDResources; +import org.apache.pdfbox.pdmodel.*; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.common.PDStream; import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; @@ -59,6 +52,7 @@ import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDVisibleS 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.pdmodel.font.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -76,15 +70,18 @@ public class PDFAsVisualSignatureBuilder extends PDVisibleSigBuilder implements private PDFAsVisualSignatureProperties properties; private PDFAsVisualSignatureDesigner designer; private ISettings settings; + public SignatureProfileSettings signatureProfileSettings; private PDResources innerFormResources; private Map images = new HashMap(); public PDFAsVisualSignatureBuilder( PDFAsVisualSignatureProperties properties, ISettings settings, - PDFAsVisualSignatureDesigner designer) { + PDFAsVisualSignatureDesigner designer, + SignatureProfileSettings signatureProfileSettings) { this.properties = properties; this.settings = settings; this.designer = designer; + this.signatureProfileSettings = signatureProfileSettings; } @Override @@ -657,4 +654,33 @@ public class PDFAsVisualSignatureBuilder extends PDVisibleSigBuilder implements this.getStructure().getTemplate().close(); } + public void removeCidSet(PDDocument document) throws IOException { + + PDDocumentCatalog catalog = document.getDocumentCatalog(); + + COSName cidSet = COSName.getPDFName("CIDSet"); + + Iterator pdPageIterator = catalog.getPages().iterator(); + while(pdPageIterator.hasNext()) { + + PDPage page = pdPageIterator.next(); + + Iterator cosNameIterator = page.getResources().getFontNames().iterator(); + while (cosNameIterator.hasNext()) { + COSName fontName = cosNameIterator.next(); + PDFont pdFont = page.getResources().getFont(fontName); + + if (pdFont instanceof PDType0Font) { + PDType0Font typedFont = (PDType0Font) pdFont; + + if (typedFont.getDescendantFont() != null) { + if (typedFont.getDescendantFont().getFontDescriptor() != null) { + typedFont.getDescendantFont().getFontDescriptor().getCOSObject().removeItem(cidSet); + } + } + } + } + } + } + } diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureProperties.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureProperties.java index db96767a..adfd6694 100644 --- a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureProperties.java +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFAsVisualSignatureProperties.java @@ -112,7 +112,7 @@ public class PDFAsVisualSignatureProperties extends PDVisibleSigProperties { @Override public void buildSignature() throws IOException { - PDFAsVisualSignatureBuilder builder = new PDFAsVisualSignatureBuilder(this, this.settings, designer); + PDFAsVisualSignatureBuilder builder = new PDFAsVisualSignatureBuilder(this, this.settings, designer, this.signatureProfileSettings); PDFAsTemplateCreator creator = new PDFAsTemplateCreator(builder); try { setVisibleSignature(creator.buildPDF(designer, this.origDoc)); diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFBoxFont.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFBoxFont.java index 8795907d..9c848ff9 100644 --- a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFBoxFont.java +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/PDFBoxFont.java @@ -24,8 +24,11 @@ package at.gv.egiz.pdfas.lib.impl.stamping.pdfbox2; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import at.gv.egiz.pdfas.common.settings.SignatureProfileSettings; +import at.gv.egiz.pdfas.lib.impl.stamping.TableFactory; import org.apache.pdfbox.pdmodel.font.PDFont; import org.apache.pdfbox.pdmodel.font.PDType0Font; import org.apache.pdfbox.pdmodel.font.PDType1Font; @@ -158,6 +161,7 @@ public class PDFBoxFont { private PDFont generateTTF(String fonttype, PDFBOXObject pdfObject) throws IOException { + ttfFontDesc = fonttype; String fontName = fonttype.replaceFirst("TTF:", ""); String fontPath = this.settings.getWorkingDirectory() + File.separator @@ -165,6 +169,7 @@ public class PDFBoxFont { logger.debug("Font from: \"" + fontPath + "\"."); + PDFAsFontCache fontCache = pdfObject.getSigBlockFontCache(); if(fontCache.contains(fontPath)){ @@ -185,8 +190,14 @@ public class PDFBoxFont { // } logger.debug("Instantiating new font."); - - PDType0Font font = PDType0Font.load(pdfObject.getDocument(), new File(fontPath)); + +/* + SignatureProfileSettings signatureProfileSettings = TableFactory + .createProfile(pdfObject.getStatus().getRequestedSignature().getSignatureProfileID(), pdfObject.getStatus().getSettings()); + + boolean requirePDFA3 = signatureProfileSettings.isPDFA3(); +*/ + PDType0Font font = PDType0Font.load(pdfObject.getDocument(), new FileInputStream(fontPath)); fontCache.addFont(fontPath,font); return font; diff --git a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/TableDrawUtils.java b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/TableDrawUtils.java index 5162b287..df8d3e3b 100644 --- a/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/TableDrawUtils.java +++ b/pdf-as-pdfbox-2/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/pdfbox2/TableDrawUtils.java @@ -230,8 +230,10 @@ public class TableDrawUtils { drawDebugLineString(contentStream, tx, ty, maxWidth, textHeight, descent, settings); contentStream.beginText(); - - + + contentStream.setFont(textFont, fontSize); + contentStream.newLineAtOffset(tx, (ty - fontSize + (descent * (-1)))); + /* if (formResources.getFont(COSName.getPDFName(textFont.getName())) != null) { String fontID = getFontID(textFont, formResources); logger.debug("Using Font: " + fontID); @@ -246,11 +248,17 @@ public class TableDrawUtils { contentStream.moveTextPositionByAmount(tx, (ty - fontSize + (descent * (-1)))); contentStream.appendRawCommands(fontSize + " TL\n"); - + */ + + if(textFont.willBeSubset()) { + logger.debug("Font will be subset!"); + } + for (int k = 0; k < tlines.length; k++) { contentStream.showText(tlines[k]); if (k < tlines.length - 1) { - contentStream.appendRawCommands("T*\n"); + contentStream.newLineAtOffset(0, -1 * fontSize ); + //contentStream.appendRawCommands("T*\n"); } } -- cgit v1.2.3