*/
protected COSBase parseObjectDynamically(int objNr, int objGenNr,
boolean requireExistingNotCompressedObj) throws IOException
{
// ---- create object key and get object (container) from pool
final COSObjectKey objKey = new COSObjectKey(objNr, objGenNr);
final COSObject pdfObject = document.getObjectFromPool(objKey);
if (pdfObject.getObject() == null)
{
// not previously parsed
// ---- read offset or object stream object number from xref table
Long offsetOrObjstmObNr = xrefTrailerResolver.getXrefTable().get(objKey);
// sanity test to circumvent loops with broken documents
if (requireExistingNotCompressedObj
&& ((offsetOrObjstmObNr == null) || (offsetOrObjstmObNr <= 0)))
{
throw new IOException("Object must be defined and must not be compressed object: "
+ objKey.getNumber() + ":" + objKey.getGeneration());
}
if (offsetOrObjstmObNr == null)
{
// not defined object -> NULL object (Spec. 1.7, chap. 3.2.9)
pdfObject.setObject(COSNull.NULL);
}
else if (offsetOrObjstmObNr > 0)
{
// offset of indirect object in file
// ---- go to object start
setPdfSource(offsetOrObjstmObNr);
// ---- we must have an indirect object
final long readObjNr = readObjectNumber();
final long readObjGen = readGenerationNumber();
readPattern(OBJ_MARKER);
// ---- consistency check
if ((readObjNr != objKey.getNumber()) || (readObjGen != objKey.getGeneration()))
{
throw new IOException("XREF for " + objKey.getNumber() + ":"
+ objKey.getGeneration() + " points to wrong object: " + readObjNr
+ ":" + readObjGen);
}
skipSpaces();
COSBase pb = parseDirObject();
String endObjectKey = readString();
if (endObjectKey.equals("stream"))
{
pdfSource.unread(endObjectKey.getBytes("ISO-8859-1"));
pdfSource.unread(' ');
if (pb instanceof COSDictionary)
{
COSStream stream = parseCOSStream((COSDictionary) pb);
if (securityHandler != null)
{
securityHandler.decryptStream(stream, objNr, objGenNr);
}
pb = stream;
}
else
{
// this is not legal
// the combination of a dict and the stream/endstream
// forms a complete stream object
throw new IOException("Stream not preceded by dictionary (offset: "
+ offsetOrObjstmObNr + ").");
}
skipSpaces();
endObjectKey = readLine();
// we have case with a second 'endstream' before endobj
if (!endObjectKey.startsWith("endobj"))
{
if (endObjectKey.startsWith("endstream"))
{
endObjectKey = endObjectKey.substring(9).trim();
if (endObjectKey.length() == 0)
{
// no other characters in extra endstream line
endObjectKey = readLine(); // read next line
}
}
}
}
else if (securityHandler != null)
{
// decrypt
if (pb instanceof COSString)
{
decrypt((COSString) pb, objNr, objGenNr);
}
else if (pb instanceof COSDictionary)
{
COSDictionary dict = (COSDictionary) pb;
// skip dictionary containing the signature
if (!COSName.SIG.equals(dict.getCOSName(COSName.TYPE)))
{
for (Entry<COSName, COSBase> entry : dict.entrySet())
{
if (entry.getValue() instanceof COSString)
{
decrypt((COSString) entry.getValue(), objNr, objGenNr);
}
else if (entry.getValue() instanceof COSArray)
{
securityHandler.decryptArray((COSArray) entry.getValue(), objNr, objGenNr);
}
}
}
}
else if (pb instanceof COSArray)
{
final COSArray array = (COSArray) pb;
for (int aIdx = 0, len = array.size(); aIdx < len; aIdx++)
{
if (array.get(aIdx) instanceof COSString)
{
decrypt((COSString) array.get(aIdx), objNr, objGenNr);
}
}
}
}
pdfObject.setObject(pb);
if (!endObjectKey.startsWith("endobj"))
{
if (isLenient)
{
LOG.warn("Object (" + readObjNr + ":" + readObjGen + ") at offset "
+ offsetOrObjstmObNr + " does not end with 'endobj' but with '"
+ endObjectKey + "'");
}
else
{
throw new IOException("Object (" + readObjNr + ":" + readObjGen
+ ") at offset " + offsetOrObjstmObNr
+ " does not end with 'endobj' but with '" + endObjectKey + "'");
}
}
releasePdfSourceInputStream();
}
else
{
// xref value is object nr of object stream containing object to
// be parsed;
// since our object was not found it means object stream was not
// parsed so far
final int objstmObjNr = (int) (-offsetOrObjstmObNr);
final COSBase objstmBaseObj = parseObjectDynamically(objstmObjNr, 0, true);
if (objstmBaseObj instanceof COSStream)
{
// parse object stream
PDFObjectStreamParser parser = new PDFObjectStreamParser(
(COSStream) objstmBaseObj, document, forceParsing);
parser.parse();
// get set of object numbers referenced for this object
// stream
final Set<Long> refObjNrs = xrefTrailerResolver
.getContainedObjectNumbers(objstmObjNr);
// register all objects which are referenced to be contained
// in object stream
for (COSObject next : parser.getObjects())
{
COSObjectKey stmObjKey = new COSObjectKey(next);
if (refObjNrs.contains(stmObjKey.getNumber()))
{
COSObject stmObj = document.getObjectFromPool(stmObjKey);
stmObj.setObject(next.getObject());
}
}