You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by le...@apache.org on 2013/03/01 17:33:56 UTC

svn commit: r1451638 [1/2] - in /pdfbox/trunk: examples/ examples/src/main/java/org/apache/pdfbox/examples/signature/ pdfbox/src/main/java/org/apache/pdfbox/cos/ pdfbox/src/main/java/org/apache/pdfbox/pdfparser/ pdfbox/src/main/java/org/apache/pdfbox/p...

Author: lehmi
Date: Fri Mar  1 16:33:56 2013
New Revision: 1451638

URL: http://svn.apache.org/r1451638
Log:
PDFBOX-1513 added signature improvements as proposed by Thomas Chojecki

Added:
    pdfbox/trunk/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java   (with props)
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/PDFXRef.java   (with props)
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/PDFXRefStream.java   (with props)
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDPropBuild.java   (with props)
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDPropBuildDataDict.java   (with props)
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSeedValue.java   (with props)
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSeedValueMDP.java   (with props)
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSeedValueTimeStamp.java   (with props)
Modified:
    pdfbox/trunk/examples/pom.xml
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDocument.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSStream.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/BaseParser.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/PDFParser.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/common/filespecification/PDComplexFileSpecification.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDSignatureField.java

Modified: pdfbox/trunk/examples/pom.xml
URL: http://svn.apache.org/viewvc/pdfbox/trunk/examples/pom.xml?rev=1451638&r1=1451637&r2=1451638&view=diff
==============================================================================
--- pdfbox/trunk/examples/pom.xml (original)
+++ pdfbox/trunk/examples/pom.xml Fri Mar  1 16:33:56 2013
@@ -43,6 +43,12 @@
       <artifactId>pdfbox</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.bouncycastle</groupId>
+      <artifactId>bcmail-jdk15</artifactId>
+      <version>1.44</version>
+      <optional>true</optional>
+    </dependency>
   </dependencies>
 
   <build>

Added: pdfbox/trunk/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java?rev=1451638&view=auto
==============================================================================
--- pdfbox/trunk/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java (added)
+++ pdfbox/trunk/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java Fri Mar  1 16:33:56 2013
@@ -0,0 +1,303 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.examples.signature;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertStore;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CollectionCertStoreParameters;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.apache.pdfbox.exceptions.COSVisitorException;
+import org.apache.pdfbox.exceptions.SignatureException;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSProcessable;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.CMSSignedDataGenerator;
+import org.bouncycastle.cms.CMSSignedGenerator;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+/**
+ * <p>This is an example for singing a pdf with bouncy castle.</p>
+ * <p>A keystore can be created with the java keytool 
+ * (e.g. keytool -genkeypair -storepass 123456 -storetype pkcs12 -alias test -validity 365 -v -keyalg RSA -keystore keystore.p12 ) 
+ * </p>
+ * 
+ * @author Thomas Chojecki
+ * 
+ */
+public class CreateSignature implements SignatureInterface
+{
+
+  private static BouncyCastleProvider provider = new BouncyCastleProvider();
+
+  private PrivateKey privKey;
+
+  private Certificate[] cert;
+
+  private SignatureOptions options;
+  
+  /**
+   * Initialize the signature creator with a keystore (pkcs12) and pin that
+   * should be used for the signature.
+   * 
+   * @param keystore
+   *          is a pkcs12 keystore.
+   * @param pin
+   *          is the pin for the keystore / private key
+   */
+  public CreateSignature(KeyStore keystore, char[] pin)
+  {
+    try
+    {
+      /*
+       * grabs the first alias from the keystore and get the private key. An
+       * alternative method or constructor could be used for setting a specific
+       * alias that should be used.
+       */
+      Enumeration<String> aliases = keystore.aliases();
+      String alias = null;
+      if (aliases.hasMoreElements())
+      {
+          alias = aliases.nextElement();
+      }
+      else
+      {
+          throw new RuntimeException("Could not find alias");
+      }
+      privKey = (PrivateKey) keystore.getKey(alias, pin);
+      cert = keystore.getCertificateChain(alias);
+    }
+    catch (KeyStoreException e)
+    {
+      e.printStackTrace();
+    }
+    catch (UnrecoverableKeyException e)
+    {
+      System.err.println("Could not extract private key.");
+      e.printStackTrace();
+    }
+    catch (NoSuchAlgorithmException e)
+    {
+      System.err.println("Unknown algorithm.");
+      e.printStackTrace();
+    }
+  }
+  
+  
+
+  /**
+   * Signs the given pdf file.
+   * 
+   * @param document is the pdf document
+   * @return the signed pdf document
+   * @throws IOException 
+   * @throws COSVisitorException
+   * @throws SignatureException
+   */
+  public File signPDF(File document) throws IOException, COSVisitorException,
+      SignatureException
+  {
+    byte[] buffer = new byte[8 * 1024];
+    if (document == null || !document.exists())
+    {
+        new RuntimeException("Document for signing does not exist");
+    }
+
+    // creating output document and prepare the IO streams.
+    String name = document.getName();
+    String substring = name.substring(0, name.lastIndexOf("."));
+    
+    File outputDocument = new File(document.getParent(), substring+"_signed.pdf");
+    FileInputStream fis = new FileInputStream(document);
+    FileOutputStream fos = new FileOutputStream(outputDocument);
+
+    int c;
+    while ((c = fis.read(buffer)) != -1)
+    {
+      fos.write(buffer, 0, c);
+    }
+    fis.close();
+    fis = new FileInputStream(outputDocument);
+
+    // load document
+    PDDocument doc = PDDocument.load(document);
+
+    // create signature dictionary
+    PDSignature signature = new PDSignature();
+    signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); // default filter
+    // subfilter for basic and PAdES Part 2 signatures
+    signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
+    signature.setName("signer name");
+    signature.setLocation("signer location");
+    signature.setReason("reason for signature");
+
+    // the signing date, needed for valid signature
+    signature.setSignDate(Calendar.getInstance());
+
+    // register signature dictionary and sign interface
+    if (options==null)
+    {
+      doc.addSignature(signature, this);
+    } 
+    else 
+    {
+      doc.addSignature(signature, this, options);
+    }
+    
+    // write incremental (only for signing purpose)
+    doc.saveIncremental(fis, fos);
+
+    return outputDocument;
+  }
+
+  /**
+   * <p>
+   * SignatureInterface implementation.
+   * </p>
+   * 
+   * <p>
+   * This method will be called from inside of the pdfbox and create the pkcs7
+   * signature. The given InputStream contains the bytes that are providen by
+   * the byte range.
+   * </p>
+   * 
+   * <p>
+   * This method is for internal use only.
+   * </p>
+   * 
+   * <p>
+   * Here the user should use his favorite cryptographic library and implement a
+   * pkcs7 signature creation.
+   * </p>
+   */
+  public byte[] sign(InputStream content) throws SignatureException,
+      IOException
+  {
+    CMSProcessableInputStream input = new CMSProcessableInputStream(content);
+    CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
+    // CertificateChain
+    List<Certificate> certList = Arrays.asList(cert);
+
+    CertStore certStore = null;
+    try
+    {
+      certStore = CertStore.getInstance("Collection",
+          new CollectionCertStoreParameters(certList), provider);
+      gen.addSigner(privKey, (X509Certificate) certList.get(0),
+          CMSSignedGenerator.DIGEST_SHA256);
+      gen.addCertificatesAndCRLs(certStore);
+      CMSSignedData signedData = gen.generate(input, false, provider);
+      return signedData.getEncoded();
+    }
+    catch (Exception e)
+    {
+      // should be handled
+      System.err.println("Error while creating pkcs7 signature.");
+      e.printStackTrace();
+    }
+    throw new RuntimeException("Problem while preparing signature");
+  }
+
+  public static void main(String[] args) throws KeyStoreException,
+      NoSuchAlgorithmException, CertificateException, FileNotFoundException,
+      IOException, COSVisitorException, SignatureException
+  {
+    if (args.length != 3)
+    {
+      usage();
+      System.exit(1);
+    }
+    else
+    {
+      File ksFile = new File(args[0]);
+      KeyStore keystore = KeyStore.getInstance("PKCS12", provider);
+      char[] pin = args[1].toCharArray();
+      keystore.load(new FileInputStream(ksFile), pin);
+
+      File document = new File(args[2]);
+
+      CreateSignature signing = new CreateSignature(keystore, pin.clone());
+      signing.signPDF(document);
+    }
+
+  }
+
+  /**
+   * This will print the usage for this program.
+   */
+  private static void usage()
+  {
+    System.err.println("Usage: java " + CreateSignature.class.getName()
+        + " <pkcs12-keystore-file> <pin> <input-pdf>");
+  }
+}
+
+/**
+ * Wrap a InputStream into a CMSProcessable object for bouncy castle. It's an
+ * alternative to the CMSProcessableByteArray.
+ * 
+ * @author Thomas Chojecki
+ * 
+ */
+class CMSProcessableInputStream implements CMSProcessable
+{
+
+  InputStream in;
+
+  public CMSProcessableInputStream(InputStream is)
+  {
+    in = is;
+  }
+
+  public Object getContent()
+  {
+    return null;
+  }
+
+  public void write(OutputStream out) throws IOException, CMSException
+  {
+    // read the content only one time
+    byte[] buffer = new byte[8 * 1024];
+    int read;
+    while ((read = in.read(buffer)) != -1)
+    {
+      out.write(buffer, 0, read);
+    }
+    in.close();
+  }
+}

