UNPKG

@syncfusion/ej2-pdf

Version:

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

2,544 lines 114 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
import { _PdfStream } from './base-stream';
import { _PdfDictionary, _PdfReferenceSet, _isCommand, _PdfReference, _PdfName } from './pdf-primitives';
import { BaseException, FormatError, _escapePdfName, _bytesToString, ParserEndOfFileException, _numberToString, _stringToPdfString, _stringToBigEndianBytes, _getSize, _compressStream, _hasUnicodeCharacters, _stringToBytes, _byteArrayToHexString } from './utils';
import { _PdfParser, _PdfLexicalOperator } from './pdf-parser';
import { _PdfBaseStream } from './base-stream';
import { PdfCrossReferenceType } from './enumerator';
import { _PdfEncryptor } from './security/encryptor';
import { _MD5 } from './security/encryptors/messageDigest5';
import { initializeTelemetryFeature } from '@syncfusion/ej2-base';
/**
 * Manages PDF cross-reference tables and streams, object lookup, and saving operations.
 *
 * @private
 */
var _PdfCrossReference = /** @class */ (function () {
    function _PdfCrossReference(document, password) {
        /**
         * Chunks of output bytes used during saving.
         *
         * @private
         */
        this._uint8Chunks = [];
        /**
         * PDF version string written on save.
         *
         * @private
         */
        this._version = '';
        /**
         * Line separator used in output.
         *
         * @private
         */
        this._newLine = '\r\n';
        /**
         * Buffer length used while saving.
         *
         * @private
         */
        this._bufferLength = 0;
        /**
         * Whether decoder (image extraction) is supported.
         *
         * @private
         */
        this._isDecoderSupport = false;
        /**
         * Collection of signatures present in the document.
         *
         * @private
         */
        this._signatureCollection = [];
        /**
         * Whether cross reference is written as table.
         *
         * @private
         */
        this._isCrossReferenceTable = false;
        /**
         * Whether cross reference is written as stream.
         *
         * @private
         */
        this._isCrossReferenceStream = false;
        this._password = password;
        this._document = document;
        this._stream = document._stream;
        this._entries = [];
        this._crossReferencePosition = Object.create(null);
        this._cacheMap = new Map(); // eslint-disable-line
        this._offsetReference = new Map(); // eslint-disable-line
        this._pendingRefs = new _PdfReferenceSet();
        this._offsets = [];
    }
    /**
     * Sets the starting XRef position for parsing.
     *
     * @private
     * @param {number} startXRef - The startxref offset to parse.
     * @returns {void} nothing.
     */
    _PdfCrossReference.prototype._setStartXRef = function (startXRef) {
        this._startXRefQueue = [startXRef];
        this._prevStartXref = startXRef;
        if (typeof this._prevXRefOffset === 'undefined' || this._prevXRefOffset === null) {
            this._prevXRefOffset = startXRef;
        }
    };
    /**
     * Parses cross-reference structures and initializes trailer/root state.
     *
     * @private
     * @param {boolean} recoveryMode - If true, uses fallback indexing.
     * @returns {void} nothing.
     */
    _PdfCrossReference.prototype._parse = function (recoveryMode) {
        var trailerDictionary;
        if (!recoveryMode) {
            trailerDictionary = this._readXRef();
        }
        else {
            trailerDictionary = this._indexObjects();
        }
        trailerDictionary.assignXref(this);
        var entrySize = trailerDictionary.get('Size');
        if (this._entries.length < entrySize || this._entries.length === entrySize) {
            this._nextReferenceNumber = entrySize;
        }
        else if (this._entries.length > entrySize) {
            this._nextReferenceNumber = this._entries.length > 0 ? this._entries.length : 1;
        }
        this._trailer = trailerDictionary;
        var encrypt = trailerDictionary.get('Encrypt');
        if (encrypt) {
            initializeTelemetryFeature('DecryptPDF', 'PDFLibrary');
            this._document._isEncrypted = true;
            this._ids = trailerDictionary.get('ID');
            this._permissionFlags = encrypt.get('P');
            var fileId = this._ids && this._ids.length ? this._ids[0] : '';
            encrypt.suppressEncryption = true;
            this._encrypt = new _PdfEncryptor(encrypt, fileId, this._password);
            this._document._isUserPassword = this._encrypt._isUserPassword;
            this._document._encryptOnlyAttachment = this._encrypt._encryptOnlyAttachment;
            if (this._document._encryptOnlyAttachment) {
                this._document.fileStructure.isIncrementalUpdate = false;
            }
            if (this._document.fileStructure.isIncrementalUpdate) {
                this._document.fileStructure.crossReferenceType = PdfCrossReferenceType.stream;
            }
            else {
                this._document.fileStructure.crossReferenceType = PdfCrossReferenceType.table;
            }
            if (this._encrypt._encryptOnlyAttachment) {
                this._document._hasUserPasswordOnly = true;
                this._document._encryptMetaData = false;
            }
            else {
                this._document._hasUserPasswordOnly = this._encrypt._hasUserPasswordOnly;
                this._document._encryptMetaData = encrypt.has('EncryptMetadata') ? encrypt.get('EncryptMetadata') : true;
            }
        }
        var hasRoot = false;
        var root;
        try {
            root = trailerDictionary.get('Root');
        }
        catch (e) {
            throw new BaseException('Invalid cross reference', 'XRefParseException');
        }
        if (root) {
            try {
                var pagesEntry = root.get('Pages');
                if (pagesEntry) {
                    this._root = root;
                    hasRoot = true;
                }
            }
            catch (ex) {
                throw new BaseException('Invalid cross reference', 'InvalidXRef');
            }
        }
        if (!hasRoot) {
            if (!recoveryMode) {
                throw new BaseException('Invalid cross reference', 'XRefParseException');
            }
            else {
                throw new BaseException('Invalid cross reference', 'InvalidXRef');
            }
        }
    };
    /**
     * Gets the XRef entry information for the given object index.
     *
     * @private
     * @param {number} i - Object number index.
     * @returns {_PdfObjectInformation|null} Entry info or null.
     */
    _PdfCrossReference.prototype._getEntry = function (i) {
        var xrefEntry = this._entries[i]; // eslint-disable-line
        if (xrefEntry && !xrefEntry.free && xrefEntry.offset) {
            return xrefEntry;
        }
        return null;
    };
    /**
     * Fetches an indirect object by reference, handling caching and circular refs.
     *
     * @private
     * @param {_PdfReference} ref - Indirect reference to fetch.
     * @param {boolean} [suppressEncryption] - Whether to suppress decryption.
     * @returns {any} The fetched object.
     */
    _PdfCrossReference.prototype._fetch = function (ref, suppressEncryption) {
        var entry; // eslint-disable-line
        if (!(ref instanceof _PdfReference)) {
            throw new Error('ref object is not a reference');
        }
        var objectNumber = ref.objectNumber;
        var cacheEntry = this._cacheMap.get(ref); // eslint-disable-line
        if (typeof cacheEntry !== 'undefined') {
            if (cacheEntry instanceof _PdfDictionary && !cacheEntry.objId) {
                cacheEntry.objId = objectNumber;
            }
            return cacheEntry;
        }
        var xrefEntry = this._getEntry(objectNumber);
        if (xrefEntry === null) {
            this._cacheMap.set(ref, xrefEntry);
            return xrefEntry;
        }
        if (this._pendingRefs.has(ref)) {
            this._pendingRefs.remove(ref);
            throw new Error('circular reference');
        }
        this._pendingRefs.put(ref);
        try {
            if (xrefEntry.uncompressed) {
                entry = this._fetchUncompressed(ref, xrefEntry, suppressEncryption);
            }
            else {
                entry = this._fetchCompressed(ref, xrefEntry);
            }
            this._pendingRefs.remove(ref);
        }
        catch (ex) {
            this._pendingRefs.remove(ref);
            throw ex;
        }
        return entry;
    };
    /**
     * Fetches an uncompressed object from the file stream.
     *
     * @private
     * @param {_PdfReference} reference - Reference to the object.
     * @param {_PdfObjectInformation} xrefEntry - XRef entry describing the object.
     * @param {boolean} [makeFilter] - Whether to apply stream filters.
     * @returns {any} The parsed object.
     */
    _PdfCrossReference.prototype._fetchUncompressed = function (reference, xrefEntry, makeFilter) {
        var generationNumber = reference.generationNumber;
        var objectNumber = reference.objectNumber;
        if (xrefEntry.gen !== generationNumber) {
            throw new BaseException("Inconsistent generation in XRef: " + reference, 'XRefEntryException');
        }
        var stream = this._stream.makeSubStream(xrefEntry.offset + this._stream.start, undefined);
        var parser = new _PdfParser(new _PdfLexicalOperator(stream), this, true, false, this._encrypt);
        parser._isImageExtraction = this._isDecoderSupport;
        var obj1 = parser.getObject();
        var obj2 = parser.getObject();
        var obj3 = parser.getObject();
        if (obj1 !== objectNumber || obj2 !== generationNumber || typeof obj3 === 'undefined') {
            throw new BaseException("Bad (uncompressed) XRef entry: " + reference, 'XRefEntryException');
        }
        var entry; // eslint-disable-line
        if (this._encrypt && !makeFilter) {
            entry = parser.getObject(reference.objectNumber, reference.generationNumber, true);
        }
        else {
            entry = parser.getObject(null, makeFilter);
        }
        if (!(entry instanceof _PdfBaseStream)) {
            this._cacheMap.set(reference, entry);
        }
        if (entry instanceof _PdfDictionary) {
            entry.objId = reference.toString();
        }
        else if (entry instanceof _PdfBaseStream) {
            entry.dictionary.objId = reference.toString();
        }
        return entry;
    };
    /**
     * Fetches an object stored inside an object stream (ObjStm).
     *
     * @private
     * @param {_PdfReference} ref - Reference to fetch.
     * @param {_PdfObjectInformation} xrefEntry - XRef entry for the object.
     * @returns {any} The fetched object.
     */
    _PdfCrossReference.prototype._fetchCompressed = function (ref, xrefEntry) {
        var tableOffset = xrefEntry.offset;
        var stream = this._fetch(_PdfReference.get(tableOffset, 0));
        if (typeof stream === 'undefined') {
            throw new FormatError('bad ObjStm stream');
        }
        var first = stream.dictionary.get('First');
        var n = stream.dictionary.get('N');
        var gen = ref.generationNumber;
        if (!Number.isInteger(first) || !Number.isInteger(n)) {
            throw new FormatError('invalid first and n parameters for ObjStm stream');
        }
        var parser = new _PdfParser(new _PdfLexicalOperator(stream), this, true);
        parser._isImageExtraction = this._isDecoderSupport;
        var nums = new Array(n);
        var offsets = new Array(n);
        for (var i = 0; i < n; ++i) {
            var value = parser.getObject();
            if (!Number.isInteger(value)) {
                throw new FormatError("invalid object number in the ObjStm stream: " + value);
            }
            var offset = parser.getObject();
            if (!Number.isInteger(offset)) {
                throw new FormatError("invalid object offset in the ObjStm stream: " + offset);
            }
            nums[i] = value; // eslint-disable-line
            offsets[i] = offset; // eslint-disable-line
        }
        var start = (stream.start || 0) + first;
        var entries = new Array(n); // eslint-disable-line
        for (var i = 0; i < n; ++i) {
            var length_1 = (i < n - 1 ? (offsets[i + 1] - offsets[i]) : undefined); // eslint-disable-line
            if (length_1 < 0) {
                throw new FormatError('Invalid offset in the ObjStm stream.');
            }
            parser = new _PdfParser(new _PdfLexicalOperator(stream.makeSubStream(start + offsets[i], length_1, stream.dictionary)), this, true); // eslint-disable-line
            var obj = parser.getObject(); // eslint-disable-line
            entries[i] = obj; // eslint-disable-line
            if (obj instanceof _PdfBaseStream) {
                continue;
            }
            var value = nums[i]; // eslint-disable-line
            var entry = this._entries[value]; // eslint-disable-line
            if (entry && entry.offset === tableOffset && entry.gen === i) {
                var objId = value + " " + gen;
                this._cacheMap.set(_PdfReference.get(value, gen), obj);
                if (obj instanceof _PdfDictionary) {
                    obj.objId = objId;
                }
            }
        }
        var result = entries[xrefEntry.gen]; // eslint-disable-line
        if (typeof result === 'undefined') {
            throw new BaseException("Bad (compressed) XRef entry: " + ref, 'XRefEntryException');
        }
        return result;
    };
    /**
     * Reads cross-reference structures (table/stream) starting from queued offsets.
     *
     * @private
     * @param {boolean} [recoveryMode=false] - If true, tries heuristic indexing.
     * @returns {_PdfDictionary} The trailer dictionary.
     */
    _PdfCrossReference.prototype._readXRef = function (recoveryMode) {
        if (recoveryMode === void 0) { recoveryMode = false; }
        var stream = this._stream;
        var startXRefParsedCache = new Set();
        try {
            while (this._startXRefQueue.length) {
                var startXRef = this._startXRefQueue[0];
                if (this._prevStartXref < startXRef) {
                    this._prevStartXref = startXRef;
                }
                if (startXRefParsedCache.has(startXRef)) {
                    this._startXRefQueue.shift();
                    continue;
                }
                startXRefParsedCache.add(startXRef);
                stream.position = startXRef + stream.start;
                var parser = new _PdfParser(new _PdfLexicalOperator(stream), this, true);
                var obj = parser.getObject(); // eslint-disable-line
                var dictionary = void 0;
                if (_isCommand(obj, 'xref')) {
                    if (typeof this._document._fileStructure._crossReferenceType === 'undefined') {
                        this._document._fileStructure._crossReferenceType = PdfCrossReferenceType.table;
                        this._isCrossReferenceTable = true;
                    }
                    dictionary = this._processXRefTable(parser);
                    if (!this._topDictionary) {
                        this._topDictionary = dictionary;
                    }
                    obj = dictionary.get('XRefStm');
                    if (Number.isInteger(obj)) {
                        var position = obj; // eslint-disable-line
                        if (!(position in this._crossReferencePosition)) {
                            this._crossReferencePosition[position] = 1; // eslint-disable-line
                            this._startXRefQueue.push(position);
                        }
                    }
                }
                else if (Number.isInteger(obj)) {
                    if (typeof this._document._fileStructure._crossReferenceType === 'undefined') {
                        this._document._fileStructure._crossReferenceType = PdfCrossReferenceType.stream;
                        this._isCrossReferenceStream = true;
                    }
                    var gen = parser.getObject();
                    var command = parser.getObject();
                    obj = parser.getObject();
                    if (typeof gen === 'undefined' ||
                        !Number.isInteger(gen) ||
                        !_isCommand(command, 'obj') ||
                        !(obj instanceof _PdfBaseStream)) {
                        throw new FormatError('Invalid cross reference stream');
                    }
                    dictionary = this._processXRefStream(obj);
                    if (!this._topDictionary) {
                        this._topDictionary = dictionary;
                    }
                    if (!dictionary) {
                        throw new FormatError('Failed to read XRef stream');
                    }
                }
                else {
                    throw new FormatError('Invalid XRef stream header');
                }
                obj = dictionary.get('Prev');
                if (Number.isInteger(obj)) {
                    this._startXRefQueue.push(obj);
                }
                else if (obj instanceof _PdfReference) {
                    this._startXRefQueue.push(obj.objectNumber);
                }
                this._startXRefQueue.shift();
            }
            var startXRefParsed_1 = [];
            startXRefParsedCache.forEach(function (value) { return startXRefParsed_1.push(value); });
            this._document._startXRefParsedCache = startXRefParsed_1;
            return this._topDictionary;
        }
        catch (e) {
            this._startXRefQueue.shift();
        }
        if (recoveryMode) {
            return undefined;
        }
        throw new BaseException('Invalid cross reference', 'XRefParseException');
    };
    /**
     * Reads a token string from a byte buffer starting at offset.
     *
     * @private
     * @param {Uint8Array} data - Buffer to read from.
     * @param {number} offset - Start offset.
     * @returns {string} The token read.
     */
    _PdfCrossReference.prototype._readToken = function (data, offset) {
        var lf = 0xa;
        var cr = 0xd;
        var lt = 0x3c;
        var token = '';
        var ch = data[offset]; // eslint-disable-line
        while (ch !== lf && ch !== cr && ch !== lt) {
            if (++offset >= data.length) {
                break;
            }
            token += String.fromCharCode(ch);
            ch = data[offset]; // eslint-disable-line
        }
        return token;
    };
    /**
     * Skips bytes until the given sequence is found.
     *
     * @private
     * @param {Uint8Array} data - Buffer to search.
     * @param {number} offset - Starting offset.
     * @param {Uint8Array} what - Sequence to find.
     * @returns {number} Number of bytes skipped.
     */
    _PdfCrossReference.prototype._skipUntil = function (data, offset, what) {
        var length = what.length;
        var dataLength = data.length;
        var skipped = 0;
        while (offset < dataLength) {
            var i = 0;
            while (i < length && data[offset + i] === what[i]) { // eslint-disable-line
                ++i;
            }
            if (i >= length) {
                break;
            }
            offset++;
            skipped++;
        }
        return skipped;
    };
    /**
     * Indexes objects by scanning the entire PDF stream when no valid XRef exists.
     *
     * @private
     * @returns {_PdfDictionary} Best trailer dictionary found.
     */
    _PdfCrossReference.prototype._indexObjects = function () {
        var tab = 0x9;
        var lf = 0xa;
        var cr = 0xd;
        var space = 0x20;
        var percent = 0x25;
        var objRegExp = /^(\d+)\s+(\d+)\s+obj\b/;
        var endobjRegExp = /\bendobj[\b\s]$/;
        var nestedObjRegExp = /\s+(\d+\s+\d+\s+obj[\b\s<])$/;
        var checkContentLength = 25;
        var trailerBytes = new Uint8Array([116, 114, 97, 105, 108, 101, 114]);
        var startxrefBytes = new Uint8Array([115, 116, 97, 114, 116, 120, 114, 101, 102]);
        var objBytes = new Uint8Array([111, 98, 106]);
        var xrefBytes = new Uint8Array([47, 88, 82, 101, 102]);
        this._entries.length = 0;
        this._cacheMap.clear();
        var stream = this._stream;
        stream.position = 0;
        var buffer = stream.getBytes();
        var length = buffer.length;
        var position = stream.start;
        var trailers = [];
        var crossReferencePosition = [];
        while (position < length) {
            var ch = buffer[position]; // eslint-disable-line
            if (ch === tab || ch === lf || ch === cr || ch === space) {
                ++position;
                continue;
            }
            if (ch === percent) {
                do {
                    ++position;
                    if (position >= length) {
                        break;
                    }
                    ch = buffer[position]; // eslint-disable-line
                } while (ch !== lf && ch !== cr);
                continue;
            }
            var token = this._readToken(buffer, position);
            var m = void 0; // eslint-disable-line
            if (token.startsWith('xref') && (token.length === 4 || /\s/.test(token[4]))) {
                position += this._skipUntil(buffer, position, trailerBytes);
                trailers.push(position);
                position += this._skipUntil(buffer, position, startxrefBytes);
            }
            else {
                m = objRegExp.exec(token);
                if (m) {
                    var objectNumber = (m[1]) | 0;
                    var gen = (m[2]) | 0;
                    var contentLength = void 0;
                    var startPos = position + token.length;
                    var updateEntries = false;
                    if (!this._entries[objectNumber]) { // eslint-disable-line
                        updateEntries = true;
                    }
                    else if (this._entries[objectNumber].gen === gen) { // eslint-disable-line
                        try {
                            var subStream = stream.makeSubStream(startPos, stream.length - startPos);
                            var lexicalOperator = new _PdfLexicalOperator(subStream);
                            var parser = new _PdfParser(lexicalOperator, null);
                            parser.getObject();
                            updateEntries = true;
                        }
                        catch (ex) {
                            updateEntries = !(ex instanceof ParserEndOfFileException);
                        }
                    }
                    if (updateEntries) {
                        var info = new _PdfObjectInformation();
                        info.offset = position - stream.start;
                        info.gen = gen;
                        info.uncompressed = true;
                        this._entries[objectNumber] = info; // eslint-disable-line
                    }
                    while (startPos < buffer.length) {
                        var endPos = startPos + this._skipUntil(buffer, startPos, objBytes) + 4;
                        contentLength = endPos - position;
                        var checkPos = Math.max(endPos - checkContentLength, startPos);
                        var tokenStr = _bytesToString(buffer.subarray(checkPos, endPos));
                        if (endobjRegExp.test(tokenStr)) {
                            break;
                        }
                        else {
                            var objToken = nestedObjRegExp.exec(tokenStr); // eslint-disable-line
                            if (objToken && objToken[1]) {
                                contentLength -= objToken[1].length;
                                break;
                            }
                        }
                        startPos = endPos;
                    }
                    var content = buffer.subarray(position, position + contentLength);
                    var xrefTagOffset = this._skipUntil(content, 0, xrefBytes);
                    if (xrefTagOffset < contentLength && content[xrefTagOffset + 5] < 64) {
                        crossReferencePosition.push(position - stream.start);
                        this._crossReferencePosition[position - stream.start] = 1;
                    }
                    position += contentLength;
                }
                else if (token.startsWith('trailer') && (token.length === 7 || /\s/.test(token[7]))) {
                    trailers.push(position);
                    position += this._skipUntil(buffer, position, startxrefBytes);
                }
                else {
                    position += token.length + 1;
                }
            }
        }
        for (var i = 0; i < crossReferencePosition.length; ++i) {
            this._startXRefQueue.push(crossReferencePosition[i]); // eslint-disable-line
            this._readXRef(true);
        }
        var trailerDict;
        for (var i = 0; i < trailers.length; ++i) {
            stream.position = trailers[i]; // eslint-disable-line
            var parser = new _PdfParser(new _PdfLexicalOperator(stream), this, true, true);
            var obj = parser.getObject(); // eslint-disable-line
            if (!_isCommand(obj, 'trailer')) {
                continue;
            }
            var dictionary = parser.getObject(); // eslint-disable-line
            if (!(dictionary instanceof _PdfDictionary)) {
                continue;
            }
            try {
                var rootDict = dictionary.get('Root'); // eslint-disable-line
                if (!(rootDict instanceof _PdfDictionary)) {
                    continue;
                }
                var pagesDict = rootDict.get('Pages'); // eslint-disable-line
                if (!(pagesDict instanceof _PdfDictionary)) {
                    continue;
                }
                var pagesCount = pagesDict.get('Count');
                if (typeof pagesCount === 'undefined' || !Number.isInteger(pagesCount)) {
                    continue;
                }
            }
            catch (ex) {
                continue;
            }
            if (dictionary.has('ID')) {
                return dictionary;
            }
            trailerDict = dictionary;
        }
        if (trailerDict) {
            return trailerDict;
        }
        if (this._topDictionary) {
            return this._topDictionary;
        }
        throw new BaseException('Invalid PDF structure.', 'InvalidPDFException');
    };
    /**
     * Processes an XRef table and returns its trailer dictionary.
     *
     * @private
     * @param {_PdfParser} parser - Parser positioned after 'xref'.
     * @returns {_PdfDictionary} The trailer dictionary.
     */
    _PdfCrossReference.prototype._processXRefTable = function (parser) {
        if (typeof this._tableState === 'undefined') {
            var tableState = new _PdfCrossTableState();
            tableState.entryNum = 0;
            tableState.streamPos = parser.lexicalOperator.stream.position;
            tableState.parserBuf1 = parser.first;
            tableState.parserBuf2 = parser.second;
            this._tableState = tableState;
        }
        var obj = this._readXRefTable(parser);
        if (!_isCommand(obj, 'trailer')) {
            throw new FormatError('Invalid XRef table: could not find trailer dictionary');
        }
        var topDictionary = parser.getObject(); // eslint-disable-line
        var dictionary;
        if (topDictionary) {
            if (topDictionary instanceof _PdfDictionary) {
                dictionary = topDictionary;
            }
            else if (topDictionary instanceof _PdfBaseStream && topDictionary.dictionary) {
                dictionary = topDictionary.dictionary;
            }
        }
        if (!dictionary) {
            throw new FormatError('Invalid cross reference: could not parse trailer dictionary');
        }
        this._tableState = undefined;
        return dictionary;
    };
    /**
     * Reads entries from an XRef table subsection.
     *
     * @private
     * @param {_PdfParser} parser - Parser to read entries from.
     * @returns {_PdfCommand} The command following the table.
     */
    _PdfCrossReference.prototype._readXRefTable = function (parser) {
        var stream = parser.lexicalOperator.stream;
        stream.position = this._tableState.streamPos;
        parser.first = this._tableState.parserBuf1;
        parser.second = this._tableState.parserBuf2;
        var obj; // eslint-disable-line
        while (true) { // eslint-disable-line
            if (typeof this._tableState.firstEntryNum === 'undefined' || typeof this._tableState.entryCount === 'undefined') {
                obj = parser.getObject();
                if (_isCommand(obj, 'trailer')) {
                    break;
                }
                this._tableState.firstEntryNum = obj;
                this._tableState.entryCount = parser.getObject();
            }
            var first = this._tableState.firstEntryNum;
            var count = this._tableState.entryCount;
            if (!Number.isInteger(first) || !Number.isInteger(count)) {
                throw new FormatError('Invalid cross reference: wrong types in subsection header');
            }
            for (var i = this._tableState.entryNum; i < count; i++) {
                this._tableState.streamPos = stream.position;
                this._tableState.entryNum = i;
                this._tableState.parserBuf1 = parser.first;
                this._tableState.parserBuf2 = parser.second;
                var entry = new _PdfObjectInformation();
                entry.offset = parser.getObject();
                entry.gen = parser.getObject();
                var type = parser.getObject();
                if (type) {
                    switch (type.command) {
                        case 'f':
                            entry.free = true;
                            break;
                        case 'n':
                            entry.uncompressed = true;
                            break;
                    }
                }
                if (!Number.isInteger(entry.offset) || !Number.isInteger(entry.gen) || !(entry.free || entry.uncompressed)) {
                    throw new FormatError("Invalid entry in cross reference subsection: " + first + ", " + count);
                }
                if (i === 0 && entry.free && first === 1) {
                    first = 0;
                }
                if (!this._entries[i + first]) {
                    this._entries[i + first] = entry;
                }
            }
            this._tableState.entryNum = 0;
            this._tableState.streamPos = stream.position;
            this._tableState.parserBuf1 = parser.first;
            this._tableState.parserBuf2 = parser.second;
            this._tableState.firstEntryNum = undefined;
            this._tableState.entryCount = undefined;
        }
        if (this._entries[0] && !this._entries[0].free) {
            throw new FormatError('Invalid XRef table: unexpected first object');
        }
        return obj;
    };
    /**
     * Processes an XRef stream and returns its dictionary.
     *
     * @private
     * @param {_PdfStream} stream - The XRef stream.
     * @returns {_PdfDictionary} The stream dictionary.
     */
    _PdfCrossReference.prototype._processXRefStream = function (stream) {
        if (typeof this._streamState === 'undefined') {
            var streamParameters = stream.dictionary;
            var streamState = new _PdfStreamState();
            var index = streamParameters.getArray('Index');
            if (!index) {
                index = [0, streamParameters.get('Size')];
            }
            streamState.entryRanges = index;
            streamState.byteWidths = streamParameters.getArray('W');
            streamState.entryNum = 0;
            streamState.streamPos = stream.position;
            this._streamState = streamState;
        }
        this._readXRefStream(stream);
        this._streamState = undefined;
        return stream.dictionary;
    };
    /**
     * Reads entries from an XRef stream into internal state.
     *
     * @private
     * @param {_PdfStream} stream - Stream to read.
     * @returns {void} nothing.
     */
    _PdfCrossReference.prototype._readXRefStream = function (stream) {
        stream.position = this._streamState.streamPos;
        var typeFieldWidth = this._streamState.byteWidths[0];
        var offsetFieldWidth = this._streamState.byteWidths[1];
        var generationFieldWidth = this._streamState.byteWidths[2];
        var entryRanges = this._streamState.entryRanges;
        while (entryRanges.length > 0) {
            var first = entryRanges[0];
            var n = entryRanges[1];
            if (!Number.isInteger(first) || !Number.isInteger(n)) {
                throw new FormatError("Invalid XRef range fields: " + first + ", " + n);
            }
            if (!Number.isInteger(typeFieldWidth) || !Number.isInteger(offsetFieldWidth) || !Number.isInteger(generationFieldWidth)) {
                throw new FormatError("Invalid XRef entry fields length: " + first + ", " + n);
            }
            for (var i = this._streamState.entryNum; i < n; ++i) {
                this._streamState.entryNum = i;
                this._streamState.streamPos = stream.position;
                var type = 0;
                var offset = 0;
                var generation = 0;
                for (var j = 0; j < typeFieldWidth; ++j) {
                    var typeByte = stream.getByte();
                    if (typeByte === -1) {
                        throw new FormatError('invalid cross reference byte width type.');
                    }
                    type = (type << 8) | typeByte;
                }
                if (typeFieldWidth === 0) {
                    type = 1;
                }
                for (var j = 0; j < offsetFieldWidth; ++j) {
                    var offsetByte = stream.getByte();
                    if (offsetByte === -1) {
                        throw new FormatError('invalid cross reference byte width offset.');
                    }
                    offset = (offset << 8) | offsetByte;
                }
                for (var j = 0; j < generationFieldWidth; ++j) {
                    var generationByte = stream.getByte();
                    if (generationByte === -1) {
                        throw new FormatError('invalid cross reference byte width generation.');
                    }
                    generation = (generation << 8) | generationByte;
                }
                var entry = new _PdfObjectInformation();
                entry.offset = offset;
                entry.gen = generation;
                switch (type) {
                    case 0:
                        entry.free = true;
                        break;
                    case 1:
                        entry.uncompressed = true;
                        break;
                    case 2:
                        break;
                    default:
                        throw new FormatError("Invalid XRef entry type: " + type);
                }
                if (!this._entries[first + i]) {
                    this._entries[first + i] = entry;
                }
            }
            this._streamState.entryNum = 0;
            this._streamState.streamPos = stream.position;
            entryRanges.splice(0, 2);
        }
    };
    /**
     * Returns the document catalog (root) dictionary.
     *
     * @private
     * @returns {_PdfDictionary} The root dictionary.
     */
    _PdfCrossReference.prototype._getCatalogObj = function () {
        return this._root;
    };
    _PdfCrossReference.prototype._flushBuffer = function (data) {
        if (data.length === 0) {
            return;
        }
        var chunk = new Uint8Array(data.length);
        for (var i = 0; i < data.length; i++) {
            chunk[i] = data[i];
        }
        this._uint8Chunks.push(chunk);
        this._bufferLength += chunk.length;
        data.length = 0;
    };
    /**
     * Serializes and returns the full PDF bytes synchronously.
     *
     * @private
     * @returns {Uint8Array} Serialized document bytes.
     */
    _PdfCrossReference.prototype._save = function () {
        this._uint8Chunks = [];
        this._bufferLength = 0;
        var buffer = [37, 80, 68, 70, 45];
        this._writeString("" + this._version + this._newLine, buffer);
        buffer.push(0x25, 0x83, 0x92, 0xfa, 0xfe);
        this._writeString(this._newLine, buffer);
        var result;
        var offset = 0;
        if (this._signatureCollection && this._signatureCollection.length > 0) {
            if (this._isCrossReferenceStream) {
                this._document.fileStructure.crossReferenceType = PdfCrossReferenceType.stream;
            }
            else if (this._isCrossReferenceTable) {
                this._document.fileStructure.crossReferenceType = PdfCrossReferenceType.table;
            }
            var totalSignatures = this._signatureCollection.length;
            for (var i = 0; i < totalSignatures; i++) {
                this._signatureCollection[i]._catalogBeginSave();
            }
        }
        if (!this._document.fileStructure.isIncrementalUpdate) {
            this._currentLength = 0;
            this._objectCollection = new _PdfMainObjectCollection(this);
            this._writeObjectCollection(this._objectCollection._mainObjectCollection, buffer);
            if (buffer.length > 0) {
                this._flushBuffer(buffer);
            }
            result = new Uint8Array(this._bufferLength);
        }
        else {
            this._currentLength = this._stream.length;
            if (this._document._fileStructure._crossReferenceType === PdfCrossReferenceType.stream) {
                this._saveAsStream(this._currentLength, buffer);
            }
            else {
                this._saveAsTable(this._currentLength, buffer);
            }
            if (buffer.length > 0) {
                this._flushBuffer(buffer);
            }
            result = new Uint8Array(this._stream.length + this._bufferLength);
            result.set(this._stream.bytes);
            offset = this._stream.length;
        }
        for (var _i = 0, _a = this._uint8Chunks; _i < _a.length; _i++) {
            var chunk = _a[_i];
            result.set(chunk, offset);
            offset += chunk.length;
        }
        if (this._signatureCollection && this._signatureCollection.length > 0) {
            var totalSignatures = this._signatureCollection.length;
            for (var i = 0; i < totalSignatures; i++) {
                this._signature = this._signatureCollection[i]._signatureDictionary;
                this._signature._documentSaved(result);
            }
        }
        if (!this._document.fileStructure.isIncrementalUpdate) {
            var stream = new _PdfStream(result);
            this._stream = stream;
            this._document._stream = stream;
        }
        this._uint8Chunks = [];
        return result;
    };
    /**
     * Saves objects as a cross-reference stream.
     *
     * @private
     * @param {number} currentLength - Current file length prior to writing.
     * @param {number[]} buffer - Buffer to append to.
     * @returns {void} nothing.
     */
    _PdfCrossReference.prototype._saveAsStream = function (currentLength, buffer) {
        var _this = this;
        var objectStreamCollection = new Map();
        this._indexes = [];
        this._indexes.push(0, 1);
        this._offsets = [];
        var flushThreshold = 512000; // 500KB threshold
        this._cacheMap.forEach(function (value) {
            var dictionary = value instanceof _PdfBaseStream ? value.dictionary :
                value instanceof _PdfDictionary ? value : undefined;
            if (dictionary) {
                dictionary._isProcessed = false;
            }
        });
        this._cacheMap.forEach(function (value, key) {
            if (value instanceof _PdfBaseStream) {
                var dictionary = value.dictionary;
                if (dictionary && dictionary._updated && (!dictionary.isCatalog || _this._allowCatalog) && !dictionary._isProcessed) {
                    var cipher = void 0;
                    if (_this._encrypt) {
                        cipher = _this._encrypt._createCipherTransform(key.objectNumber, key.generationNumber);
                    }
                    _this._updatedDictionary(currentLength, key, buffer, value, cipher);
                    dictionary._isProcessed = true;
                    if (buffer.length > flushThreshold) {
                        _this._flushBuffer(buffer);
                    }
                }
            }
        });
        this._cacheMap.forEach(function (value, key) {
            if (value instanceof _PdfDictionary) {
                if (value._updated && !value.isCatalog && !value._isProcessed || value._isSignature) {
                    if (value._isSignature) {
                        _this._updatedDictionary(currentLength, key, buffer, value);
                    }
                    else {
                        _this._writeArchiveStream(objectStreamCollection, key, value);
                    }
                }
                else if (value._updated && (value.isCatalog || _this._allowCatalog)) {
                    _this._updatedDictionary(currentLength, key, buffer, value);
                    if (buffer.length > flushThreshold) {
                        _this._flushBuffer(buffer);
                    }
                }
            }
            else if (value instanceof _PdfBaseStream) {
                var dictionary = value.dictionary;
                if (dictionary && dictionary._updated && (!dictionary.isCatalog || _this._allowCatalog) && !dictionary._isProcessed) {
                    _this._updatedDictionary(currentLength, key, buffer, value);
                    if (buffer.length > flushThreshold) {
                        _this._flushBuffer(buffer);
                    }
                }
            }
        });
        this._objectStream = undefined;
        this._objectStreamCollection = objectStreamCollection;
        this._writeXrefStream(buffer);
    };
    /**
     * Writes the XRef stream and related structures into the buffer.
     *
     * @private
     * @param {number[]} buffer - Buffer to write to.
     * @returns {void} nothing.
     */
    _PdfCrossReference.prototype._writeXrefStream = function (buffer) {
        var _this = this;
        this._objectStreamCollection.forEach(function (value, key) {
            var _a;
            value._save(buffer, _this._currentLength);
            if (Array.isArray(value._collection)) {
                (_a = _this._indexes).push.apply(_a, value._collection);
            }
            _this._indexes.push(key.objectNumber, 1);
            if (buffer.length > 524288) {
                _this._flushBuffer(buffer);
            }
        });
        var formatValue = Math.max(_getSize(this._currentLength + this._bufferLength + buffer.length), _getSize(this._nextReferenceNumber));
        var newRef = this._getNextReference();
        this._indexes.push(newRef.objectNumber, 1);
        var newStartXref = this._currentLength + this._bufferLength + buffer.length;
        var newXref = new _PdfDictionary(this);
        newXref.set('Type', _PdfName.get('XRef'));
        newXref.set('Index', this._indexes);
        newXref.set('W', [1, formatValue, 1]);
        this._copyTrailer(newXref);
        if (this._ids && this._ids.length > 0) {
            newXref.update('ID', [this._ids[0], this._computeMessageDigest(newStartXref)]);
        }
        var newXrefData = [];
        this._writeLong(0, 1, newXrefData);
        this._writeLong(0, formatValue, newXrefData);
        this._writeLong(-1, 1, newXrefData);
        if (this._offsets.length > 0) {
            for (var index = 0; index < this._offsets.length; index++) {
                this._writeLong(1, 1, newXrefData);
                this._writeLong(this._offsets[index], formatValue, newXrefData);
                this._writeLong(0, 1, newXrefData);
            }
        }
        if (this._objectStreamCollection.size > 0) {
            this._objectStreamCollection.forEach(function (value, key) {
                for (var index = 0; index < value._length; index++) {
                    _this._writeLong(2, 1, newXrefData);
                    _this._writeLong(key.objectNumber, formatValue, newXrefData);
                    _this._writeLong(index, 1, newXrefData);
                }
                _this._writeLong(1, 1, newXrefData);
                _this._writeLong(value._archiveOffset, formatValue, newXrefData);
                _this._writeLong(0, 1, newXrefData);
            });
        }
        this._writeLong(1, 1, newXrefData);
        this._writeLong(newStartXref, formatValue, newXrefData);
        this._writeLong(0, 1, newXrefData);
        newXref.set('Length', newXrefData.length);
        var newXrefStream = new _PdfStream(newXrefData, newXref, 0, newXrefData.length);
        var cipher;
        if (this._encrypt) {
            cipher = this._encrypt._createCipherTransform(newRef.objectNumber, newRef.generationNumber);
        }
        this._writeObject(newXrefStream, buffer, newRef, cipher, true);
        this._writeString("startxref" + this._newLine + newStartXref + this._newLine + "%%EOF" + this._newLine, buffer);
        if (buffer.length > 0) {
            this._flushBuffer(buffer);
        }
    };
    /**
     * Writes an updated dictionary object to the output and records offsets.
     *
     * @private
     * @param {number} currentLength - Current output length.
     * @param {_PdfReference} key - Object reference.
     * @param {number[]} buffer - Output buffer.
     * @param {any} value - Object value.
     * @param {_CipherTransform} [cipher] - Optional cipher transform.
     * @returns {void} nothing.
     */
    _PdfCrossReference.prototype._updatedDictionary = function (currentLength, key, buffer, value, // eslint-disable-line
    cipher) {
        this._indexes.push(key.objectNumber, 1);
        this._offsets.push(currentLength + this._bufferLength + buffer.length);
        this._writeObject(value, buffer, key, cipher);
        value._updated = false;
    };
    /**
     * Writes an XRef table into the buffer.
     *
     * @private
     * @param {number[]} buffer - Buffer to write to.
     * @returns {void} nothing.
     */
    _PdfCrossReference.prototype._writeXrefTable = function (buffer) {
        var _this = this;
        var tempBuffer = '';
        var collection = this._getSortedReferences(this._offsetReference); // eslint-disable-line
        collection.forEach(function (value, key) {
            var offsetString = _this._processString(value.toString(), 10);
            var genString = _this._processString(key.generationNumber ? '0' : '', 5);
            if (value !== 0) {
                tempBuffer += offsetString + " " + genString + " n" + _this._newLine;
            }
            else {
                tempBuffer += offsetString + " " + genString + " f" + _this._newLine;
            }
        });
        var newStartXref = buffer.length + this._bufferLength;
        var xrefHeader = "xref" + this._newLine;
        var xrefEntry = "0 " + (collection.size + 1) + this._newLine;
        var initialEntry = "0000000000 65535 f" + this._newLine;
        this._writeString(xrefHeader + xrefEntry + initialEntry, buffer);
        this._writeXref(buffer, tempBuffer, newStartXref);
        if (buffer.length > 512000) { // 500KB threshold
            this._flushBuffer(buffer);
        }
    };
    /**
     * Writes low-level XRef text and trailer into the buffer.
     *
     * @private
     * @param {number[]} buffer - Buffer to append to.
     * @param {string} tempBuffer - Precomputed entry lines.
     * @param {number} newStartXref - Offset of the new xref.
     * @returns {void} nothing.
     */
    _PdfCrossReference.prototype._writeXref = function (buffer, tempBuffer, newStartXref) {
        this._writeString(tempBuffer, buffer);
        this._writeString("trailer" + this._newLine, buffer);
        var newXref = new _PdfDictionary(this);
        this._copyTrailer(newXref);
        this._writeDictionary(newXref, buffer, this._newLine);
        this._writeString("startxref" + this._newLine + newStartXref + this._newLine + "%%EOF" + this._newLine, buffer);
    };
    /**
     * Pads a numeric string with leading zeros to the desired length.
     *
     * @private
     * @param {string} value - Value to pad.
     * @param {number} length - Desired length.
     * @returns {string} Padded string.
     */
    _PdfCrossReference.prototype._processString = function (value, length) {
        while (value.length < length) {
            value = '0' + value;
        }
        return value;
    };
    /**
     * Copies trailer keys (Root/Info/Encrypt) into a new xref dictionary.
     *
     * @private
     * @param {_PdfDictionary} newXref - Dictionary to populate.
     * @returns {void} nothing.
     */
    _PdfCrossReference.prototype._copyTrailer = function (newXref) {
        var reference = this._getNextReference();
        newXref.set('Size', reference.objectNumber);
        if (this._document.fileStructure.isIncrementalUpdate) {
            newXref.set('Prev', this._prevXRefOffset);
        }
        var root = this._trailer.getRaw('Root'); // eslint-disable-line
        if (typeof root !== 'undefined' && root !== null) {
            newXref.set('Root', root);
        }
        var info = this._trailer.getRaw('Info'); // eslint-disable-line
        if (typeof info !== 'undefined' && info !== null) {
            newXref.set('Info', info);
        }
        var encrypt = this._trailer.getRaw('Encrypt'); // eslint-disable-line
        if (typeof encrypt !== 'undefined' && encrypt !== null) {
            newXref.set('Encrypt', encrypt);
        }
    };
    /**
     * Computes a message digest used for signature or ID updates.
     *
     * @private
     * @param {number} size - Current size used in the digest.
     * @returns {string} Hex digest string.
     */
    _PdfCrossReference.prototype._computeMessageDigest = function (size) {
        var _this = this;
        var time = Math.floor(Date.now() / 1000);
        var buffer = [time.toString(), '', size.toString()];
        var info = this._trailer.getRaw('Info');
        var crossReferenceInfo = new _PdfDictionary();
        if (info && info instanceof _PdfDictionary) {
            info.forEach(function (key, value) {
                if (value && typeof value === 'string') {
                    crossReferenceInfo.set(key, _stringToPdfString(value));
                }
            });
        }
        crossReferenceInfo.forEach(function (key, value) {
            buffer.push(value);
        });
        var array = [];
        buffer.forEach(function (str) {
            _this._writeString(str, array);
        });
        return _bytesToString((new _MD5().hash(new Uint8Array(array))));
    };
    /**
     * Allocates the next object reference number.
     *
     * @private
     * @returns {_PdfReference} New reference object.
     */
    _PdfCrossReference.prototype._getNextReference = function () {
        var reference = new _PdfReference(this._nextReferenceNumber++, 0);
        reference._isNew = true;
        return reference;
    };
    /**
     * Writes a PDF object (dictionary, stream, array, number, or string) into the output
     * buffer, applying optional encryption and handling reference wrappers when present.
     *
     * @private
     * @param {_PdfDictionary | _PdfBaseStream | any} obj - The PDF object to serialize.
     * @param {number[]} buffer - The output buffer receiving encoded bytes.
     * @param {_PdfReference} [reference] - Optional object reference used to wrap the output in an `obj` / `endobj` container.
     * @param {_CipherTransform} [transform] - Optional cipher transform applied to encrypt stream or string values.
     * @param {boolean} [isCrossReference] - Indicates whether this object belongs to a cross-reference section.
     * @returns {void} nothing.
     */
    _PdfCrossReference.prototype._writeObject = function (obj, // eslint-disable-line
    buffer, reference, transform, isCrossReference) {
        var _this = this;
        if (reference && reference instanceof _PdfReference) {
            this._writeString(reference.objectNumber + " " + reference.generationNumber + " obj" + this._newLine, buffer);
        }
        if (obj instanceof _PdfDictionary) {
            this._writeDictionary(obj, buffer, this._newLine, transform, isCrossReference);
        }
        else if (obj instanceof _PdfBaseStream) {
            this._writeStream(obj, buffer, transform, isCrossReference);
        }
        else if (Array.isArray(obj) && obj.length > 0) {
            this._writeString('[ ', buffer);
            obj.forEach(function (value, index) {
                if (value instanceof _PdfReference) {
                    _this._writeString(value.objectNumber + " " + value.generationNumber + " R", buffer);
                }
                else if (Array.isArray(value)) {
                    _this._writeString('[ ', buffer);
                    value.forEach(function (nestedValue) {
                        if (nestedValue instanceof _PdfReference) {
                            _this._writeString(nestedValue.objectNumber + " " + nestedValue.generationNumber + " R", buffer);
                        }
                        else if (nestedValue instanceof _PdfName) {
                            _this._writeString("/" + _escapePdfName(nestedValue.name), buffer);
                        }
                        else {
                            _this._writeString(nestedValue + " ", buffer);
                        }
                    });
                    _this._writeString(']', buffer);
                }
                else if (value instanceof _PdfName) {
                    _this._writeString("/" + _escapePdfName(value.name), buffer);
                }
                else if (value instanceof _PdfDictionary) {
                    _this._writeDictionary(value, buffer, _this._newLine, transform, isCrossReference);
                }
                else if (typeof (value) === 'string') {
                    if (_hasUnicodeCharacters(value)) {
                        var bytes = _stringToBytes(value);
                        var text = _byteArrayToHexString(bytes);
                        _this._writeString('<', buffer);
                        _this._writeString("" + text, buffer);
                        _this._writeString('>', buffer);
                    }
                    else {
                        _this._writeString(value + "\n", buffer);
                    }
                }
                else {
                    _this._writeString(value + "\n", buffer);
                }
                if (index < obj.length - 1) {
                    _this._writeString(' ', buffer);
                }
            });
            this._writeString(']', buffer);
            this._writeString('\n', buffer);
        }
        else if (typeof obj === 'number') {
            this._writeString(obj + "\n", buffer);
        }
        else if (typeof obj === 'string') {
            this._writeString("(" + _escapePdfName(obj) + ")\n", buffer);
        }
        if (obj instanceof _PdfName) {
            if (obj.name.indexOf(' ') !== -1) {
                obj.name = obj.name.replace(/ /g, '#20'); // eslint-disable-line
            }
            var escapedName = obj.name;
            this._writeString("/" + escapedName, buffer);
        }
        if (reference && reference instanceof _PdfReference) {
            this._writeString("endobj" + this._newLine, buffer);
        }
    };
    /**
     * Writes a dictionary object into the output buffer using the specified spacing
     * and optionally applies encryption or cross reference rules.
     *
     * @private
     * @param {_PdfDictionary} dictionary - The dictionary to serialize into the buffer.
     * @param {number[]} buffer - The output buffer receiving serialized bytes.
     * @param {string} spaceChar - The spacing string written between dictionary entries.
     * @param {_CipherTransform} [transform] - Optional cipher transform for encrypting values.
     * @param {boolean} [isCrossReference] - Indicates whether the dictionary belongs to a cross reference stream.
     * @returns {void} nothing.
     */
    _PdfCrossReference.prototype._writeDictionary = function (dictionary, buffer, spaceChar, transform, isCrossReference) {
        var _this = this;
        if (dictionary._currentObj) {
            dictionary._currentObj._beginSave();
        }
        if (dictionary._isFont) {
            this._writeFontDictionary(dictionary);
        }
        this._writeString("<<" + spaceChar, buffer);
        if (dictionary._isSignature && this._signatureCollection && this._signatureCollection.length > 0) {
            var matchingSignature = this._signatureCollection.find(function (signature) {
                return signature._signatureDictionary._dictionary.objId === dictionary.objId;
            });
            if (matchingSignature) {
                matchingSignature._signatureDictionary._dictionarySave(buffer);
            }
        }
        dictionary.forEach(function (key, value) {
            _this._writeString("/" + _escapePdfName(key) + " ", buffer);
            _this._writeValue(value, key, buffer, transform, isCrossReference);
            _this._writeString(spaceChar, buffer);
        });
        this._writeString(">>" + this._newLine, buffer);
    };
    /**
     * Ensures font-related dictionary entries are converted to references.
     *
     * @private
     * @param {_PdfDictionary} dictionary - Font dictionary to process.
     * @returns {void} nothing.
     */
    _PdfCrossReference.prototype._writeFontDictionary = function (dictionary) {
        if (dictionary.has('DescendantFonts')) {
            var fonts = dictionary.get('DescendantFonts'); // eslint-disable-line
            if (!Array.isArray(fonts)) {
                var reference = this._getNextReference();
                this._cacheMap.set(reference, fonts);
                dictionary.update('DescendantFonts', [reference]);
            }
        }
        this._createFontReference('ToUnicode', dictionary);
        this._createFontReference('FontFile2', dictionary);
        this._createFontReference('FontFile3', dictionary);
        this._createFontReference('FontDescriptor', dictionary);
    };
    /**
     * Ensures a font-related subkey is stored as an indirect reference.
     *
     * @private
     * @param {string} key - Dictionary key to convert.
     * @param {_PdfDictionary} dictionary - Dictionary to modify.
     * @returns {void} nothing.
     */
    _PdfCrossReference.prototype._createFontReference = function (key, dictionary) {
        if (dictionary.has(key)) {
            var fonts = dictionary.get(key); // eslint-disable-line
            if (!(fonts instanceof _PdfReference)) {
                var reference = this._getNextReference();
                this._cacheMap.set(reference, fonts);
                if (this._document.fileStructure.isIncrementalUpdate === false && this._objectCollection &&
                    this._objectCollection._mainObjectCollection) {
                    this._objectCollection._mainObjectCollection.set(reference, fonts);
                }
                dictionary.update(key, reference);
            }
        }
    };
    /**
     * Writes a stream object into the output buffer, optionally applying encryption
     * or handling cross-reference stream rules.
     *
     * @private
     * @param {_PdfBaseStream} stream - The source PDF stream to write.
     * @param {number[]} buffer - The output buffer receiving the encoded stream.
     * @param {_CipherTransform} [transform] - Optional cipher transform used to encrypt stream data.
     * @param {boolean} [isCrossReference] - Indicates whether the stream is part of a cross-reference structure.
     * @returns {void} nothing.
     */
    _PdfCrossReference.prototype._writeStream = function (stream, buffer, transform, isCrossReference) {
        var value;
        var streamBuffer = [];
        if (!isCrossReference) {
            if (stream._isCompress && !stream._isImage) {
                value = _compressStream(stream);
            }
            else {
                value = stream.getString();
            }
            if (transform) {
                value = transform.encryptString(value);
            }
        }
        else {
            value = stream.getString();
        }
        this._writeString(value, streamBuffer);
        stream.dictionary.update('Length', streamBuffer.length);
        this._writeDictionary(stream.dictionary, buffer, this._newLine, transform, isCrossReference);
        this._writeString("stream" + this._newLine, buffer);
        this._writeBytes(streamBuffer, buffer);
        this._writeString(this._newLine + "endstream" + this._newLine, buffer);
    };
    /**
     * Writes a value (name, reference, array, string, number, etc.) into the buffer.
     *
     * @private
     * @param {any} value - String to write.
     * @param {any} key - Destination buffer.
     * @param {Array<number>} buffer - buffer for the text.
     * @param {_CipherTransform} [transform] - String to write.
     * @param {boolean} [isCrossReference] - Destination buffer.
     * @returns {void} nothing.
     */
    _PdfCrossReference.prototype._writeValue = function (value, key, buffer, transform, isCrossReference) {
        if (value instanceof _PdfName) {
            if (value.name.indexOf(' ') !== -1) {
                value.name = value.name.replace(/ /g, '#20'); // eslint-disable-line
            }
            var escapedName = (key === 'V' || key === 'AS') ? _escapePdfName(value.name) : value.name;
            this._writeString("/" + escapedName, buffer);
        }
        else if (value instanceof _PdfReference) {
            this._writeString(value.toString() + " R", buffer);
        }
        else if (Array.isArray(value)) {
            this._writeString('[', buffer);
            var first = true;
            for (var _i = 0, value_1 = value; _i < value_1.length; _i++) {
                var val = value_1[_i];
                if (!first) {
                    this._writeString(' ', buffer);
                }
                else {
                    first = false;
                }
                this._writeValue(val, key, buffer, transform, isCrossReference);
            }
            this._writeString(']', buffer);
        }
        else if (typeof value === 'string') {
            if (!isCrossReference && transform) {
                value = transform.encryptString(value);
            }
            var isUnicode = false;
            for (var i = 0; i < value.length; i++) {
                if (value.charCodeAt([i]) > 255) {
                    isUnicode = true;
                    break;
                }
            }
            if (isUnicode) {
                this._writeUnicodeString(value, buffer);
            }
            else {
                this._writeString("(" + this._escapeString(value) + ")", buffer);
            }
        }
        else if (typeof value === 'number') {
            this._writeString(_numberToString(value), buffer);
        }
        else if (typeof value === 'boolean') {
            this._writeString(value.toString(), buffer);
        }
        else if (value instanceof _PdfDictionary) {
            this._writeDictionary(value, buffer, this._newLine, transform, isCrossReference);
        }
        else if (value instanceof _PdfBaseStream) {
            this._writeStream(value, buffer, transform, isCrossReference);
        }
        else if (value === null) {
            this._writeString('null', buffer);
        }
    };
    /**
     * Writes a Unicode string as big-endian bytes into the buffer.
     *
     * @private
     * @param {string} value - String to write.
     * @param {Array<number>} buffer - Destination buffer.
     * @returns {void} nothing.
     */
    _PdfCrossReference.prototype._writeUnicodeString = function (value, buffer) {
        var byteValues = _stringToBigEndianBytes(value);
        byteValues.unshift(254, 255);
        var data = [];
        byteValues.forEach(function (byte) {
            switch (byte) {
                case 40:
                case 41:
                    data.push(92);
                    data.push(byte);
                    break;
                case 13:
                    data.push(92);
                    data.push(114);
                    break;
                case 92:
                    data.push(92);
                    data.push(byte);
                    break;
                default:
                    data.push(byte);
                    break;
            }
        });
        buffer.push('('.charCodeAt(0) & 0xff);
        data.forEach(function (byte) {
            buffer.push(byte & 0xff);
        });
        buffer.push(')'.charCodeAt(0) & 0xff);
    };
    /**
     * Writes raw string characters into the numeric buffer.
     *
     * @private
     * @param {string} value - String to write.
     * @param {Array<number>} buffer - Destination buffer.
     * @returns {void} nothing.
     */
    _PdfCrossReference.prototype._writeString = function (value, buffer) {
        for (var i = 0; i < value.length; i++) {
            buffer.push(value.charCodeAt(i) & 0xff);
        }
    };
    /**
     * Writes raw bytes into the numeric buffer.
     *
     * @private
     * @param {number[]} data - Bytes to write.
     * @param {Array<number>} buffer - Destination buffer.
     * @returns {void} nothing.
     */
    _PdfCrossReference.prototype._writeBytes = function (data, buffer) {
        for (var i = 0; i < data.length; i++) {
            buffer.push(data[i]); // eslint-disable-line
        }
    };
    /**
     * Writes a multi-byte long integer into the buffer.
     *
     * @private
     * @param {number} value - Destination buffer.
     * @param {number} count - Destination buffer.
     * @param {Array<number>} buffer - Destination buffer.
     * @returns {void} nothing.
     */
    _PdfCrossReference.prototype._writeLong = function (value, count, buffer) {
        for (var i = count - 1; i >= 0; --i) {
            buffer.push(value >> (i << 3) & 0xff);
        }
    };
    /**
     * Escapes special characters in a string for PDF string literals.
     *
     * @private
     * @param {string} value - Input string.
     * @returns {string} Escaped string.
     */
    _PdfCrossReference.prototype._escapeString = function (value) {
        return value.replace(/([()\\\n\r])/g, function (substring) {
            if (substring === '\n') {
                return '\\n';
            }
            else if (substring === '\r') {
                return '\\r';
            }
            return "\\" + substring;
        });
    };
    _PdfCrossReference.prototype._destroy = function () {
        this._entries = undefined;
        if (this._pendingRefs) {
            this._pendingRefs.clear();
            this._pendingRefs = undefined;
        }
        if (this._cacheMap) {
            this._cacheMap.clear();
        }
        if (this._offsetReference) {
            this._offsetReference.clear();
        }
        if (this._objectStreamCollection) {
            this._objectStreamCollection.clear();
        }
        if (this._objectCollection) {
            this._objectCollection = undefined;
        }
        this._offsets = [];
        this._startXRefQueue = [];
        this._root = undefined;
        this._startXRefQueue = undefined;
        this._stream = undefined;
        this._streamState = undefined;
        this._tableState = undefined;
        this._topDictionary = undefined;
        this._trailer = undefined;
        this._version = undefined;
        this._crossReferencePosition = undefined;
    };
    _PdfCrossReference.prototype._writeObjectCollection = function (objectCollection, buffer) {
        var _this = this;
        var objectStreamCollection = new Map();
        this._indexes = [];
        this._indexes.push(0, 1);
        var flushThreshold = 512000;
        objectCollection.forEach(function (value, key) {
            _this._writeObjectToBuffer(key, value, buffer, objectStreamCollection);
            if (buffer.length > flushThreshold) {
                _this._flushBuffer(buffer);
            }
        });
        if (this._cacheMap.size > objectCollection.size) {
            this._cacheMap.forEach(function (value, key) {
                if (!objectCollection.has(key)) {
                    _this._writeObjectToBuffer(key, value, buffer, objectStreamCollection);
                    if (buffer.length > flushThreshold) {
                        _this._flushBuffer(buffer);
                    }
                }
            });
        }
        if (this._document.fileStructure._crossReferenceType === PdfCrossReferenceType.stream) {
            this._objectStream = undefined;
            this._objectStreamCollection = objectStreamCollection;
            this._writeXrefStream(buffer);
        }
        else {
            this._writeXrefTable(buffer);
        }
    };
    _PdfCrossReference.prototype._saveAsTable = function (currentLength, buffer) {
        var _this = this;
        var tempBuffer = '';
        var flushThreshold = 512000; // 500KB threshold
        var processedCount = 0;
        this._cacheMap.forEach(function (value, key) {
            var dictionary;
            if (value instanceof _PdfDictionary) {
                dictionary = value;
            }
            else if (value instanceof _PdfBaseStream) {
                dictionary = value.dictionary;
            }
            if (dictionary) {
                if (dictionary._updated && (!dictionary.isCatalog || _this._allowCatalog) || dictionary._isSignature) {
                    var offsetString = _this._processString((currentLength + _this._bufferLength + buffer.length).toString(), 10);
                    var genString = _this._processString(key.generationNumber.toString(), 5);
                    tempBuffer += key.objectNumber + " 1" + _this._newLine + offsetString + " " + genString + " n" + _this._newLine;
                    _this._writeObject(value, buffer, key);
                    processedCount++;
                    if (buffer.length > flushThreshold || processedCount % 2 === 0) {
                        _this._flushBuffer(buffer);
                    }
                }
            }
        });
        var newStartXref = this._bufferLength + buffer.length + currentLength;
        this._writeString("xref" + this._newLine + "0 1" + this._newLine + "0000000000 65535 f" + this._newLine, buffer);
        this._writeXref(buffer, tempBuffer, newStartXref);
        if (buffer.length > 0) {
            this._flushBuffer(buffer);
        }
    };
    _PdfCrossReference.prototype._writeArchiveStream = function (objectStreamCollection, key, value) {
        if (typeof this._objectStream === 'undefined' || this._objectStream._length === 100) {
            var archiveObj = new _PdfArchievedStream(this);
            objectStreamCollection.set(archiveObj._reference, archiveObj);
            this._objectStream = archiveObj;
        }
        this._objectStream._writeObject(key, value);
    };
    _PdfCrossReference.prototype._writeObjectToBuffer = function (key, value, buffer, // eslint-disable-line
    objectStreamCollection) {
        var cipher;
        if (value instanceof _PdfDictionary && value.isCatalog) {
            this._writeToBuffer(buffer, key, value);
        }
        else if (value instanceof _PdfName) {
            if (this._document.fileStructure._crossReferenceType === PdfCrossReferenceType.stream) {
                this._writeArchiveStream(objectStreamCollection, key, value);
            }
            else {
                this._writeToBuffer(buffer, key, value);
            }
        }
        else if (value instanceof _PdfDictionary) {
            var type = value.get('Filter');
            var typeIsFilter = type && type.name === 'Standard';
            if (this._document.fileStructure._crossReferenceType === PdfCrossReferenceType.stream) {
                if (!typeIsFilter && !value._isSignature) {
                    this._writeArchiveStream(objectStreamCollection, key, value);
                }
                else {
                    this._writeToBuffer(buffer, key, value);
                }
            }
            else {
                this._offsetReference.set(key, this._bufferLength + buffer.length);
                this._indexes.push(key.objectNumber, 1);
                this._writeObject(value, buffer, key);
            }
        }
        else {
            if (value instanceof _PdfBaseStream) {
                var dictionary = value.dictionary;
                if (dictionary && dictionary._updated && !dictionary.isCatalog) {
                    if (this._encrypt) {
                        cipher = this._encrypt._createCipherTransform(key.objectNumber, key.generationNumber);
                    }
                    dictionary._updated = false;
                }
            }
            else if ((!Array.isArray(value) || value.length === 0) && typeof value !== 'number' && typeof value !== 'string') {
                return;
            }
            this._writeToBuffer(buffer, key, value, cipher);
        }
    };
    _PdfCrossReference.prototype._writeToBuffer = function (buffer, key, value, cipher) {
        this._offsets.push(this._bufferLength + buffer.length);
        this._offsetReference.set(key, this._bufferLength + buffer.length);
        this._indexes.push(key.objectNumber, 1);
        this._writeObject(value, buffer, key, cipher);
        if (buffer.length > 512000) { // 500KB threshold
            this._flushBuffer(buffer);
        }
    };
    _PdfCrossReference.prototype._getSortedReferences = function (collection) {
        var entriesArray = []; // eslint-disable-line
        collection.forEach(function (value, key) {
            entriesArray.push([key, value]);
        });
        entriesArray.sort(function (a, b) {
            return a[0].objectNumber - b[0].objectNumber;
        });
        var sortedCollection = new Map(); // eslint-disable-line
        var lastObjectNumber = 1;
        for (var _i = 0, entriesArray_1 = entriesArray; _i < entriesArray_1.length; _i++) {
            var _a = entriesArray_1[_i], key = _a[0], value = _a[1];
            var currentObjectNumber = key.objectNumber;
            while (lastObjectNumber < currentObjectNumber) {
                sortedCollection.set({ objectNumber: lastObjectNumber }, 0);
                lastObjectNumber++;
            }
            sortedCollection.set(key, value);
            lastObjectNumber = currentObjectNumber + 1;
        }
        return sortedCollection;
    };
    _PdfCrossReference.prototype._saveAsync = function () {
        return __awaiter(this, void 0, void 0, function () {
            var buffer, result, offset, totalSignatures, i, objectCollection, _i, _a, chunk, totalSignatures, i, stream;
            return __generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        this._uint8Chunks = [];
                        this._bufferLength = 0;
                        buffer = [37, 80, 68, 70, 45];
                        return [4 /*yield*/, this._writeStringAsync("" + this._version + this._newLine, buffer)];
                    case 1:
                        _b.sent();
                        buffer.push(0x25, 0x83, 0x92, 0xfa, 0xfe);
                        return [4 /*yield*/, this._writeStringAsync(this._newLine, buffer)];
                    case 2:
                        _b.sent();
                        offset = 0;
                        if (!(this._signatureCollection && this._signatureCollection.length > 0)) return [3 /*break*/, 6];
                        if (this._isCrossReferenceStream) {
                            this._document.fileStructure.crossReferenceType = PdfCrossReferenceType.stream;
                        }
                        else if (this._isCrossReferenceTable) {
                            this._document.fileStructure.crossReferenceType = PdfCrossReferenceType.table;
                        }
                        totalSignatures = this._signatureCollection.length;
                        i = 0;
                        _b.label = 3;
                    case 3:
                        if (!(i < totalSignatures)) return [3 /*break*/, 6];
                        this._signatureCollection[i]._catalogBeginSave();
                        if (!(i % 50 === 0)) return [3 /*break*/, 5];
                        return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 0); })];
                    case 4:
                        _b.sent(); // eslint-disable-line
                        _b.label = 5;
                    case 5:
                        i++;
                        return [3 /*break*/, 3];
                    case 6:
                        if (!!this._document.fileStructure.isIncrementalUpdate) return [3 /*break*/, 10];
                        this._currentLength = 0;
                        objectCollection = new _PdfMainObjectCollection(this);
                        return [4 /*yield*/, this._writeObjectCollectionAsync(objectCollection._mainObjectCollection, buffer)];
                    case 7:
                        _b.sent();
                        if (!(buffer.length > 0)) return [3 /*break*/, 9];
                        return [4 /*yield*/, this._flushBufferAsync(buffer)];
                    case 8:
                        _b.sent();
                        _b.label = 9;
                    case 9:
                        result = new Uint8Array(this._bufferLength);
                        return [3 /*break*/, 17];
                    case 10:
                        this._currentLength = this._stream.length;
                        if (!(this._document._fileStructure._crossReferenceType === PdfCrossReferenceType.stream)) return [3 /*break*/, 12];
                        return [4 /*yield*/, this._saveAsStreamAsync(this._currentLength, buffer)];
                    case 11:
                        _b.sent();
                        return [3 /*break*/, 14];
                    case 12: return [4 /*yield*/, this._saveAsTableAsync(this._currentLength, buffer)];
                    case 13:
                        _b.sent();
                        _b.label = 14;
                    case 14:
                        if (!(buffer.length > 0)) return [3 /*break*/, 16];
                        return [4 /*yield*/, this._flushBufferAsync(buffer)];
                    case 15:
                        _b.sent();
                        _b.label = 16;
                    case 16:
                        result = new Uint8Array(this._stream.length + this._bufferLength);
                        result.set(this._stream.bytes);
                        offset = this._stream.length;
                        _b.label = 17;
                    case 17:
                        for (_i = 0, _a = this._uint8Chunks; _i < _a.length; _i++) {
                            chunk = _a[_i];
                            result.set(chunk, offset);
                            offset += chunk.length;
                        }
                        if (!(this._signatureCollection && this._signatureCollection.length > 0)) return [3 /*break*/, 21];
                        totalSignatures = this._signatureCollection.length;
                        i = 0;
                        _b.label = 18;
                    case 18:
                        if (!(i < totalSignatures)) return [3 /*break*/, 21];
                        this._signature = this._signatureCollection[i]._signatureDictionary;
                        return [4 /*yield*/, this._signature._documentSavedAsync(result)];
                    case 19:
                        _b.sent();
                        _b.label = 20;
                    case 20:
                        i++;
                        return [3 /*break*/, 18];
                    case 21:
                        if (!this._document.fileStructure.isIncrementalUpdate) {
                            stream = new _PdfStream(result);
                            this._stream = stream;
                            this._document._stream = stream;
                        }
                        this._uint8Chunks = [];
                        return [2 /*return*/, result];
                }
            });
        });
    };
    _PdfCrossReference.prototype._writeStringAsync = function (value, buffer) {
        return __awaiter(this, void 0, void 0, function () {
            var i;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        i = 0;
                        _a.label = 1;
                    case 1:
                        if (!(i < value.length)) return [3 /*break*/, 4];
                        buffer.push(value.charCodeAt(i) & 0xff);
                        if (!(i % 10000 === 0)) return [3 /*break*/, 3];
                        return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 0); })];
                    case 2:
                        _a.sent(); // eslint-disable-line
                        _a.label = 3;
                    case 3:
                        i++;
                        return [3 /*break*/, 1];
                    case 4: return [2 /*return*/];
                }
            });
        });
    };
    _PdfCrossReference.prototype._writeObjectCollectionAsync = function (objectCollection, buffer) {
        return __awaiter(this, void 0, void 0, function () {
            var objectStreamCollection, flushThreshold, counter;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        objectStreamCollection = new Map();
                        this._indexes = [];
                        this._indexes.push(0, 1);
                        flushThreshold = 512000;
                        counter = 0;
                        objectCollection.forEach(function (value, key) { return __awaiter(_this, void 0, void 0, function () {
                            return __generator(this, function (_a) {
                                switch (_a.label) {
                                    case 0:
                                        this._writeObjectToBuffer(key, value, buffer, objectStreamCollection);
                                        if (!(buffer.length > flushThreshold)) return [3 /*break*/, 2];
                                        return [4 /*yield*/, this._flushBufferAsync(buffer)];
                                    case 1:
                                        _a.sent();
                                        _a.label = 2;
                                    case 2:
                                        if (!(++counter % 100 === 0)) return [3 /*break*/, 4];
                                        return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 0); })];
                                    case 3:
                                        _a.sent(); // eslint-disable-line
                                        _a.label = 4;
                                    case 4: return [2 /*return*/];
                                }
                            });
                        }); });
                        this._cacheMap.forEach(function (value, key) { return __awaiter(_this, void 0, void 0, function () {
                            return __generator(this, function (_a) {
                                switch (_a.label) {
                                    case 0:
                                        if (!!objectCollection.has(key)) return [3 /*break*/, 4];
                                        this._writeObjectToBuffer(key, value, buffer, objectStreamCollection);
                                        if (!(buffer.length > flushThreshold)) return [3 /*break*/, 2];
                                        return [4 /*yield*/, this._flushBufferAsync(buffer)];
                                    case 1:
                                        _a.sent();
                                        _a.label = 2;
                                    case 2:
                                        if (!(++counter % 100 === 0)) return [3 /*break*/, 4];
                                        return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 0); })];
                                    case 3:
                                        _a.sent(); // eslint-disable-line
                                        _a.label = 4;
                                    case 4: return [2 /*return*/];
                                }
                            });
                        }); });
                        if (!(this._document.fileStructure._crossReferenceType === PdfCrossReferenceType.stream)) return [3 /*break*/, 2];
                        this._objectStream = undefined;
                        this._objectStreamCollection = objectStreamCollection;
                        return [4 /*yield*/, this._writeXrefStreamAsync(buffer)];
                    case 1:
                        _a.sent();
                        return [3 /*break*/, 4];
                    case 2: return [4 /*yield*/, this._writeXrefTableAsync(buffer)];
                    case 3:
                        _a.sent();
                        _a.label = 4;
                    case 4: return [2 /*return*/];
                }
            });
        });
    };
    _PdfCrossReference.prototype._flushBufferAsync = function (data) {
        return __awaiter(this, void 0, void 0, function () {
            var chunk, i;
            return __generator(this, function (_a) {
                if (data.length === 0) {
                    return [2 /*return*/];
                }
                chunk = new Uint8Array(data.length);
                for (i = 0; i < data.length; i++) {
                    chunk[i] = data[i];
                }
                this._uint8Chunks.push(chunk);
                this._bufferLength += chunk.length;
                data.length = 0;
                return [2 /*return*/];
            });
        });
    };
    _PdfCrossReference.prototype._writeXrefStreamAsync = function (buffer) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                this._writeXrefStream(buffer);
                return [2 /*return*/];
            });
        });
    };
    _PdfCrossReference.prototype._writeXrefTableAsync = function (buffer) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                this._writeXrefTable(buffer);
                return [2 /*return*/];
            });
        });
    };
    _PdfCrossReference.prototype._saveAsStreamAsync = function (currentLength, buffer) {
        return __awaiter(this, void 0, void 0, function () {
            var objectStreamCollection, flushThreshold, counter;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        objectStreamCollection = new Map();
                        this._indexes = [];
                        this._indexes.push(0, 1);
                        this._offsets = [];
                        flushThreshold = 512000;
                        counter = 0;
                        this._cacheMap.forEach(function (value, key) { return __awaiter(_this, void 0, void 0, function () {
                            var dictionary, cipher;
                            return __generator(this, function (_a) {
                                switch (_a.label) {
                                    case 0:
                                        if (!(value instanceof _PdfBaseStream)) return [3 /*break*/, 4];
                                        dictionary = value.dictionary;
                                        if (!(dictionary && dictionary._updated && (!dictionary.isCatalog || this._allowCatalog) && !dictionary._isProcessed)) return [3 /*break*/, 4];
                                        cipher = void 0;
                                        if (this._encrypt) {
                                            cipher = this._encrypt._createCipherTransform(key.objectNumber, key.generationNumber);
                                        }
                                        this._updatedDictionary(currentLength, key, buffer, value, cipher);
                                        dictionary._isProcessed = true;
                                        if (!(buffer.length > flushThreshold)) return [3 /*break*/, 2];
                                        return [4 /*yield*/, this._flushBufferAsync(buffer)];
                                    case 1:
                                        _a.sent();
                                        _a.label = 2;
                                    case 2:
                                        if (!(++counter % 100 === 0)) return [3 /*break*/, 4];
                                        return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 0); })];
                                    case 3:
                                        _a.sent(); // eslint-disable-line
                                        _a.label = 4;
                                    case 4: return [2 /*return*/];
                                }
                            });
                        }); });
                        this._cacheMap.forEach(function (value, key) { return __awaiter(_this, void 0, void 0, function () {
                            var dictionary;
                            return __generator(this, function (_a) {
                                switch (_a.label) {
                                    case 0:
                                        if (!(value instanceof _PdfDictionary)) return [3 /*break*/, 4];
                                        if (!((value._updated && !value.isCatalog && !value._isProcessed) || value._isSignature)) return [3 /*break*/, 1];
                                        if (value._isSignature) {
                                            this._updatedDictionary(currentLength, key, buffer, value);
                                        }
                                        else {
                                            this._writeArchiveStream(objectStreamCollection, key, value);
                                        }
                                        return [3 /*break*/, 3];
                                    case 1:
                                        if (!(value._updated && (value.isCatalog || this._allowCatalog))) return [3 /*break*/, 3];
                                        this._updatedDictionary(currentLength, key, buffer, value);
                                        if (!(buffer.length > flushThreshold)) return [3 /*break*/, 3];
                                        return [4 /*yield*/, this._flushBufferAsync(buffer)];
                                    case 2:
                                        _a.sent();
                                        _a.label = 3;
                                    case 3: return [3 /*break*/, 6];
                                    case 4:
                                        if (!(value instanceof _PdfBaseStream)) return [3 /*break*/, 6];
                                        dictionary = value.dictionary;
                                        if (!(dictionary && dictionary._updated && (!dictionary.isCatalog || this._allowCatalog) && !dictionary._isProcessed)) return [3 /*break*/, 6];
                                        this._updatedDictionary(currentLength, key, buffer, value);
                                        if (!(buffer.length > flushThreshold)) return [3 /*break*/, 6];
                                        return [4 /*yield*/, this._flushBufferAsync(buffer)];
                                    case 5:
                                        _a.sent();
                                        _a.label = 6;
                                    case 6:
                                        if (!(++counter % 100 === 0)) return [3 /*break*/, 8];
                                        return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 0); })];
                                    case 7:
                                        _a.sent(); // eslint-disable-line
                                        _a.label = 8;
                                    case 8: return [2 /*return*/];
                                }
                            });
                        }); });
                        this._objectStream = undefined;
                        this._objectStreamCollection = objectStreamCollection;
                        return [4 /*yield*/, this._writeXrefStreamAsync(buffer)];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    _PdfCrossReference.prototype._saveAsTableAsync = function (currentLength, buffer) {
        return __awaiter(this, void 0, void 0, function () {
            var tempBuffer, flushThreshold, processedCount, newStartXref;
            var _this = this;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        tempBuffer = '';
                        flushThreshold = 512000;
                        processedCount = 0;
                        this._cacheMap.forEach(function (value, key) { return __awaiter(_this, void 0, void 0, function () {
                            var dictionary, offsetString, genString;
                            return __generator(this, function (_a) {
                                switch (_a.label) {
                                    case 0:
                                        if (value instanceof _PdfDictionary) {
                                            dictionary = value;
                                        }
                                        else if (value instanceof _PdfBaseStream) {
                                            dictionary = value.dictionary;
                                        }
                                        if (!dictionary) return [3 /*break*/, 4];
                                        if (!((dictionary._updated && (!dictionary.isCatalog || this._allowCatalog)) || dictionary._isSignature)) return [3 /*break*/, 4];
                                        offsetString = this._processString((currentLength + this._bufferLength + buffer.length).toString(), 10);
                                        genString = this._processString(key.generationNumber.toString(), 5);
                                        tempBuffer += key.objectNumber + " 1" + this._newLine + offsetString + " " + genString + " n" + this._newLine;
                                        this._writeObject(value, buffer, key);
                                        processedCount++;
                                        if (!(buffer.length > flushThreshold || processedCount % 2 === 0)) return [3 /*break*/, 2];
                                        return [4 /*yield*/, this._flushBufferAsync(buffer)];
                                    case 1:
                                        _a.sent();
                                        _a.label = 2;
                                    case 2:
                                        if (!(processedCount % 100 === 0)) return [3 /*break*/, 4];
                                        return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 0); })];
                                    case 3:
                                        _a.sent(); // eslint-disable-line
                                        _a.label = 4;
                                    case 4: return [2 /*return*/];
                                }
                            });
                        }); });
                        newStartXref = this._bufferLength + buffer.length + currentLength;
                        return [4 /*yield*/, this._writeStringAsync("xref" + this._newLine + "0 1" + this._newLine + "0000000000 65535 f" + this._newLine, buffer)];
                    case 1:
                        _a.sent();
                        return [4 /*yield*/, this._writeXrefAsync(buffer, tempBuffer, newStartXref)];
                    case 2:
                        _a.sent();
                        if (!(buffer.length > 0)) return [3 /*break*/, 4];
                        return [4 /*yield*/, this._flushBufferAsync(buffer)];
                    case 3:
                        _a.sent();
                        _a.label = 4;
                    case 4: return [2 /*return*/];
                }
            });
        });
    };
    _PdfCrossReference.prototype._writeXrefAsync = function (buffer, tempBuffer, newStartXref) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                this._writeXref(buffer, tempBuffer, newStartXref);
                return [2 /*return*/];
            });
        });
    };
    return _PdfCrossReference;
}());
export { _PdfCrossReference };
/**
 * Represents metadata for a single object entry in the XRef table/stream.
 *
 * @private
 */
