You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ad...@apache.org on 2011/04/16 04:38:31 UTC
svn commit: r1092855 - in
/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox: cos/ pdfparser/
pdfwriter/ pdmodel/ pdmodel/interactive/annotation/
pdmodel/interactive/digitalsignature/ pdmodel/interactive/form/
Author: adam
Date: Sat Apr 16 02:38:31 2011
New Revision: 1092855
URL: http://svn.apache.org/viewvc?rev=1092855&view=rev
Log:
PDFBOX-912: PDF signing interface and improvements
Modified:
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSArray.java
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSBase.java
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/COSString.java
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfparser/PDFParser.java
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSStandardOutputStream.java
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriterXRefEntry.java
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotation.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/PDField.java
pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDFieldFactory.java
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSArray.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSArray.java?rev=1092855&r1=1092854&r2=1092855&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSArray.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSArray.java Sat Apr 16 02:38:31 2011
@@ -207,7 +207,7 @@ public class COSArray extends COSBase im
*/
public COSBase get( int index )
{
- return (COSBase)objects.get( index );
+ return objects.get( index );
}
/**
@@ -356,7 +356,7 @@ public class COSArray extends COSBase im
*/
public COSBase remove( int i )
{
- return (COSBase)objects.remove( i );
+ return objects.remove( i );
}
/**
@@ -404,6 +404,7 @@ public class COSArray extends COSBase im
/**
* {@inheritDoc}
*/
+ @Override
public String toString()
{
return "COSArray{" + objects + "}";
@@ -503,6 +504,7 @@ public class COSArray extends COSBase im
* @return any object, depending on the visitor implementation, or null
* @throws COSVisitorException If an error occurs while visiting this object.
*/
+ @Override
public Object accept(ICOSVisitor visitor) throws COSVisitorException
{
return visitor.visitFromArray(this);
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSBase.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSBase.java?rev=1092855&r1=1092854&r2=1092855&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSBase.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSBase.java Sat Apr 16 02:38:31 2011
@@ -32,8 +32,14 @@ public abstract class COSBase implements
/**
* Constructor.
*/
+
+ private boolean needToBeUpdate;
+
+ private boolean direct;
+
public COSBase()
{
+ needToBeUpdate = false;
}
/**
@@ -69,4 +75,36 @@ public abstract class COSBase implements
* @throws COSVisitorException If an error occurs while visiting this object.
*/
public abstract Object accept(ICOSVisitor visitor) throws COSVisitorException;
+
+ public void setNeedToBeUpdate(boolean flag)
+ {
+ needToBeUpdate = flag;
+ }
+
+ /**
+ * If the state is set true, the dictionary will be written direct into the called object.
+ * This means, no indirect object will be created.
+ *
+ * @return the state
+ */
+ public boolean isDirect()
+ {
+ return direct;
+ }
+
+ /**
+ * Set the state true, if the dictionary should be written as a direct object and not indirect.
+ *
+ * @param direct set it true, for writting direct object
+ */
+ public void setDirect(boolean direct)
+ {
+ this.direct = direct;
+ }
+
+ public boolean isNeedToBeUpdate()
+ {
+ return needToBeUpdate;
+ }
+
}
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=1092855&r1=1092854&r2=1092855&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 Sat Apr 16 02:38:31 2011
@@ -31,6 +31,7 @@ import org.apache.pdfbox.io.RandomAccess
import org.apache.pdfbox.io.RandomAccessFile;
import org.apache.pdfbox.pdfparser.PDFObjectStreamParser;
import org.apache.pdfbox.pdfparser.PDFXrefStreamParser;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
import org.apache.pdfbox.persistence.util.COSObjectKey;
/**
@@ -67,6 +68,16 @@ public class COSDocument extends COSBase
* Document trailer dictionary.
*/
private COSDictionary trailer;
+
+ /**
+ * Document signature dictionary
+ */
+ private COSDictionary signDictionary = null;
+
+ /**
+ * Some doc
+ */
+ private SignatureInterface signatureInterface;
/**
* This file will store the streams in order to conserve memory.
@@ -78,7 +89,9 @@ public class COSDocument extends COSBase
private String headerString = "%PDF-1.4";
private boolean warnMissingClose = true;
-
+
+ private int startXref;
+
private boolean closed = false;
/**
@@ -313,9 +326,13 @@ public class COSDocument extends COSBase
return (COSDictionary)trailer.getDictionaryObject( COSName.ENCRYPT );
}
+ public SignatureInterface getSignatureInterface() {
+ return signatureInterface;
+ }
+
/**
* This will set the encryption dictionary, this should only be called when
- * encypting the document.
+ * encrypting the document.
*
* @param encDictionary The encryption dictionary.
*/
@@ -324,6 +341,35 @@ public class COSDocument extends COSBase
trailer.setItem( COSName.ENCRYPT, encDictionary );
}
+ public COSDictionary getLastSignatureDictionary() throws IOException {
+ if (signDictionary == null)
+ {
+ COSObject documentCatalog = getCatalog();
+ if (documentCatalog != null)
+ {
+ COSDictionary acroForm = (COSDictionary)documentCatalog.getDictionaryObject(COSName.getPDFName("AcroForm"));
+ if (acroForm !=null)
+ {
+ COSArray fields = (COSArray)acroForm.getDictionaryObject("Fields");
+ for ( Object object : fields )
+ {
+ COSObject dict = (COSObject)object;
+ if(dict.getItem(COSName.getPDFName("FT")).equals(COSName.getPDFName("Sig")))
+ {
+ COSBase dictionaryObject = dict.getDictionaryObject(COSName.V);
+
+ if (dictionaryObject != null)
+ {
+ signDictionary = (COSDictionary)dictionaryObject;
+ }
+ }
+ }
+ }
+ }
+ }
+ return signDictionary;
+ }
+
/**
* This will get the document ID.
*
@@ -343,6 +389,10 @@ public class COSDocument extends COSBase
{
getTrailer().setItem(COSName.ID, id);
}
+
+ public void setSignatureInterface(SignatureInterface signatureInterface) {
+ this.signatureInterface = signatureInterface;
+ }
/**
* This will get the document catalog.
@@ -401,6 +451,7 @@ public class COSDocument extends COSBase
* @return any object, depending on the visitor implementation, or null
* @throws COSVisitorException If an error occurs while visiting this object.
*/
+ @Override
public Object accept(ICOSVisitor visitor) throws COSVisitorException
{
return visitor.visitFromDocument( this );
@@ -428,6 +479,7 @@ public class COSDocument extends COSBase
* idea for the user to close the PDF document at the earliest possible to conserve resources.
* @throws IOException if an error occurs while closing the temporary files
*/
+ @Override
protected void finalize() throws IOException
{
if (!closed) {
@@ -505,7 +557,7 @@ public class COSDocument extends COSBase
COSObject obj = null;
if( key != null )
{
- obj = (COSObject) objectPool.get(key);
+ obj = objectPool.get(key);
}
if (obj == null)
{
@@ -573,5 +625,25 @@ public class COSDocument extends COSBase
}
setTrailer( trailerDict );
}
+
+ /**
+ * This method set the startxref value of the document. This will only
+ * be needed for incremental updates.
+ *
+ * @param readInt
+ */
+ public void setStartXref(int startXref)
+ {
+ this.startXref = startXref;
+ }
+ /**
+ * Return the startXref Position of the parsed document. This will only be needed for incremental updates.
+ *
+ * @return a int with the old position of the startxref
+ */
+ public int getStartXref()
+ {
+ return startXref;
+ }
}
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=1092855&r1=1092854&r2=1092855&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSName.java Sat Apr 16 02:38:31 2011
@@ -1115,6 +1115,56 @@ public final class COSName extends COSBa
*/
public static final byte[] NAME_ESCAPE = new byte[] { 35 }; //The # character
+ /**
+ * A common COSName value.
+ */
+ public static final COSName SUBFILTER = new COSName("SubFilter");
+ /**
+ * A signature filter value.
+ */
+ public static final COSName ADOBE_PPKLITE = new COSName("Adobe.PPKLite");
+ /**
+ * A signature filter value.
+ */
+ public static final COSName ENTRUST_PPKEF = new COSName("Entrust.PPKEF");
+ /**
+ * A signature filter value.
+ */
+ public static final COSName CICI_SIGNIT = new COSName("CICI.SignIt");
+ /**
+ * A signature filter value.
+ */
+ public static final COSName VERISIGN_PPKVS = new COSName("VeriSign.PPKVS");
+ /**
+ * A signature subfilter value.
+ */
+ public static final COSName ADBE_X509_RSA_SHA1 = new COSName("adbe.x509.rsa_sha1");
+ /**
+ * A signature subfilter value.
+ */
+ public static final COSName ADBE_PKCS7_DETACHED = new COSName("adbe.pkcs7.detached");
+ /**
+ * A signature subfilter value.www
+ */
+ public static final COSName ADBE_PKCS7_SHA1 = new COSName("adbe.pkcs7.sha1");
+ /**
+ * A common COSName value.
+ */
+ public static final COSName LOCATION = new COSName("Location");
+ /**
+ * A common COSName value.
+ */
+ public static final COSName REASON = new COSName("Reason");
+ /**
+ * A common COSName value.
+ */
+ public static final COSName BYTERANGE = new COSName("ByteRange");
+ /**
+ * A common COSName value.
+ */
+ public static final COSName SIG = new COSName("Sig");
+
+
private String name;
private int hashCode;
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSString.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSString.java?rev=1092855&r1=1092854&r2=1092855&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSString.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/cos/COSString.java Sat Apr 16 02:38:31 2011
@@ -21,9 +21,8 @@ import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
-import org.apache.pdfbox.persistence.util.COSHEXTable;
-
import org.apache.pdfbox.exceptions.COSVisitorException;
+import org.apache.pdfbox.persistence.util.COSHEXTable;
/**
* This represents a string object in a PDF document.
@@ -83,6 +82,10 @@ public class COSString extends COSBase
*/
private boolean forceLiteralForm = false;
+ /**
+ * Forces the string to be serialized in hex form but not literal form.
+ */
+ private boolean forceHexForm = false;
/**
* Constructor.
@@ -166,6 +169,18 @@ public class COSString extends COSBase
}
/**
+ * Forces the string to be written in hexadecimal form instead of literal form.
+ *
+ * @param v if v is true the string will be written in hexadecimal form otherwise it will be written in literal if
+ * necessary.
+ */
+
+ public void setForceHexForm(boolean v)
+ {
+ forceHexForm = v;
+ }
+
+ /**
* This will create a COS string from a string of hex characters.
*
* @param hex A hex string.
@@ -322,6 +337,7 @@ public class COSString extends COSBase
/**
* {@inheritDoc}
*/
+ @Override
public String toString()
{
return "COSString{" + this.getString() + "}";
@@ -345,7 +361,7 @@ public class COSString extends COSBase
//outside the ASCII range.
outsideASCII = bytes[i] <0;
}
- if( !outsideASCII || forceLiteralForm )
+ if ((!outsideASCII || forceLiteralForm) && !forceHexForm)
{
output.write(STRING_OPEN);
for( int i=0; i<length; i++ )
@@ -358,7 +374,7 @@ public class COSString extends COSBase
case '\\':
{
output.write(ESCAPE);
- output.write(b);
+ output.write((byte)b);
break;
}
case 10: //LF
@@ -388,7 +404,7 @@ public class COSString extends COSBase
}
default:
{
- output.write( b );
+ output.write( (byte)b );
}
}
}
@@ -414,6 +430,7 @@ public class COSString extends COSBase
* @return any object, depending on the visitor implementation, or null
* @throws COSVisitorException If an error occurs while visiting this object.
*/
+ @Override
public Object accept(ICOSVisitor visitor) throws COSVisitorException
{
return visitor.visitFromString( this );
@@ -422,6 +439,7 @@ public class COSString extends COSBase
/**
* {@inheritDoc}
*/
+ @Override
public boolean equals(Object obj)
{
if (obj instanceof COSString)
@@ -435,6 +453,7 @@ public class COSString extends COSBase
/**
* {@inheritDoc}
*/
+ @Override
public int hashCode()
{
return getString().hashCode();
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=1092855&r1=1092854&r2=1092855&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 Sat Apr 16 02:38:31 2011
@@ -218,7 +218,7 @@ public class PDFParser extends BaseParse
if( !wasLastParsedObjectEOF )
{
throw e;
- }
+ }
}
}
catch( Throwable t )
@@ -647,9 +647,9 @@ public class PDFParser extends BaseParse
}
skipSpaces();
/* This integer is the byte offset of the first object referenced by the xref or xref stream
- * Not needed for PDFbox
+ * Needed for the incremental update (PREV)
*/
- readInt();
+ getDocument().setStartXref(readInt());
return true;
}
@@ -806,6 +806,7 @@ public class PDFParser extends BaseParse
this.objectKey = key;
this.object = pdfObject;
}
+ @Override
public String toString()
{
return "Object(" + offset + ", " + objectKey + ")";
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSStandardOutputStream.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSStandardOutputStream.java?rev=1092855&r1=1092854&r2=1092855&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSStandardOutputStream.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSStandardOutputStream.java Sat Apr 16 02:38:31 2011
@@ -18,9 +18,14 @@ package org.apache.pdfbox.pdfwriter;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.FilterOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.channels.FileChannel;
import org.apache.pdfbox.util.StringUtil;
@@ -53,7 +58,10 @@ public class COSStandardOutputStream ext
private long pos = 0;
// flag to prevent generating two newlines in sequence
private boolean onNewLine = false;
-
+ private FileChannel fileChannel = null;
+ private FileDescriptor fileDescriptor = null;
+ private long mark = -1;
+
/**
* COSOutputStream constructor comment.
*
@@ -62,7 +70,17 @@ public class COSStandardOutputStream ext
public COSStandardOutputStream(OutputStream out)
{
super(out);
+ if(out instanceof FileOutputStream) {
+ try {
+ fileChannel = ((FileOutputStream)out).getChannel();
+ fileDescriptor = ((FileOutputStream)out).getFD();
+ pos = fileChannel.position();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
}
+
/**
* This will get the current position in the stream.
*
@@ -72,8 +90,24 @@ public class COSStandardOutputStream ext
{
return pos;
}
+
/**
- * This will tell if we are on a newling.
+ * This will get the current position in the stream.
+ *
+ * @return The current position in the stream.
+ * @throws IOException
+ */
+ public void setPos(long pos) throws IOException
+ {
+ if(fileChannel!=null) {
+ checkPos();
+ this.pos=pos;
+ fileChannel.position(pos);
+ }
+ }
+
+ /**
+ * This will tell if we are on a newline.
*
* @return true If we are on a newline.
*/
@@ -100,8 +134,10 @@ public class COSStandardOutputStream ext
*
* @throws IOException If the underlying stream throws an exception.
*/
+ @Override
public void write(byte[] b, int off, int len) throws IOException
{
+ checkPos();
setOnNewLine(false);
out.write(b, off, len);
pos += len;
@@ -114,13 +150,15 @@ public class COSStandardOutputStream ext
*
* @throws IOException If there is an error writing to the underlying stream.
*/
+ @Override
public void write(int b) throws IOException
{
+ checkPos();
setOnNewLine(false);
out.write(b);
pos++;
}
-
+
/**
* This will write a CRLF to the stream.
*
@@ -129,7 +167,6 @@ public class COSStandardOutputStream ext
public void writeCRLF() throws IOException
{
write(CRLF);
- // setOnNewLine(true);
}
/**
@@ -154,6 +191,34 @@ public class COSStandardOutputStream ext
public void writeLF() throws IOException
{
write(LF);
- // setOnNewLine(true);
+ }
+
+ public void mark() throws IOException
+ {
+ checkPos();
+ mark = getPos();
+ }
+
+ public void reset() throws IOException
+ {
+ if(mark<0)
+ return;
+ setPos(mark);
+ }
+
+ private void checkPos() throws IOException
+ {
+ if(fileChannel.position() != getPos())
+ throw new IOException("OutputStream has an invalid position");
+ }
+
+ public byte[] getFileInBytes(int[] byteRange) throws IOException
+ {
+ return null;
+ }
+
+ public InputStream getFilterInputStream(int[] byteRange)
+ {
+ return new COSFilterInputStream(new FileInputStream(fileDescriptor), byteRange);
}
}
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=1092855&r1=1092854&r2=1092855&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 Sat Apr 16 02:38:31 2011
@@ -16,6 +16,9 @@
*/
package org.apache.pdfbox.pdfwriter;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -24,11 +27,13 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -42,14 +47,17 @@ import org.apache.pdfbox.cos.COSFloat;
import org.apache.pdfbox.cos.COSInteger;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSNull;
+import org.apache.pdfbox.cos.COSNumber;
import org.apache.pdfbox.cos.COSObject;
import org.apache.pdfbox.cos.COSStream;
import org.apache.pdfbox.cos.COSString;
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.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.encryption.SecurityHandler;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
import org.apache.pdfbox.persistence.util.COSObjectKey;
import org.apache.pdfbox.util.StringUtil;
@@ -156,6 +164,7 @@ public class COSWriter implements ICOSVi
// the stream where we create the pdf output
private OutputStream output;
+
// the stream used to write standard cos data
private COSStandardOutputStream standardOutput;
@@ -170,13 +179,14 @@ public class COSWriter implements ICOSVi
//A hashtable is used on purpose over a hashmap
//so that null entries will not get added.
private Map<COSBase,COSObjectKey> objectKeys = new Hashtable<COSBase,COSObjectKey>();
+ private Map<COSObjectKey,COSBase> keyObject = new Hashtable<COSObjectKey,COSBase>();
// the list of x ref entries to be made so far
- private LinkedList xRefEntries = new LinkedList();
+ private List<COSWriterXRefEntry> xRefEntries = new ArrayList<COSWriterXRefEntry>();
+ private HashSet<COSBase> objectsToWriteSet = new HashSet<COSBase>();
//A list of objects to write.
private LinkedList<COSBase> objectsToWrite = new LinkedList<COSBase>();
- private HashSet<COSBase> objectsToWriteSet = new HashSet<COSBase>();
//a list of objects already written
private Set<COSBase> writtenObjects = new HashSet<COSBase>();
@@ -193,6 +203,16 @@ public class COSWriter implements ICOSVi
private PDDocument document = null;
private boolean willEncrypt = false;
+
+ private boolean incrementalUpdate = false;
+
+ private boolean reachedSignature = false;
+
+ private int[] signaturePosition = new int[2];
+
+ private int[] byterangePosition = new int[2];
+
+ private FileInputStream in;
/**
* COSWriter constructor comment.
@@ -203,10 +223,57 @@ public class COSWriter implements ICOSVi
{
super();
setOutput(os);
- setStandardOutput(new COSStandardOutputStream(getOutput()));
+ setStandardOutput(new COSStandardOutputStream(output));
formatDecimal.setMaximumFractionDigits( 10 );
formatDecimal.setGroupingUsed( false );
}
+
+ /**
+ * COSWriter constructor for incremental updates.
+ *
+ * @param os The wrapped output stream.
+ */
+ public COSWriter(OutputStream os, FileInputStream is)
+ {
+ this(os);
+ in = is;
+ incrementalUpdate = true;
+ }
+
+ protected void prepareIncrement(PDDocument doc)
+ {
+ try
+ {
+ if (doc != null)
+ {
+ COSDocument cosDoc = doc.getDocument();
+
+ Map<COSObjectKey, Integer> xrefTable = cosDoc.getXrefTable();
+ Set<COSObjectKey> keySet = xrefTable.keySet();
+ long highestNumber=0;
+ for ( COSObjectKey cosObjectKey : keySet ) {
+ COSBase object = cosDoc.getObjectFromPool(cosObjectKey).getObject();
+ if (object != null && cosObjectKey!= null && !(object instanceof COSNumber))
+ {
+ objectKeys.put(object, cosObjectKey);
+ keyObject.put(cosObjectKey,object);
+ }
+
+ long num = cosObjectKey.getNumber();
+ if (num > highestNumber)
+ highestNumber=num;
+ }
+ setNumber(highestNumber);
+ // xrefTable.clear();
+
+ }
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
/**
* add an entry in the x ref table for later dump.
*
@@ -249,7 +316,7 @@ public class COSWriter implements ICOSVi
*
* @return A map of all object keys.
*/
- public java.util.Map<COSBase,COSObjectKey> getObjectKeys()
+ public Map<COSBase,COSObjectKey> getObjectKeys()
{
return objectKeys;
}
@@ -288,7 +355,7 @@ public class COSWriter implements ICOSVi
*
* @return All available xref entries.
*/
- protected java.util.List getXRefEntries()
+ protected List<COSWriterXRefEntry> getXRefEntries()
{
return xRefEntries;
}
@@ -347,15 +414,14 @@ public class COSWriter implements ICOSVi
COSDictionary root = (COSDictionary)trailer.getDictionaryObject( COSName.ROOT );
COSDictionary info = (COSDictionary)trailer.getDictionaryObject( COSName.INFO );
COSDictionary encrypt = (COSDictionary)trailer.getDictionaryObject( COSName.ENCRYPT );
- if( root != null )
- {
- addObjectToWrite( root );
- }
- if( info != null )
- {
- addObjectToWrite( info );
- }
-
+ if( root != null )
+ {
+ addObjectToWrite( root );
+ }
+ if( info != null )
+ {
+ addObjectToWrite( info );
+ }
while( objectsToWrite.size() > 0 )
{
@@ -378,7 +444,6 @@ public class COSWriter implements ICOSVi
objectsToWriteSet.remove(nextObject);
doWriteObject( nextObject );
}
-
}
private void addObjectToWrite( COSBase object )
@@ -393,6 +458,21 @@ public class COSWriter implements ICOSVi
!objectsToWriteSet.contains( object ) &&
!actualsAdded.contains( actual ) )
{
+ COSBase cosBase=null;
+ COSObjectKey cosObjectKey = null;
+ if(actual != null)
+ cosObjectKey= objectKeys.get(actual);
+
+ if(cosObjectKey!=null)
+ cosBase = keyObject.get(cosObjectKey);
+
+ if(actual != null && objectKeys.containsKey(actual) &&
+ !object.isNeedToBeUpdate() && (cosBase!= null &&
+ !cosBase.isNeedToBeUpdate()))
+ {
+ return;
+ }
+
objectsToWrite.add( object );
objectsToWriteSet.add( object );
if( actual != null )
@@ -414,6 +494,15 @@ public class COSWriter implements ICOSVi
try
{
writtenObjects.add( obj );
+ if(obj instanceof COSDictionary)
+ {
+ COSDictionary dict = (COSDictionary)obj;
+ COSName item = (COSName)dict.getItem(COSName.TYPE);
+ if(COSName.SIG.equals(item)) {
+ reachedSignature = true;
+ }
+ }
+
// find the physical reference
currentObjectKey = getObjectKey( obj );
// add a x ref entry
@@ -469,9 +558,14 @@ public class COSWriter implements ICOSVi
COSDictionary trailer = doc.getTrailer();
//sort xref, needed only if object keys not regenerated
Collections.sort(getXRefEntries());
- COSWriterXRefEntry lastEntry = (COSWriterXRefEntry)getXRefEntries().get( getXRefEntries().size()-1);
+ COSWriterXRefEntry lastEntry = getXRefEntries().get( getXRefEntries().size()-1);
trailer.setInt(COSName.SIZE, (int)lastEntry.getKey().getNumber()+1);
- trailer.removeItem( COSName.PREV );
+ // Only need to stay, if an incremental update will be performed
+ if (!incrementalUpdate)
+ trailer.removeItem( COSName.PREV );
+ // Remove a checksum if present
+ trailer.removeItem( COSName.getPDFName("DocChecksum") );
+
/**
COSObject catalog = doc.getCatalog();
if (catalog != null)
@@ -486,6 +580,7 @@ public class COSWriter implements ICOSVi
getStandardOutput().write(String.valueOf(getStartxref()).getBytes("ISO-8859-1"));
getStandardOutput().writeEOL();
getStandardOutput().write(EOF);
+ getStandardOutput().writeEOL();
}
/**
@@ -501,12 +596,9 @@ public class COSWriter implements ICOSVi
*/
protected void doWriteXRef(COSDocument doc) throws IOException
{
- String offset;
- String generation;
-
// sort xref, needed only if object keys not regenerated
Collections.sort(getXRefEntries());
- COSWriterXRefEntry lastEntry = (COSWriterXRefEntry)getXRefEntries().get( getXRefEntries().size()-1 );
+ COSWriterXRefEntry lastEntry = getXRefEntries().get( getXRefEntries().size()-1 );
// remember the position where x ref is written
setStartxref(getStandardOutput().getPos());
@@ -515,48 +607,175 @@ public class COSWriter implements ICOSVi
getStandardOutput().writeEOL();
// write start object number and object count for this x ref section
// we assume starting from scratch
- getStandardOutput().write(String.valueOf(0).getBytes("ISO-8859-1"));
+ 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();)
+ {
+ COSWriterXRefEntry entry = i.next();
+ while( lastObjectNumber<entry.getKey().getNumber()-1 )
+ {
+ writeXrefEntry(COSWriterXRefEntry.getNullEntry());
+ }
+ lastObjectNumber = entry.getKey().getNumber();
+ writeXrefEntry(entry);
+ }
+ }
+
+ protected void doWriteXRefInc(COSDocument doc) throws IOException
+ {
+ 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;
+ }
+ }
+
+ protected void doWriteSignature(COSDocument doc) throws IOException, SignatureException
+ {
+ // need to calculate the ByteRange
+ if (signaturePosition[0]>0 && byterangePosition[1] > 0)
+ {
+ int left = (int)getStandardOutput().getPos()-signaturePosition[1];
+ String newByteRange = "0 "+signaturePosition[0]+" "+signaturePosition[1]+" "+left+"]";
+ int leftByterange = byterangePosition[1]-byterangePosition[0]-newByteRange.length();
+ if(leftByterange<0)
+ throw new IOException("Can't write new ByteRange, not enough space");
+ getStandardOutput().setPos(byterangePosition[0]);
+ getStandardOutput().write(newByteRange.getBytes());
+ for(int i=0;i<leftByterange;++i)
+ {
+ getStandardOutput().write(0x20);
+ }
+
+ getStandardOutput().setPos(0);
+ // Begin - extracting document
+ InputStream filterInputStream = new COSFilterInputStream(in, new int[] {0,signaturePosition[0],signaturePosition[1],left});
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ try {
+ byte[] buffer = new byte[1024];
+ int c;
+ while((c = filterInputStream.read(buffer)) != -1)
+ bytes.write(buffer, 0, c);
+ } finally {
+ if(filterInputStream !=null)
+ filterInputStream.close();
+ }
+
+ byte[] pdfContent = bytes.toByteArray();
+ // End - extracting document
+
+ SignatureInterface signatureInterface = doc.getSignatureInterface();
+ byte[] sign = signatureInterface.sign(new ByteArrayInputStream(pdfContent));
+ String signature = new COSString(sign).getHexString();
+ int leftSignaturerange = signaturePosition[1]-signaturePosition[0]-signature.length();
+ if(leftSignaturerange<0)
+ throw new IOException("Can't write signature, not enough space");
+ getStandardOutput().setPos(signaturePosition[0]+1);
+ getStandardOutput().write(signature.getBytes());
+ }
+ }
+
+ protected void writeXrefRange(long x, long y) throws IOException
+ {
+ getStandardOutput().write(String.valueOf(x).getBytes());
getStandardOutput().write(SPACE);
- getStandardOutput().write(String.valueOf(lastEntry.getKey().getNumber() + 1).getBytes("ISO-8859-1"));
+ getStandardOutput().write(String.valueOf(y).getBytes());
getStandardOutput().writeEOL();
- // write initial start object with ref to first deleted object and magic generation number
- offset = formatXrefOffset.format(0);
- generation = formatXrefGeneration.format(65535);
+ }
+
+ protected void writeXrefEntry(COSWriterXRefEntry entry) throws IOException
+ {
+ String offset = formatXrefOffset.format(entry.getOffset());
+ String generation = formatXrefGeneration.format(entry.getKey().getGeneration());
getStandardOutput().write(offset.getBytes("ISO-8859-1"));
getStandardOutput().write(SPACE);
getStandardOutput().write(generation.getBytes("ISO-8859-1"));
getStandardOutput().write(SPACE);
- getStandardOutput().write(XREF_FREE);
+ getStandardOutput().write(entry.isFree() ? XREF_FREE : XREF_USED);
getStandardOutput().writeCRLF();
- // write entry for every object
- long lastObjectNumber = 0;
- for (Iterator i = getXRefEntries().iterator(); i.hasNext();)
+ }
+
+ /**
+ * check the xref entries and write out the ranges. The format of the
+ * returned array is exactly the same as the pdf specification. See section
+ * 7.5.4 of ISO32000-1:2008, example 1 (page 40) for reference.
+ * <p>
+ * example: 0 1 2 5 6 7 8 10
+ * <p>
+ * will create a array with follow ranges
+ * <p>
+ * 0 3 5 4 10 1
+ * <p>
+ * this mean that the element 0 is followed by two other related numbers
+ * that represent a cluster of the size 3. 5 is follow by three other
+ * related numbers and create a cluster of size 4. etc.
+ *
+ * @param xRefEntries list with the xRef entries that was written
+ * @return a integer array with the ranges
+ */
+ protected Integer[] getXRefRanges(List<COSWriterXRefEntry> xRefEntries)
+ {
+ int nr = 0;
+ int last = -2;
+ int count = 1;
+
+ ArrayList<Integer> list = new ArrayList<Integer>();
+ for( Object object : xRefEntries )
{
- COSWriterXRefEntry entry = (COSWriterXRefEntry) i.next();
- while( lastObjectNumber<entry.getKey().getNumber()-1 )
+ nr = (int)((COSWriterXRefEntry)object).getKey().getNumber();
+ if (nr == last + 1)
{
- offset = formatXrefOffset.format(0);
- generation = formatXrefGeneration.format(65535);
- getStandardOutput().write(offset.getBytes("ISO-8859-1"));
- getStandardOutput().write(SPACE);
- getStandardOutput().write(generation.getBytes("ISO-8859-1"));
- getStandardOutput().write(SPACE);
- getStandardOutput().write(XREF_FREE);
- getStandardOutput().writeCRLF();
- lastObjectNumber++;
+ ++count;
+ last = nr;
+ }
+ else if (last == -2)
+ {
+ last = nr;
+ }
+ else
+ {
+ list.add(last - count + 1);
+ list.add(count);
+ last = nr;
+ count = 1;
}
- lastObjectNumber = entry.getKey().getNumber();
- offset = formatXrefOffset.format(entry.getOffset());
- generation = formatXrefGeneration.format(entry.getKey().getGeneration());
- getStandardOutput().write(offset.getBytes("ISO-8859-1"));
- getStandardOutput().write(SPACE);
- getStandardOutput().write(generation.getBytes("ISO-8859-1"));
- getStandardOutput().write(SPACE);
- getStandardOutput().write(entry.isFree() ? XREF_FREE : XREF_USED);
- getStandardOutput().writeCRLF();
}
+ // If no new entry is found, we need to write out the last result
+ if(xRefEntries.size() > 0)
+ {
+ list.add(last - count + 1);
+ list.add(count);
+ }
+ return list.toArray(new Integer[list.size()]);
}
-
+
/**
* This will get the object key for the object.
*
@@ -574,11 +793,11 @@ public class COSWriter implements ICOSVi
COSObjectKey key = null;
if( actual != null )
{
- key = (COSObjectKey)objectKeys.get(actual);
+ key = objectKeys.get(actual);
}
if( key == null )
{
- key = (COSObjectKey)objectKeys.get(obj);
+ key = objectKeys.get(obj);
}
if (key == null)
{
@@ -712,8 +931,31 @@ public class COSWriter implements ICOSVi
getStandardOutput().write(SPACE);
if( value instanceof COSDictionary )
{
- addObjectToWrite( value );
- writeReference( value );
+ COSDictionary dict = (COSDictionary)value;
+
+ // write all XObjects as direct objects, this will save some size
+ COSBase item = dict.getItem(COSName.XOBJECT);
+ if(item!=null)
+ {
+ item.setDirect(true);
+ }
+ item = dict.getItem(COSName.RESOURCES);
+ if(item!=null)
+ {
+ item.setDirect(true);
+ }
+
+ if(dict.isDirect())
+ {
+ // If the object should be written direct, we need
+ // to pass the dictionary to the visitor again.
+ visitFromDictionary(dict);
+ }
+ else
+ {
+ addObjectToWrite( dict );
+ writeReference( dict );
+ }
}
else if( value instanceof COSObject )
{
@@ -730,7 +972,27 @@ public class COSWriter implements ICOSVi
}
else
{
- value.accept(this);
+ // If we reach the pdf signature, we need to determinate the position of the
+ // content and byterange
+ if(reachedSignature && COSName.CONTENTS.equals(entry.getKey()))
+ {
+ signaturePosition = new int[2];
+ signaturePosition[0] = (int)getStandardOutput().getPos();
+ value.accept(this);
+ signaturePosition[1] = (int)getStandardOutput().getPos();
+ }
+ else if(reachedSignature && COSName.BYTERANGE.equals(entry.getKey()))
+ {
+ byterangePosition = new int[2];
+ byterangePosition[0] = (int)getStandardOutput().getPos()+1;
+ value.accept(this);
+ byterangePosition[1] = (int)getStandardOutput().getPos()-1;
+ reachedSignature = false;
+ }
+ else
+ {
+ value.accept(this);
+ }
}
getStandardOutput().writeEOL();
@@ -766,16 +1028,26 @@ public class COSWriter implements ICOSVi
{
try
{
- doWriteHeader(doc);
+ if(!incrementalUpdate)
+ doWriteHeader(doc);
doWriteBody(doc);
- doWriteXRef(doc);
+ if(incrementalUpdate)
+ doWriteXRefInc(doc);
+ else
+ doWriteXRef(doc);
doWriteTrailer(doc);
+ doWriteSignature(doc);
+
return null;
}
catch (IOException e)
{
throw new COSVisitorException(e);
}
+ catch (SignatureException e)
+ {
+ throw new COSVisitorException(e);
+ }
}
/**
@@ -995,6 +1267,8 @@ public class COSWriter implements ICOSVi
public void write(PDDocument doc) throws COSVisitorException
{
document = doc;
+ if(incrementalUpdate)
+ prepareIncrement(doc);
// if the document says we should remove encryption, then we shouldn't encrypt
if(doc.isAllSecurityToBeRemoved())
@@ -1034,7 +1308,7 @@ public class COSWriter implements ICOSVi
COSDocument cosDoc = document.getDocument();
COSDictionary trailer = cosDoc.getTrailer();
COSArray idArray = (COSArray)trailer.getDictionaryObject( COSName.ID );
- if( idArray == null )
+ if( idArray == null || incrementalUpdate)
{
try
{
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriterXRefEntry.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriterXRefEntry.java?rev=1092855&r1=1092854&r2=1092855&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriterXRefEntry.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriterXRefEntry.java Sat Apr 16 02:38:31 2011
@@ -27,29 +27,45 @@ import org.apache.pdfbox.cos.COSBase;
* @author Michael Traut
* @version $Revision: 1.7 $
*/
-public class COSWriterXRefEntry implements Comparable
+public class COSWriterXRefEntry implements Comparable<COSWriterXRefEntry>
{
private long offset;
private COSBase object;
private COSObjectKey key;
private boolean free = false;
-
+ private static COSWriterXRefEntry nullEntry;
/**
* {@inheritDoc}
*/
- public int compareTo(Object obj)
+ public int compareTo(COSWriterXRefEntry obj)
{
if (obj instanceof COSWriterXRefEntry)
{
- return (int)(getKey().getNumber() - ((COSWriterXRefEntry)obj).getKey().getNumber());
+ return (int)(getKey().getNumber() - obj.getKey().getNumber());
}
else
{
return -1;
}
}
+
+ /**
+ * This will return a null entry: 0000000000 65535 f
+ *
+ * @return null COSWriterXRefEntry
+ */
+ public static COSWriterXRefEntry getNullEntry()
+ {
+ if (nullEntry == null)
+ {
+ nullEntry = new COSWriterXRefEntry(0, null, new COSObjectKey(0, 65535));
+ nullEntry.setFree(true);
+ }
+ return nullEntry;
+ }
+
/**
* This will get the Object key.
*
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=1092855&r1=1092854&r2=1092855&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDDocument.java Sat Apr 16 02:38:31 2011
@@ -29,6 +29,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -44,10 +45,12 @@ import org.apache.pdfbox.cos.COSStream;
import org.apache.pdfbox.exceptions.COSVisitorException;
import org.apache.pdfbox.exceptions.CryptographyException;
import org.apache.pdfbox.exceptions.InvalidPasswordException;
+import org.apache.pdfbox.exceptions.SignatureException;
import org.apache.pdfbox.io.RandomAccess;
import org.apache.pdfbox.pdfparser.PDFParser;
import org.apache.pdfbox.pdfwriter.COSWriter;
import org.apache.pdfbox.pdmodel.common.COSArrayList;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.common.PDStream;
import org.apache.pdfbox.pdmodel.encryption.AccessPermission;
import org.apache.pdfbox.pdmodel.encryption.BadSecurityHandlerException;
@@ -58,6 +61,13 @@ 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.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.PDSignatureField;
/**
* This is the in-memory representation of the PDF document. You need to call
@@ -72,6 +82,8 @@ import org.apache.pdfbox.pdmodel.encrypt
*/
public class PDDocument implements Pageable
{
+ private static final String COSDictionary = null;
+
private COSDocument document;
// NOTE BGUILLON: this property must be removed because it is
@@ -86,7 +98,7 @@ public class PDDocument implements Pagea
//cached values
private PDDocumentInformation documentInformation;
private PDDocumentCatalog documentCatalog;
-
+
//The encParameters will be cached here. When the document is decrypted then
//the COSDocument will not have an "Encrypt" dictionary anymore and this object
//must be used.
@@ -110,14 +122,14 @@ public class PDDocument implements Pagea
* the page number for bookmarks (or page numbers for anything else for
* which you have an object id for that matter).
*/
- private Map pageMap = null;
+ private Map<String, Integer> pageMap = null;
/**
* This will hold a flag which tells us if we should remove all security
* from this documents
*/
private boolean allSecurityToBeRemoved = false;
-
+
/**
* Constructor, creates a new PDF Document with no pages. You need to add
* at least one page for the document to be valid.
@@ -149,12 +161,13 @@ public class PDDocument implements Pagea
private void generatePageMap()
{
- pageMap = new HashMap();
+ pageMap = new HashMap<String,Integer>();
// these page nodes could be references to pages,
// or references to arrays which have references to pages
// or references to arrays which have references to arrays which have references to pages
// or ... (I think you get the idea...)
- processListOfPageReferences(getDocumentCatalog().getPages().getKids());
+ processListOfPageReferences(getDocumentCatalog().getPages().getKids());
+
}
private void processListOfPageReferences(List<Object> pageNodes)
@@ -164,7 +177,7 @@ public class PDDocument implements Pagea
Object pageOrArray = pageNodes.get(i);
if(pageOrArray instanceof PDPage)
{
- COSArray pageArray = ((COSArrayList)(((PDPage)pageOrArray).getParent()).getKids()).toList();
+ List pageArray = ((((PDPage)pageOrArray).getParent()).getKids());
parseCatalogObject((COSObject)pageArray.get(i));
}
else if(pageOrArray instanceof PDPageNode)
@@ -240,7 +253,7 @@ public class PDDocument implements Pagea
*
* @return the pageMap
*/
- public final Map getPageMap()
+ public final Map<String,Integer> getPageMap()
{
if (pageMap == null)
{
@@ -264,6 +277,175 @@ public class PDDocument implements Pagea
rootPages.updateCount();
}
+ public void addSignature(PDSignature sigObject, SignatureInterface signatureInterface) throws IOException, SignatureException
+ {
+ SignatureOptions defaultOptions = new SignatureOptions();
+ defaultOptions.setPage(1);
+
+ addSignature(sigObject, signatureInterface,defaultOptions);
+ }
+
+ /**
+ * This will add a signature to the document.
+ *
+ * @param sigObject is the PDSignature model
+ * @param signatureInterface is a interface which provides signing capabilities
+ * @param options
+ * @throws IOException if there is an error creating required fields
+ */
+ public void addSignature(PDSignature sigObject, SignatureInterface signatureInterface, SignatureOptions options) throws IOException, SignatureException
+ {
+ // Content reservieren
+ // Um auch grosse Zertifikatsketten unterbringen zu koennen,
+ // wird ein sehr grosser Bereich reserviert
+ sigObject.setContents(new byte[0x2500 * 2 + 2]);
+
+ // ByteRange reservieren
+ sigObject.setByteRange(new int[] {0,1000000000,1000000000,1000000000});
+
+ getDocument().setSignatureInterface(signatureInterface);
+
+ // #########################################
+ // # SignatureForm fuer Signatur erstellen #
+ // # und mit an das Dokument haengen. #
+ // #########################################
+
+ // Erste Seite besorgen
+ PDDocumentCatalog root = getDocumentCatalog();
+ PDPageNode rootPages = root.getPages();
+ List<PDPage> kids = new ArrayList<PDPage>();
+ rootPages.getAllKids(kids);
+
+ int size = (int)rootPages.getCount();
+ PDPage page = null;
+ if (size == 0)
+ throw new SignatureException(SignatureException.INVALID_PAGE_FOR_SIGNATURE, "The PDF file has no pages");
+ if (options.getPage()>size)
+ page = kids.get(size-1);
+ else if(options.getPage()<=0)
+ page = kids.get(0);
+ else
+ page = kids.get(options.getPage()-1);
+
+
+ // AcroForm aus dem Root dict besorgen und Annotation einfügen
+ PDAcroForm acroForm = root.getAcroForm();
+ root.getCOSObject().setNeedToBeUpdate(true);
+
+ if (acroForm==null)
+ {
+ acroForm = new PDAcroForm(this);
+ root.setAcroForm(acroForm);
+ } else
+ acroForm.getCOSObject().setNeedToBeUpdate(true);
+
+ /*
+ * For invisible signatures, the annotation has a rectangle array with values [ 0 0 0 0 ].
+ * This annotation is usually attached to the viewed page when the signature is created.
+ * Despite not having an appearance, the annotation AP and N dictionaries may be present
+ * in some versions of Acrobat. If present, N references the DSBlankXObj (blank) XObject.
+ */
+
+ // Annotation / Field für die Signatur erzeugen
+ PDSignatureField signatureField = new PDSignatureField(acroForm);
+ signatureField.setSignature(sigObject); // Signaturobjekt vermerken
+ signatureField.getWidget().setPage(page); // Rückverkettung
+
+ // AcroForm Fields setzen
+ List acroFormFields = acroForm.getFields();
+ COSDictionary acroFormDict = acroForm.getDictionary();
+ acroFormDict.setDirect(true);
+ acroFormDict.setInt("SigFlags", 3);
+ acroFormFields.add(signatureField);
+
+ // Objekte aus der visuellen Signatur besorgen
+ COSDocument visualSignature = options.getVisualSignature();
+
+ // Fallunterscheidung zwischen sichtbarer und unsichtbarer Signatur vorbereiten
+ if (visualSignature == null) // unsichtbare Signatur
+ {
+ // Rectangle fuer unsichtbare Signatur auf 0 0 0 0 setzen
+ signatureField.getWidget().setRectangle(new PDRectangle()); // rectangle array [ 0 0 0 0 ]
+ // AcroForm leere DefaultRessource setzen
+ acroFormDict.setItem("DR", null);
+ // Leeres Appearance-Dictionary setzten
+ PDAppearanceDictionary ap = new PDAppearanceDictionary();
+ COSStream apsStream = new COSStream(getDocument().getScratchFile());
+ apsStream.createUnfilteredStream();
+ PDAppearanceStream aps = new PDAppearanceStream(apsStream);
+ COSDictionary cosObject = (COSDictionary)aps.getCOSObject();
+ cosObject.setItem(COSName.SUBTYPE, COSName.getPDFName("Form"));
+ cosObject.setItem(COSName.BBOX, new PDRectangle());
+
+ ap.setNormalAppearance(aps);
+ ap.getDictionary().setDirect(true);
+ signatureField.getWidget().setAppearance(ap);
+ }
+ else // sichtbare Signatur
+ {
+ // Visuelle Objekte besorgen
+ List<COSObject> cosObjects = visualSignature.getObjects();
+
+ boolean annotNotFound = true;
+ boolean sigFieldNotFound = true;
+
+ for ( COSObject cosObject : cosObjects )
+ {
+ COSBase base = cosObject.getObject();
+ if (base != null && base instanceof COSDictionary)
+ {
+ COSBase ft = ((COSDictionary)base).getItem(COSName.getPDFName("FT"));
+ COSBase type = ((COSDictionary)base).getItem(COSName.TYPE);
+ COSBase apDict = ((COSDictionary)base).getItem(COSName.AP);
+
+ // Nach Signatur-Annotation suchen
+ if (annotNotFound && COSName.getPDFName("Annot").equals(type))
+ {
+ COSDictionary cosBaseDict = (COSDictionary)base;
+
+ // Rectangle fuer visuelle Signatur auslesen und setzen
+ COSArray rectAry = (COSArray)cosBaseDict.getItem(COSName.getPDFName("Rect"));
+ PDRectangle rect = new PDRectangle(rectAry);
+ signatureField.getWidget().setRectangle(rect);
+ annotNotFound = false;
+ }
+
+ // Nach Signatur-Field suchen
+ if (sigFieldNotFound && COSName.getPDFName("Sig").equals(ft) && apDict != null)
+ {
+ COSDictionary cosBaseDict = (COSDictionary)base;
+
+ // Appearance Dictionary auslesen und setzen
+ PDAppearanceDictionary ap = new PDAppearanceDictionary((COSDictionary)cosBaseDict.getItem(COSName.AP));
+ ap.getDictionary().setDirect(true);
+ signatureField.getWidget().setAppearance(ap);
+
+ // AcroForm DefaultRessource auslesen und setzen
+ COSBase dr = cosBaseDict.getItem(COSName.getPDFName("DR"));
+ dr.setDirect(true);
+ dr.setNeedToBeUpdate(true);
+ acroFormDict.setItem("DR", dr);
+ sigFieldNotFound=false;
+ }
+ }
+ }
+
+ if (annotNotFound || sigFieldNotFound )
+ throw new SignatureException(SignatureException.VISUAL_SIGNATURE_INVALID, "Could not read all needed objects from template");
+ }
+
+ // Seite besorgen und Signatur-Annotation anbringen
+ List annotations = page.getAnnotations();
+ if (annotations== null)
+ {
+ annotations = new COSArrayList();
+ }
+ annotations.add(signatureField.getWidget());
+ page.setAnnotations(annotations);
+ page.getCOSObject().setNeedToBeUpdate(true);
+
+ }
+
/**
* Remove the page from the document.
*
@@ -473,6 +655,17 @@ public class PDDocument implements Pagea
encParameters = encDictionary;
}
+ public PDSignature getSignatureDictionary() throws IOException
+ {
+ COSDictionary signatureDictionary = document.getLastSignatureDictionary();
+
+ if (signatureDictionary!= null)
+ {
+ return new PDSignature(signatureDictionary);
+ }
+ return null;
+ }
+
/**
* This will determine if this is the user password. This only applies when
* the document is encrypted and uses standard encryption.
@@ -486,6 +679,7 @@ public class PDDocument implements Pagea
*
* @deprecated
*/
+ @Deprecated
public boolean isUserPassword( String password ) throws IOException, CryptographyException
{
return false;
@@ -537,6 +731,7 @@ public class PDDocument implements Pagea
*
* @deprecated
*/
+ @Deprecated
public boolean isOwnerPassword( String password ) throws IOException, CryptographyException
{
return false;
@@ -608,6 +803,7 @@ public class PDDocument implements Pagea
*
* @deprecated use <code>getCurrentAccessPermission</code> instead
*/
+ @Deprecated
public boolean wasDecryptedWithOwnerPassword()
{
return false;
@@ -651,6 +847,7 @@ public class PDDocument implements Pagea
*
* @deprecated Do not rely on this method anymore.
*/
+ @Deprecated
public String getOwnerPasswordForEncryption()
{
return null;
@@ -665,6 +862,7 @@ public class PDDocument implements Pagea
*
* @deprecated Do not rely on this method anymore.
*/
+ @Deprecated
public String getUserPasswordForEncryption()
{
return null;
@@ -679,6 +877,7 @@ public class PDDocument implements Pagea
* @deprecated Do not rely on this method anymore. It is the responsibility of
* COSWriter to hold this state
*/
+ @Deprecated
public boolean willEncryptWhenSaving()
{
return false;
@@ -690,6 +889,7 @@ public class PDDocument implements Pagea
* @deprecated Do not rely on this method anymore. It is the responsability of
* COSWriter to hold this state.
*/
+ @Deprecated
public void clearWillEncryptWhenSaving()
{
//method is deprecated.
@@ -858,7 +1058,7 @@ public class PDDocument implements Pagea
*/
public static PDDocument load( InputStream input, RandomAccess scratchFile ) throws IOException
{
- PDFParser parser = new PDFParser( new BufferedInputStream( input ), scratchFile );
+ PDFParser parser = new PDFParser( new BufferedInputStream( input ) , scratchFile );
parser.parse();
return parser.getPDDocument();
}
@@ -923,6 +1123,31 @@ public class PDDocument implements Pagea
}
}
+ public void saveIncremental( String fileName ) throws IOException, COSVisitorException
+ {
+ saveIncremental( new FileInputStream( fileName ) , new FileOutputStream( fileName , true) );
+ }
+
+ public void saveIncremental( FileInputStream input, OutputStream output ) throws IOException, COSVisitorException
+ {
+ //update the count in case any pages have been added behind the scenes.
+ getDocumentCatalog().getPages().updateCount();
+ COSWriter writer = null;
+ try
+ {
+ writer = new COSWriter( output, input );
+ writer.write( this );
+ writer.close();
+ }
+ finally
+ {
+ if( writer != null )
+ {
+ writer.close();
+ }
+ }
+ }
+
/**
* This will return the total page count of the PDF document. Note: This method
* is deprecated in favor of the getNumberOfPages method. The getNumberOfPages is
@@ -932,6 +1157,7 @@ public class PDDocument implements Pagea
* @return The total number of pages in the PDF document.
* @deprecated Use the getNumberOfPages method instead!
*/
+ @Deprecated
public int getPageCount()
{
return getNumberOfPages();
@@ -955,6 +1181,7 @@ public class PDDocument implements Pagea
* @return page format
* @throws IndexOutOfBoundsException if the page index is invalid
*/
+ @Deprecated
public PageFormat getPageFormat(int pageIndex)
{
try {
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotation.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotation.java?rev=1092855&r1=1092854&r2=1092855&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotation.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/annotation/PDAnnotation.java Sat Apr 16 02:38:31 2011
@@ -617,6 +617,16 @@ public abstract class PDAnnotation imple
}
/**
+ * This will set the corresponding page for this annotation.
+ *
+ * @param page is the corresponding page
+ */
+ public void setPage(PDPage page)
+ {
+ this.getDictionary().setItem(COSName.P, page);
+ }
+
+ /**
* This will retrieve the corresponding page of this annotation.
*
* @return the corresponding page
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java?rev=1092855&r1=1092854&r2=1092855&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/PDSignature.java Sat Apr 16 02:38:31 2011
@@ -16,56 +16,315 @@
*/
package org.apache.pdfbox.pdmodel.interactive.digitalsignature;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Calendar;
+
+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.COSString;
+import org.apache.pdfbox.pdfwriter.COSFilterInputStream;
import org.apache.pdfbox.pdmodel.common.COSObjectable;
/**
* This represents a digital signature that can be attached to a document.
- *
+ *
* @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
+ * @author Thomas Chojecki
* @version $Revision: 1.2 $
*/
public class PDSignature implements COSObjectable
{
- private COSDictionary sig;
- /**
- * Default constructor.
- */
- public PDSignature()
+ private COSDictionary dictionary;
+
+ /**
+ * A signature filter value.
+ */
+ public static final COSName FILTER_ADOBE_PPKLITE = COSName.ADOBE_PPKLITE;
+
+ /**
+ * A signature filter value.
+ */
+ public static final COSName FILTER_ENTRUST_PPKEF = COSName.ENTRUST_PPKEF;
+
+ /**
+ * A signature filter value.
+ */
+ public static final COSName FILTER_CICI_SIGNIT = COSName.CICI_SIGNIT;
+
+ /**
+ * A signature filter value.
+ */
+ public static final COSName FILTER_VERISIGN_PPKVS = COSName.VERISIGN_PPKVS;
+
+ /**
+ * A signature subfilter value.
+ */
+ public static final COSName SUBFILTER_ADBE_X509_RSA_SHA1 = COSName.ADBE_X509_RSA_SHA1;
+
+ /**
+ * A signature subfilter value.
+ */
+ public static final COSName SUBFILTER_ADBE_PKCS7_DETACHED = COSName.ADBE_PKCS7_DETACHED;
+
+ /**
+ * A signature subfilter value.
+ */
+ public static final COSName SUBFILTER_ETSI_CADES_DETACHED = COSName.getPDFName("ETSI.CAdES.detached");
+
+ /**
+ * A signature subfilter value.
+ */
+ public static final COSName SUBFILTER_ADBE_PKCS7_SHA1 = COSName.ADBE_PKCS7_SHA1;
+
+ /**
+ * Default constructor.
+ */
+ public PDSignature()
+ {
+ dictionary = new COSDictionary();
+ dictionary.setItem(COSName.TYPE, COSName.SIG);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param dict The signature dictionary.
+ */
+ public PDSignature(COSDictionary dict)
+ {
+ dictionary = dict;
+ }
+
+ /**
+ * Convert this standard java object to a COS object.
+ *
+ * @return The cos object that matches this Java object.
+ */
+ public COSBase getCOSObject()
+ {
+ return getDictionary();
+ }
+
+ /**
+ * Convert this standard java object to a COS dictionary.
+ *
+ * @return The COS dictionary that matches this Java object.
+ */
+ public COSDictionary getDictionary()
+ {
+ return dictionary;
+ }
+
+ public void setFilter(COSName filter)
+ {
+ dictionary.setItem(COSName.FILTER, filter);
+ }
+
+ /**
+ * Set a subfilter that specify the signature that should be used.
+ *
+ * @param subfilter the subfilter that shall be used.
+ */
+ public void setSubFilter(COSName subfilter)
+ {
+ dictionary.setItem(COSName.SUBFILTER, subfilter);
+ }
+
+ public void setName(String name)
+ {
+ dictionary.setString(COSName.NAME, name);
+ }
+
+ public void setLocation(String location)
+ {
+ dictionary.setString(COSName.LOCATION, location);
+ }
+
+ public void setReason(String reason)
+ {
+ dictionary.setString(COSName.REASON, reason);
+ }
+
+ public void setSignDate(Calendar cal)
+ {
+ dictionary.setDate("M", cal);
+ }
+
+ public String getFilter()
+ {
+ return ((COSName)dictionary.getItem(COSName.FILTER)).getName();
+ }
+
+ public String getSubFilter()
+ {
+ return ((COSName)dictionary.getItem(COSName.SUBFILTER)).getName();
+ }
+
+ public String getName()
+ {
+ return dictionary.getString(COSName.NAME);
+ }
+
+ public String getLocation()
+ {
+ return dictionary.getString(COSName.LOCATION);
+ }
+
+ public String getReason()
+ {
+ return dictionary.getString(COSName.REASON);
+ }
+
+ public Calendar getSignDate()
+ {
+ try
{
- sig = new COSDictionary();
+ return dictionary.getDate("M");
}
-
- /**
- * Constructor.
- *
- * @param s The signature dictionary.
- */
- public PDSignature( COSDictionary s )
+ catch (IOException e)
{
- sig = s;
+ return null;
}
+ }
- /**
- * Convert this standard java object to a COS object.
- *
- * @return The cos object that matches this Java object.
- */
- public COSBase getCOSObject()
+ public void setByteRange(int[] range)
+ {
+ if (range.length!=4)
+ return;
+
+ COSArray ary = new COSArray();
+ for ( int i : range )
{
- return sig;
+ ary.add(COSInteger.get(i));
}
+
+ dictionary.setItem("ByteRange", ary);
+ }
+
+ /**
+ * Read out the byterange from the file
+ *
+ * @return a integer array with the byterange
+ */
+ public int[] getByteRange()
+ {
+ COSArray byteRange = (COSArray)dictionary.getDictionaryObject("ByteRange");
+ int[] ary = new int[byteRange.size()];
+ for (int i = 0; i<ary.length;++i)
+ ary[i] = byteRange.getInt(i);
+
+ return ary;
+ }
+
+ /**
+ * Will return the embedded signature between the byterange gap.
+ *
+ * @param pdfFile The signed pdf file as InputStream
+ * @return a byte array containing the signature
+ * @throws IOException if the pdfFile can't be read
+ */
+ public byte[] getContents(InputStream pdfFile) throws IOException
+ {
+ int[] byteRange = getByteRange();
+ int begin = byteRange[0]+byteRange[1]+1;
+ int end = byteRange[2]-begin;
+
+ return getContents(new COSFilterInputStream(pdfFile,new int[] {begin,end}));
+ }
+
+ /**
+ * Will return the embedded signature between the byterange gap.
+ *
+ * @param pdfFile The signed pdf file as byte array
+ * @return a byte array containing the signature
+ * @throws IOException if the pdfFile can't be read
+ */
+ public byte[] getContents(byte[] pdfFile) throws IOException
+ {
+ int[] byteRange = getByteRange();
+ int begin = byteRange[0]+byteRange[1]+1;
+ int end = byteRange[2]-begin;
+
+ return getContents(new COSFilterInputStream(pdfFile,new int[] {begin,end}));
+ }
- /**
- * Convert this standard java object to a COS object.
- *
- * @return The cos object that matches this Java object.
- */
- public COSDictionary getCOSDictionary()
+ private byte[] getContents(COSFilterInputStream fis) throws IOException
+ {
+ ByteArrayOutputStream byteOS = new ByteArrayOutputStream(1024);
+ byte[] buffer = new byte[1024];
+ int c;
+ while ((c = fis.read(buffer)) != -1)
+ {
+ // Filter < and (
+ if(buffer[0]==0x3C || buffer[0]==0x28)
+ byteOS.write(buffer, 1, c);
+ // Filter > and )
+ else if(buffer[c-1]==0x3E || buffer[c-1]==0x29)
+ byteOS.write(buffer, 0, c-1);
+ else
+ byteOS.write(buffer, 0, c);
+ }
+ fis.close();
+
+ return COSString.createFromHexString(byteOS.toString()).getBytes();
+ }
+
+ public void setContents(byte[] bytes)
+ {
+ COSString string = new COSString(bytes);
+ string.setForceHexForm(true);
+ dictionary.setItem("Contents", string);
+ }
+
+ /**
+ * Will return the signed content of the document.
+ *
+ * @param pdfFile The signed pdf file as InputStream
+ * @return a byte array containing only the signed part of the content
+ * @throws IOException if the pdfFile can't be read
+ */
+ public byte[] getSignedContent(InputStream pdfFile) throws IOException
+ {
+ COSFilterInputStream fis=null;
+
+ try
+ {
+ fis = new COSFilterInputStream(pdfFile,getByteRange());
+ return fis.toByteArray();
+ }
+ finally
+ {
+ if (fis != null)
+ fis.close();
+ }
+ }
+
+ /**
+ * Will return the signed content of the document.
+ *
+ * @param pdfFile The signed pdf file as byte array
+ * @return a byte array containing only the signed part of the content
+ * @throws IOException if the pdfFile can't be read
+ */
+ public byte[] getSignedContent(byte[] pdfFile) throws IOException
+ {
+ COSFilterInputStream fis=null;
+
+ try
+ {
+ fis = new COSFilterInputStream(pdfFile,getByteRange());
+ return fis.toByteArray();
+ }
+ finally
{
- return sig;
+ if (fis != null)
+ fis.close();
}
+ }
}
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDField.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDField.java?rev=1092855&r1=1092854&r2=1092855&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDField.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDField.java Sat Apr 16 02:38:31 2011
@@ -574,6 +574,7 @@ public abstract class PDField implements
*
* @return A string representation of this field.
*/
+ @Override
public String toString()
{
return "" + getDictionary().getDictionaryObject( COSName.V );
Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDFieldFactory.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDFieldFactory.java?rev=1092855&r1=1092854&r2=1092855&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDFieldFactory.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/form/PDFieldFactory.java Sat Apr 16 02:38:31 2011
@@ -97,7 +97,7 @@ public class PDFieldFactory
}
else if( isSignature( pdField ) )
{
- pdField = new PDSignature( acroForm, field );
+ pdField = new PDSignatureField( acroForm, field );
}
else
{