Propchange: pdfbox/trunk/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDocument.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDocument.java?rev=1451638&r1=1451637&r2=1451638&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDocument.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSDocument.java Fri Mar  1 16:33:56 2013
@@ -19,7 +19,9 @@ package org.apache.pdfbox.cos;
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -315,10 +317,11 @@ public class COSDocument extends COSBase
     /** Signals that the document is decrypted completely.
      *  Needed e.g. by {@link NonSequentialPDFParser} to circumvent
      *  additional decryption later on. */
-    public void setDecrypted() {
-    		isDecrypted = true;
+    public void setDecrypted()
+    {
+        isDecrypted = true;
     }
-    
+
     /**
      * This will tell if this is an encrypted document.
      *
@@ -326,9 +329,10 @@ public class COSDocument extends COSBase
      */
     public boolean isEncrypted()
     {
-	  	  if ( isDecrypted )
-	  	  		return false;
-  	  
+        if ( isDecrypted )
+        {
+            return false;
+        }
         boolean encrypted = false;
         if( trailer != null )
         {
@@ -369,39 +373,63 @@ public class COSDocument extends COSBase
     }
 
     /**
-     * This will return the last signature dictionary.
-     * @return the last signature dictionary 
-     * 
-     * @throws IOException if something went wrong
+     * This will return a list of signature dictionaries as COSDictionary.
+     *
+     * @return list of signature dictionaries as COSDictionary
+     * @throws IOException if no document catalog can be found
+     */
+    public List<COSDictionary> getSignatureDictionaries() throws IOException
+    {
+        List<COSDictionary> signatureFields = getSignatureFields(false);
+        List<COSDictionary> signatures = new LinkedList<COSDictionary>();
+        for ( COSDictionary dict : signatureFields )
+        {
+            COSBase dictionaryObject = dict.getDictionaryObject(COSName.V);
+            if (dictionaryObject != null)
+            {
+                signatures.add((COSDictionary)dictionaryObject);
+            }
+        }
+        return signatures;
+    }
+
+    /**
+     * This will return a list of signature fields.
+     *
+     * @return list of signature dictionaries as COSDictionary
+     * @throws IOException if no document catalog can be found
      */
-    public COSDictionary getLastSignatureDictionary() throws IOException 
+    public List<COSDictionary> getSignatureFields(boolean onlyEmptyFields) throws IOException
     {
-      if (signDictionary == null)
-      {
         COSObject documentCatalog = getCatalog();
         if (documentCatalog != null)
         {
-          COSDictionary acroForm = (COSDictionary)documentCatalog.getDictionaryObject(COSName.ACRO_FORM);
-          if (acroForm !=null)
-          {
-            COSArray fields = (COSArray)acroForm.getDictionaryObject(COSName.FIELDS);
-            for ( Object object : fields )
+            COSDictionary acroForm = (COSDictionary)documentCatalog.getDictionaryObject(COSName.ACRO_FORM);
+            if (acroForm != null)
             {
-              COSObject dict = (COSObject)object;
-              if(dict.getItem(COSName.FT).equals(COSName.SIG))
-              {
-                COSBase dictionaryObject = dict.getDictionaryObject(COSName.V);
-                
-                if (dictionaryObject != null) 
+                COSArray fields = (COSArray)acroForm.getDictionaryObject(COSName.FIELDS);
+                if (fields != null)
                 {
-                  signDictionary = (COSDictionary)dictionaryObject;
+                    // Some fields may contain twice references to a single field. 
+                    // This will prevent such double entries.
+                    HashMap<COSObjectKey, COSDictionary> signatures = new HashMap<COSObjectKey, COSDictionary>();
+                    for ( Object object : fields )
+                    {
+                        COSObject dict = (COSObject)object;
+                        if (COSName.SIG.equals(dict.getItem(COSName.FT)))
+                        {
+                            COSBase dictionaryObject = dict.getDictionaryObject(COSName.V);
+                            if (dictionaryObject == null || (dictionaryObject != null && !onlyEmptyFields))
+                            {
+                                signatures.put(new COSObjectKey(dict), (COSDictionary)dict.getObject());
+                            }
+                        }
+                    }
+                    return new LinkedList<COSDictionary>(signatures.values());
                 }
-              }
             }
-          }
         }
-      }
-      return signDictionary;
+        return Collections.emptyList();
     }
     
     /**
@@ -579,7 +607,7 @@ public class COSDocument extends COSBase
                 COSObjectKey key = new COSObjectKey( next );
                 if ( objectPool.get(key) == null || objectPool.get(key).getObject() == null ||
                      // xrefTable stores negated objNr of objStream for objects in objStreams
-                     (xrefTable.containsKey( key ) && xrefTable.get( key ) == - objStream.getObjectNumber().longValue()) )  
+                     (xrefTable.containsKey(key) && xrefTable.get(key) == -objStream.getObjectNumber().longValue()) )
                 {
                     COSObject obj = getObjectFromPool(key);
                     obj.setObject(next.getObject());
@@ -668,4 +696,18 @@ public class COSDocument extends COSBase
     {
       return startXref;
     }
+
+    /**
+     * Determines it the trailer is a XRef stream or not.
+     * 
+     * @return true if the trailer is a XRef stream
+     */
+    public boolean isXRefStream()
+    {
+        if (trailer != null)
+        {
+            return COSName.XREF.equals(trailer.getItem(COSName.TYPE));
+        }
+        return false;
+    }
 }

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=1451638&r1=1451637&r2=1451638&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 Fri Mar  1 16:33:56 2013
@@ -132,6 +132,10 @@ public final class COSName extends COSBa
      * A common COSName value.
      */
     public static final COSName AP = new COSName( "AP" );
+    /**
+     * A common COSName value.
+     */
+    public static final COSName APP = new COSName( "App" );
 
     /**
      * A common COSName value.
@@ -303,6 +307,10 @@ public final class COSName extends COSBa
     /**
      * A common COSName value.
      */
+    public static final COSName CONTACT_INFO = new COSName( "ContactInfo" );
+    /**
+     * A common COSName value.
+     */
     public static final COSName CONTENTS = new COSName( "Contents" );
      /**
       * A common COSName value.
@@ -351,6 +359,10 @@ public final class COSName extends COSBa
     /**
      * A common COSName value.
      */
+    public static final COSName DATE = new COSName( "Date" );
+    /**
+     * A common COSName value.
+     */
     public static final COSName DCT_DECODE = new COSName( "DCTDecode" );
     /**
      * A common COSName value.
@@ -407,6 +419,34 @@ public final class COSName extends COSBa
     /**
      * A common COSName value.
      */
+    public static final COSName DIGEST_METHOD = new COSName( "DigestMethod" );
+    /**
+     * Digest Method.
+     */
+    public static final COSName DIGEST_SHA1 = new COSName("SHA1");
+
+    /**
+     * Digest Method.
+     */
+    public static final COSName DIGEST_SHA256 = new COSName("SHA256");
+
+    /**
+     * Digest Method.
+     */
+    public static final COSName DIGEST_SHA384 = new COSName("SHA384");
+
+    /**
+     * Digest Method.
+     */
+    public static final COSName DIGEST_SHA512 = new COSName("SHA512");
+
+    /**
+     * Digest Method.
+     */
+    public static final COSName DIGEST_RIPEMD160 = new COSName("RIPEMD160");
+    /**
+     * A common COSName value.
+     */
     public static final COSName DIRECTION = new COSName( "Direction" );
     /**
      * A common COSName value.
@@ -422,6 +462,10 @@ public final class COSName extends COSBa
      * A common COSName value.
      */
     public static final COSName DOC_CHECKSUM = new COSName( "DocChecksum" );
+    /**
+     * A common COSName value.
+     */
+    public static final COSName DOC_TIME_STAMP = new COSName( "DocTimeStamp" );
 
     /**
      * A common COSName value.
@@ -430,6 +474,10 @@ public final class COSName extends COSBa
     /**
      * A common COSName value.
      */
+    public static final COSName DOS = new COSName( "DOS" );
+    /**
+     * A common COSName value.
+     */
     public static final COSName DP = new COSName( "DP" );
 
     /**
@@ -453,6 +501,10 @@ public final class COSName extends COSBa
      * A common COSName value.
      */
     public static final COSName E = new COSName("E");
+    /**
+     * A common COSName value.
+     */
+    public static final COSName EF = new COSName("EF");
 
     /**
      * A common COSName value.
@@ -540,6 +592,10 @@ public final class COSName extends COSBa
     /**
     * A common COSName value.
     */
+    public static final COSName FILESPEC = new COSName( "Filespec" );
+    /**
+    * A common COSName value.
+    */
     public static final COSName FILTER = new COSName( "Filter" );
     /**
     * A common COSName value.
@@ -782,6 +838,10 @@ public final class COSName extends COSBa
     /**
     * A common COSName value.
     */
+    public static final COSName LEGAL_ATTESTATION = new COSName( "LegalAttestation" );
+    /**
+    * A common COSName value.
+    */
     public static final COSName LENGTH = new COSName( "Length" );
     /**
      * A common COSName value.
@@ -816,6 +876,10 @@ public final class COSName extends COSBa
     /**
     * A common COSName value.
     */
+    public static final COSName MAC = new COSName( "Mac" );
+    /**
+    * A common COSName value.
+    */
     public static final COSName MAC_ROMAN_ENCODING = new COSName( "MacRomanEncoding" );
 
     /**
@@ -842,6 +906,10 @@ public final class COSName extends COSBa
     /**
      * A common COSName value.
      */
+    public static final COSName MDP = new COSName("MDP");
+    /**
+     * A common COSName value.
+     */
     public static final COSName MEDIA_BOX = new COSName(  "MediaBox" );
     /**
      * A common COSName value.
@@ -888,6 +956,10 @@ public final class COSName extends COSBa
     /**
      * A common COSName value.
      */
+    public static final COSName NON_EFONT_NO_WARN = new COSName( "NonEFontNoWarn" );
+    /**
+     * A common COSName value.
+     */
     public static final COSName NON_FULL_SCREEN_PAGE_MODE = new COSName( "NonFullScreenPageMode" );
     /**
      * A common COSName value.
@@ -941,6 +1013,10 @@ public final class COSName extends COSBa
     /**
      * A common COSName value.
      */
+    public static final COSName OS = new COSName( "OS" );
+    /**
+     * A common COSName value.
+     */
     public static final COSName OUTLINES = new COSName("Outlines");
 
     /**
@@ -1014,6 +1090,10 @@ public final class COSName extends COSBa
     /**
      * A common COSName value.
      */
+    public static final COSName PRE_RELEASE = new COSName( "PreRelease" );
+    /**
+     * A common COSName value.
+     */
     public static final COSName PREDICTOR = new COSName( "Predictor" );
     /**
      * A common COSName value.
@@ -1041,12 +1121,20 @@ public final class COSName extends COSBa
      */
     public static final COSName PRODUCER = new COSName( "Producer" );
 
+    /**
+     * A common COSName value.
+     */
+    public static final COSName PROP_BUILD = new COSName( "Prop_Build" );
     /** The COSName value for "Properties". */
     public static final COSName PROPERTIES = new COSName( "Properties" );
 
     /**
      * A common COSName value.
      */
+    public static final COSName PUB_SEC = new COSName( "PubSec" );
+    /**
+     * A common COSName value.
+     */
     public static final COSName Q = new COSName( "Q" );
     /**
      * A common COSName value.
@@ -1059,6 +1147,10 @@ public final class COSName extends COSBa
     /**
      * A common COSName value.
      */
+    public static final COSName REASONS = new COSName( "Reasons" );
+    /**
+     * A common COSName value.
+     */
     public static final COSName RECIPIENTS = new COSName( "Recipients" );
     /**
      * A common COSName value.
@@ -1204,6 +1296,10 @@ public final class COSName extends COSBa
      * A common COSName value.
      */
     public static final COSName SUBTYPE = new COSName( "Subtype" );
+    /**
+     * A common COSName value.
+     */
+    public static final COSName SV = new COSName( "SV" );
 
     /**
      * A common COSName value.
@@ -1227,6 +1323,10 @@ public final class COSName extends COSBa
     /**
      * A common COSName value.
      */
+    public static final COSName TIME_STAMP = new COSName( "TimeStamp" );
+    /**
+     * A common COSName value.
+     */
     public static final COSName TITLE = new COSName( "Title" );
     /**
      * A common COSName value.
@@ -1245,6 +1345,10 @@ public final class COSName extends COSBa
      */
     public static final COSName TRUE_TYPE = new COSName("TrueType" );
     /**
+     * A common COSName value.
+     */
+    public static final COSName TRUSTED_MODE = new COSName("TrustedMode" );
+    /**
     * A common COSName value.
     */
     public static final COSName TO_UNICODE = new COSName( "ToUnicode" );
@@ -1273,12 +1377,24 @@ public final class COSName extends COSBa
      * A common COSName value.
      */
     public static final COSName U = new COSName( "U" );
+    /**
+     * A common COSName value.
+     */
+    public static final COSName UF = new COSName( "UF" );
     /** the COSName for the "Unchanged" value. */
     public static final COSName UNCHANGED = new COSName("Unchanged");
     /**
      * A common COSName value.
      */
+    public static final COSName UNIX = new COSName( "Unix" );
+    /**
+     * A common COSName value.
+     */
     public static final COSName URI = new COSName("URI");
+    /**
+     * A common COSName value.
+     */
+    public static final COSName URL = new COSName("URL");
 
     /**
     * A common COSName value.
@@ -1340,6 +1456,11 @@ public final class COSName extends COSBa
      * A common COSName value.
      */
     public static final COSName XREF = new COSName( "XRef" );
+
+    /**
+     * A common COSName value.
+     */
+    public static final COSName XREF_STM = new COSName( "XRefStm" );
     /**
      * A common COSName value.
      */

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSStream.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSStream.java?rev=1451638&r1=1451637&r2=1451638&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSStream.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSStream.java Fri Mar  1 16:33:56 2013
@@ -142,6 +142,22 @@ public class COSStream extends COSDictio
     }
 
     /**
+     * This will get the length of the encoded stream.
+     * 
+     * @return the length of the encoded stream as long
+     *
+     * @throws IOException 
+     */
+    public long getFilteredLength() throws IOException
+    {
+        if (filteredStream == null)
+        {
+            doEncode();
+        }
+        return filteredStream.getLength();
+    }
+    
+    /**
      * This will get the logical content stream with none of the filters.
      *
      * @return the bytes of the logical (decoded) stream

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/BaseParser.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/BaseParser.java?rev=1451638&r1=1451637&r2=1451638&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/BaseParser.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/BaseParser.java Fri Mar  1 16:33:56 2013
@@ -54,7 +54,9 @@ import org.apache.pdfbox.persistence.uti
 public abstract class BaseParser
 {
 
-    /** system property allowing to define size of push back buffer */
+    /**
+     * system property allowing to define size of push back buffer.
+     */
     public static final String PROP_PUSHBACK_SIZE = "org.apache.pdfbox.baseParser.pushBackSize";
 
     /**
@@ -526,10 +528,12 @@ public abstract class BaseParser
                     // if 'endstream' was not found fall back to scanning
                     if ( ! foundEndstream )
                     {
-                        LOG.warn( "Specified stream length " + length + " is wrong. Fall back to reading stream until 'endstream'." );
+                        LOG.warn("Specified stream length " + length 
+                                + " is wrong. Fall back to reading stream until 'endstream'.");
                         
                         // push back all read stream bytes
-                        out.flush();    // we got a buffered stream wrapper around filteredStream thus first flush to underlying stream
+                        // we got a buffered stream wrapper around filteredStream thus first flush to underlying stream
+                        out.flush();
                         InputStream writtenStreamBytes = stream.getFilteredStream();
                         ByteArrayOutputStream     bout = new ByteArrayOutputStream( length );
                         
@@ -783,6 +787,7 @@ public abstract class BaseParser
     /**
      * This will parse a PDF string.
      *
+     * @param isDictionary indicates if the stream is a dictionary or not
      * @return The parsed PDF string.
      *
      * @throws IOException If there is an error reading from the stream.
@@ -989,8 +994,20 @@ public abstract class BaseParser
             }
             else
             {
-                // character is neither a hex char nor end of string not EOS nor whitespace
-                throw new IOException( "Not allowed character in hex string; char code: " + c );
+                // if invalid chars was found
+                if (sBuf.length()%2!=0)
+                {
+                    sBuf.deleteCharAt(strmBuf.length-1);
+                }
+                
+                // read till the closing bracket was found
+                do 
+                {
+                    c = pdfSource.read();
+                } while ( c != '>' );
+                
+                // exit loop
+                break;
             }
         }
         return COSString.createFromHexString( sBuf.toString(), forceParsing );

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/PDFParser.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/PDFParser.java?rev=1451638&r1=1451637&r2=1451638&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/PDFParser.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/PDFParser.java Fri Mar  1 16:33:56 2013
@@ -20,6 +20,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 import java.util.regex.Pattern;
@@ -887,16 +888,59 @@ public class PDFParser extends BaseParse
         private static void resolveConflicts(COSDocument document, List<ConflictObj> conflictList) throws IOException
         {
             Iterator<ConflictObj> conflicts = conflictList.iterator();
-            while(conflicts.hasNext())
+            if (conflicts.hasNext())
             {
-                ConflictObj o = conflicts.next();
-                Long offset = new Long(o.offset);
-                if(document.getXrefTable().containsValue(offset))
+                Collection<Long> values = document.getXrefTable().values();
+                do
                 {
-                    COSObject pdfObject = document.getObjectFromPool(o.objectKey);
-                    pdfObject.setObject(o.object.getObject());
+                    ConflictObj o = conflicts.next();
+                    Long offset = new Long(o.offset);
+                    if (tolerantConflicResolver(values, offset, 4))
+                    {
+                        COSObject pdfObject = document.getObjectFromPool(o.objectKey);
+                        if (pdfObject.getObjectNumber() != null 
+                                && pdfObject.getObjectNumber().equals(o.object.getObjectNumber()))
+                        {
+                            pdfObject.setObject(o.object.getObject());
+                        }
+                        else
+                        {
+                            LOG.debug("Conflict object ["+o.objectKey+"] at offset "+offset
+                                    +" found in the xref table, but the object numbers differ. Ignoring this object."
+                                    + " The document is maybe malformed.");
+                        }
+                    }
+                }
+                while (conflicts.hasNext());
+            }
+        }
+    }
+    
+    /**
+     * Check if the given object offset can be find in the xref table. If not, we try to search the table
+     * again with the given tolerance and check the given bytes before and after the xref table offset.
+     *
+     * @param values are the unsorted values from the xref table
+     * @param offset is the offset that should be found in the xref table
+     * @param tolerance is the allowed tolerance in bytes.
+     * @return true if the offset was found inside the xref table
+     */
+    private static boolean tolerantConflicResolver(Collection<Long> values, long offset, int tolerance)
+    {
+        if (values.contains(offset))
+        {
+            return true;
+        }
+        else
+        {
+            for ( Long integer : values )
+            {
+                if (Math.abs(integer - offset) <= tolerance)
+                {
+                    return true;
                 }
             }
         }
+        return false;
     }
 }

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/PDFXRef.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/PDFXRef.java?rev=1451638&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/PDFXRef.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/PDFXRef.java Fri Mar  1 16:33:56 2013
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.pdfparser;
+
+import org.apache.pdfbox.cos.COSObject;
+
+/**
+ * @author Alexander Funk
+ * @version $Revision: $
+ */
+public interface PDFXRef
+{
+
+    /**
+     * Returns the object referenced by the given object number.
+     * 
+     * @param objectNumber the object to be returned
+     * @return the object corresponding to the given object number
+     */
+    
+    COSObject getObject(int objectNumber);
+}

Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/PDFXRef.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/PDFXRefStream.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/PDFXRefStream.java?rev=1451638&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/PDFXRefStream.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/PDFXRefStream.java Fri Mar  1 16:33:56 2013
@@ -0,0 +1,345 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.pdfparser;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSInteger;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.cos.COSObject;
+import org.apache.pdfbox.cos.COSStream;
+import org.apache.pdfbox.io.RandomAccessBuffer;
+import org.apache.pdfbox.pdfwriter.COSWriterXRefEntry;
+
+/**
+ * @author Alexander Funk
+ * @version $Revision: $
+ */
+public class PDFXRefStream implements PDFXRef
+{
+
+    private static final int ENTRY_OBJSTREAM = 2;
+
+    private static final int ENTRY_NORMAL = 1;
+
+    private static final int ENTRY_FREE = 0;
+
+    private Map<Integer, Object> streamData;
+
+    private Set<Integer> objectNumbers;
+
+    private COSStream stream;
+
+    private long size = -1;
+
+    /**
+     * Create a fresh XRef stream like for a fresh file or an incremental update.
+     */
+    public PDFXRefStream()
+    {
+        this.stream = new COSStream(new COSDictionary(), new RandomAccessBuffer());
+        streamData = new TreeMap<Integer, Object>();
+        objectNumbers = new TreeSet<Integer>();
+    }
+
+    /**
+     * Returns the stream of the XRef.
+     * @return the XRef stream
+     * @throws IOException if something went wrong
+     */
+    public COSStream getStream() throws IOException
+    {
+        stream.setItem(COSName.TYPE, COSName.XREF);
+        if (size == -1)
+        {
+            throw new IllegalArgumentException("size is not set in xrefstream");
+        }
+        stream.setLong(COSName.SIZE, getSizeEntry());
+        stream.setFilters(COSName.FLATE_DECODE);
+
+        {
+            List<Integer> indexEntry = getIndexEntry();
+            COSArray indexAsArray = new COSArray();
+            for ( Integer i : indexEntry )
+            {
+                indexAsArray.add(COSInteger.get(i));
+            }
+            stream.setItem(COSName.INDEX, indexAsArray);
+        }
+        {
+            int[] wEntry = getWEntry();
+            COSArray wAsArray = new COSArray();
+            for ( int i = 0; i < wEntry.length; i++ )
+            {
+                int j = wEntry[i];
+                wAsArray.add(COSInteger.get(j));
+            }
+            stream.setItem(COSName.W, wAsArray);
+            OutputStream unfilteredStream = stream.createUnfilteredStream();
+            writeStreamData(unfilteredStream, wEntry);
+        }
+        Set<COSName> keySet = stream.keySet();
+        for ( COSName cosName : keySet )
+        {
+            COSBase dictionaryObject = stream.getDictionaryObject(cosName);
+            dictionaryObject.setDirect(true);
+        }
+        return stream;
+    }
+
+    /**
+     * Copy all Trailer Information to this file.
+     * 
+     * @param trailerDict dictionary to be added as trailer info
+     */
+    public void addTrailerInfo(COSDictionary trailerDict)
+    {
+        Set<Entry<COSName, COSBase>> entrySet = trailerDict.entrySet();
+        for ( Entry<COSName, COSBase> entry : entrySet )
+        {
+            COSName key = entry.getKey();
+            if (COSName.INFO.equals(key) || COSName.ROOT.equals(key) || COSName.ENCRYPT.equals(key) 
+                    || COSName.ID.equals(key) || COSName.PREV.equals(key))
+            {
+                stream.setItem(key, entry.getValue());
+            }
+        }
+    }
+
+    /**
+     * Add an new entry to the XRef stream.
+     * 
+     * @param entry new entry to be added
+     */
+    public void addEntry(COSWriterXRefEntry entry)
+    {
+        objectNumbers.add((int)entry.getKey().getNumber());
+        if (entry.isFree())
+        {
+            // what would be a f-Entry in the xref table
+            FreeReference value = new FreeReference();
+            value.nextGenNumber = entry.getKey().getGeneration();
+            value.nextFree = entry.getKey().getNumber();
+            streamData.put((int)value.nextFree, value);
+        }
+        else
+        {
+            // we don't care for ObjectStreamReferences for now and only handle
+            // normal references that would be f-Entrys in the xref table.
+            NormalReference value = new NormalReference();
+            value.genNumber = entry.getKey().getGeneration();
+            value.offset = entry.getOffset();
+            streamData.put((int)entry.getKey().getNumber(), value);
+        }
+    }
+
+    /**
+     * determines the minimal length required for all the lengths.
+     * 
+     * @return
+     */
+    private int[] getWEntry()
+    {
+        long[] wMax = new long[3];
+        for ( Object entry : streamData.values() )
+        {
+            if (entry instanceof FreeReference)
+            {
+                FreeReference free = (FreeReference)entry;
+                wMax[0] = Math.max(wMax[0], ENTRY_FREE); // the type field for a free reference
+                wMax[1] = Math.max(wMax[1], free.nextFree);
+                wMax[2] = Math.max(wMax[2], free.nextGenNumber);
+            }
+            else if (entry instanceof NormalReference)
+            {
+                NormalReference ref = (NormalReference)entry;
+                wMax[0] = Math.max(wMax[0], ENTRY_NORMAL); // the type field for a normal reference
+                wMax[1] = Math.max(wMax[1], ref.offset);
+                wMax[2] = Math.max(wMax[2], ref.genNumber);
+            }
+            else if (entry instanceof ObjectStreamReference)
+            {
+                ObjectStreamReference objStream = (ObjectStreamReference)entry;
+                wMax[0] = Math.max(wMax[0], ENTRY_OBJSTREAM); // the type field for a objstm reference
+                wMax[1] = Math.max(wMax[1], objStream.offset);
+                wMax[2] = Math.max(wMax[2], objStream.objectNumberOfObjectStream);
+            }
+            // TODO add here if new standard versions define new types
+            else
+            {
+                throw new RuntimeException("unexpected reference type");
+            }
+        }
+        // find the max bytes needed to display that column
+        int[] w = new int[3];
+        for ( int i = 0; i < w.length; i++ )
+        {
+            while (wMax[i] > 0)
+            {
+                w[i]++;
+                wMax[i] >>= 8;
+            }
+        }
+        return w;
+    }
+
+    private long getSizeEntry()
+    {
+        return size;
+    }
+
+    /**
+     * Set the size of the XRef stream.
+     * 
+     * @param streamSize size to bet set as stream size
+     */
+    public void setSize(long streamSize)
+    {
+        this.size = streamSize;
+    }
+
+    private List<Integer> getIndexEntry()
+    {
+        LinkedList<Integer> linkedList = new LinkedList<Integer>();
+        Integer first = null;
+        Integer length = null;
+
+        for ( Integer objNumber : objectNumbers )
+        {
+            if (first == null)
+            {
+                first = objNumber;
+                length = 1;
+            }
+            if (first + length == objNumber)
+            {
+                length += 1;
+            }
+            if (first + length < objNumber)
+            {
+                linkedList.add(first);
+                linkedList.add(length);
+                first = objNumber;
+                length = 1;
+            }
+        }
+        linkedList.add(first);
+        linkedList.add(length);
+
+        return linkedList;
+    }
+
+    private void writeNumber(OutputStream os, long number, int bytes) throws IOException
+    {
+        byte[] buffer = new byte[bytes];
+        for ( int i = 0; i < bytes; i++ )
+        {
+            buffer[i] = (byte)(number & 0xff);
+            number >>= 8;
+        }
+
+        for ( int i = 0; i < bytes; i++ )
+        {
+            os.write(buffer[bytes-i-1]);
+        }
+    }
+
+    private void writeStreamData(OutputStream os, int[] w) throws IOException
+    {
+        // iterate over all streamData and write it in the required format
+        for ( Object entry : streamData.values() )
+        {
+            if (entry instanceof FreeReference)
+            {
+                FreeReference free = (FreeReference)entry;
+                writeNumber(os, ENTRY_FREE, w[0]);
+                writeNumber(os, free.nextFree, w[1]);
+                writeNumber(os, free.nextGenNumber, w[2]);
+            }
+            else if (entry instanceof NormalReference)
+            {
+                NormalReference ref = (NormalReference)entry;
+                writeNumber(os, ENTRY_NORMAL, w[0]);
+                writeNumber(os, ref.offset, w[1]);
+                writeNumber(os, ref.genNumber, w[2]);
+            }
+            else if (entry instanceof ObjectStreamReference)
+            {
+                ObjectStreamReference objStream = (ObjectStreamReference)entry;
+                writeNumber(os, ENTRY_OBJSTREAM, w[0]);
+                writeNumber(os, objStream.offset, w[1]);
+                writeNumber(os, objStream.objectNumberOfObjectStream, w[2]);
+            }
+            // TODO add here if new standard versions define new types
+            else
+            {
+                throw new RuntimeException("unexpected reference type");
+            }
+        }
+        os.flush();
+        os.close();
+    }
+
+    /**
+     * A class representing an object stream reference. 
+     *
+     */
+    class ObjectStreamReference
+    {
+        long objectNumberOfObjectStream;
+        long offset;
+    }
+
+    /**
+     * A class representing a normal reference. 
+     *
+     */
+    class NormalReference
+    {
+        long genNumber;
+        long offset;
+    }
+
+    /**
+     * A class representing a free reference. 
+     *
+     */
+    class FreeReference
+    {
+        long nextGenNumber;
+        long nextFree;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public COSObject getObject(int objectNumber)
+    {
+        return null;
+    }
+}

Propchange: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/PDFXRefStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java?rev=1451638&r1=1451637&r2=1451638&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java Fri Mar  1 16:33:56 2013
@@ -55,6 +55,7 @@ import org.apache.pdfbox.cos.ICOSVisitor
 import org.apache.pdfbox.exceptions.COSVisitorException;
 import org.apache.pdfbox.exceptions.CryptographyException;
 import org.apache.pdfbox.exceptions.SignatureException;
+import org.apache.pdfbox.pdfparser.PDFXRefStream;
 import org.apache.pdfbox.pdmodel.PDDocument;
 import org.apache.pdfbox.pdmodel.encryption.SecurityHandler;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
@@ -429,7 +430,7 @@ public class COSWriter implements ICOSVi
 
         while( objectsToWrite.size() > 0 )
         {
-            COSBase nextObject = (COSBase)objectsToWrite.removeFirst();
+            COSBase nextObject = objectsToWrite.removeFirst();
             objectsToWriteSet.remove(nextObject);
             doWriteObject( nextObject );
         }
@@ -444,7 +445,7 @@ public class COSWriter implements ICOSVi
 
         while( objectsToWrite.size() > 0 )
         {
-            COSBase nextObject = (COSBase)objectsToWrite.removeFirst();
+            COSBase nextObject = objectsToWrite.removeFirst();
             objectsToWriteSet.remove(nextObject);
             doWriteObject( nextObject );
         }
@@ -504,7 +505,7 @@ public class COSWriter implements ICOSVi
             {
                 COSDictionary dict = (COSDictionary)obj;
                 COSName item = (COSName)dict.getItem(COSName.TYPE);
-                if(COSName.SIG.equals(item)) 
+                if (COSName.SIG.equals(item) || COSName.DOC_TIME_STAMP.equals(item))
                 {
                     reachedSignature = true;
                 }
@@ -575,21 +576,7 @@ public class COSWriter implements ICOSVi
         // Remove a checksum if present
         trailer.removeItem( COSName.DOC_CHECKSUM );
         
-        /**
-        COSObject catalog = doc.getCatalog();
-        if (catalog != null)
-        {
-            trailer.setItem(COSName.getPDFName("Root"), catalog);
-        }
-        */
         trailer.accept(this);
-
-        getStandardOutput().write(STARTXREF);
-        getStandardOutput().writeEOL();
-        getStandardOutput().write(String.valueOf(getStartxref()).getBytes("ISO-8859-1"));
-        getStandardOutput().writeEOL();
-        getStandardOutput().write(EOF);
-        getStandardOutput().writeEOL();
     }
 
     /**
@@ -605,64 +592,142 @@ public class COSWriter implements ICOSVi
      */
     protected void doWriteXRef(COSDocument doc) throws IOException
     {
-        // sort xref, needed only if object keys not regenerated
-        Collections.sort(getXRefEntries());
-        COSWriterXRefEntry lastEntry = getXRefEntries().get( getXRefEntries().size()-1 );
-
-        // remember the position where x ref is written
-        setStartxref(getStandardOutput().getPos());
-        //
-        getStandardOutput().write(XREF);
-        getStandardOutput().writeEOL();
-        // write start object number and object count for this x ref section
-        // we assume starting from scratch
-        writeXrefRange(0, lastEntry.getKey().getNumber() + 1);
-        // write initial start object with ref to first deleted object and magic generation number
-        writeXrefEntry(COSWriterXRefEntry.getNullEntry());
-        // write entry for every object
-        long lastObjectNumber = 0;
-        for (Iterator<COSWriterXRefEntry> i = getXRefEntries().iterator(); i.hasNext();)
+        if (doc.isXRefStream())
         {
-            COSWriterXRefEntry entry = i.next();
-            while( lastObjectNumber<entry.getKey().getNumber()-1 )
+            // sort xref, needed only if object keys not regenerated
+            Collections.sort(getXRefEntries());
+            COSWriterXRefEntry lastEntry = getXRefEntries().get( getXRefEntries().size()-1 );
+    
+            // remember the position where x ref is written
+            setStartxref(getStandardOutput().getPos());
+            //
+            getStandardOutput().write(XREF);
+            getStandardOutput().writeEOL();
+            // write start object number and object count for this x ref section
+            // we assume starting from scratch
+            writeXrefRange(0, lastEntry.getKey().getNumber() + 1);
+            // write initial start object with ref to first deleted object and magic generation number
+            writeXrefEntry(COSWriterXRefEntry.getNullEntry());
+            // write entry for every object
+            long lastObjectNumber = 0;
+            for (Iterator<COSWriterXRefEntry> i = getXRefEntries().iterator(); i.hasNext();)
             {
-              writeXrefEntry(COSWriterXRefEntry.getNullEntry());
+                COSWriterXRefEntry entry = i.next();
+                while( lastObjectNumber<entry.getKey().getNumber()-1 )
+                {
+                  writeXrefEntry(COSWriterXRefEntry.getNullEntry());
+                }
+                lastObjectNumber = entry.getKey().getNumber();
+                writeXrefEntry(entry);
+            }
+        }
+        else
+        {
+
+            COSDictionary trailer = doc.getTrailer();
+            trailer.setLong(COSName.PREV, doc.getStartXref());
+            addXRefEntry(COSWriterXRefEntry.getNullEntry());
+
+            // sort xref, needed only if object keys not regenerated
+            Collections.sort(getXRefEntries());
+
+            // remember the position where x ref was written
+            setStartxref(getStandardOutput().getPos());
+
+            getStandardOutput().write(XREF);
+            getStandardOutput().writeEOL();
+            // write start object number and object count for this x ref section
+            // we assume starting from scratch
+
+            Integer[] xRefRanges = getXRefRanges(getXRefEntries());
+            int xRefLength = xRefRanges.length;
+            int x = 0;
+            int j = 0;
+            while (x < xRefLength && (xRefLength % 2) == 0)
+            {
+                writeXrefRange(xRefRanges[x], xRefRanges[x + 1]);
+
+                for ( int i = 0; i < xRefRanges[x + 1]; ++i )
+                {
+                    writeXrefEntry(xRefEntries.get(j++));
+                }
+                x += 2;
             }
-            lastObjectNumber = entry.getKey().getNumber();
-            writeXrefEntry(entry);
         }
     }
 
-    private void doWriteXRefInc(COSDocument doc) throws IOException
+    private void doWriteXRefInc(COSDocument doc, long hybridPrev) throws IOException, COSVisitorException
     {
-        COSDictionary trailer = doc.getTrailer();
-        trailer.setLong(COSName.PREV, doc.getStartXref());
-        addXRefEntry(COSWriterXRefEntry.getNullEntry());
+        if (doc.isXRefStream() || hybridPrev != -1)
+        {
+            // the file uses XrefStreams, so we need to update
+            // it with an xref stream. We create a new one and fill it
+            // with data available here
+            // first set an entry for the null entry in the xref table
+            // this is probably not necessary
+            // addXRefEntry(COSWriterXRefEntry.getNullEntry());
 
-        // sort xref, needed only if object keys not regenerated
-        Collections.sort(getXRefEntries());
-      
-        // remember the position where x ref was written
-        setStartxref(getStandardOutput().getPos());
+            // create a new XRefStrema object
+            PDFXRefStream pdfxRefStream = new PDFXRefStream();
 
-        getStandardOutput().write(XREF);
-        getStandardOutput().writeEOL();
-        // write start object number and object count for this x ref section
-        // we assume starting from scratch
+            // add all entries from the incremental update.
+            List<COSWriterXRefEntry> xRefEntries2 = getXRefEntries();
+            for ( COSWriterXRefEntry cosWriterXRefEntry : xRefEntries2 )
+            {
+                pdfxRefStream.addEntry(cosWriterXRefEntry);
+            }
 
-        Integer[] xRefRanges = getXRefRanges(getXRefEntries());
-        int xRefLength = xRefRanges.length;
-        int x = 0;
-        int j = 0;
-        while(x < xRefLength && (xRefLength % 2) == 0)
-        {
-            writeXrefRange(xRefRanges[x], xRefRanges[x + 1]);
+            COSDictionary trailer = doc.getTrailer();
+            //            trailer.setLong(COSName.PREV, hybridPrev == -1 ? prev : hybridPrev);
+            trailer.setLong(COSName.PREV, doc.getStartXref());
+
+            pdfxRefStream.addTrailerInfo(trailer);
+            // the size is the highest object number+1. we add one more
+            // for the xref stream object we are going to write
+            pdfxRefStream.setSize(getNumber() + 2);
 
-            for(int i = 0; i < xRefRanges[x + 1]; ++i)
+            setStartxref(getStandardOutput().getPos());
+            COSStream stream2 = pdfxRefStream.getStream();
+            doWriteObject(stream2);
+        }
+
+        if (!doc.isXRefStream() || hybridPrev != -1)
+        {
+            COSDictionary trailer = doc.getTrailer();
+            trailer.setLong(COSName.PREV, doc.getStartXref());
+            if (hybridPrev != -1)
             {
-                writeXrefEntry(xRefEntries.get(j++));
+                COSName xrefStm = COSName.XREF_STM;
+                trailer.removeItem(xrefStm);
+                trailer.setLong(xrefStm, getStartxref());
+            }
+            addXRefEntry(COSWriterXRefEntry.getNullEntry());
+    
+            // sort xref, needed only if object keys not regenerated
+            Collections.sort(getXRefEntries());
+          
+            // remember the position where x ref was written
+            setStartxref(getStandardOutput().getPos());
+    
+            getStandardOutput().write(XREF);
+            getStandardOutput().writeEOL();
+            // write start object number and object count for this x ref section
+            // we assume starting from scratch
+    
+            Integer[] xRefRanges = getXRefRanges(getXRefEntries());
+            int xRefLength = xRefRanges.length;
+            int x = 0;
+            int j = 0;
+            while(x < xRefLength && (xRefLength % 2) == 0)
+            {
+                writeXrefRange(xRefRanges[x], xRefRanges[x + 1]);
+    
+                for(int i = 0; i < xRefRanges[x + 1]; ++i)
+                {
+                    writeXrefEntry(xRefEntries.get(j++));
+                }
+                x += 2;
             }
-            x += 2;
         }
     }
 
@@ -1054,15 +1119,39 @@ public class COSWriter implements ICOSVi
                 doWriteHeader(doc);
             }
             doWriteBody(doc);
+            
+            // get the previous trailer
+            COSDictionary trailer = doc.getTrailer();
+            long hybridPrev = -1;
+
+            if (trailer != null)
+            {
+                hybridPrev = trailer.getLong(COSName.XREF_STM);
+            }
+            
             if(incrementalUpdate)
             {
-                doWriteXRefInc(doc);
+                doWriteXRefInc(doc, hybridPrev);
             }
             else
             {
                 doWriteXRef(doc);
             }
-            doWriteTrailer(doc);
+            
+            // the trailer section should only be used for xref tables not for xref streams
+            if (!doc.isXRefStream() || hybridPrev != -1)
+            {
+                doWriteTrailer(doc);
+            }
+            
+            // write endof
+            getStandardOutput().write(STARTXREF);
+            getStandardOutput().writeEOL();
+            getStandardOutput().write(String.valueOf(getStartxref()).getBytes("ISO-8859-1"));
+            getStandardOutput().writeEOL();
+            getStandardOutput().write(EOF);
+            getStandardOutput().writeEOL();
+            
             if(incrementalUpdate)
             {
                 doWriteSignature(doc);
@@ -1205,42 +1294,61 @@ public class COSWriter implements ICOSVi
     public Object visitFromStream(COSStream obj) throws COSVisitorException
     {
         InputStream input = null;
-
         try
         {
-            if(willEncrypt)
+            if (willEncrypt)
             {
-                document.getSecurityHandler().encryptStream(
-                    obj,
-                    currentObjectKey.getNumber(),
-                    currentObjectKey.getGeneration());
+                document.getSecurityHandler().encryptStream(obj, currentObjectKey.getNumber()
+                        , currentObjectKey.getGeneration());
             }
 
-            input = obj.getFilteredStream();
-            // set the length of the stream and write stream dictionary
-            COSObject lengthObject = new COSObject( null );
+            COSObject lengthObject = null;
+            // check if the length object is required to be direct, like in
+            // a cross reference stream dictionary
+            COSBase lengthEntry = obj.getDictionaryObject(COSName.LENGTH);
+            String type = obj.getNameAsString(COSName.TYPE);
+            if (lengthEntry != null && lengthEntry.isDirect() || "XRef".equals(type))
+            {
+                // the length might be the non encoded length,
+                // set the real one as direct object
+                COSInteger cosInteger = COSInteger.get(obj.getFilteredLength());
+                cosInteger.setDirect(true);
+                obj.setItem(COSName.LENGTH, cosInteger);
 
-            obj.setItem(COSName.LENGTH, lengthObject);
+            }
+            else
+            {
+                // make the length an implicit indirect object
+                // set the length of the stream and write stream dictionary
+                lengthObject = new COSObject(null);
+
+                obj.setItem(COSName.LENGTH, lengthObject);
+            }
+            input = obj.getFilteredStream();
             //obj.accept(this);
             // write the stream content
-            visitFromDictionary( obj );
+            visitFromDictionary(obj);
             getStandardOutput().write(STREAM);
             getStandardOutput().writeCRLF();
             byte[] buffer = new byte[1024];
             int amountRead = 0;
             int totalAmountWritten = 0;
-            while( (amountRead = input.read(buffer,0,1024)) != -1 )
+            while ((amountRead = input.read(buffer, 0, 1024)) != -1)
             {
-                getStandardOutput().write( buffer, 0, amountRead );
+                getStandardOutput().write(buffer, 0, amountRead);
                 totalAmountWritten += amountRead;
             }
-            lengthObject.setObject( COSInteger.get( totalAmountWritten ) );
+            // set the length as an indirect object
+            if (lengthObject != null)
+            {
+                lengthObject.setObject(COSInteger.get(totalAmountWritten));
+            }
             getStandardOutput().writeCRLF();
             getStandardOutput().write(ENDSTREAM);
             getStandardOutput().writeEOL();
             return null;
         }
-        catch( Exception e )
+        catch (Exception e)
         {
             throw new COSVisitorException(e);
         }
@@ -1392,3 +1500,4 @@ public class COSWriter implements ICOSVi
         cosDoc.accept(this);
     }
 }
+

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=1451638&r1=1451637&r2=1451638&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 Fri Mar  1 16:33:56 2013
@@ -31,6 +31,7 @@ import java.io.OutputStream;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -62,12 +63,14 @@ import org.apache.pdfbox.pdmodel.encrypt
 import org.apache.pdfbox.pdmodel.encryption.SecurityHandlersManager;
 import org.apache.pdfbox.pdmodel.encryption.StandardDecryptionMaterial;
 import org.apache.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
 import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
 import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
 import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
 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;
 
 /**
@@ -110,7 +113,7 @@ public class PDDocument implements Pagea
     
     /**
      * This will hold a flag which tells us if we should remove all security
-     * from this documents
+     * from this documents.
      */
     private boolean allSecurityToBeRemoved = false;
     
@@ -261,7 +264,16 @@ public class PDDocument implements Pagea
         rootPages.updateCount();
     }
 