var _PdfObjectInformation = /** @class */ (function () {
    function _PdfObjectInformation() {
    }
    return _PdfObjectInformation;
}());
/**
 * Internal state used when parsing an XRef table across reads.
 *
 * @private
 */
var _PdfCrossTableState = /** @class */ (function () {
    function _PdfCrossTableState() {
    }
    return _PdfCrossTableState;
}());
/**
 * State for parsing cross-reference streams (byte widths and ranges).
 *
 * @private
 */
var _PdfStreamState = /** @class */ (function () {
    function _PdfStreamState() {
    }
    return _PdfStreamState;
}());
/**
 * Helper that collects objects to write into an archived object stream (.objstm).
 *
 * @private
 */
var _PdfArchievedStream = /** @class */ (function () {
    /**
     * Initializes a new archived stream helper.
     *
     * @private
     * @param {_PdfCrossReference} crossReference - Owner cross-reference.
     */
    function _PdfArchievedStream(crossReference) {
        /** Serialized index lines for the archive stream. */
        this._indexes = '';
        /** Number of objects stored in this archived stream. */
        this._length = 0;
        this._crossReference = crossReference;
        this._reference = crossReference._getNextReference();
        this._archiveXRef = '';
        this._updatedStream = [];
        this._collection = [];
    }
    /**
     * Appends an object to the archived stream.
     *
     * @private
     * @param {_PdfReference} key - Reference of the object.
     * @param {_PdfDictionary} value - Object value to write.
     * @returns {void} nothing.
     */
    _PdfArchievedStream.prototype._writeObject = function (key, value) {
        this._archiveXRef += key.objectNumber + " " + this._updatedStream.length + this._crossReference._newLine;
        this._collection.push(key.objectNumber, 1);
        this._crossReference._writeObject(value, this._updatedStream);
        this._length++;
    };
    /**
     * Saves the archived stream into the provided buffer and updates offsets.
     *
     * @private
     * @param {number[]} buffer - Output buffer to append to.
     * @param {number} currentLength - Current length before writing.
     * @returns {void} nothing.
     */
    _PdfArchievedStream.prototype._save = function (buffer, currentLength) {
        var data = [];
        this._crossReference._writeString(this._archiveXRef, data);
        this._crossReference._writeBytes(this._updatedStream, data);
        var newDict = new _PdfDictionary(this._crossReference);
        newDict.set('Type', _PdfName.get('ObjStm'));
        newDict.set('N', this._length);
        newDict.set('First', this._archiveXRef.length);
        newDict.set('Length', data.length);
        var archiveStream = new _PdfStream(data, newDict, 0, data.length);
        this._archiveOffset = this._crossReference._bufferLength + currentLength + buffer.length;
        var cipher;
        if (this._crossReference._encrypt) {
            cipher = this._crossReference._encrypt._createCipherTransform(this._reference.objectNumber, this._reference.generationNumber);
        }
        this._crossReference._writeObject(archiveStream, buffer, this._reference, cipher);
    };
    return _PdfArchievedStream;
}());
/**
 * Represents the main object collection that will be written into the file.
 * It collects and orders objects to be saved.
 *
 * @private
 */
