@syncfusion/ej2-pdf
Version:
Feature-rich JavaScript PDF library with built-in support for loading and manipulating PDF document.
2,544 lines • 114 kB
JavaScript
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;
}());