UNPKG

@syncfusion/ej2-pdf

Version:

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

1,065 lines 53.2 kB
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
import { _PdfDictionary, _PdfName, _PdfReference } from './../pdf-primitives';
import { _PdfParser, _PdfLexicalOperator } from '../pdf-parser';
import { _PdfContentStream, _PdfStream } from '../base-stream';
import { _PdfCommand } from './../pdf-primitives';
import { _ExportHelper } from './xfdf-document';
import { PdfRubberStampAnnotation, PdfFileLinkAnnotation, PdfTextWebLinkAnnotation, PdfRectangleAnnotation, PdfDocumentLinkAnnotation, PdfPopupAnnotation } from './../annotations/annotation';
import { _bytesToString, _getNewGuidString, _byteArrayToHexString, _stringToBytes, _isNullOrUndefined } from './../utils';
import { PdfCheckBoxField, PdfComboBoxField, PdfListBoxField, PdfRadioButtonListField, PdfTextBoxField } from './../form/field';
import { _StringTokenizer } from './../fonts/string-layouter';
import { _PdfFlateStream } from '../flate-stream';
import { CompressedStreamWriter } from '@syncfusion/ej2-compression';
/**
 * Provides utilities to export and import FDF data for annotations and form fields and manages object mapping and serialization.
 *
 * @private
 */
