Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pdfbox 5068 2 #124

Open
wants to merge 4 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions pdfbox/src/main/java/org/apache/pdfbox/cos/COSBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public abstract class COSBase implements COSObjectable
{
private boolean direct;
private COSObjectKey key;
private COSObject referencedObject;

/**
* Constructor.
Expand Down Expand Up @@ -98,4 +99,13 @@ public void setKey(COSObjectKey key)
this.key = key;
}

public COSObject getReferencedObject()
{
return referencedObject;
}

public void setReferencedObject(COSObject referencedObject)
{
this.referencedObject = referencedObject;
}
}
43 changes: 34 additions & 9 deletions pdfbox/src/main/java/org/apache/pdfbox/cos/COSObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,24 +106,49 @@ public boolean isObjectNull()
*/
public COSBase getObject()
{
if (!isDereferenced && parser != null)
COSBase dereferencedObject = dereferenceObject();

if(dereferencedObject != null)
{
isDereferenced = true;
baseObject = dereferencedObject;
}

return baseObject;
}

public COSBase getObjectWithoutCaching() {
if(baseObject != null)
{
return baseObject;
}

return dereferenceObject();
}

private COSBase dereferenceObject()
{
COSBase dereferencedObject = null;

if(!isDereferenced && parser != null) {
try
{
// mark as dereferenced to avoid endless recursions
isDereferenced = true;
baseObject = parser.dereferenceCOSObject(this);
dereferencedObject = parser.dereferenceCOSObject(this);

if(dereferencedObject != null)
{
dereferencedObject.setReferencedObject(this);
}

}
catch (IOException e)
catch(IOException e)
{
LOG.error("Can't dereference " + this, e);
}
finally
{
parser = null;
}
}
return baseObject;

return dereferencedObject;
}

/**
Expand Down
109 changes: 47 additions & 62 deletions pdfbox/src/main/java/org/apache/pdfbox/pdfwriter/COSWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
Expand All @@ -50,7 +48,6 @@
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.COSObjectKey;
import org.apache.pdfbox.cos.COSStream;
Expand Down Expand Up @@ -185,15 +182,6 @@ public class COSWriter implements ICOSVisitor
// indicates whether existing object keys should be reused or not
private boolean reuseObjectNumbers = true;

// maps the object to the keys generated in the writer
// these are used for indirect references in other objects
//A hashtable is used on purpose over a hashmap
//so that null entries will not get added.
@SuppressWarnings({"squid:S1149"})
private final Map<COSBase,COSObjectKey> objectKeys = new Hashtable<>();

private final Map<COSObjectKey,COSBase> keyObject = new HashMap<>();

// the list of x ref entries to be made so far
private final List<XReferenceEntry> xRefEntries = new ArrayList<>();

Expand Down Expand Up @@ -231,6 +219,8 @@ public class COSWriter implements ICOSVisitor
private final CompressParameters compressParameters;
private boolean blockAddingObject = false;

private COSWriterObjectStorage objectStorage;

/**
* COSWriter constructor.
*
Expand All @@ -253,6 +243,7 @@ public COSWriter(OutputStream outputStream, CompressParameters compressParameter
setOutput(outputStream);
setStandardOutput(new COSStandardOutputStream(output));
this.compressParameters = compressParameters;
this.objectStorage = new COSWriterObjectStorage();
}

/**
Expand All @@ -279,6 +270,8 @@ public COSWriter(OutputStream outputStream, RandomAccessRead inputData) throws I
incrementalInput = inputData;
incrementalOutput = outputStream;
incrementalUpdate = true;

this.objectStorage = new COSWriterObjectStorage();
}

/**
Expand Down Expand Up @@ -323,24 +316,6 @@ public boolean isCompress()
{
return compressParameters != null && compressParameters.isCompress();
}

private void prepareIncrement()
{
COSDocument cosDoc = pdDocument.getDocument();
Set<COSObjectKey> keySet = cosDoc.getXrefTable().keySet();
for (COSObjectKey cosObjectKey : keySet)
{
COSBase object = cosDoc.getObjectFromPool(cosObjectKey).getObject();
if (object != null && cosObjectKey != null && !(object instanceof COSNumber))
{
// FIXME see PDFBOX-4997: objectKeys is (theoretically) risky because a COSName in
// different objects would appear only once. Rev 1092855 considered this
// but only for COSNumber.
objectKeys.put(object, cosObjectKey);
keyObject.put(cosObjectKey, object);
}
}
}

/**
* add an entry in the x ref table for later dump.
Expand Down Expand Up @@ -474,21 +449,19 @@ private void doWriteBodyCompressed(COSDocument document) throws IOException
{
COSBase object = compressionPool.getObject(key);
writtenObjects.add(object);
objectKeys.put(object, key);
keyObject.put(key, object);
objectStorage.put(key, object);
}
// Append top level objects to document.
for (COSObjectKey key : compressionPool.getTopLevelObjects())
{
COSBase object = compressionPool.getObject(key);
writtenObjects.add(object);
objectKeys.put(object, key);
keyObject.put(key, object);
objectStorage.put(key, object);
}
for (COSObjectKey key : compressionPool.getTopLevelObjects())
{
currentObjectKey = key;
doWriteObject(key, keyObject.get(key));
doWriteObject(key, objectStorage.getObject(key));
}
// Append object streams to document.
number = compressionPool.getHighestXRefObjectNumber();
Expand Down Expand Up @@ -520,8 +493,7 @@ private void doWriteBodyCompressed(COSDocument document) throws IOException
COSObjectKey encryptKey = new COSObjectKey(++number, 0);
currentObjectKey = encryptKey;
writtenObjects.add(encrypt);
keyObject.put(encryptKey, encrypt);
objectKeys.put(encrypt, encryptKey);
objectStorage.put(encryptKey, encrypt);

doWriteObject(encryptKey, encrypt);
}
Expand All @@ -543,11 +515,7 @@ private void addObjectToWrite( COSBase object )
{
return;
}
COSBase actual = object;
if( actual instanceof COSObject )
{
actual = ((COSObject)actual).getObject();
}
COSBase actual = objectStorage.convertToActual(object);

if (!writtenObjects.contains(object) //
&& !objectsToWrite.contains(object) //
Expand All @@ -557,13 +525,13 @@ private void addObjectToWrite( COSBase object )
COSObjectKey cosObjectKey = null;
if (actual != null)
{
cosObjectKey = objectKeys.get(actual);
cosObjectKey = objectStorage.getKey(actual);
}
if (cosObjectKey != null)
{
cosBase = keyObject.get(cosObjectKey);
cosBase = objectStorage.getObject(cosObjectKey);
}
if (actual != null && objectKeys.containsKey(actual) && !isNeedToBeUpdated(object)
if (actual != null && objectStorage.getKey(actual) != null && !isNeedToBeUpdated(object)
&& !isNeedToBeUpdated(cosBase))
{
return;
Expand Down Expand Up @@ -1052,7 +1020,7 @@ else if (last == -2)
}
return list.toArray(new Long[list.size()]);
}

/**
* This will get the object key for the object.
*
Expand All @@ -1062,25 +1030,42 @@ else if (last == -2)
*/
private COSObjectKey getObjectKey( COSBase obj )
{
COSBase actual = obj;
if( actual instanceof COSObject )
if(
reuseObjectNumbers &&
obj instanceof COSObject &&
obj.getKey() != null
)
{
if (reuseObjectNumbers)
{
COSObjectKey key = obj.getKey();
if (key != null)
{
objectKeys.put(obj, key);
return key;
}
}
actual = ((COSObject) obj).getObject();
objectStorage.put(obj.getKey(), obj);
return obj.getKey();
}

COSBase actual = objectStorage.convertToActual(obj);

// PDFBOX-4540: because objectKeys is accessible from outside, it is possible
// that a COSObject obj is already in the objectKeys map.
return objectKeys.computeIfAbsent(actual, k -> new COSObjectKey(++number, 0));
COSObjectKey key = objectStorage.getKey(obj);
if( key == null && actual != null )
{
key = objectStorage.getKey(actual);
}
if (key == null)
{
key = new COSObjectKey(++number, 0);
if( actual != null )
{
objectStorage.put(key, actual);
}
else
{
objectStorage.put(key, obj);
}
}

return key;
}


@Override
public Object visitFromArray( COSArray obj ) throws IOException
{
Expand All @@ -1103,7 +1088,7 @@ public Object visitFromArray( COSArray obj ) throws IOException
}
else if( current instanceof COSObject )
{
COSBase subValue = ((COSObject)current).getObject();
COSBase subValue = objectStorage.convertToActual(current);
if (willEncrypt || incrementalUpdate //
|| subValue instanceof COSDictionary //
|| subValue instanceof COSArray //
Expand Down Expand Up @@ -1208,7 +1193,7 @@ public Object visitFromDictionary(COSDictionary obj) throws IOException
}
else if( value instanceof COSObject )
{
COSBase subValue = ((COSObject)value).getObject();
COSBase subValue = objectStorage.convertToActual(value);
if (willEncrypt || incrementalUpdate //
|| subValue instanceof COSDictionary //
|| subValue instanceof COSArray //
Expand Down Expand Up @@ -1470,7 +1455,7 @@ public void write(PDDocument doc, SignatureInterface signInterface) throws IOExc
number = pdDocument.getDocument().getHighestXRefObjectNumber();
if(incrementalUpdate)
{
prepareIncrement();
objectStorage.setDocument(doc.getDocument());
}
Long idTime = pdDocument.getDocumentId() == null ? System.currentTimeMillis()
: pdDocument.getDocumentId();
Expand Down
Loading