Author: lehmi Date: Sat Jun 25 16:51:57 2011 New Revision: 1139570 URL: http://svn.apache.org/viewvc?rev=1139570&view=rev Log: PDFBOX-1031: added a preferred signature size to the SignatureOptions as proposed by Thomas Chojecki
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/SignatureOptions.java Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java?rev=1139570&r1=1139569&r2=1139570&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java Sat Jun 25 16:51:57 2011 @@ -391,12 +391,16 @@ public final class COSName extends COSBa public static final COSName DP = new COSName( "DP" ); /** - * A common COSName value. - */ + * A common COSName value. + */ + public static final COSName DR = new COSName( "DR" ); + /** + * A common COSName value. + */ public static final COSName DV = new COSName( "DV" ); /** - * A common COSName value. - */ + * A common COSName value. + */ public static final COSName DW = new COSName( "DW" ); /** @@ -537,6 +541,10 @@ public final class COSName extends COSBa /** * A common COSName value. */ + public static final COSName FORM = new COSName( "Form" ); + /** + * A common COSName value. + */ public static final COSName FORMTYPE = new COSName( "FormType" ); /** * A common COSName value. @@ -1193,6 +1201,10 @@ public final class COSName extends COSBa * A common COSName value. */ public static final COSName SIG = new COSName("Sig"); + /** + * A common COSName value. + */ + public static final COSName SIG_FLAGS = new COSName("SigFlags"); private String name; Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java?rev=1139570&r1=1139569&r2=1139570&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java Sat Jun 25 16:51:57 2011 @@ -82,19 +82,9 @@ import org.apache.pdfbox.pdmodel.interac */ public class PDDocument implements Pageable { - private static final String COSDictionary = null; private COSDocument document; - // NOTE BGUILLON: this property must be removed because it is - // not the responsability of this class to know - //private boolean encryptOnSave = false; - - - // NOTE BGUILLON: these properties are not used anymore. See getCurrentAccessPermission() instead - //private String encryptUserPassword = null; - //private String encryptOwnerPassword = null; - //cached values private PDDocumentInformation documentInformation; private PDDocumentCatalog documentCatalog; @@ -105,13 +95,6 @@ public class PDDocument implements Pagea private PDEncryptionDictionary encParameters = null; /** - * This will tell if the document was decrypted with the master password. - * NOTE BGUILLON: this property is not used anymore. See getCurrentAccessPermission() instead - */ - //private boolean decryptedWithOwnerPassword = false; - - - /** * The security handler used to decrypt / encrypt the document. */ private SecurityHandler securityHandler = null; @@ -166,8 +149,7 @@ public class PDDocument implements Pagea // or references to arrays which have references to pages // or references to arrays which have references to arrays which have references to pages // or ... (I think you get the idea...) - processListOfPageReferences(getDocumentCatalog().getPages().getKids()); - + processListOfPageReferences(getDocumentCatalog().getPages().getKids()); } private void processListOfPageReferences(List<Object> pageNodes) @@ -279,10 +261,9 @@ public class PDDocument implements Pagea public void addSignature(PDSignature sigObject, SignatureInterface signatureInterface) throws IOException, SignatureException { - SignatureOptions defaultOptions = new SignatureOptions(); - defaultOptions.setPage(1); - - addSignature(sigObject, signatureInterface,defaultOptions); + SignatureOptions defaultOptions = new SignatureOptions(); + defaultOptions.setPage(1); + addSignature(sigObject, signatureInterface,defaultOptions); } /** @@ -295,155 +276,175 @@ public class PDDocument implements Pagea */ public void addSignature(PDSignature sigObject, SignatureInterface signatureInterface, SignatureOptions options) throws IOException, SignatureException { - // Content reservieren - // Um auch grosse Zertifikatsketten unterbringen zu koennen, - // wird ein sehr grosser Bereich reserviert - sigObject.setContents(new byte[0x2500 * 2 + 2]); - - // ByteRange reservieren - sigObject.setByteRange(new int[] {0,1000000000,1000000000,1000000000}); - - getDocument().setSignatureInterface(signatureInterface); - - // ######################################### - // # SignatureForm fuer Signatur erstellen # - // # und mit an das Dokument haengen. # - // ######################################### - - // Erste Seite besorgen - PDDocumentCatalog root = getDocumentCatalog(); - PDPageNode rootPages = root.getPages(); - List<PDPage> kids = new ArrayList<PDPage>(); - rootPages.getAllKids(kids); - - int size = (int)rootPages.getCount(); - PDPage page = null; - if (size == 0) - throw new SignatureException(SignatureException.INVALID_PAGE_FOR_SIGNATURE, "The PDF file has no pages"); - if (options.getPage()>size) - page = kids.get(size-1); - else if(options.getPage()<=0) - page = kids.get(0); - else - page = kids.get(options.getPage()-1); + // Reserve content + // We need to reserve some space for the signature. Some signatures including + // big certificate chain and we need enough space to store it. + int preferedSignatureSize = options.getPreferedSignatureSize(); + if (preferedSignatureSize > 0) + { + sigObject.setContents(new byte[preferedSignatureSize * 2 + 2]); + } + else + { + sigObject.setContents(new byte[0x2500 * 2 + 2]); + } + + // Reserve ByteRange + sigObject.setByteRange(new int[] {0,1000000000,1000000000,1000000000}); - - // AcroForm aus dem Root dict besorgen und Annotation einfügen - PDAcroForm acroForm = root.getAcroForm(); - root.getCOSObject().setNeedToBeUpdate(true); - - if (acroForm==null) - { - acroForm = new PDAcroForm(this); - root.setAcroForm(acroForm); - } else - acroForm.getCOSObject().setNeedToBeUpdate(true); + getDocument().setSignatureInterface(signatureInterface); - /* - * For invisible signatures, the annotation has a rectangle array with values [ 0 0 0 0 ]. - * This annotation is usually attached to the viewed page when the signature is created. - * Despite not having an appearance, the annotation AP and N dictionaries may be present - * in some versions of Acrobat. If present, N references the DSBlankXObj (blank) XObject. - */ - - // Annotation / Field für die Signatur erzeugen - PDSignatureField signatureField = new PDSignatureField(acroForm); - signatureField.setSignature(sigObject); // Signaturobjekt vermerken - signatureField.getWidget().setPage(page); // Rückverkettung + // ######################################### + // # Create SignatureForm for signature # + // # and appending it to the document # + // ######################################### + + // Get the first page + PDDocumentCatalog root = getDocumentCatalog(); + PDPageNode rootPages = root.getPages(); + List<PDPage> kids = new ArrayList<PDPage>(); + rootPages.getAllKids(kids); + + int size = (int)rootPages.getCount(); + PDPage page = null; + if (size == 0) + { + throw new SignatureException(SignatureException.INVALID_PAGE_FOR_SIGNATURE, "The PDF file has no pages"); + } + if (options.getPage()>size) + { + page = kids.get(size-1); + } + else if(options.getPage()<=0) + { + page = kids.get(0); + } + else + { + page = kids.get(options.getPage()-1); + } - // AcroForm Fields setzen - List acroFormFields = acroForm.getFields(); - COSDictionary acroFormDict = acroForm.getDictionary(); - acroFormDict.setDirect(true); - acroFormDict.setInt("SigFlags", 3); - acroFormFields.add(signatureField); - - // Objekte aus der visuellen Signatur besorgen - COSDocument visualSignature = options.getVisualSignature(); - // Fallunterscheidung zwischen sichtbarer und unsichtbarer Signatur vorbereiten - if (visualSignature == null) // unsichtbare Signatur - { - // Rectangle fuer unsichtbare Signatur auf 0 0 0 0 setzen - signatureField.getWidget().setRectangle(new PDRectangle()); // rectangle array [ 0 0 0 0 ] - // AcroForm leere DefaultRessource setzen - acroFormDict.setItem("DR", null); - // Leeres Appearance-Dictionary setzten - PDAppearanceDictionary ap = new PDAppearanceDictionary(); - COSStream apsStream = new COSStream(getDocument().getScratchFile()); - apsStream.createUnfilteredStream(); - PDAppearanceStream aps = new PDAppearanceStream(apsStream); - COSDictionary cosObject = (COSDictionary)aps.getCOSObject(); - cosObject.setItem(COSName.SUBTYPE, COSName.getPDFName("Form")); - cosObject.setItem(COSName.BBOX, new PDRectangle()); + // Get the AcroForm from the Root-Dictionary and append the annotation + PDAcroForm acroForm = root.getAcroForm(); + root.getCOSObject().setNeedToBeUpdate(true); - ap.setNormalAppearance(aps); - ap.getDictionary().setDirect(true); - signatureField.getWidget().setAppearance(ap); - } - else // sichtbare Signatur - { - // Visuelle Objekte besorgen - List<COSObject> cosObjects = visualSignature.getObjects(); - - boolean annotNotFound = true; - boolean sigFieldNotFound = true; - - for ( COSObject cosObject : cosObjects ) + if (acroForm==null) + { + acroForm = new PDAcroForm(this); + root.setAcroForm(acroForm); + } + else { - COSBase base = cosObject.getObject(); - if (base != null && base instanceof COSDictionary) - { - COSBase ft = ((COSDictionary)base).getItem(COSName.getPDFName("FT")); - COSBase type = ((COSDictionary)base).getItem(COSName.TYPE); - COSBase apDict = ((COSDictionary)base).getItem(COSName.AP); + acroForm.getCOSObject().setNeedToBeUpdate(true); + } + + /* + * For invisible signatures, the annotation has a rectangle array with values [ 0 0 0 0 ]. + * This annotation is usually attached to the viewed page when the signature is created. + * Despite not having an appearance, the annotation AP and N dictionaries may be present + * in some versions of Acrobat. If present, N references the DSBlankXObj (blank) XObject. + */ + + // Create Annotation / Field for signature + PDSignatureField signatureField = new PDSignatureField(acroForm); + signatureField.setSignature(sigObject); // append the signature object + signatureField.getWidget().setPage(page); // backward linking + + // Set the AcroForm Fields + List acroFormFields = acroForm.getFields(); + COSDictionary acroFormDict = acroForm.getDictionary(); + acroFormDict.setDirect(true); + acroFormDict.setInt(COSName.SIG_FLAGS, 3); + acroFormFields.add(signatureField); + + // Get the object from the visual signature + COSDocument visualSignature = options.getVisualSignature(); + + // Distinction of case for visual and non-visual signature + if (visualSignature == null) // non-visual signature + { + // Set rectangle for non-visual signature to 0 0 0 0 + signatureField.getWidget().setRectangle(new PDRectangle()); // rectangle array [ 0 0 0 0 ] + // Clear AcroForm / Set DefaultRessource + acroFormDict.setItem(COSName.DR, null); + // Set empty Appearance-Dictionary + PDAppearanceDictionary ap = new PDAppearanceDictionary(); + COSStream apsStream = new COSStream(getDocument().getScratchFile()); + apsStream.createUnfilteredStream(); + PDAppearanceStream aps = new PDAppearanceStream(apsStream); + COSDictionary cosObject = (COSDictionary)aps.getCOSObject(); + cosObject.setItem(COSName.SUBTYPE, COSName.FORM); + cosObject.setItem(COSName.BBOX, new PDRectangle()); - // Nach Signatur-Annotation suchen - if (annotNotFound && COSName.getPDFName("Annot").equals(type)) + ap.setNormalAppearance(aps); + ap.getDictionary().setDirect(true); + signatureField.getWidget().setAppearance(ap); + } + else // visual signature + { + // Obtain visual signature object + List<COSObject> cosObjects = visualSignature.getObjects(); + + boolean annotNotFound = true; + boolean sigFieldNotFound = true; + + for ( COSObject cosObject : cosObjects ) { - COSDictionary cosBaseDict = (COSDictionary)base; + COSBase base = cosObject.getObject(); + if (base != null && base instanceof COSDictionary) + { + COSBase ft = ((COSDictionary)base).getItem(COSName.FT); + COSBase type = ((COSDictionary)base).getItem(COSName.TYPE); + COSBase apDict = ((COSDictionary)base).getItem(COSName.AP); + + // Search for signature annotation + if (annotNotFound && COSName.ANNOT.equals(type)) + { + COSDictionary cosBaseDict = (COSDictionary)base; - // Rectangle fuer visuelle Signatur auslesen und setzen - COSArray rectAry = (COSArray)cosBaseDict.getItem(COSName.getPDFName("Rect")); - PDRectangle rect = new PDRectangle(rectAry); - signatureField.getWidget().setRectangle(rect); - annotNotFound = false; - } + // Read and set the Rectangle for visual signature + COSArray rectAry = (COSArray)cosBaseDict.getItem(COSName.RECT); + PDRectangle rect = new PDRectangle(rectAry); + signatureField.getWidget().setRectangle(rect); + annotNotFound = false; + } - // Nach Signatur-Field suchen - if (sigFieldNotFound && COSName.getPDFName("Sig").equals(ft) && apDict != null) - { - COSDictionary cosBaseDict = (COSDictionary)base; + // Search for Signature-Field + if (sigFieldNotFound && COSName.SIG.equals(ft) && apDict != null) + { + COSDictionary cosBaseDict = (COSDictionary)base; - // Appearance Dictionary auslesen und setzen - PDAppearanceDictionary ap = new PDAppearanceDictionary((COSDictionary)cosBaseDict.getItem(COSName.AP)); - ap.getDictionary().setDirect(true); - signatureField.getWidget().setAppearance(ap); + // Appearance Dictionary auslesen und setzen + PDAppearanceDictionary ap = new PDAppearanceDictionary((COSDictionary)cosBaseDict.getItem(COSName.AP)); + ap.getDictionary().setDirect(true); + signatureField.getWidget().setAppearance(ap); - // AcroForm DefaultRessource auslesen und setzen - COSBase dr = cosBaseDict.getItem(COSName.getPDFName("DR")); - dr.setDirect(true); - dr.setNeedToBeUpdate(true); - acroFormDict.setItem("DR", dr); - sigFieldNotFound=false; + // AcroForm DefaultRessource auslesen und setzen + COSBase dr = cosBaseDict.getItem(COSName.DR); + dr.setDirect(true); + dr.setNeedToBeUpdate(true); + acroFormDict.setItem(COSName.DR, dr); + sigFieldNotFound=false; + } + } } - } - } - if (annotNotFound || sigFieldNotFound ) - throw new SignatureException(SignatureException.VISUAL_SIGNATURE_INVALID, "Could not read all needed objects from template"); - } - - // Seite besorgen und Signatur-Annotation anbringen - List annotations = page.getAnnotations(); - if (annotations== null) - { - annotations = new COSArrayList(); - } - annotations.add(signatureField.getWidget()); - page.setAnnotations(annotations); - page.getCOSObject().setNeedToBeUpdate(true); + if (annotNotFound || sigFieldNotFound ) + { + throw new SignatureException(SignatureException.VISUAL_SIGNATURE_INVALID, "Could not read all needed objects from template"); + } + } + // Get the annotations of the page and append the signature-annotation to it + List annotations = page.getAnnotations(); + if (annotations== null) + { + annotations = new COSArrayList(); + } + annotations.add(signatureField.getWidget()); + page.setAnnotations(annotations); + page.getCOSObject().setNeedToBeUpdate(true); } /** Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/SignatureOptions.java URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/SignatureOptions.java?rev=1139570&r1=1139569&r2=1139570&view=diff ============================================================================== --- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/SignatureOptions.java (original) +++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/SignatureOptions.java Sat Jun 25 16:51:57 2011 @@ -25,28 +25,77 @@ import org.apache.pdfbox.pdfparser.Visua public class SignatureOptions { - private COSDocument visualSignature; + private COSDocument visualSignature; - private int pageNo; + private int preferedSignatureSize; + + private int pageNo; - public void setPage(int pageNo) - { - this.pageNo = pageNo; - } + /** + * Set the page number. + * + * @param pageNo the page number + * + */ + public void setPage(int pageNo) + { + this.pageNo = pageNo; + } - public int getPage() { - return pageNo; - } + /** + * Get the page number. + * + * @return the page number + */ + public int getPage() + { + return pageNo; + } - public void setVisualSignature(InputStream is) throws IOException - { - VisualSignatureParser visParser = new VisualSignatureParser(is); - visParser.parse(); - visualSignature = visParser.getDocument(); - } + /** + * Reads the visual signature from the given input stream. + * + * @param is the input stream containing the visual signature + * + * @throws IOException when something went wrong during parsing + */ + public void setVisualSignature(InputStream is) throws IOException + { + VisualSignatureParser visParser = new VisualSignatureParser(is); + visParser.parse(); + visualSignature = visParser.getDocument(); + } - public COSDocument getVisualSignature() - { - return visualSignature; - } + /** + * Get the visual signature. + * + * @return the visual signature + */ + public COSDocument getVisualSignature() + { + return visualSignature; + } + + /** + * Get the preferred size of the signature. + * + * @return the preferred size + */ + public int getPreferedSignatureSize() + { + return preferedSignatureSize; + } + + /** + * Set the preferred size of the signature. + * + * @param size the size of the signature + */ + public void setPreferedSignatureSize(int size) + { + if (size > 0) + { + preferedSignatureSize = size; + } + } }