var _PdfMainObjectCollection = /** @class */ (function () {
    /**
     * Initializes a new instance of the `_PdfMainObjectCollection` class.
     *
     * @private
     * @param { _PdfCrossReference } collection - The cross-reference collection containing the PDF objects.
     * ```typescript
     * // Load an existing PDF document
     * let document: PdfDocument = new PdfDocument(data);
     * // Create a new object collection instance
     * let mainObjectCollection = new _PdfMainObjectCollection(document._crossReference);
     * // Access the main object collection
     * let objects = mainObjectCollection._mainObjectCollection;
     * // Save the document
     * document.save('output.pdf');
     * // Destroy the document
     * document.destroy();
     */
    function _PdfMainObjectCollection(collection) {
        var _this = this;
        /** Pointer into the ordered collection. */
        this._pointer = 0;
        if (!(collection._cacheMap instanceof Map)) {
            throw new Error('Expected _cacheMap to be a Map.');
        }
        this._reference = [];
        this._cache = collection._cacheMap;
        this._mainObjectCollection = new Map(); // eslint-disable-line
        var foundCatalog = false;
        this._crossReference = collection;
        this._cache.forEach(function (value, key) {
            if (!foundCatalog && value instanceof _PdfDictionary && value.isCatalog) {
                _this._addToMainObjectCollection(key, value);
                foundCatalog = true;
            }
        });
        if (collection._trailer && collection._trailer.has('Info')) {
            var infoRef = collection._trailer.getRaw('Info'); // eslint-disable-line
            if (infoRef && infoRef instanceof _PdfReference) {
                var infoDict = collection._fetch(infoRef);
                if (infoDict && infoDict instanceof _PdfDictionary) {
                    infoDict._updated = true;
                    this._addToMainObjectCollection(infoRef, infoDict);
                }
            }
            else if (infoRef && infoRef instanceof _PdfDictionary) {
                infoRef._updated = true;
                this._addToMainObjectCollection(this._crossReference._getNextReference(), infoRef);
            }
        }
        this._parseObjectCollection();
    }
    /**
     * Parses the accumulated main object collection, expanding referenced objects.
     *
     * @private
     * @returns {Map<_PdfReference, any>} The populated main object collection.
     */
    _PdfMainObjectCollection.prototype._parseObjectCollection = function () {
        var _this = this;
        var _loop_1 = function () {
            var collection = new Map(); // eslint-disable-line
            var currentIndex = 0;
            this_1._mainObjectCollection.forEach(function (value, key) {
                if (currentIndex === _this._pointer) {
                    collection.set(key, value);
                    _this._parse(key, value);
                }
                currentIndex++;
            });
            this_1._pointer++;
        };
        var this_1 = this;
        while (this._pointer < this._mainObjectCollection.size) {
            _loop_1();
        }
        this._addReferencesToMainCollection();
        return this._mainObjectCollection;
    };
    /**
     * Adds an object to the main collection.
     *
     * @private
     * @param {_PdfReference} key - Object reference.
     * @param {any} value - Object value.
     * @returns {void} nothing.
     */
    _PdfMainObjectCollection.prototype._addToMainObjectCollection = function (key, value) {
        this._reference.push(key);
        this._mainObjectCollection.set(key, value);
    };
    /**
     * Fetches a reference and parses it into the main collection.
     *
     * @private
     * @param {_PdfReference} reference - Reference to fetch and parse.
     * @returns {void} nothing.
     */
    _PdfMainObjectCollection.prototype._parseFetchValue = function (reference) {
        var fetchvalue = this._crossReference._fetch(reference); // eslint-disable-line
        this._parse(reference, fetchvalue);
    };
    /**
     * Internal parser that inspects a value and ensures referenced objects are included.
     *
     * @private
     * @param {_PdfReference} key - Reference for the value.
     * @param {any} value - Value to inspect.
     * @returns {void} nothing.
     */
    _PdfMainObjectCollection.prototype._parse = function (key, value) {
        var _this = this;
        if (value instanceof _PdfDictionary) {
            this._parseDictionary(value);
        }
        else if (value instanceof _PdfBaseStream) {
            this._parseStream(key, value);
        }
        else if (value instanceof _PdfReference) {
            this._parseFetchValue(value);
        }
        else if (Array.isArray(value) && value.length > 0) {
            var isPdfReferenceArray = value.every(function (value) { return value instanceof _PdfReference; }); // eslint-disable-line
            if (isPdfReferenceArray) {
                value.forEach(function (ref) { return _this._parseFetchValue(ref); }); // eslint-disable-line
            }
            else {
                value.forEach(function (item) {
                    if (item instanceof _PdfReference) {
                        _this._parseFetchValue(item);
                    }
                });
                if (this._reference.indexOf(key) === -1 && !this._mainObjectCollection.has(key)) {
                    this._addToMainObjectCollection(key, value);
                }
            }
        }
        else if (typeof value === 'number') {
            if (this._reference.indexOf(key) === -1 && !this._mainObjectCollection.has(key)) {
                this._addToMainObjectCollection(key, value);
            }
        }
    };
    /**
     * Adds any remaining cached objects into the main collection.
     *
     * @private
     * @returns {void} nothing.
     */
    _PdfMainObjectCollection.prototype._addReferencesToMainCollection = function () {
        var _this = this;
        var objectsToWrite = []; // eslint-disable-line
        this._cache.forEach(function (value, key) {
            if (!_this._mainObjectCollection.has(key)) {
                objectsToWrite.push({ key: key, value: value });
            }
        });
        objectsToWrite.forEach(function (_a) {
            var key = _a.key, value = _a.value;
            _this._addToMainObjectCollection(key, value);
        });
    };
    /**
     * Walks a dictionary and ensures referenced objects are included.
     *
     * @private
     * @param {_PdfDictionary} element - Dictionary to scan.
     * @returns {void} nothing.
     */
    _PdfMainObjectCollection.prototype._parseDictionary = function (element) {
        var _this = this;
        if (element._isVisited) {
            return;
        }
        element._isVisited = true;
        element.forEach(function (key, value) {
            var processReference = function (ref) {
                if (!_this._mainObjectCollection.has(ref) && _this._reference.indexOf(ref) === -1) {
                    var fetchValue = _this._crossReference._fetch(ref); // eslint-disable-line
                    if (fetchValue instanceof _PdfReference) {
                        fetchValue = _this._crossReference._fetch(fetchValue);
                    }
                    if (fetchValue instanceof _PdfBaseStream) {
                        _this._parseStream(ref, fetchValue);
                    }
                    else {
                        _this._addToMainObjectCollection(ref, fetchValue);
                    }
                }
            };
            if (value instanceof _PdfReference) {
                processReference(value);
            }
            else if (Array.isArray(value)) {
                value.forEach(function (item) {
                    if (item instanceof _PdfReference) {
                        processReference(item);
                    }
                    else if (item instanceof _PdfDictionary) {
                        _this._parseDictionary(item);
                    }
                });
            }
            else if (value instanceof _PdfDictionary) {
                _this._parseDictionary(value);
            }
        });
    };
    /**
     * Parses a stream object dictionary and includes its referenced objects.
     *
     * @private
     * @param {_PdfReference} key - Reference of the stream.
     * @param {_PdfBaseStream} element - The stream to parse.
     * @returns {void} nothing.
     */
    _PdfMainObjectCollection.prototype._parseStream = function (key, element) {
        this._parseDictionary(element.dictionary);
        if (this._reference.indexOf(key) === -1 && !this._mainObjectCollection.has(key)) {
            var type = element.dictionary.get('Type');
            var subtype = element.dictionary.get('Subtype');
            var isUpdated = element.dictionary._updated;
            var uncompressedValue = void 0;
            if (isUpdated || (type && (type.name === 'XObject' || type.name === 'Metadata') &&
                (subtype.name === 'Form' || subtype.name === 'XML'))) {
                uncompressedValue = this._crossReference._fetch(key);
            }
            else {
                uncompressedValue = this._crossReference._fetch(key, true);
                uncompressedValue._isCompress = false;
            }
            this._addToMainObjectCollection(key, uncompressedValue);
        }
    };
    return _PdfMainObjectCollection;
}());