var _FdfDocument = /** @class */ (function (_super) {
    __extends(_FdfDocument, _super);
    /**
     * Initializes the FDF export and import helper with an optional file name.
     *
     * @private
     * @param {string} fileName represent filename.
     */
    function _FdfDocument(fileName) {
        var _this = _super.call(this) || this;
        /**
         * Stores annotation objects collected during FDF export.
         *
         * Key and value types vary depending on annotation structure.
         *
         * @private
         */
        _this._annotationObjects = new Map(); // eslint-disable-line
        /**
         * Special character set used to normalize or escape content during FDF processing.
         *
         * @private
         */
        _this._specialCharacters = 'âãÏÓ';
        if (fileName !== null && typeof fileName !== 'undefined') {
            _this._fileName = fileName;
        }
        return _this;
    }
    /**
     * Prepares and exports annotations from the document as an FDF byte array.
     *
     * @private
     * @param {PdfDocument} document Source PDF document to read annotations.
     * @returns {Uint8Array} Generated FDF payload containing exported annotations.
     */
    _FdfDocument.prototype._exportAnnotations = function (document) {
        this._document = document;
        this._crossReference = document._crossReference;
        this._isAnnotationExport = true;
        this._exportAnnotationData(document, document.pageCount);
        return this._save();
    };
    /**
     * Prepares and exports form fields from the document as an FDF byte array.
     *
     * @private
     * @param {PdfDocument} document Source PDF document to read form fields.
     * @returns {Uint8Array} Generated FDF payload containing exported form fields.
     */
    _FdfDocument.prototype._exportFormFields = function (document) {
        this._document = document;
        this._crossReference = document._crossReference;
        this._isAnnotationExport = false;
        this._key = _getNewGuidString();
        return this._save();
    };
    /**
     * Finalizes FDF serialization and returns the composed byte array.
     *
     * @private
     * @returns {Uint8Array} Serialized FDF data representing the current export.
     */
    _FdfDocument.prototype._save = function () {
        var _this = this;
        var objectID = 0;
        var objectArray = [];
        if (!this._isAnnotationExport) {
            if (this._asPerSpecification) {
                this.fdfString += '%FDF-1.2\n%' + this._specialCharacters + '\r\n1 0 obj\r<</FDF<</F(';
                this.fdfString += this._fileName + ')';
                this.fdfString += '/Fields[';
            }
            else {
                this.fdfString += '%FDF-1.2\n';
            }
            var form = this._document.form;
            if (form !== null && typeof form !== 'undefined') {
                this._exportEmptyFields = form.exportEmptyFields;
                var count = this._document.form.count;
                var _loop_1 = function (i) {
                    var field = this_1._document.form.fieldAt(i);
                    var value = this_1._exportFormFieldsData(field);
                    if (field instanceof PdfTextBoxField || field instanceof PdfListBoxField || field instanceof PdfComboBoxField
                        || field instanceof PdfRadioButtonListField || field instanceof PdfCheckBoxField) {
                        objectID++;
                    }
                    if (!this_1._asPerSpecification) {
                        if (field instanceof PdfTextBoxField || field instanceof PdfListBoxField || field instanceof PdfComboBoxField) {
                            objectArray.push(objectID);
                            this_1.fdfString += objectID + ' 0 obj<</T <' + this_1._stringToHexString(field.name) + '> /V ';
                            if (typeof value === 'string' || (Array.isArray(value) && value.length === 1)) {
                                this_1.fdfString += '<' + this_1._stringToHexString((Array.isArray(value) ? value[0] : value)) + '>';
                            }
                            else if (Array.isArray(value)) {
                                this_1.fdfString += '[';
                                value.forEach(function (val, index) {
                                    _this.fdfString += '<' + _this._stringToHexString(val) + '>';
                                    if (index !== value.length - 1) {
                                        _this.fdfString += ' ';
                                    }
                                });
                                this_1.fdfString += ']';
                            }
                            this_1.fdfString += ' >>endobj\n';
                        }
                        else if (field instanceof PdfRadioButtonListField || field instanceof PdfCheckBoxField) {
                            objectArray.push(objectID);
                            this_1.fdfString += objectID + ' 0 obj<</T <' + this_1._stringToHexString(field.name) + '> /V /';
                            this_1.fdfString += value + ' >>endobj\n';
                        }
                    }
                    else {
                        if (field instanceof PdfTextBoxField || field instanceof PdfListBoxField || field instanceof PdfComboBoxField) {
                            objectArray.push(objectID);
                            this_1.fdfString += '<</T(' + field.name + ')/V';
                            if (typeof value === 'string' || (Array.isArray(value) && value.length === 1)) {
                                this_1.fdfString += '(' + (Array.isArray(value) ? value[0] : value) + ')';
                            }
                            else if (Array.isArray(value)) {
                                this_1.fdfString += '[';
                                value.forEach(function (val, index) {
                                    _this.fdfString += '(' + val + ')';
                                    if (index !== value.length - 1) {
                                        _this.fdfString += ' ';
                                    }
                                });
                                this_1.fdfString += ']';
                            }
                            this_1.fdfString += '>>';
                        }
                        else if (field instanceof PdfRadioButtonListField || field instanceof PdfCheckBoxField) {
                            objectArray.push(objectID);
                            this_1.fdfString += '<</T(' + field.name + ')/V/' + value + '>>';
                        }
                    }
                };
                var this_1 = this;
                for (var i = 0; i < count; i++) {
                    _loop_1(i);
                }
            }
            if (this._asPerSpecification) {
                this.fdfString += ']';
                this.fdfString += '/ID[]/UF(' + this._fileName + ')>>/Type/Catalog>>\rendobj\rtrailer\r\n<</Root 1 0 R>>\r\n';
                this.fdfString += '%%EOF\r\n';
            }
            else {
                this.fdfString += (this._table.size + 1) + ' 0 obj<</F <' + this._stringToHexString(this._fileName) + '>  /Fields [';
                for (var i = 0; i < this._table.size; i++) {
                    var field = this._document.form.fieldAt(i);
                    if (field !== null && typeof field !== 'undefined' && field.export) {
                        this.fdfString += objectArray[i] + ' 0 R ';
                    }
                }
                this.fdfString += ']>>endobj\n';
                this.fdfString += (objectArray.length + 2) + ' 0 obj<</Version /1.4 /FDF ' + (objectArray.length + 1) + ' 0 R>>endobj\n';
                this.fdfString += 'trailer\n<</Root ' + (objectArray.length + 2) + ' 0 R>>';
            }
        }
        var arrayBuffer = new ArrayBuffer(this.fdfString.length * 1);
        var result = new Uint8Array(arrayBuffer);
        result.forEach(function (val, i) {
            result[i] = _this.fdfString.charCodeAt(i); // eslint-disable-line
        });
        return result;
    };
    /**
     * Imports annotations from FDF bytes into the target PDF document.
     *
     * @private
     * @param {PdfDocument} document Target PDF document to receive annotations.
     * @param {Uint8Array} data Input FDF data containing annotations.
     * @returns {void}
     */
    _FdfDocument.prototype._importAnnotations = function (document, data) {
        this._document = document;
        this._crossReference = document._crossReference;
        this._isAnnotationExport = false;
        this._checkFdf(_bytesToString(data));
        var stream = new _PdfStream(data);
        this._isAnnotationImport = true;
        var parser = new _PdfParser(new _PdfLexicalOperator(stream, true, this._isAnnotationImport), null, true, false);
        this._readFdfData(parser);
        if (_isNullOrUndefined(this._annotationObjects) && this._annotationObjects.size > 0) {
            this._annotationObjects.clear();
        }
        if (_isNullOrUndefined(this._table) && this._table.size > 0) {
            this._table.clear();
        }
    };
    /**
     * Imports form field values from FDF bytes into the target PDF document.
     *
     * @private
     * @param {PdfDocument} document Target PDF document to receive form values.
     * @param {Uint8Array} data Input FDF data containing form field entries.
     * @returns {void}
     */
    _FdfDocument.prototype._importFormData = function (document, data) {
        this._document = document;
        this._crossReference = document._crossReference;
        this._isAnnotationExport = false;
        this._checkFdf(_bytesToString(data));
        var stream = new _PdfStream(data);
        var parser = new _PdfParser(new _PdfLexicalOperator(stream, true, this._isAnnotationExport), null, false, false);
        this._readFdfData(parser);
    };
    /**
     * Parses FDF content and fills internal tables for annotations or form values depending on mode.
     *
     * @private
     * @param {any} parser Parser used to iterate tokens and objects from FDF data.
     * @returns {void}
     */
    _FdfDocument.prototype._readFdfData = function (parser) {
        var _this = this;
        var token = parser.getObject(); // eslint-disable-line
        if (this._isAnnotationImport) {
            var key = '';
            while (token !== null && typeof token !== 'undefined' && token !== 'EOF') {
                if (token instanceof _PdfDictionary) {
                    this._table.set(key, token);
                    key = '';
                }
                else if (token instanceof _PdfStream || token instanceof _PdfFlateStream) {
                    this._table.set(key, token);
                    key = '';
                }
                else if (token instanceof _PdfName || Array.isArray(token)) {
                    this._table.set(key, token);
                    key = '';
                }
                else if (token !== null && Number.isInteger(token) && token !== 0) {
                    if (parser.first >= 0) {
                        key = token.toString() + ' ' + parser.first.toString();
                    }
                }
                else if (token instanceof _PdfCommand && token.command !== null && typeof token.command !== 'undefined' &&
                    token.command === 'trailer') {
                    key = token.command;
                }
                token = parser.getObject();
            }
            this._annotationObjects = this._parseAnnotationData();
            this._annotationObjects.forEach(function (value, key) {
                var dictionary = value;
                _this._parseDictionary(dictionary);
                dictionary._crossReference = _this._crossReference;
                dictionary._updated = true;
                if (dictionary && dictionary.has('Page')) {
                    var pageNumber = dictionary.get('Page');
                    if (pageNumber !== null && typeof pageNumber !== 'undefined') {
                        var pageIndex = pageNumber;
                        if (pageIndex < _this._document.pageCount) {
                            var page = _this._document.getPage(pageIndex);
                            var pageDictionary = page._pageDictionary;
                            if (pageDictionary) {
                                var annotations = page.annotations;
                                var annotation = annotations._parseAnnotation(dictionary);
                                if (annotation !== null && typeof annotation !== 'undefined') {
                                    annotation._isImported = true;
                                    var reference = _this._crossReference._getNextReference();
                                    _this._crossReference._cacheMap.set(reference, dictionary);
                                    if (dictionary.has('NM') || dictionary.has('IRT')) {
                                        _this._addReferenceToGroup(reference, dictionary);
                                    }
                                    annotation._ref = reference;
                                    var index = annotations._annotations.length;
                                    if (!annotation._dictionary.has('P') && annotation._dictionary.get('Subtype').name !== 'Popup') {
                                        var _a = pageDictionary.objId.split(' ').map(Number), objNum = _a[0], genNum = _a[1];
                                        annotation._dictionary.update('P', _PdfReference.get(objNum, genNum));
                                    }
                                    delete annotation._dictionary._map.Page;
                                    annotations._annotations.push(reference);
                                    if (annotations._comments && annotations._comments.length > 0) {
                                        annotations._comments = [];
                                    }
                                    pageDictionary.set('Annots', annotations._annotations);
                                    pageDictionary._updated = true;
                                    annotations._parsedAnnotations.set(index, annotation);
                                    _this._handlePopup(annotations, reference, dictionary, pageDictionary);
                                    if (annotation instanceof PdfPopupAnnotation && annotation._dictionary.has('Parent')) {
                                        var parent_1 = annotation._dictionary.getRaw('Parent');
                                        if (parent_1 && parent_1 instanceof _PdfReference) {
                                            var Parent = _this._crossReference._cacheMap.get(parent_1);
                                            if (Parent && Parent instanceof _PdfDictionary && Parent.has('Popup')) {
                                                var value_1 = Parent.get('Popup'); // eslint-disable-line
                                                if (value_1 instanceof _PdfDictionary && value_1.has('Parent')) {
                                                    Parent.update('Popup', reference);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            });
            if (this._groupHolders && this._groupHolders.length > 0) {
                this._groupHolders.forEach(function (dictionary) {
                    if (dictionary && dictionary.has('IRT')) {
                        var inReplyTo = dictionary.get('IRT');
                        if (inReplyTo) {
                            if (_this._groupReferences && _this._groupReferences.has(inReplyTo)) {
                                dictionary.update('IRT', _this._groupReferences.get(inReplyTo));
                            }
                            else {
                                delete dictionary._map.IRT;
                            }
                        }
                    }
                });
            }
            this._groupHolders = [];
            this._groupReferences = new Map();
        }
        else {
            token = parser.getObject();
            if (!this._asPerSpecification) {
                token = parser.getObject();
                if (token instanceof _PdfCommand && token.command !== null) {
                    token = token.command;
                }
                while (token !== null && typeof token !== 'undefined' && token !== 'EOF') {
                    if (token instanceof _PdfDictionary) {
                        var t = token.getArray('T'); // eslint-disable-line
                        var v = void 0; // eslint-disable-line
                        if (token._map && token._map.V instanceof _PdfName) {
                            v = token.get('V').name;
                        }
                        else {
                            v = token.getArray('V');
                        }
                        if (t !== null && t !== undefined && t.length > 0) {
                            this._table.set(t, v);
                        }
                    }
                    token = parser.getObject();
                }
            }
            else {
                while (token !== null && typeof token !== 'undefined' && token !== 'EOF') {
                    if (token instanceof _PdfDictionary && token !== null && token._map.FDF !== null && token._map.FDF !== undefined) {
                        token = token._map.FDF;
                        if (token instanceof _PdfDictionary && token._map.Fields !== null && token._map.Fields !== undefined) {
                            token = token._map.Fields;
                            if (token !== null && token !== undefined) {
                                token.forEach(function (field) {
                                    if (field instanceof _PdfDictionary && field !== null && field !== undefined) {
                                        var t = field.getArray('T'); // eslint-disable-line
                                        var v = void 0; // eslint-disable-line
                                        if (field._map && field._map.V instanceof _PdfName) {
                                            v = field.get('V').name;
                                        }
                                        else {
                                            v = field.getArray('V');
                                        }
                                        if (t !== null && t !== undefined && t.length > 0) {
                                            _this._table.set(t, v);
                                        }
                                    }
                                });
                            }
                        }
                    }
                    token = parser.getObject();
                }
            }
            this._importField();
        }
    };
    /**
     * Recursively parses a dictionary and resolves nested structures and references.
     *
     * @private
     * @param {_PdfDictionary} dictionary Dictionary to parse and normalize.
     * @returns {void}
     */
    _FdfDocument.prototype._parseDictionary = function (dictionary) {
        var _this = this;
        if (dictionary != null && dictionary.size > 0) {
            dictionary.forEach(function (key, value) {
                if (key !== 'Parent' && key !== 'P' && key !== 'Page') {
                    _this._parseDictionaryData(dictionary, key);
                }
            });
        }
    };
    /**
     * Resolves a specific dictionary entry and replaces references with concrete objects when available.
     *
     * @private
     * @param {_PdfDictionary} dictionary Dictionary holding the key to process.
     * @param {string} key Entry key to examine and resolve.
     * @returns {void}
     */
    _FdfDocument.prototype._parseDictionaryData = function (dictionary, key) {
        var value = dictionary.get(key); // eslint-disable-line
        if (value instanceof _PdfDictionary) {
            this._parseDictionary(value);
        }
        else if (value instanceof _PdfFlateStream) {
            this._parseDictionary(value.dictionary);
        }
        else if (Array.isArray(value)) {
            this._parseArray(value); // eslint-disable-line
        }
        else if (value instanceof _PdfReference) {
            var reference = value;
            if (reference) {
                var objectKey = reference.objectNumber + " " + reference.generationNumber;
                if (this._annotationObjects.has(objectKey)) {
                    dictionary.update(key, this._annotationObjects.get(objectKey));
                }
                else if (this._table.has(objectKey)) {
                    var objects = this._table; // eslint-disable-line
                    var object = objects.get(objectKey); // eslint-disable-line
                    if (object instanceof _PdfStream || object instanceof _PdfFlateStream) {
                        this._parseDictionary(object.dictionary);
                        var reference_1 = this._crossReference._getNextReference();
                        this._crossReference._cacheMap.set(reference_1, object);
                        object.dictionary._updated = true;
                        dictionary.update(key, reference_1);
                        objects.set(objectKey, reference_1);
                    }
                    else if (object instanceof _PdfDictionary) {
                        this._parseDictionary(object);
                        var reference_2 = this._crossReference._getNextReference();
                        this._crossReference._cacheMap.set(reference_2, object);
                        dictionary.update(key, reference_2);
                        objects.set(objectKey, reference_2);
                    }
                    else if (object instanceof _PdfReference) {
                        dictionary.update(key, object);
                    }
                    else if (object instanceof _PdfName) {
                        var reference_3 = this._crossReference._getNextReference();
                        this._crossReference._cacheMap.set(reference_3, object);
                        dictionary.update(key, reference_3);
                        objects.set(objectKey, reference_3);
                    }
                    else if (Array.isArray(object)) {
                        this._parseArray(object); // eslint-disable-line
                        var reference_4 = this._crossReference._getNextReference();
                        this._crossReference._cacheMap.set(reference_4, object);
                        dictionary.update(key, reference_4);
                        objects.set(objectKey, reference_4);
                    }
                }
                else {
                    delete dictionary._map[key.toString()];
                }
            }
        }
    };
    /**
     * Parses an array and resolves any contained references into usable objects.
     *
     * @private
     * @param {Array<any>} array Array to inspect and rewrite when necessary.
     * @returns {void}
     */
    _FdfDocument.prototype._parseArray = function (array) {
        if (Array.isArray(array) && array.length > 0) {
            var count = array.length;
            for (var i = 0; i < count; i++) {
                var referenceHolder = array[Number.parseInt(i.toString(), 10)];
                if (referenceHolder && referenceHolder instanceof _PdfReference) {
                    var objectKey = referenceHolder.objectNumber + " " + referenceHolder.generationNumber;
                    if (this._annotationObjects.has(objectKey)) {
                        array[Number.parseInt(i.toString(), 10)] = this._annotationObjects.get(objectKey);
                    }
                    else if (this._table.has(objectKey)) {
                        var objects = this._table; // eslint-disable-line
                        var object = objects.get(objectKey); // eslint-disable-line
                        if (object instanceof _PdfReference) {
                            array[Number.parseInt(i.toString(), 10)] = object;
                        }
                        else if (object instanceof _PdfDictionary || object instanceof _PdfStream) {
                            var reference = this._crossReference._getNextReference();
                            this._crossReference._cacheMap.set(reference, object);
                            array[Number.parseInt(i.toString(), 10)] = reference;
                            objects.set(objectKey, reference);
                        }
                    }
                }
            }
        }
    };
    /**
     * Extracts annotation objects from parsed structures and removes trailer and root bookkeeping.
     *
     * @private
     * @returns {Map<any, any>} Map of annotation keys to their resolved dictionaries.
     */
    _FdfDocument.prototype._parseAnnotationData = function () {
        var objects = new Map(); // eslint-disable-line
        var mappedObjects = new Map(); // eslint-disable-line  
        objects = this._table;
        if (objects !== null && typeof objects !== 'undefined' && objects.size > 0 && objects.has('trailer')) {
            var trailer = objects.get('trailer');
            if (trailer instanceof _PdfDictionary && trailer !== null && typeof trailer !== 'undefined' && trailer.has('Root')) {
                var holder = trailer.getRaw('Root');
                if (holder !== null && typeof holder !== 'undefined') {
                    var rootKey = holder.objectNumber.toString() + ' ' + holder.generationNumber.toString();
                    if (objects.has(rootKey)) {
                        var root = objects.get(rootKey);
                        if (root !== null && typeof root !== 'undefined' && root.has('FDF')) {
                            var fdf = root.get('FDF');
                            if (fdf !== null && typeof fdf !== 'undefined' && fdf.has('Annots')) {
                                var annots = fdf.get('Annots') || [];
                                for (var _i = 0, annots_1 = annots; _i < annots_1.length; _i++) {
                                    var annot = annots_1[_i];
                                    var key = annot.objectNumber + " " + annot.generationNumber;
                                    var storedAnnot = objects.get(key); // eslint-disable-line
                                    if (storedAnnot) {
                                        if (storedAnnot instanceof _PdfDictionary && storedAnnot.has('Parent')
                                            && storedAnnot.has('Subtype')) {
                                            var subtype = storedAnnot.get('Subtype');
                                            var parentReference = storedAnnot._get('Parent');
                                            if (subtype.name === 'Popup' && parentReference.objectNumber === annot.objectNumber) {
                                                continue;
                                            }
                                        }
                                        mappedObjects.set(key, storedAnnot);
                                    }
                                    objects.delete(key);
                                }
                            }
                        }
                    }
                    objects.delete(rootKey);
                }
            }
            objects.delete('trailer');
        }
        return mappedObjects;
    };
    /**
     * Applies imported form values from the table to matching fields in the document form.
     *
     * @private
     * @returns {void}
     */
    _FdfDocument.prototype._importField = function () {
        var _this = this;
        var form = this._document.form;
        var count = form.count;
        if (count) {
            this._table.forEach(function (value, key) {
                var textValue;
                if (_this._table.size > 0 && _this._table.has(key)) {
                    textValue = _this._table.get(key);
                }
                var index = form._getFieldIndex(key);
                if (index !== -1 && index < count) {
                    var field = form.fieldAt(index);
                    if (field) {
                        if (textValue && textValue !== '') {
                            field._dictionary.update('RV', textValue);
                        }
                        var param = [];
                        if (Array.isArray(value)) {
                            param = value;
                        }
                        else {
                            param.push(value);
                        }
                        _this._importFieldData(field, param);
                    }
                }
            });
        }
    };
    //#region Export Annotations
    /**
     * Iterates document pages and collects exportable annotations then writes FDF catalog and annots.
     *
     * @private
     * @param {PdfDocument} document Source document providing pages and annotations.
     * @param {number} pageCount Number of pages to process from the document.
     * @returns {void}
     */
    _FdfDocument.prototype._exportAnnotationData = function (document, pageCount) {
        var genNumber = _StringTokenizer._whiteSpace + '0' + _StringTokenizer._whiteSpace;
        var startDictionary = '<<' + '/';
        this.fdfString += '%FDF-1.2' + '\r\n';
        var index = 2;
        var annot = new Array();
        var appearance = this.exportAppearance;
        for (var i = 0; i < pageCount; i++) {
            var page = document.getPage(i);
            if (page !== null && typeof page !== 'undefined' && page.annotations.count > 0) {
                for (var k = 0; k < page.annotations.count; k++) {
                    var annotation = page.annotations.at(k);
                    if (annotation !== null && typeof annotation !== 'undefined' && !(annotation instanceof PdfFileLinkAnnotation ||
                        annotation instanceof PdfTextWebLinkAnnotation || annotation instanceof PdfDocumentLinkAnnotation)) {
                        if (annotation instanceof PdfPopupAnnotation && annotation._dictionary.has('Parent')) {
                            continue;
                        }
                        if (annotation instanceof PdfRubberStampAnnotation || annotation instanceof PdfRectangleAnnotation) {
                            var value = this._exportAnnotation(annotation, this.fdfString, index, annot, i, true);
                            index = value.index;
                            annot = value.annot;
                        }
                        else {
                            var value = this._exportAnnotation(annotation, this.fdfString, index, annot, i, appearance);
                            index = value.index;
                            annot = value.annot;
                        }
                    }
                }
            }
        }
        if (index !== 2) {
            var root = '1' + genNumber;
            this.fdfString += root + 'obj' + '\r\n' + startDictionary + 'FDF' + startDictionary + 'Annots' + '[';
            for (var i = 0; i < annot.length - 1; i++) {
                this.fdfString += annot[i] + genNumber + 'R' + ' ';
            }
            this.fdfString += annot[annot.length - 1] + genNumber + 'R' + ']' + '/' + 'F' + '(' + this._fileName + ')' + '/' + 'UF' + '(';
            this.fdfString += this._fileName + ')>>' + '/' + 'Type' + '/' + 'Catalog' + '>>' + '\r\n' + 'endobj' + '\r\n';
            this.fdfString += 'trailer' + '\r\n' + startDictionary + 'Root' + ' ' + root + 'R' + '>>' + '\r\n' + '%%EOF' + '\r\n';
        }
    };
    /**
     * Serializes a single annotation and related objects into FDF objects updating indices and references.
     *
     * @private
     * @param {PdfAnnotation} annotation Annotation to export.
     * @param {string} fdfString Current FDF buffer for concatenation.
     * @param {number} index Current object index used for indirect references.
     * @param {string[]} annot Accumulator of annotation object ids.
     * @param {number} pageIndex Page index where the annotation resides.
     * @param {boolean} appearance Indicates whether appearance streams should be included.
     * @returns {_FdfHelper} Helper with updated index annotation ids and queued objects.
     */
    _FdfDocument.prototype._exportAnnotation = function (annotation, fdfString, index, annot, pageIndex, appearance) {
        this.fdfString = fdfString;
        var helper = new _FdfHelper();
        var dictionary = annotation._dictionary;
        var startObject = _StringTokenizer._whiteSpace + '0' + _StringTokenizer._whiteSpace + 'obj' + '\r\n';
        var endObject = '\r\n' + 'endobj' + '\r\n';
        this._annotationID = index.toString();
        this.fdfString += index + startObject + '<<';
        var list = new Map(); // eslint-disable-line
        var streamReference = new Array();
        annot.push(this._annotationID);
        dictionary.set('Page', pageIndex);
        var annotValue = this._getEntries(list, streamReference, index, dictionary, this.fdfString, appearance);
        index = annotValue.index;
        list = annotValue.list;
        streamReference = annotValue.streamReference;
        delete dictionary._map.Page;
        this.fdfString += '>>' + endObject;
        var _loop_2 = function () {
            var keys = Array();
            list.forEach(function (key, value) {
                keys.push(value);
            });
            for (var i = 0; i < keys.length; i++) {
                var key = keys[i];
                if (list.get(key) instanceof _PdfDictionary || list.get(key) instanceof _PdfStream ||
                    list.get(key) instanceof _PdfContentStream || list.get(key) instanceof _PdfFlateStream) {
                    if (list.get(key) instanceof _PdfDictionary) {
                        dictionary = list.get(key);
                    }
                    else {
                        dictionary = list.get(key).dictionary;
                    }
                    if (dictionary !== null && typeof dictionary !== 'undefined') {
                        if (dictionary instanceof _PdfDictionary && dictionary.has('Type')) {
                            var type = dictionary.get('Type');
                            if (type !== null && typeof type !== 'undefined' && type.name === 'Annot') {
                                annot.push(key.toString());
                                dictionary.set('Page', pageIndex);
                            }
                        }
                        this_2.fdfString += key + startObject + '<<';
                        var value = this_2._getEntries(list, streamReference, index, dictionary, this_2.fdfString, appearance);
                        list = value.list;
                        streamReference = value.streamReference;
                        index = value.index;
                        if (dictionary instanceof _PdfDictionary && dictionary.has('Page')) {
                            delete dictionary._map.Page;
                        }
                        this_2.fdfString += '>>';
                        if (streamReference !== null && typeof streamReference !== 'undefined' && streamReference.indexOf(key) !== -1) {
                            this_2._appendStream(list.get(key), this_2.fdfString);
                        }
                        this_2.fdfString += endObject;
                    }
                }
                else if (list.get(key) instanceof _PdfName) {
                    this_2.fdfString += key + startObject;
                    this_2.fdfString += '/' + list.get(key).name;
                    this_2.fdfString += endObject;
                }
                else if (Array.isArray(list.get(key))) {
                    this_2.fdfString += key + startObject;
                    var primitive = list.get(key); // eslint-disable-line
                    var value = this_2._appendArray(primitive, this_2.fdfString, index, appearance, list, streamReference);
                    index = value.index;
                    this_2.fdfString += endObject;
                }
                else if (typeof list.get(key) === 'boolean') {
                    this_2.fdfString += key + startObject;
                    this_2.fdfString += ' ' + ((list.get(key)) ? 'true' : 'false');
                    this_2.fdfString += endObject;
                }
                else if (typeof list.get(key) === 'string') {
                    this_2.fdfString += key + startObject;
                    this_2.fdfString += '(' + this_2._getFormattedString(list.get(key)) + ')';
                    this_2.fdfString += endObject;
                }
                list.delete(key);
            }
        };
        var this_2 = this;
        while (list.size > 0) {
            _loop_2();
        }
        index++;
        helper.index = index;
        helper.annot = annot;
        return helper;
    };
    /**
     * Appends a stream section to the FDF output using content or compressed data as required.
     *
     * @private
     * @param {any} value Stream or stream holder to serialize.
     * @param {string} fdfString Current FDF buffer for concatenation.
     * @returns {void}
     */
    _FdfDocument.prototype._appendStream = function (value, fdfString) {
        var stream = value; // eslint-disable-line
        this.fdfString = fdfString;
        if (value instanceof _PdfFlateStream || value instanceof _PdfStream || value instanceof _PdfContentStream) {
            if (value instanceof _PdfFlateStream) {
                stream = value.stream;
            }
            else {
                stream = value;
            }
        }
        if (value instanceof _PdfFlateStream || value instanceof _PdfStream || value instanceof _PdfContentStream) {
            var byteArray = void 0;
            var compressString = void 0;
            var dataArray = void 0;
            if (value instanceof _PdfContentStream) {
                compressString = stream.getString();
            }
            else if (value instanceof _PdfFlateStream || value instanceof _PdfStream) {
                byteArray = stream.getByteRange(stream.start, stream.end);
                dataArray = new Uint8Array(byteArray);
                compressString = _bytesToString(dataArray);
            }
            else {
                byteArray = stream.getBytes();
                dataArray = new Uint8Array(byteArray);
                var sw = new CompressedStreamWriter();
                sw.write(dataArray, 0, dataArray.length);
                sw.close();
                compressString = sw.getCompressedString;
            }
            this.fdfString += 'stream' + '\r\n';
            this.fdfString += compressString;
            this.fdfString += '\r\n' + 'endstream';
        }
    };
    /**
     * Appends dictionary entries to FDF output and queues nested objects for subsequent emission while tracking streams.
     *
     * @private
     * @param {Map<any, any>} list Queue of pending objects keyed by assigned object id.
     * @param {number[]} streamReference List of object ids that must include a stream body.
     * @param {number} index Current object index used to allocate new ids.
     * @param {_PdfDictionary} dictionary Dictionary to serialize.
     * @param {string} fdfString Current FDF buffer for concatenation.
     * @param {boolean} hasAppearance Indicates whether appearance related entries are allowed.
     * @returns {_FdfHelper} Helper with updated list stream references and index.
     */
    _FdfDocument.prototype._getEntries = function (list, // eslint-disable-line
    streamReference, index, dictionary, fdfString, hasAppearance) {
        var _this = this;
        var flag = false;
        var helper = new _FdfHelper();
        this.fdfString = fdfString;
        var listDictionary = list; // eslint-disable-line
        dictionary.forEach(function (key, value) {
            if (!((!hasAppearance && key === 'AP'))) {
                if (key !== 'P') {
                    _this.fdfString += '/' + key;
                }
                if (key === 'Sound' || key === 'F' || hasAppearance) {
                    flag = true;
                }
                var primitive = value; // eslint-disable-line
                if (typeof primitive === 'string') {
                    _this.fdfString += '(' + _this._getFormattedString(primitive) + ')';
                }
                else if (primitive instanceof _PdfName) {
                    _this.fdfString += '/' + primitive.name;
                }
                else if (primitive instanceof Array) {
                    primitive = primitive; // eslint-disable-line
                    var value_2 = _this._appendArray(primitive, _this.fdfString, index, flag, listDictionary, streamReference);
                    listDictionary = value_2.list;
                    streamReference = value_2.streamReference;
                    index = value_2.index;
                }
                else if (typeof primitive === 'number') {
                    _this.fdfString += ' ' + primitive.toString();
                }
                else if (typeof primitive === 'boolean') {
                    _this.fdfString += ' ' + ((primitive) ? 'true' : 'false');
                }
                else if (primitive instanceof _PdfDictionary) {
                    _this.fdfString += '<<';
                    primitive = primitive;
                    var value_3 = _this._getEntries(listDictionary, streamReference, index, primitive, _this.fdfString, hasAppearance);
                    listDictionary = value_3.list;
                    streamReference = value_3.streamReference;
                    index = value_3.index;
                    _this.fdfString += '>>';
                }
                else if (primitive instanceof _PdfReference) {
                    var pageNumber = dictionary.get('Page');
                    if (key === 'Parent') {
                        _this.fdfString += ' ' + _this._annotationID + ' 0 R';
                        _this.fdfString += '/Page ' + pageNumber;
                    }
                    else if (key === 'IRT') {
                        if (_this._crossReference && _this._crossReference._fetch && primitive) {
                            var inReplyToDictionary = _this._crossReference._fetch(primitive);
                            if (inReplyToDictionary && inReplyToDictionary.has('NM')) {
                                var input = inReplyToDictionary.get('NM');
                                if (input !== null && typeof input !== 'undefined') {
                                    _this.fdfString += '(' + _this._getFormattedString(input) + ')';
                                }
                            }
                        }
                    }
                    else if (key !== 'P') {
                        var holder = primitive;
                        if (holder !== null && typeof holder !== 'undefined') {
                            index++;
                            _this.fdfString += ' ' + index + ' 0 R';
                            if (flag) {
                                streamReference.push(index);
                            }
                            if (!dictionary._crossReference) {
                                dictionary.assignXref(_this._crossReference);
                            }
                            listDictionary.set(index, dictionary.get(key));
                        }
                    }
                }
                flag = false;
            }
        });
        helper.list = listDictionary;
        helper.streamReference = streamReference;
        helper.index = index;
        return helper;
    };
    /**
     * Appends an array to FDF output and captures indirect references for later emission.
     *
     * @private
     * @param {any[]} array Array to serialize.
     * @param {string} fdfString Current FDF buffer for concatenation.
     * @param {number} index Current object index used to allocate new ids.
     * @param {boolean} flag Marks whether contained references should be added to stream tracking.
     * @param {Map<any, any>} list Queue of pending objects keyed by assigned object id.
     * @param {number[]} streamReference List of object ids that must include a stream body.
     * @returns {_FdfHelper} Helper with updated list stream references and index.
     */
    _FdfDocument.prototype._appendArray = function (array, // eslint-disable-line
    fdfString, index, flag, list, // eslint-disable-line
    streamReference) {
        this.fdfString = fdfString;
        this.fdfString += '[';
        var helper = new _FdfHelper();
        var listDictionary = list; // eslint-disable-line
        if (_isNullOrUndefined(array) && array.length > 0) {
            var count = array.length;
            for (var i = 0; i < count; i++) {
                var element = array[i]; // eslint-disable-line
                if (i !== 0 && (typeof element === 'number' || element instanceof _PdfReference || typeof element === 'boolean')) {
                    this.fdfString += ' ';
                }
                var value = this._appendElement(element, this.fdfString, index, flag, listDictionary, streamReference);
                listDictionary = value.list;
                streamReference = value.streamReference;
                index = value.index;
            }
        }
        this.fdfString += ']';
        helper.list = listDictionary;
        helper.streamReference = streamReference;
        helper.index = index;
        return helper;
    };
    /**
     * Appends a single element to FDF output handling names numbers strings arrays dictionaries references and booleans.
     *
     * @private
     * @param {any} element Element to serialize.
     * @param {string} fdfString Current FDF buffer for concatenation.
     * @param {number} index Current object index used to allocate new ids.
     * @param {boolean} flag Marks whether references should be added to stream tracking.
     * @param {Map<any, any>} list Queue of pending objects keyed by assigned object id.
     * @param {number[]} streamReference List of object ids that must include a stream body.
     * @returns {_FdfHelper} Helper with updated list stream references and index.
     */
    _FdfDocument.prototype._appendElement = function (element, // eslint-disable-line
    fdfString, index, flag, list, // eslint-disable-line
    streamReference) {
        this.fdfString = fdfString;
        var helper = new _FdfHelper();
        var listDictionary = list; // eslint-disable-line
        if (typeof element === 'number') {
            this.fdfString += (element).toString();
        }
        else if (element instanceof _PdfName) {
            this.fdfString += '/' + (element.name.toString());
        }
        else if (element instanceof Array) {
            element = element; // eslint-disable-line
            var value = this._appendArray(element, this.fdfString, index, flag, listDictionary, streamReference);
            listDictionary = value.list;
            streamReference = value.streamReference;
            index = value.index;
        }
        else if (element instanceof _PdfDictionary) {
            this.fdfString += '<<';
            element = element;
            var value = this._getEntries(listDictionary, streamReference, index, element, this.fdfString, flag);
            listDictionary = value.list;
            streamReference = value.streamReference;
            index = value.index;
            this.fdfString += '>>';
        }
        else if (element instanceof _PdfReference) {
            var holder = element;
            if (holder !== null && typeof holder !== 'undefined') {
                index++;
                this.fdfString += ' ' + index + ' 0 R';
                if (flag) {
                    streamReference.push(index);
                }
                listDictionary.set(index, this._crossReference._cacheMap.get(element));
            }
        }
        else if (typeof element === 'boolean') {
            this.fdfString += ' ' + ((element) ? 'true' : 'false');
        }
        else if (typeof element === 'string') {
            this.fdfString += '(' + this._getFormattedString(element) + ')';
        }
        helper.list = listDictionary;
        helper.streamReference = streamReference;
        helper.index = index;
        return helper;
    };
    /**
     * Escapes special characters in text for safe inclusion in FDF string literals.
     *
     * @private
     * @param {string} value Input text to sanitize for FDF output.
     * @returns {string} Text with escaped characters for FDF compatibility.
     */
    _FdfDocument.prototype._getFormattedString = function (value) {
        var result = '';
        for (var i = 0; i < value.length; i++) {
            var c = value.charCodeAt(i);
            if (c === 40 || c === 41) {
                result += '\\';
            }
            result += String.fromCharCode(c);
        }
        return result;
    };
    //#endregion
    /**
     * Validates FDF header and toggles specification mode based on special character markers.
     *
     * @private
     * @param {string} element Complete FDF text used to detect header and encoding markers.
     * @returns {void}
     */
    _FdfDocument.prototype._checkFdf = function (element) {
        if (element.includes(this._specialCharacters) || element.includes('âãÃ\u008fÃ\u0093') || element.includes('%����')) {
            this._asPerSpecification = true;
        }
        if (element.startsWith('%')) {
            var token = element.substring(1, 5);
            if (token !== 'FDF-') { // eslint-disable-line
                throw new Error('Invalid FDF file.');
            }
        }
    };
    /**
     * Converts a text value to a hexadecimal string using PDF byte conversion rules.
     *
     * @private
     * @param {string} text Input text to convert into hex representation.
     * @returns {string} Hexadecimal string representing the input text.
     */
    _FdfDocument.prototype._stringToHexString = function (text) {
        var hexString = '';
        if (text !== null && typeof text !== 'undefined' && text.length > 0) {
            var bytesValue = _stringToBytes(text);
            hexString = _byteArrayToHexString(bytesValue);
        }
        return hexString;
    };
    return _FdfDocument;
}(_ExportHelper));
export { _FdfDocument };
/**
 * Holds intermediate data during FDF serialization including queued objects reference indices and annotation id list.
 *
 * @private
 */
var _FdfHelper = /** @class */ (function () {
    function _FdfHelper() {
    }
    return _FdfHelper;
}());
export { _FdfHelper };