Merge pull request #4374 from yurydelendik/dictgetall
Doesn't traverse cyclic references in Dict.getAll; reduces empty-Dict GC
This commit is contained in:
commit
608c6cea5a
@ -68,7 +68,7 @@ var Page = (function PageClosure() {
|
||||
// present, but can be empty. Some document omit it still. In this case
|
||||
// return an empty dictionary:
|
||||
if (value === undefined) {
|
||||
value = new Dict();
|
||||
value = Dict.empty;
|
||||
}
|
||||
return shadow(this, 'resources', value);
|
||||
},
|
||||
|
@ -48,6 +48,11 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
return false;
|
||||
}
|
||||
|
||||
var processed = Object.create(null);
|
||||
if (resources.objId) {
|
||||
processed[resources.objId] = true;
|
||||
}
|
||||
|
||||
var nodes = [resources];
|
||||
while (nodes.length) {
|
||||
var node = nodes.shift();
|
||||
@ -75,10 +80,13 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
continue;
|
||||
}
|
||||
var xResources = xObject.dict.get('Resources');
|
||||
// Only add the resource if it's different from the current one,
|
||||
// otherwise we can get stuck in an infinite loop.
|
||||
if (isDict(xResources) && xResources !== node) {
|
||||
// Checking objId to detect an infinite loop.
|
||||
if (isDict(xResources) &&
|
||||
(!xResources.objId || !processed[xResources.objId])) {
|
||||
nodes.push(xResources);
|
||||
if (xResources.objId) {
|
||||
processed[xResources.objId] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -466,9 +474,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
|
||||
operatorList = (operatorList || new OperatorList());
|
||||
|
||||
resources = (resources || new Dict());
|
||||
var xobjs = (resources.get('XObject') || new Dict());
|
||||
var patterns = (resources.get('Pattern') || new Dict());
|
||||
resources = (resources || Dict.empty);
|
||||
var xobjs = (resources.get('XObject') || Dict.empty);
|
||||
var patterns = (resources.get('Pattern') || Dict.empty);
|
||||
var preprocessor = new EvaluatorPreprocessor(stream, xref);
|
||||
if (evaluatorState) {
|
||||
preprocessor.setState(evaluatorState);
|
||||
@ -659,7 +667,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
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.
|
||||
var xobjs = null;
|
||||
var xobjsCache = {};
|
||||
@ -753,7 +761,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
}
|
||||
|
||||
if (!xobjs) {
|
||||
xobjs = (resources.get('XObject') || new Dict());
|
||||
xobjs = (resources.get('XObject') || Dict.empty);
|
||||
}
|
||||
|
||||
var name = args[0].name;
|
||||
@ -1147,7 +1155,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
if (type.name == 'Type3') {
|
||||
// FontDescriptor is only required for Type3 fonts when the document
|
||||
// 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));
|
||||
} else {
|
||||
// Before PDF 1.5 if the font was one of the base 14 fonts, having a
|
||||
|
@ -62,11 +62,30 @@ var Dict = (function DictClosure() {
|
||||
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
|
||||
function Dict(xref) {
|
||||
// Map should only be used internally, use functions below to access.
|
||||
this.map = Object.create(null);
|
||||
this.xref = xref;
|
||||
this.objId = null;
|
||||
this.__nonSerializable__ = nonSerializable; // disable cloning of the Dict
|
||||
}
|
||||
|
||||
@ -130,10 +149,51 @@ var Dict = (function DictClosure() {
|
||||
|
||||
// creates new map and dereferences all Refs
|
||||
getAll: function Dict_getAll() {
|
||||
var all = {};
|
||||
var all = Object.create(null);
|
||||
var queue = null;
|
||||
for (var key in this.map) {
|
||||
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;
|
||||
},
|
||||
@ -153,6 +213,8 @@ var Dict = (function DictClosure() {
|
||||
}
|
||||
};
|
||||
|
||||
Dict.empty = new Dict(null);
|
||||
|
||||
return Dict;
|
||||
})();
|
||||
|
||||
@ -1061,10 +1123,15 @@ var XRef = (function XRefClosure() {
|
||||
}
|
||||
|
||||
if (xrefEntry.uncompressed) {
|
||||
return this.fetchUncompressed(ref, xrefEntry, suppressEncryption);
|
||||
xrefEntry = this.fetchUncompressed(ref, xrefEntry, suppressEncryption);
|
||||
} 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,
|
||||
|
@ -131,7 +131,7 @@ var Parser = (function ParserClosure() {
|
||||
var stream = lexer.stream;
|
||||
|
||||
// parse dictionary
|
||||
var dict = new Dict();
|
||||
var dict = new Dict(null);
|
||||
while (!isCmd(this.buf1, 'ID') && !isEOF(this.buf1)) {
|
||||
if (!isName(this.buf1)) {
|
||||
error('Dictionary key must be a name object');
|
||||
|
@ -1744,7 +1744,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
||||
this.str = str;
|
||||
this.dict = str.dict;
|
||||
|
||||
params = params || new Dict();
|
||||
params = params || Dict.empty;
|
||||
|
||||
this.encoding = params.get('K') || 0;
|
||||
this.eoline = params.get('EndOfLine') || false;
|
||||
|
@ -378,7 +378,7 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() {
|
||||
var fieldType = Util.getInheritableProperty(dict, 'FT');
|
||||
data.fieldType = isName(fieldType) ? fieldType.name : '';
|
||||
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
|
||||
// its ancestors 'T' data and joining them using '.'.
|
||||
|
@ -249,7 +249,7 @@ function readFontIndexData(aStream, aIsByte) {
|
||||
}
|
||||
|
||||
var Type2Parser = function type2Parser(aFilePath) {
|
||||
var font = new Dict();
|
||||
var font = new Dict(null);
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', aFilePath, false);
|
||||
|
Loading…
Reference in New Issue
Block a user