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;
}
-
}
+