Browse Source

Doesn't traverse cyclic references in Dict.getAll; reduces empty-Dict garbage

Yury Delendik 11 years ago
parent
commit
31f081ae17
  1. 2
      src/core/core.js
  2. 26
      src/core/evaluator.js
  3. 75
      src/core/obj.js
  4. 2
      src/core/parser.js
  5. 2
      src/core/stream.js
  6. 2
      src/shared/annotation.js
  7. 2
      src/shared/fonts_utils.js

2
src/core/core.js

@ -68,7 +68,7 @@ var Page = (function PageClosure() {
// present, but can be empty. Some document omit it still. In this case // present, but can be empty. Some document omit it still. In this case
// return an empty dictionary: // return an empty dictionary:
if (value === undefined) { if (value === undefined) {
value = new Dict(); value = Dict.empty;
} }
return shadow(this, 'resources', value); return shadow(this, 'resources', value);
}, },

26
src/core/evaluator.js

@ -48,6 +48,11 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
return false; return false;
} }
var processed = Object.create(null);
if (resources.objId) {
processed[resources.objId] = true;
}
var nodes = [resources]; var nodes = [resources];
while (nodes.length) { while (nodes.length) {
var node = nodes.shift(); var node = nodes.shift();
@ -75,10 +80,13 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
continue; continue;
} }
var xResources = xObject.dict.get('Resources'); var xResources = xObject.dict.get('Resources');
// Only add the resource if it's different from the current one, // Checking objId to detect an infinite loop.
// otherwise we can get stuck in an infinite loop. if (isDict(xResources) &&
if (isDict(xResources) && xResources !== node) { (!xResources.objId || !processed[xResources.objId])) {
nodes.push(xResources); nodes.push(xResources);
if (xResources.objId) {
processed[xResources.objId] = true;
}
} }
} }
} }
@ -466,9 +474,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
operatorList = (operatorList || new OperatorList()); operatorList = (operatorList || new OperatorList());
resources = (resources || new Dict()); resources = (resources || Dict.empty);
var xobjs = (resources.get('XObject') || new Dict()); var xobjs = (resources.get('XObject') || Dict.empty);
var patterns = (resources.get('Pattern') || new Dict()); var patterns = (resources.get('Pattern') || Dict.empty);
var preprocessor = new EvaluatorPreprocessor(stream, xref); var preprocessor = new EvaluatorPreprocessor(stream, xref);
if (evaluatorState) { if (evaluatorState) {
preprocessor.setState(evaluatorState); preprocessor.setState(evaluatorState);
@ -659,7 +667,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
return self.loadFont(fontName, fontRef, xref, resources, null); return self.loadFont(fontName, fontRef, xref, resources, null);
} }
resources = (xref.fetchIfRef(resources) || new Dict()); resources = (xref.fetchIfRef(resources) || Dict.empty);
// The xobj is parsed iff it's needed, e.g. if there is a `DO` cmd. // The xobj is parsed iff it's needed, e.g. if there is a `DO` cmd.
var xobjs = null; var xobjs = null;
var xobjsCache = {}; var xobjsCache = {};
@ -753,7 +761,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
} }
if (!xobjs) { if (!xobjs) {
xobjs = (resources.get('XObject') || new Dict()); xobjs = (resources.get('XObject') || Dict.empty);
} }
var name = args[0].name; var name = args[0].name;
@ -1147,7 +1155,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (type.name == 'Type3') { if (type.name == 'Type3') {
// FontDescriptor is only required for Type3 fonts when the document // FontDescriptor is only required for Type3 fonts when the document
// is a tagged pdf. Create a barbebones one to get by. // is a tagged pdf. Create a barbebones one to get by.
descriptor = new Dict(); descriptor = new Dict(null);
descriptor.set('FontName', Name.get(type.name)); descriptor.set('FontName', Name.get(type.name));
} else { } else {
// Before PDF 1.5 if the font was one of the base 14 fonts, having a // Before PDF 1.5 if the font was one of the base 14 fonts, having a

75
src/core/obj.js

@ -62,11 +62,30 @@ var Dict = (function DictClosure() {
return nonSerializable; // creating closure on some variable return nonSerializable; // creating closure on some variable
}; };
var GETALL_DICTIONARY_TYPES_WHITELIST = {
'Background': true,
'ExtGState': true,
'Halftone': true,
'Layout': true,
'Mask': true,
'Pagination': true,
'Printing': true
};
function isRecursionAllowedFor(dict) {
if (!isName(dict.Type)) {
return true;
}
var dictType = dict.Type.name;
return GETALL_DICTIONARY_TYPES_WHITELIST[dictType] === true;
}
// xref is optional // xref is optional
function Dict(xref) { function Dict(xref) {
// Map should only be used internally, use functions below to access. // Map should only be used internally, use functions below to access.
this.map = Object.create(null); this.map = Object.create(null);
this.xref = xref; this.xref = xref;
this.objId = null;
this.__nonSerializable__ = nonSerializable; // disable cloning of the Dict this.__nonSerializable__ = nonSerializable; // disable cloning of the Dict
} }
@ -130,10 +149,51 @@ var Dict = (function DictClosure() {
// creates new map and dereferences all Refs // creates new map and dereferences all Refs
getAll: function Dict_getAll() { getAll: function Dict_getAll() {
var all = {}; var all = Object.create(null);
var queue = null;
for (var key in this.map) { for (var key in this.map) {
var obj = this.get(key); var obj = this.get(key);
all[key] = (obj instanceof Dict ? obj.getAll() : obj); if (obj instanceof Dict) {
if (isRecursionAllowedFor(obj)) {
(queue || (queue = [])).push({target: all, key: key, obj: obj});
} else {
all[key] = this.getRaw(key);
}
} else {
all[key] = obj;
}
}
if (!queue) {
return all;
}
// trying to take cyclic references into the account
var processed = Object.create(null);
while (queue.length > 0) {
var item = queue.shift();
var itemObj = item.obj;
var objId = itemObj.objId;
if (objId && objId in processed) {
item.target[item.key] = processed[objId];
continue;
}
var dereferenced = Object.create(null);
for (var key in itemObj.map) {
var obj = itemObj.get(key);
if (obj instanceof Dict) {
if (isRecursionAllowedFor(obj)) {
queue.push({target: dereferenced, key: key, obj: obj});
} else {
dereferenced[key] = itemObj.getRaw(key);
}
} else {
dereferenced[key] = obj;
}
}
if (objId) {
processed[objId] = dereferenced;
}
item.target[item.key] = dereferenced;
} }
return all; return all;
}, },
@ -153,6 +213,8 @@ var Dict = (function DictClosure() {
} }
}; };
Dict.empty = new Dict(null);
return Dict; return Dict;
})(); })();
@ -1061,10 +1123,15 @@ var XRef = (function XRefClosure() {
} }
if (xrefEntry.uncompressed) { if (xrefEntry.uncompressed) {
return this.fetchUncompressed(ref, xrefEntry, suppressEncryption); xrefEntry = this.fetchUncompressed(ref, xrefEntry, suppressEncryption);
} else { } else {
return this.fetchCompressed(xrefEntry, suppressEncryption); xrefEntry = this.fetchCompressed(xrefEntry, suppressEncryption);
} }
if (isDict(xrefEntry)) {
xrefEntry.objId = 'R' + ref.num + '.' + ref.gen;
}
return xrefEntry;
}, },
fetchUncompressed: function XRef_fetchUncompressed(ref, xrefEntry, fetchUncompressed: function XRef_fetchUncompressed(ref, xrefEntry,

2
src/core/parser.js

@ -131,7 +131,7 @@ var Parser = (function ParserClosure() {
var stream = lexer.stream; var stream = lexer.stream;
// parse dictionary // parse dictionary
var dict = new Dict(); var dict = new Dict(null);
while (!isCmd(this.buf1, 'ID') && !isEOF(this.buf1)) { while (!isCmd(this.buf1, 'ID') && !isEOF(this.buf1)) {
if (!isName(this.buf1)) { if (!isName(this.buf1)) {
error('Dictionary key must be a name object'); error('Dictionary key must be a name object');

2
src/core/stream.js

@ -1744,7 +1744,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
this.str = str; this.str = str;
this.dict = str.dict; this.dict = str.dict;
params = params || new Dict(); params = params || Dict.empty;
this.encoding = params.get('K') || 0; this.encoding = params.get('K') || 0;
this.eoline = params.get('EndOfLine') || false; this.eoline = params.get('EndOfLine') || false;

2
src/shared/annotation.js

@ -378,7 +378,7 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() {
var fieldType = Util.getInheritableProperty(dict, 'FT'); var fieldType = Util.getInheritableProperty(dict, 'FT');
data.fieldType = isName(fieldType) ? fieldType.name : ''; data.fieldType = isName(fieldType) ? fieldType.name : '';
data.fieldFlags = Util.getInheritableProperty(dict, 'Ff') || 0; data.fieldFlags = Util.getInheritableProperty(dict, 'Ff') || 0;
this.fieldResources = Util.getInheritableProperty(dict, 'DR') || new Dict(); this.fieldResources = Util.getInheritableProperty(dict, 'DR') || Dict.empty;
// Building the full field name by collecting the field and // Building the full field name by collecting the field and
// its ancestors 'T' data and joining them using '.'. // its ancestors 'T' data and joining them using '.'.

2
src/shared/fonts_utils.js

@ -249,7 +249,7 @@ function readFontIndexData(aStream, aIsByte) {
} }
var Type2Parser = function type2Parser(aFilePath) { var Type2Parser = function type2Parser(aFilePath) {
var font = new Dict(); var font = new Dict(null);
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.open('GET', aFilePath, false); xhr.open('GET', aFilePath, false);

Loading…
Cancel
Save