Refactor XRef in obj.js

- remove unused "if (e instanceof Stream)" in XRef_fetch
- split XRef_fetch into XRef_fetchUncompressed and XRef_fetchCompressed
This explains the actual code flow better
- add line breaks after functions
- change the document to conform to current StyleGuide
This commit is contained in:
Thorben Bochenek 2014-02-27 13:46:12 +01:00
parent cab5d7b96f
commit f2f713bf15

View File

@ -234,8 +234,9 @@ var Catalog = (function CatalogClosure() {
Catalog.prototype = { Catalog.prototype = {
get metadata() { get metadata() {
var streamRef = this.catDict.getRaw('Metadata'); var streamRef = this.catDict.getRaw('Metadata');
if (!isRef(streamRef)) if (!isRef(streamRef)) {
return shadow(this, 'metadata', null); return shadow(this, 'metadata', null);
}
var encryptMetadata = !this.xref.encrypt ? false : var encryptMetadata = !this.xref.encrypt ? false :
this.xref.encrypt.encryptMetadata; this.xref.encrypt.encryptMetadata;
@ -296,17 +297,20 @@ var Catalog = (function CatalogClosure() {
while (queue.length > 0) { while (queue.length > 0) {
var i = queue.shift(); var i = queue.shift();
var outlineDict = xref.fetchIfRef(i.obj); var outlineDict = xref.fetchIfRef(i.obj);
if (outlineDict === null) if (outlineDict === null) {
continue; continue;
if (!outlineDict.has('Title')) }
if (!outlineDict.has('Title')) {
error('Invalid outline item'); error('Invalid outline item');
}
var dest = outlineDict.get('A'); var dest = outlineDict.get('A');
if (dest) if (dest) {
dest = dest.get('D'); dest = dest.get('D');
else if (outlineDict.has('Dest')) { } else if (outlineDict.has('Dest')) {
dest = outlineDict.getRaw('Dest'); dest = outlineDict.getRaw('Dest');
if (isName(dest)) if (isName(dest)) {
dest = dest.name; dest = dest.name;
}
} }
var title = outlineDict.get('Title'); var title = outlineDict.get('Title');
var outlineItem = { var outlineItem = {
@ -351,16 +355,19 @@ var Catalog = (function CatalogClosure() {
var xref = this.xref; var xref = this.xref;
var dests = {}, nameTreeRef, nameDictionaryRef; var dests = {}, nameTreeRef, nameDictionaryRef;
var obj = this.catDict.get('Names'); var obj = this.catDict.get('Names');
if (obj) if (obj) {
nameTreeRef = obj.getRaw('Dests'); nameTreeRef = obj.getRaw('Dests');
else if (this.catDict.has('Dests')) } else if (this.catDict.has('Dests')) {
nameDictionaryRef = this.catDict.get('Dests'); nameDictionaryRef = this.catDict.get('Dests');
}
if (nameDictionaryRef) { if (nameDictionaryRef) {
// reading simple destination dictionary // reading simple destination dictionary
obj = nameDictionaryRef; obj = nameDictionaryRef;
obj.forEach(function catalogForEach(key, value) { obj.forEach(function catalogForEach(key, value) {
if (!value) return; if (!value) {
return;
}
dests[key] = fetchDestination(value); dests[key] = fetchDestination(value);
}); });
} }
@ -624,9 +631,9 @@ var XRef = (function XRefClosure() {
var obj = this.readXRefTable(parser); var obj = this.readXRefTable(parser);
// Sanity check // Sanity check
if (!isCmd(obj, 'trailer')) if (!isCmd(obj, 'trailer')) {
error('Invalid XRef table: could not find trailer dictionary'); error('Invalid XRef table: could not find trailer dictionary');
}
// Read trailer dictionary, e.g. // Read trailer dictionary, e.g.
// trailer // trailer
// << /Size 22 // << /Size 22
@ -637,9 +644,9 @@ var XRef = (function XRefClosure() {
// The parser goes through the entire stream << ... >> and provides // The parser goes through the entire stream << ... >> and provides
// a getter interface for the key-value table // a getter interface for the key-value table
var dict = parser.getObj(); var dict = parser.getObj();
if (!isDict(dict)) if (!isDict(dict)) {
error('Invalid XRef table: could not parse trailer dictionary'); error('Invalid XRef table: could not parse trailer dictionary');
}
delete this.tableState; delete this.tableState;
return dict; return dict;
@ -676,9 +683,9 @@ var XRef = (function XRefClosure() {
var first = tableState.firstEntryNum; var first = tableState.firstEntryNum;
var count = tableState.entryCount; var count = tableState.entryCount;
if (!isInt(first) || !isInt(count)) if (!isInt(first) || !isInt(count)) {
error('Invalid XRef table: wrong types in subsection header'); error('Invalid XRef table: wrong types in subsection header');
}
// Inner loop is over objects themselves // Inner loop is over objects themselves
for (var i = tableState.entryNum; i < count; i++) { for (var i = tableState.entryNum; i < count; i++) {
tableState.streamPos = stream.pos; tableState.streamPos = stream.pos;
@ -704,8 +711,9 @@ var XRef = (function XRefClosure() {
error('Invalid entry in XRef subsection: ' + first + ', ' + count); error('Invalid entry in XRef subsection: ' + first + ', ' + count);
} }
if (!this.entries[i + first]) if (!this.entries[i + first]) {
this.entries[i + first] = entry; this.entries[i + first] = entry;
}
} }
tableState.entryNum = 0; tableState.entryNum = 0;
@ -723,9 +731,9 @@ var XRef = (function XRefClosure() {
} }
// Sanity check: as per spec, first object must be free // Sanity check: as per spec, first object must be free
if (this.entries[0] && !this.entries[0].free) if (this.entries[0] && !this.entries[0].free) {
error('Invalid XRef table: unexpected first object'); error('Invalid XRef table: unexpected first object');
}
return obj; return obj;
}, },
@ -769,9 +777,9 @@ var XRef = (function XRefClosure() {
var first = entryRanges[0]; var first = entryRanges[0];
var n = entryRanges[1]; var n = entryRanges[1];
if (!isInt(first) || !isInt(n)) if (!isInt(first) || !isInt(n)) {
error('Invalid XRef range fields: ' + first + ', ' + n); error('Invalid XRef range fields: ' + first + ', ' + n);
}
if (!isInt(typeFieldWidth) || !isInt(offsetFieldWidth) || if (!isInt(typeFieldWidth) || !isInt(offsetFieldWidth) ||
!isInt(generationFieldWidth)) { !isInt(generationFieldWidth)) {
error('Invalid XRef entry fields length: ' + first + ', ' + n); error('Invalid XRef entry fields length: ' + first + ', ' + n);
@ -784,12 +792,15 @@ var XRef = (function XRefClosure() {
for (j = 0; j < typeFieldWidth; ++j) for (j = 0; j < typeFieldWidth; ++j)
type = (type << 8) | stream.getByte(); type = (type << 8) | stream.getByte();
// if type field is absent, its default value = 1 // if type field is absent, its default value = 1
if (typeFieldWidth === 0) if (typeFieldWidth === 0) {
type = 1; type = 1;
for (j = 0; j < offsetFieldWidth; ++j) }
for (j = 0; j < offsetFieldWidth; ++j) {
offset = (offset << 8) | stream.getByte(); offset = (offset << 8) | stream.getByte();
for (j = 0; j < generationFieldWidth; ++j) }
for (j = 0; j < generationFieldWidth; ++j) {
generation = (generation << 8) | stream.getByte(); generation = (generation << 8) | stream.getByte();
}
var entry = {}; var entry = {};
entry.offset = offset; entry.offset = offset;
entry.gen = generation; entry.gen = generation;
@ -805,8 +816,9 @@ var XRef = (function XRefClosure() {
default: default:
error('Invalid XRef entry type: ' + type); error('Invalid XRef entry type: ' + type);
} }
if (!this.entries[first + i]) if (!this.entries[first + i]) {
this.entries[first + i] = entry; this.entries[first + i] = entry;
}
} }
streamState.entryNum = 0; streamState.entryNum = 0;
@ -814,6 +826,7 @@ var XRef = (function XRefClosure() {
entryRanges.splice(0, 2); entryRanges.splice(0, 2);
} }
}, },
indexObjects: function XRef_indexObjects() { indexObjects: function XRef_indexObjects() {
// Simple scan through the PDF content to find objects, // Simple scan through the PDF content to find objects,
// trailers and XRef streams. // trailers and XRef streams.
@ -833,8 +846,9 @@ var XRef = (function XRefClosure() {
// finding byte sequence // finding byte sequence
while (offset < dataLength) { while (offset < dataLength) {
var i = 0; var i = 0;
while (i < length && data[offset + i] == what[i]) while (i < length && data[offset + i] == what[i]) {
++i; ++i;
}
if (i >= length) if (i >= length)
break; // sequence found break; // sequence found
@ -898,8 +912,9 @@ var XRef = (function XRefClosure() {
} }
position += contentLength; position += contentLength;
} else } else {
position += token.length + 1; position += token.length + 1;
}
} }
// reading XRef streams // reading XRef streams
for (var i = 0, ii = xrefStms.length; i < ii; ++i) { for (var i = 0, ii = xrefStms.length; i < ii; ++i) {
@ -912,18 +927,22 @@ var XRef = (function XRefClosure() {
stream.pos = trailers[i]; stream.pos = trailers[i];
var parser = new Parser(new Lexer(stream), true, null); var parser = new Parser(new Lexer(stream), true, null);
var obj = parser.getObj(); var obj = parser.getObj();
if (!isCmd(obj, 'trailer')) if (!isCmd(obj, 'trailer')) {
continue; continue;
}
// read the trailer dictionary // read the trailer dictionary
if (!isDict(dict = parser.getObj())) if (!isDict(dict = parser.getObj())) {
continue; continue;
}
// taking the first one with 'ID' // taking the first one with 'ID'
if (dict.has('ID')) if (dict.has('ID')) {
return dict; return dict;
}
} }
// no tailer with 'ID', taking last one (if exists) // no tailer with 'ID', taking last one (if exists)
if (dict) if (dict) {
return dict; return dict;
}
// nothing helps // nothing helps
// calling error() would reject worker with an UnknownErrorException. // calling error() would reject worker with an UnknownErrorException.
throw new InvalidPDFException('Invalid PDF structure'); throw new InvalidPDFException('Invalid PDF structure');
@ -975,8 +994,9 @@ var XRef = (function XRefClosure() {
this.topDict = dict; this.topDict = dict;
} }
if (!dict) if (!dict) {
error('Failed to read XRef stream'); error('Failed to read XRef stream');
}
} else { } else {
error('Invalid XRef stream header'); error('Invalid XRef stream header');
} }
@ -1002,95 +1022,111 @@ var XRef = (function XRefClosure() {
info('(while reading XRef): ' + e); info('(while reading XRef): ' + e);
} }
if (recoveryMode) if (recoveryMode) {
return; return;
}
throw new XRefParseException(); throw new XRefParseException();
}, },
getEntry: function XRef_getEntry(i) { getEntry: function XRef_getEntry(i) {
var e = this.entries[i]; var xrefEntry = this.entries[i];
if (e === null) if (xrefEntry !== null && !xrefEntry.free && xrefEntry.offset) {
return null; return xrefEntry;
return e.free || !e.offset ? null : e; // returns null if entry is free }
return null;
}, },
fetchIfRef: function XRef_fetchIfRef(obj) { fetchIfRef: function XRef_fetchIfRef(obj) {
if (!isRef(obj)) if (!isRef(obj)) {
return obj; return obj;
}
return this.fetch(obj); return this.fetch(obj);
}, },
fetch: function XRef_fetch(ref, suppressEncryption) { fetch: function XRef_fetch(ref, suppressEncryption) {
assertWellFormed(isRef(ref), 'ref object is not a reference'); assertWellFormed(isRef(ref), 'ref object is not a reference');
var num = ref.num; var num = ref.num;
var e;
if (num in this.cache) { if (num in this.cache) {
e = this.cache[num]; var cacheEntry = this.cache[num];
if (e instanceof Stream) { return cacheEntry;
return e.makeSubStream(e.start, e.length, e.dict);
}
return e;
} }
e = this.getEntry(num); var xrefEntry = this.getEntry(num);
// the referenced entry can be free // the referenced entry can be free
if (e === null) if (xrefEntry === null) {
return (this.cache[num] = e); return (this.cache[num] = null);
var gen = ref.gen;
var stream, parser;
if (e.uncompressed) {
if (e.gen != gen)
error('inconsistent generation in XRef');
stream = this.stream.makeSubStream(e.offset);
parser = new Parser(new Lexer(stream), true, this);
var obj1 = parser.getObj();
var obj2 = parser.getObj();
var obj3 = parser.getObj();
if (!isInt(obj1) || obj1 != num ||
!isInt(obj2) || obj2 != gen ||
!isCmd(obj3)) {
error('bad XRef entry');
}
if (!isCmd(obj3, 'obj')) {
// some bad pdfs use "obj1234" and really mean 1234
if (obj3.cmd.indexOf('obj') === 0) {
num = parseInt(obj3.cmd.substring(3), 10);
if (!isNaN(num))
return num;
}
error('bad XRef entry');
}
if (this.encrypt && !suppressEncryption) {
try {
e = parser.getObj(this.encrypt.createCipherTransform(num, gen));
} catch (ex) {
// almost all streams must be encrypted, but sometimes
// they are not probably due to some broken generators
// re-trying without encryption
return this.fetch(ref, true);
}
} else {
e = parser.getObj();
}
if (!isStream(e)) {
this.cache[num] = e;
}
return e;
} }
// compressed entry if (xrefEntry.uncompressed) {
var tableOffset = e.offset; return this.fetchUncompressed(ref, xrefEntry, suppressEncryption);
stream = this.fetch(new Ref(tableOffset, 0)); } else {
if (!isStream(stream)) return this.fetchCompressed(xrefEntry, suppressEncryption);
}
},
fetchUncompressed: function XRef_fetchUncompressed(ref,
xrefEntry,
suppressEncryption) {
var gen = ref.gen;
var num = ref.num;
if (xrefEntry.gen !== gen) {
error('inconsistent generation in XRef');
}
var stream = this.stream.makeSubStream(xrefEntry.offset);
var parser = new Parser(new Lexer(stream), true, this);
var obj1 = parser.getObj();
var obj2 = parser.getObj();
var obj3 = parser.getObj();
if (!isInt(obj1) || parseInt(obj1, 10) !== num ||
!isInt(obj2) || parseInt(obj2, 10) !== gen ||
!isCmd(obj3)) {
error('bad XRef entry');
}
if (!isCmd(obj3, 'obj')) {
// some bad pdfs use "obj1234" and really mean 1234
if (obj3.cmd.indexOf('obj') === 0) {
num = parseInt(obj3.cmd.substring(3), 10);
if (!isNaN(num)) {
return num;
}
}
error('bad XRef entry');
}
if (this.encrypt && !suppressEncryption) {
try {
xrefEntry = parser.getObj(this.encrypt.createCipherTransform(num,
gen));
} catch (ex) {
// almost all streams must be encrypted, but sometimes
// they are not probably due to some broken generators
// re-trying without encryption
return this.fetch(ref, true);
}
} else {
xrefEntry = parser.getObj();
}
if (!isStream(xrefEntry)) {
this.cache[num] = xrefEntry;
}
return xrefEntry;
},
fetchCompressed: function XRef_fetchCompressed(xrefEntry,
suppressEncryption) {
var tableOffset = xrefEntry.offset;
var stream = this.fetch(new Ref(tableOffset, 0));
if (!isStream(stream)) {
error('bad ObjStm stream'); error('bad ObjStm stream');
}
var first = stream.dict.get('First'); var first = stream.dict.get('First');
var n = stream.dict.get('N'); var n = stream.dict.get('N');
if (!isInt(first) || !isInt(n)) { if (!isInt(first) || !isInt(n)) {
error('invalid first and n parameters for ObjStm stream'); error('invalid first and n parameters for ObjStm stream');
} }
parser = new Parser(new Lexer(stream), false, this); var parser = new Parser(new Lexer(stream), false, this);
parser.allowStreams = true; parser.allowStreams = true;
var i, entries = [], nums = []; var i, entries = [], num, nums = [];
// read the object numbers to populate cache // read the object numbers to populate cache
for (i = 0; i < n; ++i) { for (i = 0; i < n; ++i) {
num = parser.getObj(); num = parser.getObj();
@ -1112,12 +1148,13 @@ var XRef = (function XRefClosure() {
this.cache[num] = entries[i]; this.cache[num] = entries[i];
} }
} }
e = entries[e.gen]; xrefEntry = entries[xrefEntry.gen];
if (e === undefined) { if (xrefEntry === undefined) {
error('bad XRef entry for compressed object'); error('bad XRef entry for compressed object');
} }
return e; return xrefEntry;
}, },
fetchIfRefAsync: function XRef_fetchIfRefAsync(obj) { fetchIfRefAsync: function XRef_fetchIfRefAsync(obj) {
if (!isRef(obj)) { if (!isRef(obj)) {
var promise = new LegacyPromise(); var promise = new LegacyPromise();
@ -1126,6 +1163,7 @@ var XRef = (function XRefClosure() {
} }
return this.fetchAsync(obj); return this.fetchAsync(obj);
}, },
fetchAsync: function XRef_fetchAsync(ref, suppressEncryption) { fetchAsync: function XRef_fetchAsync(ref, suppressEncryption) {
var promise = new LegacyPromise(); var promise = new LegacyPromise();
var tryFetch = function (promise) { var tryFetch = function (promise) {
@ -1142,6 +1180,7 @@ var XRef = (function XRefClosure() {
tryFetch(); tryFetch();
return promise; return promise;
}, },
getCatalogObj: function XRef_getCatalogObj() { getCatalogObj: function XRef_getCatalogObj() {
return this.root; return this.root;
} }
@ -1182,8 +1221,9 @@ var NameTree = (function NameTreeClosure() {
var kids = obj.get('Kids'); var kids = obj.get('Kids');
for (i = 0, n = kids.length; i < n; i++) { for (i = 0, n = kids.length; i < n; i++) {
var kid = kids[i]; var kid = kids[i];
if (processed.has(kid)) if (processed.has(kid)) {
error('invalid destinations'); error('invalid destinations');
}
queue.push(kid); queue.push(kid);
processed.put(kid); processed.put(kid);
} }
@ -1344,5 +1384,3 @@ var ObjectLoader = (function() {
return ObjectLoader; return ObjectLoader;
})(); })();