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