UNPKG

@syncfusion/ej2-pdf

Version:

Feature-rich JavaScript PDF library with built-in support for loading and manipulating PDF document.

334 lines (333 loc) 13.4 kB
import { _PdfDictionary, _PdfReferenceSet, _PdfReferenceSetCache, _PdfReference, _isName } from './pdf-primitives'; import { FormatError } from './utils'; /** * Provides access to the PDF catalog and utilities to traverse and cache the page tree. * * @private */ var _PdfCatalog = /** @class */ (function () { function _PdfCatalog(xref) { /** * List of page indices that have been parsed and cached. * * @private */ this._parsedPages = []; /** * Cache of page dictionaries and references keyed by page index. * * @private */ this._pageCache = new Map(); this._crossReference = xref; this._catalogDictionary = xref._getCatalogObj(); if (!(this._catalogDictionary instanceof _PdfDictionary)) { throw new FormatError('Catalog object is not a dictionary.'); } else { this._catalogDictionary.isCatalog = true; } this._topPagesDictionary = this._catalogDictionary.get('Pages'); this.pageKidsCountCache = new _PdfReferenceSetCache(); this.pageIndexCache = new _PdfReferenceSetCache(); } Object.defineProperty(_PdfCatalog.prototype, "version", { get: function () { var value; if (this._catalogDictionary.has('Version')) { var version = this._catalogDictionary.get('Version'); if (version) { value = version.name; } } return value; }, enumerable: true, configurable: true }); Object.defineProperty(_PdfCatalog.prototype, "pageCount", { get: function () { var obj = this._topPagesDictionary.get('Count'); if (typeof obj === 'undefined' || !Number.isInteger(obj)) { throw new FormatError('Invalid page count'); } return obj; }, enumerable: true, configurable: true }); Object.defineProperty(_PdfCatalog.prototype, "acroForm", { get: function () { var form; if (this._catalogDictionary.has('AcroForm')) { form = this._catalogDictionary.get('AcroForm'); } if (form === null || typeof form === 'undefined') { form = this._createForm(); } return form; }, enumerable: true, configurable: true }); /* eslint-disable */ /** * Creates and registers a new AcroForm dictionary on the catalog. * * @private * @returns {_PdfDictionary} The created AcroForm dictionary. */ _PdfCatalog.prototype._createForm = function () { var form = new _PdfDictionary(this._crossReference); var ref = this._crossReference._getNextReference(); this._crossReference._cacheMap.set(ref, form); this._catalogDictionary.set('AcroForm', ref); this._catalogDictionary._updated = true; this._crossReference._allowCatalog = true; form._updated = true; return form; }; _PdfCatalog.prototype._addToCache = function (pageIndex, dictionary, reference) { this._pageCache.set(pageIndex, { dictionary: dictionary, reference: reference, parent: dictionary.get('Parent') }); this._parsedPages.push(pageIndex); }; /** * Gets the page dictionary and reference for the specified page index, using caches when possible. * * @private * @param {number} pageIndex Zero-based page index to retrieve. * @returns {{ dictionary: _PdfDictionary, reference: _PdfReference }} The page dictionary and its indirect reference (if any). */ _PdfCatalog.prototype._getPageDictionary = function (pageIndex) { if (pageIndex !== 0 && this._checkPageTreeFormat()) { var nearestIndex = this._findNearestIndex(pageIndex); if (nearestIndex > 0) { var nearestCachedPage = this._pageCache.get(nearestIndex); if (nearestCachedPage) { var result = this._traverseFromCached(nearestIndex, nearestCachedPage, pageIndex); if (result) { return result; } } } } return this._traverseFromRoot(pageIndex); }; _PdfCatalog.prototype._checkPageTreeFormat = function () { if (typeof this._hasInvalidPageTree === 'undefined') { var kids = this._topPagesDictionary.get('Kids'); if (Array.isArray(kids)) { this._hasInvalidPageTree = this.pageCount === kids.length; } } return this._hasInvalidPageTree; }; _PdfCatalog.prototype._findNearestIndex = function (targetIndex) { var length = this._parsedPages.length; var nearest = this._parsedPages[0]; var minDistance = Math.abs(nearest - targetIndex); for (var i = 1; i < length; i++) { var index = this._parsedPages[i]; var distance = Math.abs(index - targetIndex); if (distance < minDistance) { nearest = index; minDistance = distance; } } return nearest; }; _PdfCatalog.prototype._traverseFromCached = function (index, cachedPage, targetIndex) { var visitedNodes = new _PdfReferenceSet(); var direction = targetIndex > index ? 1 : -1; var currentNode = cachedPage.dictionary; var currentIndex = index; var parent = cachedPage.parent; var currentReference = cachedPage.reference; if (currentIndex === targetIndex && _isName(currentNode.get('Type'), 'Page')) { return { dictionary: currentNode, reference: currentReference }; } while (currentNode && currentIndex !== targetIndex) { if (currentNode instanceof _PdfReference) { if (visitedNodes.has(currentNode)) { throw new FormatError('Pages tree contains circular reference.'); } visitedNodes.put(currentNode); } var nextPage = this._findNextPage(currentNode, parent, direction); if (!nextPage) { break; } currentNode = nextPage.dictionary; currentReference = nextPage.reference; currentIndex += direction; if (currentIndex === targetIndex && _isName(currentNode.get('Type'), 'Page')) { this._addToCache(currentIndex, nextPage.dictionary, nextPage.reference); return { dictionary: currentNode, reference: currentReference }; } } return null; }; _PdfCatalog.prototype._findNextPage = function (currentPage, parent, direction) { var _this = this; if (!parent || !parent.has('Kids')) { return null; } var kids = parent.getRaw('Kids'); if (kids instanceof _PdfReference) { kids = this._crossReference._fetch(kids); } if (!Array.isArray(kids)) { throw new FormatError('Page dictionary kids object is not an array.'); } var currentIndex = -1; if (currentPage._reference) { currentIndex = kids.indexOf(currentPage._reference); } else { currentIndex = kids.findIndex(function (kid) { if (kid instanceof _PdfReference) { kid = _this._crossReference._fetch(kid); } return kid === currentPage; }); } if (currentIndex === -1) { return null; } var nextIndex = currentIndex + direction; if (nextIndex < 0 || nextIndex >= kids.length) { return null; } var nextKid = kids[nextIndex]; var nextDict = nextKid instanceof _PdfReference ? this._crossReference._fetch(nextKid) : nextKid; if (!(nextDict instanceof _PdfDictionary)) { throw new FormatError('Invalid page dictionary type.'); } return { dictionary: nextDict, reference: nextKid instanceof _PdfReference ? nextKid : null }; }; _PdfCatalog.prototype._traverseFromRoot = function (pageIndex) { var nodesToVisit = [this._topPagesDictionary]; var visitedNodes = new _PdfReferenceSet(); var pagesRef = this._catalogDictionary.getRaw('Pages'); if (pagesRef && pagesRef instanceof _PdfReference) { visitedNodes.put(pagesRef); } var xref = this._crossReference; var pageKidsCountCache = this.pageKidsCountCache; var pageIndexCache = this.pageIndexCache; var currentPageIndex = 0; while (nodesToVisit.length > 0) { var currentNode = nodesToVisit.pop(); if (currentNode && currentNode instanceof _PdfReference) { var count_1 = pageKidsCountCache.get(currentNode); if (count_1 >= 0 && currentPageIndex + count_1 <= pageIndex) { currentPageIndex += count_1; continue; } if (visitedNodes.has(currentNode)) { throw new FormatError('Pages tree contains circular reference.'); } visitedNodes.put(currentNode); var obj = xref._fetch(currentNode); if (obj && obj instanceof _PdfDictionary) { var type = obj.getRaw('Type'); if (type instanceof _PdfReference) { type = xref._fetch(type); } if (_isName(type, 'Page') || !obj.has('Kids')) { if (!pageKidsCountCache.has(currentNode)) { pageKidsCountCache.put(currentNode, 1); } if (!pageIndexCache.has(currentNode)) { pageIndexCache.put(currentNode, currentPageIndex); } if (currentPageIndex === pageIndex) { this._addToCache(currentPageIndex, obj, currentNode); return { dictionary: obj, reference: currentNode }; } currentPageIndex++; continue; } } nodesToVisit.push(obj); continue; } if (!(currentNode instanceof _PdfDictionary)) { throw new FormatError('Page dictionary kid reference points to wrong type of object.'); } var objId = currentNode.objId; var count = currentNode.get('Count'); if (count instanceof _PdfReference) { count = xref._fetch(count); } if (count !== null && typeof count !== 'undefined' && Number.isInteger(count) && count >= 0) { if (objId && !pageKidsCountCache.has(objId)) { pageKidsCountCache.set(objId, count); } if (currentPageIndex + count <= pageIndex) { currentPageIndex += count; continue; } } var kids = currentNode.getRaw('Kids'); if (kids instanceof _PdfReference) { kids = xref._fetch(kids); } if (!Array.isArray(kids)) { var type = currentNode.getRaw('Type'); if (type instanceof _PdfReference) { type = xref._fetch(type); } if (_isName(type, 'Page') || !currentNode.has('Kids')) { if (currentPageIndex === pageIndex) { this._addToCache(currentPageIndex, currentNode, null); return { dictionary: currentNode, reference: null }; } currentPageIndex++; continue; } throw new FormatError('Page dictionary kids object is not an array.'); } for (var last = kids.length - 1; last >= 0; last--) { nodesToVisit.push(kids[last]); } } throw new Error("Page index " + pageIndex + " not found."); }; /** * Releases catalog and cache resources and clears references. * * @private */ _PdfCatalog.prototype._destroy = function () { if (this._catalogDictionary) { this._catalogDictionary = undefined; } if (this._topPagesDictionary) { this._topPagesDictionary = undefined; } if (this.pageIndexCache) { this.pageIndexCache.clear(); this.pageIndexCache = undefined; } if (this.pageKidsCountCache) { this.pageKidsCountCache.clear(); this.pageKidsCountCache = undefined; } this._hasInvalidPageTree = undefined; this._pageCache.clear(); this._parsedPages = []; }; return _PdfCatalog; }()); export { _PdfCatalog };