-    public void addSignature(PDSignature sigObject, SignatureInterface signatureInterface) throws IOException, SignatureException
+    /**
+     * Add a signature.
+     * 
+     * @param sigObject is the PDSignature model
+     * @param signatureInterface is a interface which provides signing capabilities
+     * @throws IOException if there is an error creating required fields
+     * @throws SignatureException if something went wrong
+     */
+    public void addSignature(PDSignature sigObject, SignatureInterface signatureInterface) 
+            throws IOException, SignatureException
     {
         SignatureOptions defaultOptions = new SignatureOptions();
         defaultOptions.setPage(1);
@@ -273,10 +285,12 @@ public class PDDocument implements Pagea
      *
      * @param sigObject is the PDSignature model
      * @param signatureInterface is a interface which provides signing capabilities
-     * @param options 
+     * @param options signature options
      * @throws IOException if there is an error creating required fields
+     * @throws SignatureException if something went wrong
      */
-    public void addSignature(PDSignature sigObject, SignatureInterface signatureInterface, SignatureOptions options) throws IOException, SignatureException
+    public void addSignature(PDSignature sigObject, SignatureInterface signatureInterface, SignatureOptions options)
+            throws IOException, SignatureException
     {
         // Reserve content
         // We need to reserve some space for the signature. Some signatures including
@@ -325,7 +339,6 @@ public class PDDocument implements Pagea
         {
             page = kids.get(options.getPage()-1);
         }
-      
 
         // Get the AcroForm from the Root-Dictionary and append the annotation
         PDAcroForm acroForm = root.getAcroForm();
@@ -349,17 +362,52 @@ public class PDDocument implements Pagea
          */
 
         // Create Annotation / Field for signature
-        PDSignatureField signatureField = new PDSignatureField(acroForm);
-        signatureField.setSignature(sigObject);              // append the signature object
-        signatureField.getWidget().setPage(page);            // backward linking
-      
+        List<PDAnnotation> annotations = page.getAnnotations();
+
+        List<PDField> fields = acroForm.getFields();
+        PDSignatureField signatureField = null;
+        for ( PDField pdField : fields )
+        {
+            if (pdField instanceof PDSignatureField)
+            {
+                PDSignature signature = ((PDSignatureField)pdField).getSignature();
+                if (signature != null && signature.getDictionary().equals(sigObject.getDictionary()))
+                {
+                    signatureField = (PDSignatureField)pdField;
+                }
+            }
+        }
+        if (signatureField == null)
+        {
+            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();
+        List<PDField> acroFormFields = acroForm.getFields();
         COSDictionary acroFormDict = acroForm.getDictionary();
         acroFormDict.setDirect(true);
         acroFormDict.setInt(COSName.SIG_FLAGS, 3);
-        acroFormFields.add(signatureField);
-
+        
+        boolean checkFields = false;
+        for ( PDField field : acroFormFields )
+        {
+            if (field instanceof PDSignatureField)
+            {
+                if (((PDSignatureField)field).getCOSObject().equals(signatureField.getCOSObject()))
+                {
+                    checkFields = true;
+                    signatureField.getCOSObject().setNeedToBeUpdate(true);
+                    break;
+                }
+            }
+        }
+        if (!checkFields)
+        {
+            acroFormFields.add(signatureField);
+        }
+        
         // Get the object from the visual signature
         COSDocument visualSignature = options.getVisualSignature();
 
@@ -418,7 +466,8 @@ public class PDDocument implements Pagea
                         COSDictionary cosBaseDict = (COSDictionary)base;
               
                         // Appearance Dictionary auslesen und setzen
-                        PDAppearanceDictionary ap = new PDAppearanceDictionary((COSDictionary)cosBaseDict.getItem(COSName.AP));
+                        PDAppearanceDictionary ap = 
+                                new PDAppearanceDictionary((COSDictionary)cosBaseDict.getItem(COSName.AP));
                         ap.getDictionary().setDirect(true);
                         signatureField.getWidget().setAppearance(ap);
               
@@ -434,21 +483,22 @@ public class PDDocument implements Pagea
         
             if (annotNotFound || sigFieldNotFound )
             {
-                throw new SignatureException(SignatureException.VISUAL_SIGNATURE_INVALID, "Could not read all needed objects from template");
+                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();
             page.setAnnotations(annotations);
         }
         // take care that page and acroforms do not share the same array (if so, we don't need to add it twice)
-        if (!(annotations instanceof COSArrayList) 
-                || !(acroFormFields instanceof COSArrayList) 
-                || !((COSArrayList)annotations).toList().equals(((COSArrayList)acroFormFields).toList()))
+        if (!((annotations instanceof COSArrayList) 
+                && (acroFormFields instanceof COSArrayList) 
+                && (((COSArrayList)annotations).toList().equals(((COSArrayList)acroFormFields).toList())))
+                && !checkFields)
         {
             annotations.add(signatureField.getWidget());
         }
@@ -456,6 +506,81 @@ public class PDDocument implements Pagea
     }
 
     /**
+     * This will add a signaturefield to the document.
+     * @param sigFields are the PDSignatureFields that should be added to the document
+     * @param signatureInterface is a interface which provides signing capabilities
+     * @param options signature options
+     * @throws IOException if there is an error creating required fields
+     * @throws SignatureException 
+     */
+    public void addSignatureField(List<PDSignatureField> sigFields,
+                                  SignatureInterface signatureInterface,
+                                  SignatureOptions options) throws IOException, SignatureException
+    {
+        PDDocumentCatalog catalog = getDocumentCatalog();
+        catalog.getCOSObject().setNeedToBeUpdate(true);
+
+        PDAcroForm acroForm = catalog.getAcroForm();
+        if (acroForm == null)
+        {
+            acroForm = new PDAcroForm(this);
+            catalog.setAcroForm(acroForm);
+        }
+        else
+        {
+            acroForm.getCOSObject().setNeedToBeUpdate(true);
+        }
+
+        COSDictionary acroFormDict = acroForm.getDictionary();
+        acroFormDict.setDirect(true);
+        acroFormDict.setNeedToBeUpdate(true);
+        if (acroFormDict.getInt(COSName.SIG_FLAGS) < 1)
+        {
+            acroFormDict.setInt(COSName.SIG_FLAGS, 1); // 1 if at least one signature field is available
+        }
+
+        List<PDField> field = acroForm.getFields();
+
+        for ( PDSignatureField sigField : sigFields )
+        {
+            PDSignature sigObject = sigField.getSignature();
+            sigField.getCOSObject().setNeedToBeUpdate(true);
+
+            // Check if the field already exist
+            boolean checkFields = false;
+            for ( Object obj : field )
+            {
+                if (obj instanceof PDSignatureField)
+                {
+                    if (((PDSignatureField)obj).getCOSObject().equals(sigField.getCOSObject()))
+                    {
+                        checkFields=true;
+                        sigField.getCOSObject().setNeedToBeUpdate(true);
+                        break;
+                    }
+                }
+            }
+            
+            if (!checkFields)
+            {
+                field.add(sigField);
+            }
+
+            // Check if we need to add a signature
+            if (sigField.getSignature() != null)
+            {
+                sigField.getCOSObject().setNeedToBeUpdate(true);
+                if (options == null)
+                {
+
+                }
+                addSignature(sigField.getSignature(), signatureInterface, options);
+            }
+        }
+    }
+
+    
+    /**
      * Remove the page from the document.
      *
      * @param page The page to remove from the document.
@@ -468,8 +593,7 @@ public class PDDocument implements Pagea
         boolean retval = parent.getKids().remove( page );
         if( retval )
         {
-            //do a recursive updateCount starting at the root
-            //of the document
+            //do a recursive updateCount starting at the root of the document
             getDocumentCatalog().getPages().updateCount();
         }
         return retval;
@@ -608,13 +732,14 @@ public class PDDocument implements Pagea
         {
             COSDictionary trailer = document.getTrailer();
             COSBase dictionary = trailer.getDictionaryObject( COSName.ROOT );
-            if (dictionary instanceof COSDictionary) {
-                documentCatalog =
-                    new PDDocumentCatalog(this, (COSDictionary) dictionary);
-            } else {
+            if (dictionary instanceof COSDictionary) 
+            {
+                documentCatalog = new PDDocumentCatalog(this, (COSDictionary) dictionary);
+            } 
+            else 
+            {
                 documentCatalog = new PDDocumentCatalog(this);
             }
-
         }
         return documentCatalog;
     }
@@ -664,15 +789,72 @@ public class PDDocument implements Pagea
         encParameters = encDictionary;
     }
 
-    public PDSignature getSignatureDictionary() throws IOException 
+    /**
+     * This will return the last signature.
+     *
+     * @return the last signature as <code>PDSignature</code>.
+     * @throws IOException if no document catalog can be found.
+     * @deprecated use {@link #getLastSignatureDictionary()} instead.
+     */
+    @Deprecated
+    public PDSignature getSignatureDictionary() throws IOException
+    {
+        return getLastSignatureDictionary();
+    }
+
+    /**
+     * This will return the last signature.
+     *
+     * @return the last signature as <code>PDSignature</code>.
+     * @throws IOException if no document catalog can be found.
+     */
+    public PDSignature getLastSignatureDictionary() throws IOException
     {
-      COSDictionary signatureDictionary = document.getLastSignatureDictionary();
+        List<PDSignature> signatureDictionaries = getSignatureDictionaries();
+        int size = signatureDictionaries.size();
+        if (size > 0)
+        {
+            return signatureDictionaries.get(size - 1);
+        }
+        return null;
+    }
+    
+    /**
+     * Retrieve all signature fields from the document.
+     *
+     * @return a <code>List</code> of <code>PDSignatureField</code>s
+     * @throws IOException if no document catalog can be found.
+     */
+    public List<PDSignatureField> getSignatureFields() throws IOException
+    {
+        List<PDSignatureField> fields = new LinkedList<PDSignatureField>();
+        PDAcroForm acroForm = getDocumentCatalog().getAcroForm();
+        if (acroForm != null)
+        {
+            List<COSDictionary> signatureDictionary = document.getSignatureFields(false);
+            for ( COSDictionary dict : signatureDictionary )
+            {
+                fields.add(new PDSignatureField(acroForm, dict));
+            }
+        }
+        return fields;
+    }
 
-      if (signatureDictionary!= null)
-      {
-        return new PDSignature(signatureDictionary);
-      }
-      return null;
+    /**
+     * Retrieve all signature dictionaries from the document.
+     *
+     * @return a <code>List</code> of <code>PDSignature</code>s
+     * @throws IOException if no document catalog can be found.
+     */
+    public List<PDSignature> getSignatureDictionaries() throws IOException
+    {
+        List<COSDictionary> signatureDictionary = document.getSignatureDictionaries();
+        List<PDSignature> signatures = new LinkedList<PDSignature>();
+        for ( COSDictionary dict : signatureDictionary )
+        {
+            signatures.add(new PDSignature(dict));
+        }
+        return signatures;
     }
     
     /**
@@ -691,40 +873,7 @@ public class PDDocument implements Pagea
     @Deprecated
     public boolean isUserPassword( String password ) throws IOException, CryptographyException
     {
-            return false;
-        /*boolean retval = false;
-        if( password == null )
-        {
-            password = "";
-        }
-        PDFEncryption encryptor = new PDFEncryption();
-        PDEncryptionDictionary encryptionDictionary = getEncryptionDictionary();
-        if( encryptionDictionary == null )
-        {
-            throw new IOException( "Error: Document is not encrypted" );
-        }
-        else
-        {
-            if( encryptionDictionary instanceof PDStandardEncryption )
-            {
-                COSString documentID = (COSString)document.getDocumentID().get(0);
-                PDStandardEncryption standard = (PDStandardEncryption)encryptionDictionary;
-                retval = encryptor.isUserPassword(
-                    password.getBytes("ISO-8859-1"),
-                    standard.getUserKey(),
-                    standard.getOwnerKey(),
-                    standard.getPermissions(),
-                    documentID.getBytes(),
-                    standard.getRevision(),
-                    standard.getLength()/8 );
-            }
-            else
-            {
-                throw new IOException( "Error: Encyption dictionary is not 'Standard'" +
-                    encryptionDictionary.getClass().getName() );
-            }
-        }
-        return retval;*/
+        return false;
     }
 
     /**
@@ -743,40 +892,7 @@ public class PDDocument implements Pagea
     @Deprecated
     public boolean isOwnerPassword( String password ) throws IOException, CryptographyException
     {
-            return false;
-        /*boolean retval = false;
-        if( password == null )
-        {
-            password = "";
-        }
-        PDFEncryption encryptor = new PDFEncryption();
-        PDEncryptionDictionary encryptionDictionary = getEncryptionDictionary();
-        if( encryptionDictionary == null )
-        {
-            throw new IOException( "Error: Document is not encrypted" );
-        }
-        else
-        {
-            if( encryptionDictionary instanceof PDStandardEncryption )
-            {
-                COSString documentID = (COSString)document.getDocumentID().get( 0 );
-                PDStandardEncryption standard = (PDStandardEncryption)encryptionDictionary;
-                retval = encryptor.isOwnerPassword(
-                    password.getBytes("ISO-8859-1"),
-                    standard.getUserKey(),
-                    standard.getOwnerKey(),
-                    standard.getPermissions(),
-                    documentID.getBytes(),
-                    standard.getRevision(),
-                    standard.getLength()/8 );
-            }
-            else
-            {
-                throw new IOException( "Error: Encyption dictionary is not 'Standard'" +
-                    encryptionDictionary.getClass().getName() );
-            }
-        }
-        return retval;*/
+        return false;
     }
 
     /**
@@ -1166,11 +1282,26 @@ public class PDDocument implements Pagea
         }
     }
 
+    /** 
+     * Save the pdf as incremental.
+     * 
+     * @param fileName the filename to be used
+     * @throws IOException if something went wrong
+     * @throws COSVisitorException  if something went wrong
+     */
     public void saveIncremental( String fileName ) throws IOException, COSVisitorException
     {
         saveIncremental( new FileInputStream( fileName ) , new FileOutputStream( fileName , true) );
     }
     
