Merge pull request #9990 from timvandermeij/catalog
Convert the `Catalog` class, in `src/core/obj.js`, to ES6 syntax
This commit is contained in:
commit
7b7cd6dc95
345
src/core/obj.js
345
src/core/obj.js
@ -32,13 +32,14 @@ function fetchDestination(dest) {
|
|||||||
return isDict(dest) ? dest.get('D') : dest;
|
return isDict(dest) ? dest.get('D') : dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
var Catalog = (function CatalogClosure() {
|
class Catalog {
|
||||||
function Catalog(pdfManager, xref) {
|
constructor(pdfManager, xref) {
|
||||||
this.pdfManager = pdfManager;
|
this.pdfManager = pdfManager;
|
||||||
this.xref = xref;
|
this.xref = xref;
|
||||||
|
|
||||||
this.catDict = xref.getCatalogObj();
|
this.catDict = xref.getCatalogObj();
|
||||||
if (!isDict(this.catDict)) {
|
if (!isDict(this.catDict)) {
|
||||||
throw new FormatError('catalog object is not a dictionary');
|
throw new FormatError('Catalog object is not a dictionary.');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.fontCache = new RefSetCache();
|
this.fontCache = new RefSetCache();
|
||||||
@ -46,21 +47,20 @@ var Catalog = (function CatalogClosure() {
|
|||||||
this.pageKidsCountCache = new RefSetCache();
|
this.pageKidsCountCache = new RefSetCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
Catalog.prototype = {
|
|
||||||
get metadata() {
|
get metadata() {
|
||||||
var streamRef = this.catDict.getRaw('Metadata');
|
const 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 :
|
const suppressEncryption = !(this.xref.encrypt &&
|
||||||
this.xref.encrypt.encryptMetadata);
|
this.xref.encrypt.encryptMetadata);
|
||||||
|
const stream = this.xref.fetch(streamRef, suppressEncryption);
|
||||||
|
let metadata;
|
||||||
|
|
||||||
var stream = this.xref.fetch(streamRef, !encryptMetadata);
|
|
||||||
var metadata;
|
|
||||||
if (stream && isDict(stream.dict)) {
|
if (stream && isDict(stream.dict)) {
|
||||||
var type = stream.dict.get('Type');
|
const type = stream.dict.get('Type');
|
||||||
var subtype = stream.dict.get('Subtype');
|
const subtype = stream.dict.get('Subtype');
|
||||||
|
|
||||||
if (isName(type, 'Metadata') && isName(subtype, 'XML')) {
|
if (isName(type, 'Metadata') && isName(subtype, 'XML')) {
|
||||||
// XXX: This should examine the charset the XML document defines,
|
// XXX: This should examine the charset the XML document defines,
|
||||||
@ -78,31 +78,35 @@ var Catalog = (function CatalogClosure() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return shadow(this, 'metadata', metadata);
|
return shadow(this, 'metadata', metadata);
|
||||||
},
|
|
||||||
get toplevelPagesDict() {
|
|
||||||
var pagesObj = this.catDict.get('Pages');
|
|
||||||
if (!isDict(pagesObj)) {
|
|
||||||
throw new FormatError('invalid top-level pages dictionary');
|
|
||||||
}
|
}
|
||||||
// shadow the prototype getter
|
|
||||||
|
get toplevelPagesDict() {
|
||||||
|
const pagesObj = this.catDict.get('Pages');
|
||||||
|
if (!isDict(pagesObj)) {
|
||||||
|
throw new FormatError('Invalid top-level pages dictionary.');
|
||||||
|
}
|
||||||
return shadow(this, 'toplevelPagesDict', pagesObj);
|
return shadow(this, 'toplevelPagesDict', pagesObj);
|
||||||
},
|
}
|
||||||
|
|
||||||
get documentOutline() {
|
get documentOutline() {
|
||||||
var obj = null;
|
let obj = null;
|
||||||
try {
|
try {
|
||||||
obj = this.readDocumentOutline();
|
obj = this._readDocumentOutline();
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
if (ex instanceof MissingDataException) {
|
if (ex instanceof MissingDataException) {
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
warn('Unable to read document outline');
|
warn('Unable to read document outline.');
|
||||||
}
|
}
|
||||||
return shadow(this, 'documentOutline', obj);
|
return shadow(this, 'documentOutline', obj);
|
||||||
},
|
}
|
||||||
readDocumentOutline: function Catalog_readDocumentOutline() {
|
|
||||||
var obj = this.catDict.get('Outlines');
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_readDocumentOutline() {
|
||||||
|
let obj = this.catDict.get('Outlines');
|
||||||
if (!isDict(obj)) {
|
if (!isDict(obj)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -110,39 +114,42 @@ var Catalog = (function CatalogClosure() {
|
|||||||
if (!isRef(obj)) {
|
if (!isRef(obj)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var root = { items: [], };
|
|
||||||
var queue = [{ obj, parent: root, }];
|
const root = { items: [], };
|
||||||
|
const queue = [{ obj, parent: root, }];
|
||||||
// To avoid recursion, keep track of the already processed items.
|
// To avoid recursion, keep track of the already processed items.
|
||||||
var processed = new RefSet();
|
const processed = new RefSet();
|
||||||
processed.put(obj);
|
processed.put(obj);
|
||||||
var xref = this.xref, blackColor = new Uint8ClampedArray(3);
|
const xref = this.xref, blackColor = new Uint8ClampedArray(3);
|
||||||
|
|
||||||
while (queue.length > 0) {
|
while (queue.length > 0) {
|
||||||
var i = queue.shift();
|
const i = queue.shift();
|
||||||
var outlineDict = xref.fetchIfRef(i.obj);
|
const outlineDict = xref.fetchIfRef(i.obj);
|
||||||
if (outlineDict === null) {
|
if (outlineDict === null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!outlineDict.has('Title')) {
|
if (!outlineDict.has('Title')) {
|
||||||
throw new FormatError('Invalid outline item');
|
throw new FormatError('Invalid outline item encountered.');
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = { url: null, dest: null, };
|
const data = { url: null, dest: null, };
|
||||||
Catalog.parseDestDictionary({
|
Catalog.parseDestDictionary({
|
||||||
destDict: outlineDict,
|
destDict: outlineDict,
|
||||||
resultObj: data,
|
resultObj: data,
|
||||||
docBaseUrl: this.pdfManager.docBaseUrl,
|
docBaseUrl: this.pdfManager.docBaseUrl,
|
||||||
});
|
});
|
||||||
var title = outlineDict.get('Title');
|
const title = outlineDict.get('Title');
|
||||||
var flags = outlineDict.get('F') || 0;
|
const flags = outlineDict.get('F') || 0;
|
||||||
|
const color = outlineDict.getArray('C');
|
||||||
|
let rgbColor = blackColor;
|
||||||
|
|
||||||
var color = outlineDict.getArray('C'), rgbColor = blackColor;
|
|
||||||
// We only need to parse the color when it's valid, and non-default.
|
// We only need to parse the color when it's valid, and non-default.
|
||||||
if (Array.isArray(color) && color.length === 3 &&
|
if (Array.isArray(color) && color.length === 3 &&
|
||||||
(color[0] !== 0 || color[1] !== 0 || color[2] !== 0)) {
|
(color[0] !== 0 || color[1] !== 0 || color[2] !== 0)) {
|
||||||
rgbColor = ColorSpace.singletons.rgb.getRgb(color, 0);
|
rgbColor = ColorSpace.singletons.rgb.getRgb(color, 0);
|
||||||
}
|
}
|
||||||
var outlineItem = {
|
|
||||||
|
const outlineItem = {
|
||||||
dest: data.dest,
|
dest: data.dest,
|
||||||
url: data.url,
|
url: data.url,
|
||||||
unsafeUrl: data.unsafeUrl,
|
unsafeUrl: data.unsafeUrl,
|
||||||
@ -154,6 +161,7 @@ var Catalog = (function CatalogClosure() {
|
|||||||
italic: !!(flags & 1),
|
italic: !!(flags & 1),
|
||||||
items: [],
|
items: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
i.parent.items.push(outlineItem);
|
i.parent.items.push(outlineItem);
|
||||||
obj = outlineDict.getRaw('First');
|
obj = outlineDict.getRaw('First');
|
||||||
if (isRef(obj) && !processed.has(obj)) {
|
if (isRef(obj) && !processed.has(obj)) {
|
||||||
@ -167,16 +175,16 @@ var Catalog = (function CatalogClosure() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (root.items.length > 0 ? root.items : null);
|
return (root.items.length > 0 ? root.items : null);
|
||||||
},
|
}
|
||||||
|
|
||||||
get numPages() {
|
get numPages() {
|
||||||
var obj = this.toplevelPagesDict.get('Count');
|
const obj = this.toplevelPagesDict.get('Count');
|
||||||
if (!Number.isInteger(obj)) {
|
if (!Number.isInteger(obj)) {
|
||||||
throw new FormatError(
|
throw new FormatError(
|
||||||
'page count in top level pages object is not an integer');
|
'Page count in top-level pages dictionary is not an integer.');
|
||||||
}
|
}
|
||||||
// shadow the prototype getter
|
|
||||||
return shadow(this, 'numPages', obj);
|
return shadow(this, 'numPages', obj);
|
||||||
},
|
}
|
||||||
|
|
||||||
get destinations() {
|
get destinations() {
|
||||||
const obj = this._readDests(), dests = Object.create(null);
|
const obj = this._readDests(), dests = Object.create(null);
|
||||||
@ -193,14 +201,19 @@ var Catalog = (function CatalogClosure() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
return shadow(this, 'destinations', dests);
|
return shadow(this, 'destinations', dests);
|
||||||
},
|
}
|
||||||
|
|
||||||
getDestination(destinationId) {
|
getDestination(destinationId) {
|
||||||
const obj = this._readDests();
|
const obj = this._readDests();
|
||||||
if (obj instanceof NameTree || obj instanceof Dict) {
|
if (obj instanceof NameTree || obj instanceof Dict) {
|
||||||
return fetchDestination(obj.get(destinationId) || null);
|
return fetchDestination(obj.get(destinationId) || null);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
_readDests() {
|
_readDests() {
|
||||||
const obj = this.catDict.get('Names');
|
const obj = this.catDict.get('Names');
|
||||||
if (obj && obj.has('Dests')) {
|
if (obj && obj.has('Dests')) {
|
||||||
@ -208,12 +221,12 @@ var Catalog = (function CatalogClosure() {
|
|||||||
} else if (this.catDict.has('Dests')) { // Simple destination dictionary.
|
} else if (this.catDict.has('Dests')) { // Simple destination dictionary.
|
||||||
return this.catDict.get('Dests');
|
return this.catDict.get('Dests');
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
get pageLabels() {
|
get pageLabels() {
|
||||||
var obj = null;
|
let obj = null;
|
||||||
try {
|
try {
|
||||||
obj = this.readPageLabels();
|
obj = this._readPageLabels();
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
if (ex instanceof MissingDataException) {
|
if (ex instanceof MissingDataException) {
|
||||||
throw ex;
|
throw ex;
|
||||||
@ -221,25 +234,29 @@ var Catalog = (function CatalogClosure() {
|
|||||||
warn('Unable to read page labels.');
|
warn('Unable to read page labels.');
|
||||||
}
|
}
|
||||||
return shadow(this, 'pageLabels', obj);
|
return shadow(this, 'pageLabels', obj);
|
||||||
},
|
}
|
||||||
readPageLabels: function Catalog_readPageLabels() {
|
|
||||||
var obj = this.catDict.getRaw('PageLabels');
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_readPageLabels() {
|
||||||
|
const obj = this.catDict.getRaw('PageLabels');
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var pageLabels = new Array(this.numPages);
|
|
||||||
var style = null;
|
|
||||||
var prefix = '';
|
|
||||||
|
|
||||||
var numberTree = new NumberTree(obj, this.xref);
|
const pageLabels = new Array(this.numPages);
|
||||||
var nums = numberTree.getAll();
|
let style = null, prefix = '';
|
||||||
var currentLabel = '', currentIndex = 1;
|
|
||||||
|
|
||||||
for (var i = 0, ii = this.numPages; i < ii; i++) {
|
const numberTree = new NumberTree(obj, this.xref);
|
||||||
|
const nums = numberTree.getAll();
|
||||||
|
let currentLabel = '', currentIndex = 1;
|
||||||
|
|
||||||
|
for (let i = 0, ii = this.numPages; i < ii; i++) {
|
||||||
if (i in nums) {
|
if (i in nums) {
|
||||||
const labelDict = nums[i];
|
const labelDict = nums[i];
|
||||||
if (!isDict(labelDict)) {
|
if (!isDict(labelDict)) {
|
||||||
throw new FormatError('The PageLabel is not a dictionary.');
|
throw new FormatError('PageLabel is not a dictionary.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (labelDict.has('Type') &&
|
if (labelDict.has('Type') &&
|
||||||
@ -288,15 +305,15 @@ var Catalog = (function CatalogClosure() {
|
|||||||
break;
|
break;
|
||||||
case 'A':
|
case 'A':
|
||||||
case 'a':
|
case 'a':
|
||||||
var LIMIT = 26; // Use only the characters A--Z, or a--z.
|
const LIMIT = 26; // Use only the characters A-Z, or a-z.
|
||||||
var A_UPPER_CASE = 0x41, A_LOWER_CASE = 0x61;
|
const A_UPPER_CASE = 0x41, A_LOWER_CASE = 0x61;
|
||||||
|
|
||||||
var baseCharCode = (style === 'a' ? A_LOWER_CASE : A_UPPER_CASE);
|
const baseCharCode = (style === 'a' ? A_LOWER_CASE : A_UPPER_CASE);
|
||||||
var letterIndex = currentIndex - 1;
|
const letterIndex = currentIndex - 1;
|
||||||
var character = String.fromCharCode(baseCharCode +
|
const character = String.fromCharCode(baseCharCode +
|
||||||
(letterIndex % LIMIT));
|
(letterIndex % LIMIT));
|
||||||
var charBuf = [];
|
const charBuf = [];
|
||||||
for (var j = 0, jj = (letterIndex / LIMIT) | 0; j <= jj; j++) {
|
for (let j = 0, jj = (letterIndex / LIMIT) | 0; j <= jj; j++) {
|
||||||
charBuf.push(character);
|
charBuf.push(character);
|
||||||
}
|
}
|
||||||
currentLabel = charBuf.join('');
|
currentLabel = charBuf.join('');
|
||||||
@ -313,10 +330,10 @@ var Catalog = (function CatalogClosure() {
|
|||||||
currentIndex++;
|
currentIndex++;
|
||||||
}
|
}
|
||||||
return pageLabels;
|
return pageLabels;
|
||||||
},
|
}
|
||||||
|
|
||||||
get pageMode() {
|
get pageMode() {
|
||||||
let obj = this.catDict.get('PageMode');
|
const obj = this.catDict.get('PageMode');
|
||||||
let pageMode = 'UseNone'; // Default value.
|
let pageMode = 'UseNone'; // Default value.
|
||||||
|
|
||||||
if (isName(obj)) {
|
if (isName(obj)) {
|
||||||
@ -331,21 +348,17 @@ var Catalog = (function CatalogClosure() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return shadow(this, 'pageMode', pageMode);
|
return shadow(this, 'pageMode', pageMode);
|
||||||
},
|
|
||||||
|
|
||||||
get attachments() {
|
|
||||||
var xref = this.xref;
|
|
||||||
var attachments = null, nameTreeRef;
|
|
||||||
var obj = this.catDict.get('Names');
|
|
||||||
if (obj) {
|
|
||||||
nameTreeRef = obj.getRaw('EmbeddedFiles');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nameTreeRef) {
|
get attachments() {
|
||||||
var nameTree = new NameTree(nameTreeRef, xref);
|
const obj = this.catDict.get('Names');
|
||||||
var names = nameTree.getAll();
|
let attachments = null;
|
||||||
for (var name in names) {
|
|
||||||
var fs = new FileSpec(names[name], xref);
|
if (obj && obj.has('EmbeddedFiles')) {
|
||||||
|
const nameTree = new NameTree(obj.getRaw('EmbeddedFiles'), this.xref);
|
||||||
|
const names = nameTree.getAll();
|
||||||
|
for (const name in names) {
|
||||||
|
const fs = new FileSpec(names[name], this.xref);
|
||||||
if (!attachments) {
|
if (!attachments) {
|
||||||
attachments = Object.create(null);
|
attachments = Object.create(null);
|
||||||
}
|
}
|
||||||
@ -353,49 +366,52 @@ var Catalog = (function CatalogClosure() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return shadow(this, 'attachments', attachments);
|
return shadow(this, 'attachments', attachments);
|
||||||
},
|
}
|
||||||
|
|
||||||
get javaScript() {
|
get javaScript() {
|
||||||
var xref = this.xref;
|
const obj = this.catDict.get('Names');
|
||||||
var obj = this.catDict.get('Names');
|
|
||||||
|
|
||||||
let javaScript = null;
|
let javaScript = null;
|
||||||
function appendIfJavaScriptDict(jsDict) {
|
function appendIfJavaScriptDict(jsDict) {
|
||||||
var type = jsDict.get('S');
|
const type = jsDict.get('S');
|
||||||
if (!isName(type, 'JavaScript')) {
|
if (!isName(type, 'JavaScript')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var js = jsDict.get('JS');
|
|
||||||
|
let js = jsDict.get('JS');
|
||||||
if (isStream(js)) {
|
if (isStream(js)) {
|
||||||
js = bytesToString(js.getBytes());
|
js = bytesToString(js.getBytes());
|
||||||
} else if (!isString(js)) {
|
} else if (!isString(js)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!javaScript) {
|
if (!javaScript) {
|
||||||
javaScript = [];
|
javaScript = [];
|
||||||
}
|
}
|
||||||
javaScript.push(stringToPDFString(js));
|
javaScript.push(stringToPDFString(js));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj && obj.has('JavaScript')) {
|
if (obj && obj.has('JavaScript')) {
|
||||||
var nameTree = new NameTree(obj.getRaw('JavaScript'), xref);
|
const nameTree = new NameTree(obj.getRaw('JavaScript'), this.xref);
|
||||||
var names = nameTree.getAll();
|
const names = nameTree.getAll();
|
||||||
for (var name in names) {
|
for (const name in names) {
|
||||||
// We don't really use the JavaScript right now. This code is
|
// We don't use most JavaScript in PDF documents. This code is
|
||||||
// defensive so we don't cause errors on document load.
|
// defensive so we don't cause errors on document load.
|
||||||
var jsDict = names[name];
|
const jsDict = names[name];
|
||||||
if (isDict(jsDict)) {
|
if (isDict(jsDict)) {
|
||||||
appendIfJavaScriptDict(jsDict);
|
appendIfJavaScriptDict(jsDict);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append OpenAction actions to javaScript array
|
// Append OpenAction actions to the JavaScript array.
|
||||||
var openactionDict = this.catDict.get('OpenAction');
|
const openActionDict = this.catDict.get('OpenAction');
|
||||||
if (isDict(openactionDict, 'Action')) {
|
if (isDict(openActionDict, 'Action')) {
|
||||||
var actionType = openactionDict.get('S');
|
const actionType = openActionDict.get('S');
|
||||||
if (isName(actionType, 'Named')) {
|
if (isName(actionType, 'Named')) {
|
||||||
// The named Print action is not a part of the PDF 1.7 specification,
|
// The named Print action is not a part of the PDF 1.7 specification,
|
||||||
// but is supported by many PDF readers/writers (including Adobe's).
|
// but is supported by many PDF readers/writers (including Adobe's).
|
||||||
var action = openactionDict.get('N');
|
const action = openActionDict.get('N');
|
||||||
if (isName(action, 'Print')) {
|
if (isName(action, 'Print')) {
|
||||||
if (!javaScript) {
|
if (!javaScript) {
|
||||||
javaScript = [];
|
javaScript = [];
|
||||||
@ -403,39 +419,40 @@ var Catalog = (function CatalogClosure() {
|
|||||||
javaScript.push('print({});');
|
javaScript.push('print({});');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
appendIfJavaScriptDict(openactionDict);
|
appendIfJavaScriptDict(openActionDict);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return shadow(this, 'javaScript', javaScript);
|
return shadow(this, 'javaScript', javaScript);
|
||||||
},
|
}
|
||||||
|
|
||||||
cleanup: function Catalog_cleanup() {
|
cleanup() {
|
||||||
this.pageKidsCountCache.clear();
|
this.pageKidsCountCache.clear();
|
||||||
|
|
||||||
var promises = [];
|
const promises = [];
|
||||||
this.fontCache.forEach(function(promise) {
|
this.fontCache.forEach(function(promise) {
|
||||||
promises.push(promise);
|
promises.push(promise);
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.all(promises).then((translatedFonts) => {
|
return Promise.all(promises).then((translatedFonts) => {
|
||||||
for (var i = 0, ii = translatedFonts.length; i < ii; i++) {
|
for (let i = 0, ii = translatedFonts.length; i < ii; i++) {
|
||||||
var font = translatedFonts[i].dict;
|
const font = translatedFonts[i].dict;
|
||||||
delete font.translated;
|
delete font.translated;
|
||||||
}
|
}
|
||||||
this.fontCache.clear();
|
this.fontCache.clear();
|
||||||
this.builtInCMapCache.clear();
|
this.builtInCMapCache.clear();
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getPageDict: function Catalog_getPageDict(pageIndex) {
|
getPageDict(pageIndex) {
|
||||||
var capability = createPromiseCapability();
|
const capability = createPromiseCapability();
|
||||||
var nodesToVisit = [this.catDict.getRaw('Pages')];
|
const nodesToVisit = [this.catDict.getRaw('Pages')];
|
||||||
var count, currentPageIndex = 0;
|
const xref = this.xref, pageKidsCountCache = this.pageKidsCountCache;
|
||||||
var xref = this.xref, pageKidsCountCache = this.pageKidsCountCache;
|
let count, currentPageIndex = 0;
|
||||||
|
|
||||||
function next() {
|
function next() {
|
||||||
while (nodesToVisit.length) {
|
while (nodesToVisit.length) {
|
||||||
var currentNode = nodesToVisit.pop();
|
const currentNode = nodesToVisit.pop();
|
||||||
|
|
||||||
if (isRef(currentNode)) {
|
if (isRef(currentNode)) {
|
||||||
count = pageKidsCountCache.get(currentNode);
|
count = pageKidsCountCache.get(currentNode);
|
||||||
@ -470,7 +487,7 @@ var Catalog = (function CatalogClosure() {
|
|||||||
// Must be a child page dictionary.
|
// Must be a child page dictionary.
|
||||||
if (!isDict(currentNode)) {
|
if (!isDict(currentNode)) {
|
||||||
capability.reject(new FormatError(
|
capability.reject(new FormatError(
|
||||||
'page dictionary kid reference points to wrong type of object'));
|
'Page dictionary kid reference points to wrong type of object.'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -478,7 +495,7 @@ var Catalog = (function CatalogClosure() {
|
|||||||
if (Number.isInteger(count) && count >= 0) {
|
if (Number.isInteger(count) && count >= 0) {
|
||||||
// Cache the Kids count, since it can reduce redundant lookups in
|
// Cache the Kids count, since it can reduce redundant lookups in
|
||||||
// documents where all nodes are found at *one* level of the tree.
|
// documents where all nodes are found at *one* level of the tree.
|
||||||
var objId = currentNode.objId;
|
const objId = currentNode.objId;
|
||||||
if (objId && !pageKidsCountCache.has(objId)) {
|
if (objId && !pageKidsCountCache.has(objId)) {
|
||||||
pageKidsCountCache.put(objId, count);
|
pageKidsCountCache.put(objId, count);
|
||||||
}
|
}
|
||||||
@ -489,7 +506,7 @@ var Catalog = (function CatalogClosure() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var kids = currentNode.get('Kids');
|
const kids = currentNode.get('Kids');
|
||||||
if (!Array.isArray(kids)) {
|
if (!Array.isArray(kids)) {
|
||||||
// Prevent errors in corrupt PDF documents that violate the
|
// Prevent errors in corrupt PDF documents that violate the
|
||||||
// specification by *inlining* Page dicts directly in the Kids
|
// specification by *inlining* Page dicts directly in the Kids
|
||||||
@ -505,42 +522,43 @@ var Catalog = (function CatalogClosure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
capability.reject(new FormatError(
|
capability.reject(new FormatError(
|
||||||
'page dictionary kids object is not an array'));
|
'Page dictionary kids object is not an array.'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always check all `Kids` nodes, to avoid getting stuck in an empty
|
// Always check all `Kids` nodes, to avoid getting stuck in an empty
|
||||||
// node further down in the tree (see issue5644.pdf, issue8088.pdf),
|
// node further down in the tree (see issue5644.pdf, issue8088.pdf),
|
||||||
// and to ensure that we actually find the correct `Page` dict.
|
// and to ensure that we actually find the correct `Page` dict.
|
||||||
for (var last = kids.length - 1; last >= 0; last--) {
|
for (let last = kids.length - 1; last >= 0; last--) {
|
||||||
nodesToVisit.push(kids[last]);
|
nodesToVisit.push(kids[last]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
capability.reject(new Error('Page index ' + pageIndex + ' not found.'));
|
capability.reject(new Error(`Page index ${pageIndex} not found.`));
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
return capability.promise;
|
return capability.promise;
|
||||||
},
|
}
|
||||||
|
|
||||||
getPageIndex: function Catalog_getPageIndex(pageRef) {
|
getPageIndex(pageRef) {
|
||||||
// The page tree nodes have the count of all the leaves below them. To get
|
// The page tree nodes have the count of all the leaves below them. To get
|
||||||
// how many pages are before we just have to walk up the tree and keep
|
// how many pages are before we just have to walk up the tree and keep
|
||||||
// adding the count of siblings to the left of the node.
|
// adding the count of siblings to the left of the node.
|
||||||
var xref = this.xref;
|
const xref = this.xref;
|
||||||
|
|
||||||
function pagesBeforeRef(kidRef) {
|
function pagesBeforeRef(kidRef) {
|
||||||
var total = 0;
|
let total = 0, parentRef;
|
||||||
var parentRef;
|
|
||||||
return xref.fetchAsync(kidRef).then(function(node) {
|
return xref.fetchAsync(kidRef).then(function(node) {
|
||||||
if (isRefsEqual(kidRef, pageRef) && !isDict(node, 'Page') &&
|
if (isRefsEqual(kidRef, pageRef) && !isDict(node, 'Page') &&
|
||||||
!(isDict(node) && !node.has('Type') && node.has('Contents'))) {
|
!(isDict(node) && !node.has('Type') && node.has('Contents'))) {
|
||||||
throw new FormatError(
|
throw new FormatError(
|
||||||
'The reference does not point to a /Page Dict.');
|
'The reference does not point to a /Page dictionary.');
|
||||||
}
|
}
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!isDict(node)) {
|
if (!isDict(node)) {
|
||||||
throw new FormatError('node must be a Dict.');
|
throw new FormatError('Node must be a dictionary.');
|
||||||
}
|
}
|
||||||
parentRef = node.getRaw('Parent');
|
parentRef = node.getRaw('Parent');
|
||||||
return node.getAsync('Parent');
|
return node.getAsync('Parent');
|
||||||
@ -549,19 +567,20 @@ var Catalog = (function CatalogClosure() {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!isDict(parent)) {
|
if (!isDict(parent)) {
|
||||||
throw new FormatError('parent must be a Dict.');
|
throw new FormatError('Parent must be a dictionary.');
|
||||||
}
|
}
|
||||||
return parent.getAsync('Kids');
|
return parent.getAsync('Kids');
|
||||||
}).then(function(kids) {
|
}).then(function(kids) {
|
||||||
if (!kids) {
|
if (!kids) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var kidPromises = [];
|
|
||||||
var found = false;
|
const kidPromises = [];
|
||||||
for (var i = 0; i < kids.length; i++) {
|
let found = false;
|
||||||
var kid = kids[i];
|
for (let i = 0, ii = kids.length; i < ii; i++) {
|
||||||
|
const kid = kids[i];
|
||||||
if (!isRef(kid)) {
|
if (!isRef(kid)) {
|
||||||
throw new FormatError('kid must be a Ref.');
|
throw new FormatError('Kid must be a reference.');
|
||||||
}
|
}
|
||||||
if (isRefsEqual(kid, kidRef)) {
|
if (isRefsEqual(kid, kidRef)) {
|
||||||
found = true;
|
found = true;
|
||||||
@ -569,18 +588,17 @@ var Catalog = (function CatalogClosure() {
|
|||||||
}
|
}
|
||||||
kidPromises.push(xref.fetchAsync(kid).then(function(kid) {
|
kidPromises.push(xref.fetchAsync(kid).then(function(kid) {
|
||||||
if (!isDict(kid)) {
|
if (!isDict(kid)) {
|
||||||
throw new FormatError('kid node must be a Dict.');
|
throw new FormatError('Kid node must be a dictionary.');
|
||||||
}
|
}
|
||||||
if (kid.has('Count')) {
|
if (kid.has('Count')) {
|
||||||
var count = kid.get('Count');
|
total += kid.get('Count');
|
||||||
total += count;
|
} else { // Page leaf node.
|
||||||
} else { // page leaf node
|
|
||||||
total++;
|
total++;
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
throw new FormatError('kid ref not found in parents kids');
|
throw new FormatError('Kid reference not found in parent\'s kids.');
|
||||||
}
|
}
|
||||||
return Promise.all(kidPromises).then(function() {
|
return Promise.all(kidPromises).then(function() {
|
||||||
return [total, parentRef];
|
return [total, parentRef];
|
||||||
@ -588,22 +606,20 @@ var Catalog = (function CatalogClosure() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var total = 0;
|
let total = 0;
|
||||||
function next(ref) {
|
function next(ref) {
|
||||||
return pagesBeforeRef(ref).then(function(args) {
|
return pagesBeforeRef(ref).then(function(args) {
|
||||||
if (!args) {
|
if (!args) {
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
var count = args[0];
|
const [count, parentRef] = args;
|
||||||
var parentRef = args[1];
|
|
||||||
total += count;
|
total += count;
|
||||||
return next(parentRef);
|
return next(parentRef);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return next(pageRef);
|
return next(pageRef);
|
||||||
},
|
}
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef ParseDestDictionaryParameters
|
* @typedef ParseDestDictionaryParameters
|
||||||
@ -618,16 +634,17 @@ var Catalog = (function CatalogClosure() {
|
|||||||
* Helper function used to parse the contents of destination dictionaries.
|
* Helper function used to parse the contents of destination dictionaries.
|
||||||
* @param {ParseDestDictionaryParameters} params
|
* @param {ParseDestDictionaryParameters} params
|
||||||
*/
|
*/
|
||||||
Catalog.parseDestDictionary = function Catalog_parseDestDictionary(params) {
|
static parseDestDictionary(params) {
|
||||||
// Lets URLs beginning with 'www.' default to using the 'http://' protocol.
|
// Lets URLs beginning with 'www.' default to using the 'http://' protocol.
|
||||||
function addDefaultProtocolToUrl(url) {
|
function addDefaultProtocolToUrl(url) {
|
||||||
if (url.indexOf('www.') === 0) {
|
if (url.indexOf('www.') === 0) {
|
||||||
return ('http://' + url);
|
return `http://${url}`;
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
// According to ISO 32000-1:2008, section 12.6.4.7, URIs should be encoded
|
// According to ISO 32000-1:2008, section 12.6.4.7, URIs should be encoded
|
||||||
// in 7-bit ASCII. Some bad PDFs use UTF-8 encoding, see Bugzilla 1122280.
|
// in 7-bit ASCII. Some bad PDFs use UTF-8 encoding; see Bugzilla 1122280.
|
||||||
function tryConvertUrlEncoding(url) {
|
function tryConvertUrlEncoding(url) {
|
||||||
try {
|
try {
|
||||||
return stringToUTF8String(url);
|
return stringToUTF8String(url);
|
||||||
@ -636,19 +653,19 @@ var Catalog = (function CatalogClosure() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var destDict = params.destDict;
|
const destDict = params.destDict;
|
||||||
if (!isDict(destDict)) {
|
if (!isDict(destDict)) {
|
||||||
warn('parseDestDictionary: "destDict" must be a dictionary.');
|
warn('parseDestDictionary: `destDict` must be a dictionary.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var resultObj = params.resultObj;
|
const resultObj = params.resultObj;
|
||||||
if (typeof resultObj !== 'object') {
|
if (typeof resultObj !== 'object') {
|
||||||
warn('parseDestDictionary: "resultObj" must be an object.');
|
warn('parseDestDictionary: `resultObj` must be an object.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var docBaseUrl = params.docBaseUrl || null;
|
const docBaseUrl = params.docBaseUrl || null;
|
||||||
|
|
||||||
var action = destDict.get('A'), url, dest;
|
let action = destDict.get('A'), url, dest;
|
||||||
if (!isDict(action) && destDict.has('Dest')) {
|
if (!isDict(action) && destDict.has('Dest')) {
|
||||||
// A /Dest entry should *only* contain a Name or an Array, but some bad
|
// A /Dest entry should *only* contain a Name or an Array, but some bad
|
||||||
// PDF generators ignore that and treat it as an /A entry.
|
// PDF generators ignore that and treat it as an /A entry.
|
||||||
@ -656,12 +673,12 @@ var Catalog = (function CatalogClosure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isDict(action)) {
|
if (isDict(action)) {
|
||||||
let actionType = action.get('S');
|
const actionType = action.get('S');
|
||||||
if (!isName(actionType)) {
|
if (!isName(actionType)) {
|
||||||
warn('parseDestDictionary: Invalid type in Action dictionary.');
|
warn('parseDestDictionary: Invalid type in Action dictionary.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let actionName = actionType.name;
|
const actionName = actionType.name;
|
||||||
|
|
||||||
switch (actionName) {
|
switch (actionName) {
|
||||||
case 'URI':
|
case 'URI':
|
||||||
@ -687,7 +704,7 @@ var Catalog = (function CatalogClosure() {
|
|||||||
/* falls through */
|
/* falls through */
|
||||||
|
|
||||||
case 'GoToR':
|
case 'GoToR':
|
||||||
var urlDict = action.get('F');
|
const urlDict = action.get('F');
|
||||||
if (isDict(urlDict)) {
|
if (isDict(urlDict)) {
|
||||||
// We assume that we found a FileSpec dictionary
|
// We assume that we found a FileSpec dictionary
|
||||||
// and fetch the URL without checking any further.
|
// and fetch the URL without checking any further.
|
||||||
@ -697,13 +714,13 @@ var Catalog = (function CatalogClosure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: the destination is relative to the *remote* document.
|
// NOTE: the destination is relative to the *remote* document.
|
||||||
var remoteDest = action.get('D');
|
let remoteDest = action.get('D');
|
||||||
if (remoteDest) {
|
if (remoteDest) {
|
||||||
if (isName(remoteDest)) {
|
if (isName(remoteDest)) {
|
||||||
remoteDest = remoteDest.name;
|
remoteDest = remoteDest.name;
|
||||||
}
|
}
|
||||||
if (isString(url)) {
|
if (isString(url)) {
|
||||||
let baseUrl = url.split('#')[0];
|
const baseUrl = url.split('#')[0];
|
||||||
if (isString(remoteDest)) {
|
if (isString(remoteDest)) {
|
||||||
url = baseUrl + '#' + remoteDest;
|
url = baseUrl + '#' + remoteDest;
|
||||||
} else if (Array.isArray(remoteDest)) {
|
} else if (Array.isArray(remoteDest)) {
|
||||||
@ -712,21 +729,23 @@ var Catalog = (function CatalogClosure() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The 'NewWindow' property, equal to `LinkTarget.BLANK`.
|
// The 'NewWindow' property, equal to `LinkTarget.BLANK`.
|
||||||
var newWindow = action.get('NewWindow');
|
const newWindow = action.get('NewWindow');
|
||||||
if (isBool(newWindow)) {
|
if (isBool(newWindow)) {
|
||||||
resultObj.newWindow = newWindow;
|
resultObj.newWindow = newWindow;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'Named':
|
case 'Named':
|
||||||
var namedAction = action.get('N');
|
const namedAction = action.get('N');
|
||||||
if (isName(namedAction)) {
|
if (isName(namedAction)) {
|
||||||
resultObj.action = namedAction.name;
|
resultObj.action = namedAction.name;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'JavaScript':
|
case 'JavaScript':
|
||||||
var jsAction = action.get('JS'), js;
|
const jsAction = action.get('JS');
|
||||||
|
let js;
|
||||||
|
|
||||||
if (isStream(jsAction)) {
|
if (isStream(jsAction)) {
|
||||||
js = bytesToString(jsAction.getBytes());
|
js = bytesToString(jsAction.getBytes());
|
||||||
} else if (isString(jsAction)) {
|
} else if (isString(jsAction)) {
|
||||||
@ -734,19 +753,19 @@ var Catalog = (function CatalogClosure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (js) {
|
if (js) {
|
||||||
// Attempt to recover valid URLs from 'JS' entries with certain
|
// Attempt to recover valid URLs from `JS` entries with certain
|
||||||
// white-listed formats, e.g.
|
// white-listed formats:
|
||||||
// - window.open('http://example.com')
|
// - window.open('http://example.com')
|
||||||
// - app.launchURL('http://example.com', true)
|
// - app.launchURL('http://example.com', true)
|
||||||
var URL_OPEN_METHODS = [
|
const URL_OPEN_METHODS = [
|
||||||
'app.launchURL',
|
'app.launchURL',
|
||||||
'window.open'
|
'window.open'
|
||||||
];
|
];
|
||||||
var regex = new RegExp(
|
const regex = new RegExp(
|
||||||
'^\\s*(' + URL_OPEN_METHODS.join('|').split('.').join('\\.') +
|
'^\\s*(' + URL_OPEN_METHODS.join('|').split('.').join('\\.') +
|
||||||
')\\((?:\'|\")([^\'\"]*)(?:\'|\")(?:,\\s*(\\w+)\\)|\\))', 'i');
|
')\\((?:\'|\")([^\'\"]*)(?:\'|\")(?:,\\s*(\\w+)\\)|\\))', 'i');
|
||||||
|
|
||||||
var jsUrl = regex.exec(stringToPDFString(js));
|
const jsUrl = regex.exec(stringToPDFString(js));
|
||||||
if (jsUrl && jsUrl[2]) {
|
if (jsUrl && jsUrl[2]) {
|
||||||
url = jsUrl[2];
|
url = jsUrl[2];
|
||||||
|
|
||||||
@ -758,7 +777,7 @@ var Catalog = (function CatalogClosure() {
|
|||||||
}
|
}
|
||||||
/* falls through */
|
/* falls through */
|
||||||
default:
|
default:
|
||||||
warn(`parseDestDictionary: Unsupported Action type "${actionName}".`);
|
warn(`parseDestDictionary: unsupported action type "${actionName}".`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (destDict.has('Dest')) { // Simple destination.
|
} else if (destDict.has('Dest')) { // Simple destination.
|
||||||
@ -767,7 +786,7 @@ var Catalog = (function CatalogClosure() {
|
|||||||
|
|
||||||
if (isString(url)) {
|
if (isString(url)) {
|
||||||
url = tryConvertUrlEncoding(url);
|
url = tryConvertUrlEncoding(url);
|
||||||
var absoluteUrl = createValidAbsoluteUrl(url, docBaseUrl);
|
const absoluteUrl = createValidAbsoluteUrl(url, docBaseUrl);
|
||||||
if (absoluteUrl) {
|
if (absoluteUrl) {
|
||||||
resultObj.url = absoluteUrl.href;
|
resultObj.url = absoluteUrl.href;
|
||||||
}
|
}
|
||||||
@ -781,10 +800,8 @@ var Catalog = (function CatalogClosure() {
|
|||||||
resultObj.dest = dest;
|
resultObj.dest = dest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
}
|
||||||
return Catalog;
|
|
||||||
})();
|
|
||||||
|
|
||||||
var XRef = (function XRefClosure() {
|
var XRef = (function XRefClosure() {
|
||||||
function XRef(stream, pdfManager) {
|
function XRef(stream, pdfManager) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user