// the table of xrefs
this.objIdx = new PDFXref[50];
int pos = this.buf.position();
PDFDecrypter newDefaultDecrypter = null;
// read a bunch of nested trailer tables
while (true) {
// make sure we are looking at an xref table
if (!nextItemIs("xref")) {
this.buf.position(pos);
readTrailer15(password);
return;
// throw new PDFParseException("Expected 'xref' at start of table");
}
// read a bunch of linked tabled
while (true) {
// read until the word "trailer"
PDFObject obj=readObject(-1, -1, IdentityDecrypter.getInstance());
if (obj.getType() == PDFObject.KEYWORD &&
obj.getStringValue().equals("trailer")) {
break;
}
// read the starting position of the reference
if (obj.getType() != PDFObject.NUMBER) {
throw new PDFParseException("Expected number for first xref entry");
}
int refstart = obj.getIntValue();
// read the size of the reference table
obj = readObject(-1, -1, IdentityDecrypter.getInstance());
if (obj.getType() != PDFObject.NUMBER) {
throw new PDFParseException("Expected number for length of xref table");
}
int reflen = obj.getIntValue();
// skip a line
readLine();
// extend the objIdx table, if necessary
if (refstart + reflen >= this.objIdx.length) {
PDFXref nobjIdx[] = new PDFXref[refstart + reflen];
System.arraycopy(this.objIdx, 0, nobjIdx, 0, this.objIdx.length);
this.objIdx = nobjIdx;
}
// read reference lines
for (int refID = refstart; refID < refstart + reflen; refID++) {
// each reference line is 20 bytes long
byte[] refline = new byte[20];
this.buf.get(refline);
// ignore this line if the object ID is already defined
if (this.objIdx[refID] != null) {
continue;
}
// see if it's an active object
if (refline[17] == 'n') {
this.objIdx[refID] = new PDFXref(refline);
} else {
this.objIdx[refID] = new PDFXref(null);
}
}
}
// at this point, the "trailer" word (not EOL) has been read.
PDFObject trailerdict = readObject(-1, -1, IdentityDecrypter.getInstance());
if (trailerdict.getType() != PDFObject.DICTIONARY) {
throw new IOException("Expected dictionary after \"trailer\"");
}
// read the root object location
if (this.root == null) {
this.root = trailerdict.getDictRef("Root");
if (this.root != null) {
this.root.setObjectId(PDFObject.OBJ_NUM_TRAILER,
PDFObject.OBJ_NUM_TRAILER);
}
}
// read the encryption information
if (this.encrypt == null) {
this.encrypt = trailerdict.getDictRef("Encrypt");
if (this.encrypt != null) {
this.encrypt.setObjectId(PDFObject.OBJ_NUM_TRAILER,
PDFObject.OBJ_NUM_TRAILER);
}
newDefaultDecrypter =
PDFDecrypterFactory.createDecryptor(
this.encrypt,
trailerdict.getDictRef("ID"),
password);
}
if (this.info == null) {
this.info = trailerdict.getDictRef("Info");
if (this.info != null) {
if (!this.info.isIndirect()) {
throw new PDFParseException(
"Info in trailer must be an indirect reference");
}
this.info.setObjectId(PDFObject.OBJ_NUM_TRAILER,
PDFObject.OBJ_NUM_TRAILER);
}
}
// support for hybrid-PDFs containing an additional compressed-xref-stream
PDFObject xrefstmPos = trailerdict.getDictRef("XRefStm");
if (xrefstmPos != null) {
int pos14 = this.buf.position();
this.buf.position(xrefstmPos.getIntValue());
readTrailer15(password);
this.buf.position(pos14);
}
// read the location of the previous xref table
PDFObject prevloc = trailerdict.getDictRef("Prev");
if (prevloc != null) {
this.buf.position(prevloc.getIntValue());
} else {
break;
}
// see if we have an optional Version entry
if (this.root.getDictRef("Version") != null) {
processVersion(this.root.getDictRef("Version").getStringValue());
}
}
// make sure we found a root
if (this.root == null) {
throw new PDFParseException("No /Root key found in trailer dictionary");
}
if (this.encrypt != null && newDefaultDecrypter!=null) {
PDFObject permissions = this.encrypt.getDictRef("P");
if (permissions!=null && !newDefaultDecrypter.isOwnerAuthorised()) {
int perms= permissions != null ? permissions.getIntValue() : 0;
if (permissions!=null) {
this.printable = (perms & 4) != 0;
this.saveable = (perms & 16) != 0;
}