+    /** 
+     * Save the pdf as incremental.
+     * 
+     * @param input 
+     * @param output
+     * @throws IOException if something went wrong
+     * @throws COSVisitorException  if something went wrong
+     */
     public void saveIncremental( FileInputStream input, OutputStream output ) throws IOException, COSVisitorException
     {
         //update the count in case any pages have been added behind the scenes.
@@ -1225,17 +1356,19 @@ public class PDDocument implements Pagea
      * default printer job returned by  {@link PrinterJob#getPrinterJob()}.
      *
      * @deprecated Use the {@link PDPageable} adapter class
-     * @param i page index, zero-based
+     * @param pageIndex page index, zero-based
      * @return page format
-     * @throws IndexOutOfBoundsException if the page index is invalid
      */
     @Deprecated
     public PageFormat getPageFormat(int pageIndex)
     {
-        try {
+        try 
+        {
             PrinterJob printerJob = PrinterJob.getPrinterJob();
             return new PDPageable(this, printerJob).getPageFormat(pageIndex);
-        } catch (PrinterException e) {
+        } 
+        catch (PrinterException e) 
+        {
             throw new RuntimeException(e);
         }
     } 
@@ -1305,12 +1438,17 @@ public class PDDocument implements Pagea
         print(printJob, true);
     }
 
-    private void print(PrinterJob job, boolean silent) throws PrinterException {
-        if (job == null) {
+    private void print(PrinterJob job, boolean silent) throws PrinterException 
+    {
+        if (job == null) 
+        {
             throw new PrinterException("The given printer job is null.");
-        } else {
+        } 
+        else 
+        {
             job.setPageable(new PDPageable(this, job));
-            if (silent || job.printDialog()) {
+            if (silent || job.printDialog()) 
+            {
                 job.print();
             }
         }
@@ -1406,26 +1544,37 @@ public class PDDocument implements Pagea
     /**
      * Sets security handler if none is set already.
      * 
-     * @param _sHandler  security handler to be assigned to document
+     * @param secHandler security handler to be assigned to document
      * @return  <code>true</code> if security handler was set, <code>false</code>
      *          otherwise (a security handler was already set)
      */
-    public boolean setSecurityHandler( SecurityHandler _sHandler )
+    public boolean setSecurityHandler(SecurityHandler secHandler)
     {
         if ( securityHandler == null )
         {
-            securityHandler = _sHandler;
+            securityHandler = secHandler;
             return true;
         }
         return false;
     }
 
-    public boolean isAllSecurityToBeRemoved() {
+    /**
+     * Indicates if all security is removed or not when writing the pdf.
+     * @return returns true if all security shall be removed otherwise false
+     */
+    public boolean isAllSecurityToBeRemoved()
+    {
         return allSecurityToBeRemoved;
     }
 
-    public void setAllSecurityToBeRemoved(boolean allSecurityToBeRemoved) {
-        this.allSecurityToBeRemoved = allSecurityToBeRemoved;
+    /**
+     * Activates/Deactivates the removal of all security when writing the pdf.
+     *  
+     * @param removeAllSecurity remove all security if set to true
+     */
+    public void setAllSecurityToBeRemoved(boolean removeAllSecurity)
+    {
+        allSecurityToBeRemoved = removeAllSecurity;
     }
-
 }
+