/* Copyright 2012 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* uses XRef */ 'use strict'; (function (root, factory) { if (typeof define === 'function' && define.amd) { define('pdfjs/core/primitives', ['exports', 'pdfjs/shared/util'], factory); } else if (typeof exports !== 'undefined') { factory(exports, require('../shared/util.js')); } else { factory((root.pdfjsCorePrimitives = {}), root.pdfjsSharedUtil); } }(this, function (exports, sharedUtil) { var isArray = sharedUtil.isArray; var Name = (function NameClosure() { function Name(name) { this.name = name; } Name.prototype = {}; var nameCache = {}; Name.get = function Name_get(name) { var nameValue = nameCache[name]; return (nameValue ? nameValue : (nameCache[name] = new Name(name))); }; return Name; })(); var Cmd = (function CmdClosure() { function Cmd(cmd) { this.cmd = cmd; } Cmd.prototype = {}; var cmdCache = {}; Cmd.get = function Cmd_get(cmd) { var cmdValue = cmdCache[cmd]; return (cmdValue ? cmdValue : (cmdCache[cmd] = new Cmd(cmd))); }; return Cmd; })(); var Dict = (function DictClosure() { var nonSerializable = function nonSerializableClosure() { 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 } Dict.prototype = { assignXref: function Dict_assignXref(newXref) { this.xref = newXref; }, // automatically dereferences Ref objects get: function Dict_get(key1, key2, key3) { var value; var xref = this.xref; if (typeof (value = this.map[key1]) !== 'undefined' || key1 in this.map || typeof key2 === 'undefined') { return xref ? xref.fetchIfRef(value) : value; } if (typeof (value = this.map[key2]) !== 'undefined' || key2 in this.map || typeof key3 === 'undefined') { return xref ? xref.fetchIfRef(value) : value; } value = this.map[key3] || null; return xref ? xref.fetchIfRef(value) : value; }, // Same as get(), but returns a promise and uses fetchIfRefAsync(). getAsync: function Dict_getAsync(key1, key2, key3) { var value; var xref = this.xref; if (typeof (value = this.map[key1]) !== 'undefined' || key1 in this.map || typeof key2 === 'undefined') { if (xref) { return xref.fetchIfRefAsync(value); } return Promise.resolve(value); } if (typeof (value = this.map[key2]) !== 'undefined' || key2 in this.map || typeof key3 === 'undefined') { if (xref) { return xref.fetchIfRefAsync(value); } return Promise.resolve(value); } value = this.map[key3] || null; if (xref) { return xref.fetchIfRefAsync(value); } return Promise.resolve(value); }, // Same as get(), but dereferences all elements if the result is an Array. getArray: function Dict_getArray(key1, key2, key3) { var value = this.get(key1, key2, key3); var xref = this.xref; if (!isArray(value) || !xref) { return value; } value = value.slice(); // Ensure that we don't modify the Dict data. for (var i = 0, ii = value.length; i < ii; i++) { if (!isRef(value[i])) { continue; } value[i] = xref.fetch(value[i]); } return value; }, // no dereferencing getRaw: function Dict_getRaw(key) { return this.map[key]; }, // creates new map and dereferences all Refs getAll: function Dict_getAll() { var all = Object.create(null); var queue = null; var key, obj; for (key in this.map) { obj = this.get(key); 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 (key in itemObj.map) { 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; }, getKeys: function Dict_getKeys() { return Object.keys(this.map); }, set: function Dict_set(key, value) { this.map[key] = value; }, has: function Dict_has(key) { return key in this.map; }, forEach: function Dict_forEach(callback) { for (var key in this.map) { callback(key, this.get(key)); } } }; Dict.empty = new Dict(null); Dict.merge = function Dict_merge(xref, dictArray) { var mergedDict = new Dict(xref); for (var i = 0, ii = dictArray.length; i < ii; i++) { var dict = dictArray[i]; if (!isDict(dict)) { continue; } for (var keyName in dict.map) { if (mergedDict.map[keyName]) { continue; } mergedDict.map[keyName] = dict.map[keyName]; } } return mergedDict; }; return Dict; })(); var Ref = (function RefClosure() { function Ref(num, gen) { this.num = num; this.gen = gen; } Ref.prototype = { toString: function Ref_toString() { // This function is hot, so we make the string as compact as possible. // |this.gen| is almost always zero, so we treat that case specially. var str = this.num + 'R'; if (this.gen !== 0) { str += this.gen; } return str; } }; return Ref; })(); // The reference is identified by number and generation. // This structure stores only one instance of the reference. var RefSet = (function RefSetClosure() { function RefSet() { this.dict = {}; } RefSet.prototype = { has: function RefSet_has(ref) { return ref.toString() in this.dict; }, put: function RefSet_put(ref) { this.dict[ref.toString()] = true; }, remove: function RefSet_remove(ref) { delete this.dict[ref.toString()]; } }; return RefSet; })(); var RefSetCache = (function RefSetCacheClosure() { function RefSetCache() { this.dict = Object.create(null); } RefSetCache.prototype = { get: function RefSetCache_get(ref) { return this.dict[ref.toString()]; }, has: function RefSetCache_has(ref) { return ref.toString() in this.dict; }, put: function RefSetCache_put(ref, obj) { this.dict[ref.toString()] = obj; }, putAlias: function RefSetCache_putAlias(ref, aliasRef) { this.dict[ref.toString()] = this.get(aliasRef); }, forEach: function RefSetCache_forEach(fn, thisArg) { for (var i in this.dict) { fn.call(thisArg, this.dict[i]); } }, clear: function RefSetCache_clear() { this.dict = Object.create(null); } }; return RefSetCache; })(); function isName(v) { return v instanceof Name; } function isCmd(v, cmd) { return v instanceof Cmd && (cmd === undefined || v.cmd === cmd); } function isDict(v, type) { if (!(v instanceof Dict)) { return false; } if (!type) { return true; } var dictType = v.get('Type'); return isName(dictType) && dictType.name === type; } function isRef(v) { return v instanceof Ref; } function isStream(v) { return typeof v === 'object' && v !== null && v.getBytes !== undefined; } exports.Cmd = Cmd; exports.Dict = Dict; exports.Name = Name; exports.Ref = Ref; exports.RefSet = RefSet; exports.RefSetCache = RefSetCache; exports.isCmd = isCmd; exports.isDict = isDict; exports.isName = isName; exports.isRef = isRef; exports.isStream = isStream; }));