@syncfusion/ej2-pdf
Version:
Feature-rich JavaScript PDF library with built-in support for loading and manipulating PDF document.
1,202 lines (1,201 loc) • 46 kB
JavaScript
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 { _PdfAbstractSyntaxElement } from './abstract-syntax';
import { _ConstructionType, _RealEncodingBase, _TagClassType, _UniversalType } from './enumerator';
import { _convertBytesToText } from './utils';
import { _PdfUniqueEncodingElement } from './unique-encoding-element';
import { _stringToBytes } from '../../../utils';
import { _PdfObjectIdentifier } from './identifier-mapping';
/**
* Basic BER/DER encoding element providing encoding/decoding helpers for primitive and constructed types.
*
* @private
*/
var _PdfBasicEncodingElement = /** @class */ (function (_super) {
__extends(_PdfBasicEncodingElement, _super);
/**
* Initializes a basic encoding element with optional tag class, construction, tag number and value.
*
* @private
* @param {_TagClassType} tagClass The tag class for the element.
* @param {_ConstructionType} construction Whether the element is primitive or constructed.
* @param {number} tagNumber The numeric tag number.
* @param {any} value Optional initial value to encode.
*/
function _PdfBasicEncodingElement(tagClass, construction, tagNumber, value) {
if (tagClass === void 0) { tagClass = _TagClassType.universal; }
if (construction === void 0) { construction = _ConstructionType.primitive; }
if (tagNumber === void 0) { tagNumber = _UniversalType.endOfContent; }
if (value === void 0) { value = undefined; }
var _this = _super.call(this) || this;
/**
* Preferred length encoding to use when serializing this element.
*
* @private
*/
_this._lengthEncodingPreference = _EncodingLength.definite;
/**
* Internal storage for this element's value; may be raw bytes or child elements.
*/
_this._value = new Uint8Array(0);
/**
* ASCII code for period `.` used by string parsing helpers.
*
* @private
*/
_this._period = '.'.charCodeAt(0);
/**
* ASCII code for comma `,` used by string parsing helpers.
*
* @private
*/
_this._comma = ','.charCodeAt(0);
/**
* ASCII code for `Z` used by time/date parsing helpers.
*
* @private
*/
_this._z = 'Z'.charCodeAt(0);
/**
* ASCII code for plus `+` used by time/date parsing helpers.
*
* @private
*/
_this._plus = '+'.charCodeAt(0);
/**
* ASCII code for minus `-` used by time/date parsing helpers.
*
* @private
*/
_this._minus = '-'.charCodeAt(0);
_this._encode(value);
_this._tagClass = tagClass;
_this._construction = construction;
_this._setTagNumber(tagNumber);
return _this;
}
/**
* Returns the raw value bytes for this element, encoding constructed content if necessary.
*
* @private
* @returns {Uint8Array} The raw value bytes.
*/
_PdfBasicEncodingElement.prototype._getValue = function () {
if (this._value instanceof Uint8Array) {
return this._value;
}
var bytes = this._encodeSequence(this._value);
this._value = bytes;
return bytes;
};
/**
* Sets the raw value bytes for this element and updates cached length.
*
* @private
* @param {Uint8Array} value The value bytes to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setValue = function (value) {
this._currentValueLength = value.length;
this._value = value;
};
/**
* Decodes and returns the boolean value for this element.
*
* @private
* @returns {boolean} The decoded boolean.
*/
_PdfBasicEncodingElement.prototype._getBooleanValue = function () {
if (this._construction !== _ConstructionType.primitive) {
throw new Error('boolean cannot be constructed.');
}
return this._decodeBoolean(this._getValue());
};
/**
* Encodes and sets a boolean value for this element.
*
* @private
* @param {boolean} value The boolean to set.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setBooleanValue = function (value) {
this._setValue(this._encodeBoolean(value));
};
/**
* Returns the bit string content for this element, concatenating parts if constructed.
*
* @private
* @returns {Uint8ClampedArray} The bit string.
*/
_PdfBasicEncodingElement.prototype._getBitString = function () {
if (this._construction === _ConstructionType.primitive) {
return this._decodeBitString(this._getValue());
}
if ((this._recursionCount + 1) > this._nestingRecursionLimit) {
throw new Error();
}
var appendy = [];
var substrings = this._getSequence();
for (var _i = 0, _a = substrings.slice(0, (substrings.length - 1)); _i < _a.length; _i++) {
var substring = _a[_i];
var value = substring._getValue();
if (substring._construction === _ConstructionType.primitive
&& value.length > 0
&& value[0] !== 0x00) {
throw new Error('Only the final part of a multi-part bit string may start with a non-zero value.');
}
}
for (var _b = 0, substrings_1 = substrings; _b < substrings_1.length; _b++) {
var substring = substrings_1[_b];
if (substring._tagClass !== this._tagClass) {
throw new Error('Invalid tag class in recursively-encoded bit string.');
}
if (substring._getTagNumber() !== this._getTagNumber()) {
throw new Error('Invalid tag class in recursively-encoded bit string.');
}
substring._recursionCount = (this._recursionCount + 1);
appendy.push.apply(appendy, Array.from(substring._getBitString()).map(function (b) { return b !== 0; }));
}
return new Uint8ClampedArray(appendy.map(function (b) { return (b ? 1 : 0); }));
};
/**
* Encodes and sets a bit string value for this element.
*
* @private
* @param {Uint8ClampedArray} value The bit string to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setBitString = function (value) {
this._setValue(this._encodeBitString(value));
};
/**
* Returns the octet string bytes for this element.
*
* @private
* @returns {Uint8Array} The octet string bytes.
*/
_PdfBasicEncodingElement.prototype._getOctetString = function () {
return this._serialize('OCTET STRING');
};
/**
* Sets the octet string value for this element.
*
* @private
* @param {Uint8Array} value The octet string bytes to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setOctetString = function (value) {
this._setValue(new Uint8Array(value));
};
/**
* Returns the object descriptor string for this element.
*
* @private
* @returns {string} The object descriptor.
*/
_PdfBasicEncodingElement.prototype._getObjectDescriptor = function () {
var bytes = this._serialize('ObjectDescriptor');
return this._decodeObjectDescriptor(bytes);
};
/**
* Encodes and sets the object descriptor string for this element.
*
* @private
* @param {string} value The descriptor to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setObjectDescriptor = function (value) {
this._setValue(this._encodeObjectDescriptor(value));
};
/**
* Returns the UTF-8 string value for this element.
*
* @private
* @returns {string} The UTF-8 decoded string.
*/
_PdfBasicEncodingElement.prototype._getUtf8String = function () {
return _convertBytesToText(this._serialize('UTF8String'));
};
/**
* Sets the UTF-8 string value for this element.
*
* @private
* @param {string} value The UTF-8 string to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setUtf8String = function (value) {
this._setValue(_stringToBytes(value));
};
/**
* Returns the sequence child elements for this constructed element.
*
* @private
* @returns {_PdfAbstractSyntaxElement[]} The sequence elements.
*/
_PdfBasicEncodingElement.prototype._getSequence = function () {
if (this._construction !== _ConstructionType.constructed) {
throw new Error('Set or sequence cannot be primitively constructed.');
}
if (Array.isArray(this._value)) {
return this._value;
}
return this._decodedSequence(this._getValue());
};
/**
* Sets the sequence child elements for this element and marks it constructed.
*
* @private
* @param {_PdfAbstractSyntaxElement[]} value The sequence elements to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setSequence = function (value) {
this._construct(value);
this._construction = _ConstructionType.constructed;
};
/**
* Returns the elements for an abstract SET value after uniqueness validation.
*
* @private
* @returns {_PdfAbstractSyntaxElement[]} The set elements.
*/
_PdfBasicEncodingElement.prototype._getAbstractSetValue = function () {
var ret = this._getSequence();
if (!this._isUniquelyTagged(ret)) {
throw new Error('Duplicate tag in Set.');
}
return ret;
};
/**
* Sets the elements for an abstract SET value.
*
* @private
* @param {_PdfAbstractSyntaxElement[]} value The set elements to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setAbstractSetValue = function (value) {
this._setSequence(value);
};
/**
* Returns the elements when this is a `SEQUENCE OF`.
*
* @private
* @returns {_PdfAbstractSyntaxElement[]} The sequence-of elements.
*/
_PdfBasicEncodingElement.prototype._getSequenceOf = function () {
if (this._construction !== _ConstructionType.constructed) {
throw new Error('Set or sequence cannot be primitively constructed.');
}
if (Array.isArray(this._value)) {
return this._value;
}
return this._decodedSequence(this._getValue());
};
/**
* Sets the elements when this is a `SEQUENCE OF` and marks constructed.
*
* @private
* @param {_PdfAbstractSyntaxElement[]} value The elements to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setSequenceOf = function (value) {
this._construct(value);
this._construction = _ConstructionType.constructed;
};
/**
* Returns the elements when this is a `SET OF`.
*
* @private
* @returns {_PdfAbstractSyntaxElement[]} The set-of elements.
*/
_PdfBasicEncodingElement.prototype._getAbstractSetOf = function () {
return this._getSequence();
};
/**
* Sets the elements when this is a `SET OF`.
*
* @private
* @param {_PdfAbstractSyntaxElement[]} value The elements to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setAbstractSetOf = function (value) {
this._setSequence(value);
};
/**
* Returns the numeric string value for this element.
*
* @private
* @returns {string} The numeric string.
*/
_PdfBasicEncodingElement.prototype._getNumericString = function () {
var bytes = this._serialize('NumericString');
return this._decodeNumericString(bytes);
};
/**
* Sets the numeric string value for this element.
*
* @private
* @param {string} value The numeric string to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setNumericString = function (value) {
this._setValue(this._encodeNumericString(value));
};
/**
* Returns the printable string value for this element.
*
* @private
* @returns {string} The printable string.
*/
_PdfBasicEncodingElement.prototype._getPrintableString = function () {
var bytes = this._serialize('PrintableString');
return this._decodePrintableString(bytes);
};
/**
* Sets the printable string value for this element.
*
* @private
* @param {string} value The printable string to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setPrintableString = function (value) {
this._setValue(this._encodePrintableString(value));
};
/**
* Returns teleprinter (Teletex) text bytes for this element.
*
* @private
* @returns {Uint8Array} The teleprinter bytes.
*/
_PdfBasicEncodingElement.prototype._getTeleprinterText = function () {
return this._serialize('TeletexString');
};
/**
* Sets teleprinter (Teletex) text bytes for this element.
*
* @private
* @param {Uint8Array} value The bytes to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setTeleprinterText = function (value) {
this._setValue(new Uint8Array(value));
};
/**
* Returns videotex information bytes for this element.
*
* @private
* @returns {Uint8Array} The videotex bytes.
*/
_PdfBasicEncodingElement.prototype._getVideoTextInformation = function () {
return this._serialize('VideotexString');
};
/**
* Sets videotex information bytes for this element.
*
* @private
* @param {Uint8Array} value The bytes to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setVideoTextInformation = function (value) {
this._setValue(new Uint8Array(value));
};
/**
* Returns the IA5 (international alphabet) string for this element.
*
* @private
* @returns {string} The IA5 string.
*/
_PdfBasicEncodingElement.prototype._getInternationalAlphabetString = function () {
return _convertBytesToText(this._serialize('IA5String'));
};
/**
* Sets the IA5 (international alphabet) string for this element.
*
* @private
* @param {string} value The IA5 string to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setInternationalAlphabetString = function (value) {
this._setValue(_stringToBytes(value));
};
/**
* Returns the graphic string for this element.
*
* @private
* @returns {string} The graphic string.
*/
_PdfBasicEncodingElement.prototype._getGraphicString = function () {
var bytes = this._serialize('GraphicString');
return this._decodeGraphicString(bytes);
};
/**
* Sets the graphic string value for this element.
*
* @private
* @param {string} value The graphic string to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setGraphicString = function (value) {
this._setValue(this._encodeGraphicString(value));
};
/**
* Returns the visible string for this element.
*
* @private
* @returns {string} The visible string.
*/
_PdfBasicEncodingElement.prototype._getVisibleString = function () {
return this._decodeVisibleString(this._getValue());
};
/**
* Sets the visible string value for this element.
*
* @private
* @param {string} value The visible string to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setVisibleString = function (value) {
this._setValue(this._encodeVisibleString(value));
};
/**
* Returns the general string for this element.
*
* @private
* @returns {string} The general string.
*/
_PdfBasicEncodingElement.prototype._getGeneralString = function () {
var bytes = this._serialize('GeneralString');
return this._decodeGeneralString(bytes);
};
/**
* Sets a character string (complex structure) for this element and marks it constructed.
*
* @private
* @param {_PdfCharacterString} value The character string structure to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setCharacterString = function (value) {
this._setValue(this._encodeCharacterString(value));
this._construction = _ConstructionType.constructed;
};
/**
* Returns the universal (UTF-32) string for this element.
*
* @private
* @returns {string} The universal string.
*/
_PdfBasicEncodingElement.prototype._getUniversalString = function () {
var valueBytes = this._serialize('UniversalString');
if (valueBytes.length % 4) {
throw new Error('Unicode string encoded on non-mulitple of four bytes.');
}
var ret = '';
for (var i = 0; i < valueBytes.length; i += 4) {
ret += String.fromCharCode((valueBytes[i + 0] << 24)
+ (valueBytes[i + 1] << 16)
+ (valueBytes[i + 2] << 8)
+ (valueBytes[i + 3] << 0));
}
return ret;
};
/**
* Sets the universal (UTF-32) string for this element.
*
* @private
* @param {string} value The universal string to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setUniversalString = function (value) {
var buf = new Uint8Array(value.length << 2);
for (var i = 0; i < value.length; i++) {
buf[(i << 2)] = value.charCodeAt(i) >>> 24;
buf[(i << 2) + 1] = value.charCodeAt(i) >>> 16;
buf[(i << 2) + 2] = value.charCodeAt(i) >>> 8;
buf[(i << 2) + 3] = value.charCodeAt(i);
}
this._setValue(buf);
};
/**
* Returns the BMP (UTF-16BE) string for this element.
*
* @private
* @returns {string} The BMP string.
*/
_PdfBasicEncodingElement.prototype._getBmpString = function () {
var valueBytes = this._serialize('BMPString');
if (valueBytes.length % 2 !== 0) {
throw new Error('BMPString encoded on non-multiple of two bytes.');
}
if (typeof TextDecoder !== 'undefined') {
return new TextDecoder('utf-16be').decode(valueBytes);
}
var result = '';
for (var i = 0; i < valueBytes.length; i += 2) {
var code = (valueBytes[i] << 8) | valueBytes[i + 1];
result += String.fromCharCode(code);
}
return result;
};
/**
* Sets the BMP (UTF-16BE) string for this element.
*
* @private
* @param {string} value The BMP string to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setBmpString = function (value) {
var buf = new Uint8Array(value.length << 1);
for (var i = 0; i < value.length; i++) {
var code = value.charCodeAt(i);
buf[i << 1] = code >>> 8;
buf[(i << 1) + 1] = code & 0xff;
}
this._setValue(buf);
};
/**
* Constructs this element from provided child elements (internal helper).
*
* @private
* @param {_PdfAbstractSyntaxElement[]} els The child elements to set.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._construct = function (els) {
this._currentValueLength = undefined;
this._value = els;
};
/**
* Encodes a JavaScript value into the appropriate ASN.1 encoding for this element.
*
* @private
* @param {any} value The value to encode.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._encode = function (value) {
switch (typeof value) {
case ('undefined'): {
this._setValue(new Uint8Array(0));
break;
}
case ('boolean'): {
this._setTagNumber(_UniversalType.abstractSyntaxBoolean);
this._setBooleanValue(value);
break;
}
case ('number'): {
if (Number.isInteger(value)) {
this._setTagNumber(_UniversalType.integer);
this._setInteger(value);
}
break;
}
case ('string'): {
this._setTagNumber(_UniversalType.utf8String);
this._setUtf8String(value);
break;
}
case ('object'): {
if (!value) {
this._setTagNumber(_UniversalType.nullValue);
this._setValue(new Uint8Array(0));
}
else if (value instanceof Uint8Array) {
this._setTagNumber(_UniversalType.octetString);
this._setOctetString(value);
}
else if (value instanceof Uint8ClampedArray) {
this._setTagNumber(_UniversalType.bitString);
this._setBitString(value);
}
else if (value instanceof _PdfAbstractSyntaxElement) {
this._construction = _ConstructionType.constructed;
this._setSequence([value]);
}
else if (Array.isArray(value) &&
value.every(function (v) { return v instanceof _PdfAbstractSyntaxElement; })) {
this._construction = _ConstructionType.constructed;
this._setAbstractSetValue(Array.from(value));
}
else if (value instanceof _PdfObjectIdentifier) {
this._setTagNumber(_UniversalType.objectIdentifier);
this._setObjectIdentifier(value);
}
else if (Array.isArray(value)) {
this._construction = _ConstructionType.constructed;
this._setTagNumber(_UniversalType.sequence);
this._setSequence(value.map(function (sub) {
var ret = new _PdfBasicEncodingElement();
ret._encode(sub);
return ret;
}));
}
else {
throw new Error("Cannot encode value of type " + value.constructor.name + ".");
}
break;
}
default: {
throw new Error("Cannot encode value of type " + typeof value + ".");
}
}
};
/**
* Wraps a BER sequence into a `_PdfBasicEncodingElement` representing a SEQUENCE.
*
* @private
* @param {_PdfAbstractSyntaxElement[]} sequence The sequence elements.
* @returns {_PdfBasicEncodingElement} The constructed sequence element.
*/
_PdfBasicEncodingElement.prototype._fromBerSequence = function (sequence) {
var ret = new _PdfBasicEncodingElement(_TagClassType.universal, _ConstructionType.constructed, _UniversalType.sequence);
ret._setSequence(sequence.filter(function (element) { return Boolean(element); }));
return ret;
};
/**
* Wraps a set of basic encoding elements into a `_PdfBasicEncodingElement` representing a SET.
*
* @private
* @param {_PdfBasicEncodingElement[]} set The set elements.
* @returns {_PdfBasicEncodingElement} The constructed set element.
*/
_PdfBasicEncodingElement.prototype._fromSet = function (set) {
var ret = new _PdfBasicEncodingElement(_TagClassType.universal, _ConstructionType.constructed, _UniversalType.abstractSyntaxSet);
ret._setAbstractSetValue(set.filter(function (element) { return Boolean(element); }));
return ret;
};
/**
* Wraps a set-of elements into a `_PdfBasicEncodingElement` representing a SET OF.
*
* @private
* @param {_PdfBasicEncodingElement[]} set The set-of elements.
* @returns {_PdfBasicEncodingElement} The constructed set-of element.
*/
_PdfBasicEncodingElement.prototype._fromSetOf = function (set) {
var ret = new _PdfBasicEncodingElement(_TagClassType.universal, _ConstructionType.constructed, _UniversalType.abstractSyntaxSet);
ret._setAbstractSetOf(set.filter(function (element) { return Boolean(element); }));
return ret;
};
/**
* Retrieves the single inner element for explicitly-encoded elements.
*
* @private
* @param {boolean} [isCertificateParsing] Optional flag to relax trailing bytes validation.
* @returns {_PdfAbstractSyntaxElement} The inner element.
*/
_PdfBasicEncodingElement.prototype._getInner = function (isCertificateParsing) {
if (this._construction !== _ConstructionType.constructed) {
throw new Error('An explicitly-encoded element cannot be encoded using '
+ 'primitive construction.');
}
if (Array.isArray(this._value)) {
if (this._value.length !== 1) {
throw new Error("An explicitly-encoding element contained " + this._value.length + " encoded elements.");
}
return this._value[0];
}
var ret = new _PdfBasicEncodingElement();
var readBytes = ret._fromBytes(this._value);
if (readBytes !== this._value.length && !isCertificateParsing) {
throw new Error('An explicitly-encoding element contained more than one single '
+ 'encoded element. The tag number of the first decoded '
+ ("element was " + ret._getTagNumber() + ", and it was encoded on ")
+ (readBytes + " bytes."));
}
return ret;
};
/**
* Sets a single inner element and marks this element as constructed.
*
* @private
* @param {_PdfAbstractSyntaxElement} value The inner element to assign.
* @returns {void} nothing.
*/
_PdfBasicEncodingElement.prototype._setInner = function (value) {
this._construction = _ConstructionType.constructed;
this._value = [value];
};
/**
* Decodes this element from the provided BER/DER bytes and returns number of bytes consumed.
*
* @private
* @param {Uint8Array} bytes The input bytes to decode.
* @returns {number} The number of bytes consumed.
*/
_PdfBasicEncodingElement.prototype._fromBytes = function (bytes) {
if (bytes.length < 2) {
throw new Error('Tried to decode a BER element that is less than two bytes.');
}
if ((this._recursionCount + 1) > this._nestingRecursionLimit) {
throw new Error();
}
var cursor = 0;
var first = bytes[cursor];
switch (first & 192) {
case 0:
this._tagClass = _TagClassType.universal;
break;
case 64:
this._tagClass = _TagClassType.application;
break;
case 128:
this._tagClass = _TagClassType.context;
break;
case 192:
this._tagClass = _TagClassType.abstractSyntaxPrivate;
break;
}
this._construction = ((first & 32) ? _ConstructionType.constructed : _ConstructionType.primitive);
var tagNumber = (first & 31);
cursor++;
if (tagNumber >= 31) {
if (cursor >= bytes.length) {
throw new Error('ASN1 tag number appears to have been truncated.');
}
if (bytes[cursor] === 0x80) {
throw new Error('Leading padding byte on long tag number encoding.');
}
tagNumber = 0;
var running = true;
while (running) {
if (cursor >= bytes.length) {
throw new Error('ASN1 tag number appears to have been truncated.');
}
var b = bytes[cursor++];
tagNumber = (tagNumber << 7) | (b & 0x7F);
if ((b & 0x80) === 0) {
running = false;
}
}
if (tagNumber < 31) {
throw new Error('ASN1 tag number could have been encoded in short form.');
}
this._setTagNumber(tagNumber);
}
else {
this._setTagNumber(tagNumber);
}
if (cursor >= bytes.length) {
throw new Error('Element length bytes appear to have been truncated.');
}
var lenOctet = bytes[cursor++];
if ((lenOctet & 0x80) === 0x80) {
var numberOfLengthOctets = (lenOctet & 0x7F);
if (numberOfLengthOctets === 0) {
if (this._construction !== _ConstructionType.constructed) {
throw new Error('Invalid format: indefinite-length elements must be constructed, not primitive.');
}
var startOfValue = cursor;
var sentinel = cursor;
while (sentinel < bytes.length) {
var child = new _PdfBasicEncodingElement();
sentinel += child._fromBytes(bytes.subarray(sentinel));
if (child._tagClass === _TagClassType.universal
&& child._construction === _ConstructionType.primitive
&& child._getTagNumber() === _UniversalType.endOfContent
&& child._getValue().length === 0) {
break;
}
}
if (sentinel === bytes.length &&
(bytes[sentinel - 1] !== 0x00 || bytes[sentinel - 2] !== 0x00)) {
throw new Error('Invalid format: indefinite-length ASN1 elements must end with an End-of-Content marker');
}
this._setValue(bytes.slice(startOfValue, sentinel - 2));
return sentinel;
}
var n = numberOfLengthOctets;
if (cursor + n > bytes.length) {
throw new Error('Element length bytes appear to have been truncated.');
}
var length_1 = 0;
for (var i = 0; i < n; i++) {
length_1 = (length_1 << 8) | bytes[cursor + i];
}
cursor += n;
if ((cursor + length_1) < cursor) {
throw new Error('ASN1 element too large.');
}
if ((cursor + length_1) > bytes.length) {
throw new Error('ASN1 element truncated.');
}
this._setValue(bytes.slice(cursor, cursor + length_1));
return cursor + length_1;
}
else {
var length_2 = (lenOctet & 0x7F);
if ((cursor + length_2) > bytes.length) {
throw new Error('ASN1 element was truncated.');
}
this._setValue(bytes.slice(cursor, cursor + length_2));
return cursor + length_2;
}
};
/**
* Computes the number of bytes required to encode the length field for a value length.
*
* @private
* @param {number} [valueLength] Optional explicit value length to evaluate.
* @returns {number} The number of bytes used to encode the length.
*/
_PdfBasicEncodingElement.prototype._lengthLength = function (valueLength) {
if (this._lengthEncodingPreference === _EncodingLength.indefinite) {
return 1;
}
var len = (valueLength !== null && typeof valueLength !== 'undefined')
? valueLength
: this._valueLength();
if (len < 128) {
return 1;
}
var n = 0;
var v = len;
while (v > 0) {
n++;
v >>>= 8;
}
return 1 + n;
};
/**
* Returns the length in bytes of this element's encoded value.
*
* @private
* @returns {number} The value length.
*/
_PdfBasicEncodingElement.prototype._valueLength = function () {
if (typeof this._currentValueLength !== 'undefined' && this._currentValueLength !== null && !Array.isArray(this._value)) {
return this._currentValueLength;
}
if (!Array.isArray(this._value)) {
var value = this._getValue();
this._currentValueLength = value.length;
return value.length;
}
var len = 0;
for (var _i = 0, _a = this._value; _i < _a.length; _i++) {
var element = _a[_i];
len += element._tagValueLength(); // eslint-disable-line
}
this._currentValueLength = len;
return len;
};
/**
* Produces the tag and length bytes for this element.
*
* @private
* @returns {Uint8Array} The tag-and-length bytes.
*/
_PdfBasicEncodingElement.prototype._tagAndLengthBytes = function () {
var tagBytes = [0x00];
tagBytes[0] |= (this._tagClass << 6);
tagBytes[0] |= ((this._lengthEncodingPreference === _EncodingLength.indefinite) ||
this._construction === _ConstructionType.constructed) ? (1 << 5) : 0;
var tagNumber = this._getTagNumber();
if (tagNumber < 31) {
tagBytes[0] |= tagNumber;
}
else {
tagBytes[0] |= 31;
var number = tagNumber;
var encodedNumber = [];
do {
encodedNumber.unshift(number & 0x7F);
number >>>= 7;
} while (number > 0);
for (var i = 0; i < encodedNumber.length - 1; i++) {
encodedNumber[i] |= 0x80;
}
tagBytes.push.apply(tagBytes, encodedNumber);
}
var lengthOctets = [];
var valueLen = this._valueLength();
switch (this._lengthEncodingPreference) {
case _EncodingLength.definite: {
if (valueLen < 128) {
lengthOctets = [valueLen];
}
else {
var octets = [];
var v = valueLen;
while (v > 0) {
octets.unshift(v & 0xff);
v >>>= 8;
}
lengthOctets = [0x80 | octets.length].concat(octets);
}
break;
}
case _EncodingLength.indefinite: {
lengthOctets = [0x80];
break;
}
default:
throw new Error('An unsupported length encoding preference was detected');
}
var ret = new Uint8Array(tagBytes.length + lengthOctets.length);
ret.set(tagBytes, 0);
ret.set(lengthOctets, tagBytes.length);
return ret;
};
/**
* Serializes this element into an array of buffers representing tag, length and value parts.
*
* @private
* @returns {Uint8Array[]} Array of buffers.
*/
_PdfBasicEncodingElement.prototype._toBuffers = function () {
var buffers = [];
buffers.push(this._tagAndLengthBytes());
if (Array.isArray(this._value)) {
for (var _i = 0, _a = this._value; _i < _a.length; _i++) {
var el = _a[_i];
var subBuffers = el._toBuffers();
for (var _b = 0, subBuffers_1 = subBuffers; _b < subBuffers_1.length; _b++) {
var buf = subBuffers_1[_b];
buffers.push(buf);
}
}
}
else {
buffers.push(this._value);
}
if (this._lengthEncodingPreference === _EncodingLength.indefinite) {
buffers.push(new Uint8Array(2));
}
return buffers;
};
/**
* Serializes constructed content into a single value buffer for the provided data type hint.
*
* @private
* @param {string} dataType A textual hint for the data type being serialized.
* @returns {Uint8Array} The serialized value bytes.
*/
_PdfBasicEncodingElement.prototype._serialize = function (dataType) {
if (this._construction === _ConstructionType.primitive) {
return this._getValue();
}
if ((this._recursionCount + 1) > this._nestingRecursionLimit) {
throw new Error("Exceeded recursion limit while deconstructing " + dataType);
}
var parts = [];
var substrings = this._getSequence();
var parentTag = this._getTagNumber();
for (var _i = 0, substrings_2 = substrings; _i < substrings_2.length; _i++) {
var substring = substrings_2[_i];
if (parentTag === _UniversalType.octetString) {
if (substring._tagClass !== _TagClassType.universal ||
substring._getTagNumber() !== _UniversalType.octetString) {
throw new Error('Invalid constructed OCTET STRING: children must be OCTET STRING (tag 4).');
}
}
else {
if (substring._tagClass !== this._tagClass ||
substring._getTagNumber() !== parentTag) {
throw new Error("Invalid constructed " + dataType + ": children must be of the same type as the parent.");
}
}
substring._recursionCount = this._recursionCount + 1;
parts.push(substring._getValue());
}
var totalLength = parts.reduce(function (sum, part) { return sum + part.length; }, 0);
var result = new Uint8Array(totalLength);
var offset = 0;
for (var _a = 0, parts_1 = parts; _a < parts_1.length; _a++) {
var part = parts_1[_a];
result.set(part, offset);
offset += part.length;
}
return result;
};
/**
* Returns the component elements when this element represents constructed content.
*
* @private
* @returns {_PdfAbstractSyntaxElement[]} Component elements.
*/
_PdfBasicEncodingElement.prototype._getComponents = function () {
if (Array.isArray(this._value)) {
return this._value;
}
var encodedElements = [];
var i = 0;
while (i < this._value.length) {
var next = new _PdfBasicEncodingElement();
i += next._fromBytes(this._getValue().subarray(i));
encodedElements.push(next);
}
return encodedElements;
};
/**
* Decodes an ASN.1 BIT STRING from bytes into a bit array.
*
* @private
* @param {Uint8Array} value The bytes to decode.
* @returns {Uint8ClampedArray} The decoded bit array.
*/
_PdfBasicEncodingElement.prototype._decodeBitString = function (value) {
if (value.length === 0) {
throw new Error('ASN1 Bit String cannot be encoded on zero bytes!');
}
if (value.length === 1 && value[0] !== 0) {
throw new Error('ASN1 Bit String encoded with deceptive first byte!');
}
if (value[0] > 7) {
throw new Error('First byte of an ASN1 Bit String must be <= 7!');
}
var ret = [];
for (var i = 1; i < value.length; i++) {
var values = value[i];
ret.push(((values & 128) ? 1 : 0), ((values & 64) ? 1 : 0), ((values & 32) ? 1 : 0), ((values & 16) ? 1 : 0), ((values & 8) ? 1 : 0), ((values & 4) ? 1 : 0), ((values & 2) ? 1 : 0), ((values & 1) ? 1 : 0));
}
ret.length -= value[0];
return new Uint8ClampedArray(ret);
};
/**
* Decodes an ASN.1 BOOLEAN from bytes.
*
* @private
* @param {Uint8Array} value The bytes to decode.
* @returns {boolean} The decoded boolean.
*/
_PdfBasicEncodingElement.prototype._decodeBoolean = function (value) {
if (value.length !== 1) {
throw new Error('Invalid Boolean format: Boolean values must be exactly one byte.');
}
return (value[0] !== 0);
};
/**
* Decodes a sequence of basic encoding elements from bytes.
*
* @private
* @param {Uint8Array} value The bytes to decode.
* @returns {_PdfBasicEncodingElement[]} Decoded elements.
*/
_PdfBasicEncodingElement.prototype._decodedSequence = function (value) {
var encodedElements = [];
var i = 0;
while (i < value.length) {
var next = new _PdfBasicEncodingElement();
i += next._fromBytes(value.subarray(i));
encodedElements.push(next);
}
return encodedElements;
};
/**
* Determines the numeric base used for REAL encoding from a control byte.
*
* @private
* @param {number} byte The control byte to inspect.
* @returns {number} The numeric base (2, 8, or 16).
*/
_PdfBasicEncodingElement.prototype._getRealEncodingBase = function (byte) {
switch (byte & 48) {
case _RealEncodingBase.base2:
return 2;
case _RealEncodingBase.base8:
return 8;
case _RealEncodingBase.base16:
return 16;
default:
throw new Error('Impossible real encoding base encountered.');
}
};
/**
* Encodes a `_PdfCharacterString` structure into bytes suitable for this element.
*
* @private
* @param {_PdfCharacterString} value The character-string structure to encode.
* @returns {Uint8Array} The encoded bytes.
*/
_PdfBasicEncodingElement.prototype._encodeCharacterString = function (value) {
return this._encodeSequence([
value._identification,
new _PdfUniqueEncodingElement(_TagClassType.universal, _ConstructionType.primitive, _UniversalType.octetString, value._stringValue)
]);
};
/**
* Decodes a sequence of unique encoding elements from bytes.
*
* @private
* @param {Uint8Array} value The bytes to decode.
* @returns {_PdfUniqueEncodingElement[]} Decoded unique elements.
*/
_PdfBasicEncodingElement.prototype._decodeSequence = function (value) {
if (value.length === 0) {
return [];
}
var encodedElements = [];
var i = 0;
while (i < value.length) {
var next = new _PdfUniqueEncodingElement();
var consumed = next._fromBytes(value.subarray(i));
i += consumed;
encodedElements.push(next);
}
return encodedElements;
};
/**
* Wraps a sequence of abstract syntax elements into a unique encoding element.
*
* @private
* @param {_PdfAbstractSyntaxElement[]} sequence The sequence elements.
* @returns {_PdfUniqueEncodingElement} The wrapped unique element.
*/
_PdfBasicEncodingElement.prototype._fromSequence = function (sequence) {
var ret = new _PdfUniqueEncodingElement(_TagClassType.universal, _ConstructionType.constructed, _UniversalType.sequence);
ret._setSequence(sequence.filter(function (element) { return Boolean(element); }));
return ret;
};
/**
* Returns the external encoding represented by an encoding element, mapping tag options to values.
*
* @private
* @param {_PdfAbstractSyntaxElement} encodingElement The encoding element describing the external.
* @returns {_PdfAbstractSyntaxElement|Uint8Array|Uint8ClampedArray} The decoded external payload.
*/
_PdfBasicEncodingElement.prototype._getExternalEncoding = function (encodingElement) {
switch (encodingElement._getTagNumber()) {
case 0:
return encodingElement._getInner();
case 1:
return encodingElement._getOctetString();
case 2:
return encodingElement._getBitString();
default:
throw new Error('external does not know of an encoding option ' +
("having tag number " + encodingElement._getTagNumber() + "."));
}
};
/**
* Returns the total encoded length of this element including tag and length fields.
*
* @private
* @returns {number} The combined tag+length+value length in bytes.
*/
_PdfBasicEncodingElement.prototype._tagValueLength = function () {
var value = this._valueLength();
return this._tagLength() + this._lengthLength(value) + value;
};
return _PdfBasicEncodingElement;
}(_PdfAbstractSyntaxElement));
export { _PdfBasicEncodingElement };
/**
* Represents the internal encoding length type used for defining whether the structure
* uses definite or indefinite length encoding.
*
* @private
*/
export var _EncodingLength;
(function (_EncodingLength) {
_EncodingLength[_EncodingLength["definite"] = 0] = "definite";
_EncodingLength[_EncodingLength["indefinite"] = 1] = "indefinite";
})(_EncodingLength || (_EncodingLength = {}));