@syncfusion/ej2-pdf
Version:
Feature-rich JavaScript PDF library with built-in support for loading and manipulating PDF document.
693 lines (692 loc) • 26.6 kB
JavaScript
import { _InflaterState, _BlockType } from './enum';
import { _DecompressedOutput } from './decompressed-output';
import { _InBuffer } from './in-buffer';
import { _HuffmanTree } from './huffman-tree';
import { _toUnsigned, _copyRange } from './../utils';
/* eslint-disable */
/**
* Implements a deflate decompressor with bitwise parsing and Huffman decoding.
*
* @private
*/
var _Inflater = /** @class */ (function () {
function _Inflater() {
/**
* Extra bits required for each length code.
*
* @private
*/
this._extraLengthBits = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0];
/**
* Precomputed lookup table for static distance Huffman codes.
*
* @private
*/
this._staticDistanceTreeTable = [0x00, 0x10, 0x08, 0x18, 0x04, 0x14, 0x0c, 0x1c, 0x02, 0x12, 0x0a, 0x1a, 0x06, 0x16, 0x0e, 0x1e,
0x01, 0x11, 0x09, 0x19, 0x05, 0x15, 0x0d, 0x1d, 0x03, 0x13, 0x0b, 0x1b, 0x07, 0x17, 0x0f, 0x1f];
/**
* Base lengths corresponding to each length code.
*
* @private
*/
this._lengthBase = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115,
131, 163, 195, 227, 258];
/**
* Base distances and positions for distance codes.
*
* @private
*/
this._distanceBasePosition = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049,
3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0];
/**
* Ordering of code length code indices used during tree construction.
*
* @private
*/
this._codeOrder = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
this._bfinal = 0;
this._bLength = 0;
this._blBuffer = [0, 0, 0, 0];
this._blockType = _BlockType.unCompressedType;
this._caSize = 0;
this._clCodeCount = 0;
this._extraBits = 0;
this._lengthCode = 0;
this._length = 0;
this._llCodeCount = 0;
this._output = new _DecompressedOutput();
this._input = new _InBuffer();
this._loopCounter = 0;
this._codeList = Array(_HuffmanTree._maxLengthTree + _HuffmanTree._maxDepthTree).fill(0);
this._cltcl = Array(_HuffmanTree._nCLength).fill(0);
this._inflaterState = _InflaterState.readingBFinal;
}
Object.defineProperty(_Inflater.prototype, "_finished", {
/**
* Indicates whether the decompressor has reached the final block or completed state.
*
* @private
*/
get: function () {
return this._inflaterState === _InflaterState.done || this._inflaterState === _InflaterState.vFooter;
},
enumerable: true,
configurable: true
});
/**
* Sets the input byte range for the decompressor to read from.
*
* @private
*/
_Inflater.prototype._setInput = function (inputBytes, offset, length) {
this._input._setInput(inputBytes, offset, length);
};
/**
* Produces decompressed bytes into the target array until the request is satisfied or input ends.
*
* @private
*/
_Inflater.prototype._inflate = function (bytes, offset, length) {
var i = 0;
do {
var result = this._output._copyTo(bytes, offset, length);
var copied = result.count;
bytes = result.data;
if (copied > 0) {
offset += copied;
i += copied;
length -= copied;
}
if (length === 0) {
break;
}
} while (!this._finished && this._decode());
return { 'count': i, 'data': bytes };
};
/**
* Advances the state machine to process the next block or symbols and reports progress.
*
* @private
*/
_Inflater.prototype._decode = function () {
var eob = false;
var result = false;
if (this._finished) {
return true;
}
if (this._inflaterState === _InflaterState.readingBFinal) {
if (!this._input._availableBits(1)) {
return false;
}
this._bfinal = this._input._getBits(1);
this._inflaterState = _InflaterState.readingBType;
}
if (this._inflaterState === _InflaterState.readingBType) {
if (!this._input._availableBits(2)) {
this._inflaterState = _InflaterState.readingBType;
return false;
}
this._blockType = this._getBlockType(this._input._getBits(2));
if (this._blockType === _BlockType.dynamicType) {
this._inflaterState = _InflaterState.readingNlCodes;
}
else if (this._blockType === _BlockType.staticType) {
this._llTree = new _HuffmanTree();
this._llTree._loadTree(true);
this._distanceTree = new _HuffmanTree();
this._distanceTree._loadTree(false);
this._inflaterState = _InflaterState.decodeTop;
}
else if (this._blockType === _BlockType.unCompressedType) {
this._inflaterState = _InflaterState.unCompressedAligning;
}
}
if (this._blockType === _BlockType.dynamicType) {
if (this._getInflaterStateValue(this._inflaterState) < this._getInflaterStateValue(_InflaterState.decodeTop)) {
result = this._decodeDynamicBlockHeader();
}
else {
var returnedValue = this._decodeBlock(eob);
result = returnedValue.result;
eob = returnedValue.eob;
this._output = returnedValue.output;
}
}
else if (this._blockType === _BlockType.staticType) {
var returnedValue = this._decodeBlock(eob);
result = returnedValue.result;
eob = returnedValue.eob;
this._output = returnedValue.output;
}
else if (this._blockType === _BlockType.unCompressedType) {
var returnedValue = this._decodeUncompressedBlock(eob);
result = returnedValue.result;
eob = returnedValue.eob;
this._output = returnedValue.output;
}
if (eob && (this._bfinal !== 0)) {
this._inflaterState = _InflaterState.done;
}
return result;
};
/**
* Processes an uncompressed block and copies bytes directly into the output buffer.
*
* @private
*/
_Inflater.prototype._decodeUncompressedBlock = function (endBlock) {
endBlock = false;
while (true) {
switch (this._inflaterState) {
case _InflaterState.unCompressedAligning:
this._input._skipByteBoundary();
this._inflaterState = _InflaterState.unCompressedByte1;
if (!this._unCompressedByte()) {
return { 'result': false, 'eob': endBlock, 'output': this._output };
}
break;
case _InflaterState.unCompressedByte1:
case _InflaterState.unCompressedByte2:
case _InflaterState.unCompressedByte3:
case _InflaterState.unCompressedByte4:
if (!this._unCompressedByte()) {
return { 'result': false, 'eob': endBlock, 'output': this._output };
}
break;
case _InflaterState.decodeUnCompressedBytes:
this._bLength -= this._output._copyFrom(this._input, this._bLength);
if (this._bLength === 0) {
this._inflaterState = _InflaterState.readingBFinal;
endBlock = true;
return { 'result': true, 'eob': endBlock, 'output': this._output };
}
if (this._output._unusedBytes === 0) {
return { 'result': true, 'eob': endBlock, 'output': this._output };
}
return { 'result': false, 'eob': endBlock, 'output': this._output };
default:
break;
}
}
};
/**
* Reads the next header byte for an uncompressed block and updates length fields.
*
* @private
*/
_Inflater.prototype._unCompressedByte = function () {
var bits = this._input._getBits(8);
if (bits < 0) {
return false;
}
var inflaterstate = this._getInflaterStateValue(this._inflaterState);
var unCompressedByte = this._getInflaterStateValue(_InflaterState.unCompressedByte1);
this._blBuffer[inflaterstate - unCompressedByte] = _toUnsigned(bits, 8);
if (this._inflaterState === _InflaterState.unCompressedByte4) {
this._bLength = this._blBuffer[0] + (this._blBuffer[1]) * 256;
if (_toUnsigned(this._bLength, 16) !== _toUnsigned((~(this._blBuffer[2] + (this._blBuffer[3]) * 256)), 16)) {
throw new Error('Ivalid block length.');
}
}
this._inflaterState = this._getInflaterState(this._getInflaterStateValue(this._inflaterState) + 1);
return true;
};
/**
* Decodes a compressed block using literal length and distance trees and writes to the output.
*
* @private
*/
_Inflater.prototype._decodeBlock = function (endBlock) {
endBlock = false;
var fb = this._output._unusedBytes;
while (fb > 258) {
var symbol = void 0;
var dCodeResult = void 0;
var fLengthResult = void 0;
var inLengthResult = void 0;
switch (this._inflaterState) {
case _InflaterState.decodeTop:
symbol = this._llTree._getNextSymbol(this._input);
if (symbol < 0) {
return { 'result': false, 'eob': endBlock, 'output': this._output };
}
if (symbol < 256) {
this._output._write(_toUnsigned(symbol, 8));
--fb;
}
else if (symbol === 256) {
endBlock = true;
this._inflaterState = _InflaterState.readingBFinal;
return { 'result': true, 'eob': endBlock, 'output': this._output };
}
else {
symbol -= 257;
if (symbol < 8) {
symbol += 3;
this._extraBits = 0;
}
else if (symbol === 28) {
symbol = 258;
this._extraBits = 0;
}
else {
if (symbol < 0 || symbol >= this._extraLengthBits.length) {
throw new Error('Invalid data.');
}
this._extraBits = this._extraLengthBits[symbol];
}
this._length = symbol;
inLengthResult = this._inLength(fb);
fb = inLengthResult.fb;
if (!inLengthResult.value) {
return { 'result': false, 'eob': endBlock, 'output': this._output };
}
}
break;
case _InflaterState.iLength:
inLengthResult = this._inLength(fb);
fb = inLengthResult.fb;
if (!inLengthResult.value) {
return { 'result': false, 'eob': endBlock, 'output': this._output };
}
break;
case _InflaterState.fLength:
fLengthResult = this._fLength(fb);
fb = fLengthResult.fb;
if (!fLengthResult.value) {
return { 'result': false, 'eob': endBlock, 'output': this._output };
}
break;
case _InflaterState.dCode:
dCodeResult = this._dcode(fb);
fb = dCodeResult.fb;
if (!dCodeResult.value) {
return { 'result': false, 'eob': endBlock, 'output': this._output };
}
break;
default:
break;
}
}
return { 'result': true, 'eob': endBlock, 'output': this._output };
};
/**
* Reads any extra length bits and prepares for distance decoding.
*
* @private
*/
_Inflater.prototype._inLength = function (fb) {
if (this._extraBits > 0) {
this._inflaterState = _InflaterState.iLength;
var bits = this._input._getBits(this._extraBits);
if (bits < 0) {
return { 'value': false, 'fb': fb };
}
if (this._length < 0 || this._length >= this._lengthBase.length) {
throw new Error('Invalid data.');
}
this._length = this._lengthBase[this._length] + bits;
}
this._inflaterState = _InflaterState.fLength;
var fLengthResult = this._fLength(fb);
fb = fLengthResult.fb;
if (!fLengthResult.value) {
return { 'value': false, 'fb': fb };
}
return { 'value': true, 'fb': fb };
};
/**
* Resolves the distance code and prepares for back reference copying.
*
* @private
*/
_Inflater.prototype._fLength = function (fb) {
if (this._blockType === _BlockType.dynamicType) {
this._distanceCode = this._distanceTree._getNextSymbol(this._input);
}
else {
this._distanceCode = this._input._getBits(5);
if (this._distanceCode >= 0) {
this._distanceCode = this._staticDistanceTreeTable[this._distanceCode];
}
}
if (this._distanceCode < 0) {
return { 'value': false, 'fb': fb };
}
this._inflaterState = _InflaterState.dCode;
var dCodeResult = this._dcode(fb);
fb = dCodeResult.fb;
if (!dCodeResult.value) {
return { 'value': false, 'fb': fb };
}
return { 'value': true, 'fb': fb };
};
/**
* Computes the match offset and copies the match into the output buffer.
*
* @private
*/
_Inflater.prototype._dcode = function (fb) {
var offset;
if (this._distanceCode > 3) {
this._extraBits = (this._distanceCode - 2) >> 1;
var bits = this._input._getBits(this._extraBits);
if (bits < 0) {
return { 'value': false, 'fb': fb };
}
offset = this._distanceBasePosition[this._distanceCode] + bits;
}
else {
offset = this._distanceCode + 1;
}
this._output._writeLD(this._length, offset);
fb -= this._length;
this._inflaterState = _InflaterState.decodeTop;
return { 'value': true, 'fb': fb };
};
/**
* Reads dynamic header tables and builds the length and distance trees.
*
* @private
*/
_Inflater.prototype._decodeDynamicBlockHeader = function () {
switch (this._inflaterState) {
case _InflaterState.readingNlCodes:
this._llCodeCount = this._input._getBits(5);
if (this._llCodeCount < 0) {
return false;
}
this._llCodeCount += 257;
this._inflaterState = _InflaterState.readingNdCodes;
if (!this._readingNDCodes()) {
return false;
}
break;
case _InflaterState.readingNdCodes:
if (!this._readingNDCodes()) {
return false;
}
break;
case _InflaterState.readingCodes:
if (!this._readingCodes()) {
return false;
}
break;
case _InflaterState.readingClCodes:
if (!this._readingCLCodes()) {
return false;
}
break;
case _InflaterState.readingTcBefore:
case _InflaterState.readingTcAfter:
if (!this._readingTCBefore()) {
return false;
}
break;
default:
break;
}
var literalTreeCodeLength = Array(_HuffmanTree._maxLengthTree).fill(0);
_copyRange(literalTreeCodeLength, 0, this._codeList, 0, this._llCodeCount);
var distanceTreeCodeLength = Array(_HuffmanTree._maxDepthTree).fill(0);
_copyRange(distanceTreeCodeLength, 0, this._codeList, this._llCodeCount, this._llCodeCount + this._dCodeCount);
this._llTree = new _HuffmanTree();
this._llTree._load(literalTreeCodeLength);
this._distanceTree = new _HuffmanTree();
this._distanceTree._load(distanceTreeCodeLength);
this._inflaterState = _InflaterState.decodeTop;
return true;
};
/**
* Reads the distance code count and proceeds to read code length counts.
*
* @private
*/
_Inflater.prototype._readingNDCodes = function () {
this._dCodeCount = this._input._getBits(5);
if (this._dCodeCount < 0) {
return false;
}
this._dCodeCount += 1;
this._inflaterState = _InflaterState.readingCodes;
if (!this._readingCodes()) {
return false;
}
return true;
};
/**
* Reads the code length code count and prepares for code length decoding.
*
* @private
*/
_Inflater.prototype._readingCodes = function () {
this._clCodeCount = this._input._getBits(4);
if (this._clCodeCount < 0) {
return false;
}
this._clCodeCount += 4;
this._loopCounter = 0;
this._inflaterState = _InflaterState.readingClCodes;
if (!this._readingCLCodes()) {
return false;
}
return true;
};
/**
* Reads the code length codes in the prescribed order and builds the code length tree.
*
* @private
*/
_Inflater.prototype._readingCLCodes = function () {
while (this._loopCounter < this._clCodeCount) {
var bits = this._input._getBits(3);
if (bits < 0) {
return false;
}
this._cltcl[this._codeOrder[this._loopCounter]] = _toUnsigned(bits, 8);
++this._loopCounter;
}
for (var i = this._clCodeCount; i < this._codeOrder.length; i++) {
this._cltcl[this._codeOrder[i]] = 0;
}
this._clTree = new _HuffmanTree();
this._clTree._load(this._cltcl);
this._caSize = this._llCodeCount + this._dCodeCount;
this._loopCounter = 0;
this._inflaterState = _InflaterState.readingTcBefore;
if (!this._readingTCBefore()) {
return false;
}
return true;
};
/**
* Reads and expands code length repeat instructions into the combined code list.
*
* @private
*/
_Inflater.prototype._readingTCBefore = function () {
while (this._loopCounter < this._caSize) {
if (this._inflaterState === _InflaterState.readingTcBefore) {
this._lengthCode = this._clTree._getNextSymbol(this._input);
if (this._lengthCode < 0) {
return false;
}
}
if (this._lengthCode <= 15) {
this._codeList[this._loopCounter++] = _toUnsigned(this._lengthCode, 8);
}
else {
if (!this._input._availableBits(7)) {
this._inflaterState = _InflaterState.readingTcAfter;
return false;
}
var repeatCount = void 0;
if (this._lengthCode === 16) {
if (this._loopCounter === 0) {
throw new Error('Invalid data.');
}
var previousCode = _toUnsigned(this._codeList[this._loopCounter - 1], 8);
repeatCount = this._input._getBits(2) + 3;
if (this._loopCounter + repeatCount > this._caSize) {
throw new Error('Invalid data.');
}
for (var j = 0; j < repeatCount; j++) {
this._codeList[this._loopCounter++] = previousCode;
}
}
else if (this._lengthCode === 17) {
repeatCount = this._input._getBits(3) + 3;
if (this._loopCounter + repeatCount > this._caSize) {
throw new Error('Invalid data.');
}
for (var j = 0; j < repeatCount; j++) {
this._codeList[this._loopCounter++] = 0;
}
}
else {
repeatCount = this._input._getBits(7) + 11;
if (this._loopCounter + repeatCount > this._caSize) {
throw new Error('Invalid data.');
}
for (var j = 0; j < repeatCount; j++) {
this._codeList[this._loopCounter++] = 0;
}
}
}
this._inflaterState = _InflaterState.readingTcBefore;
}
return true;
};
/**
* Maps the block type bits to the corresponding block type value.
*
* @private
*/
_Inflater.prototype._getBlockType = function (type) {
if (type === _BlockType.unCompressedType) {
return _BlockType.unCompressedType;
}
else if (type === _BlockType.staticType) {
return _BlockType.staticType;
}
else {
return _BlockType.dynamicType;
}
};
/**
* Maps a numeric state value to the corresponding inflater state.
*
* @private
*/
_Inflater.prototype._getInflaterState = function (value) {
switch (value) {
case 0:
return _InflaterState.readingHeader;
case 2:
return _InflaterState.readingBFinal;
case 3:
return _InflaterState.readingBType;
case 4:
return _InflaterState.readingNlCodes;
case 5:
return _InflaterState.readingNdCodes;
case 6:
return _InflaterState.readingCodes;
case 7:
return _InflaterState.readingClCodes;
case 8:
return _InflaterState.readingTcBefore;
case 9:
return _InflaterState.readingTcAfter;
case 10:
return _InflaterState.decodeTop;
case 11:
return _InflaterState.iLength;
case 12:
return _InflaterState.fLength;
case 13:
return _InflaterState.dCode;
case 15:
return _InflaterState.unCompressedAligning;
case 16:
return _InflaterState.unCompressedByte1;
case 17:
return _InflaterState.unCompressedByte2;
case 18:
return _InflaterState.unCompressedByte3;
case 19:
return _InflaterState.unCompressedByte4;
case 20:
return _InflaterState.decodeUnCompressedBytes;
case 21:
return _InflaterState.srFooter;
case 22:
return _InflaterState.rFooter;
case 23:
return _InflaterState.vFooter;
case 24:
return _InflaterState.done;
default:
return _InflaterState.readingHeader;
}
};
/**
* Maps an inflater state to its numeric value for ordering.
*
* @private
*/
_Inflater.prototype._getInflaterStateValue = function (state) {
switch (state) {
case _InflaterState.readingHeader:
return 0;
case _InflaterState.readingBFinal:
return 2;
case _InflaterState.readingBType:
return 3;
case _InflaterState.readingNlCodes:
return 4;
case _InflaterState.readingNdCodes:
return 5;
case _InflaterState.readingCodes:
return 6;
case _InflaterState.readingClCodes:
return 7;
case _InflaterState.readingTcBefore:
return 8;
case _InflaterState.readingTcAfter:
return 9;
case _InflaterState.decodeTop:
return 10;
case _InflaterState.iLength:
return 11;
case _InflaterState.fLength:
return 12;
case _InflaterState.dCode:
return 13;
case _InflaterState.unCompressedAligning:
return 15;
case _InflaterState.unCompressedByte1:
return 16;
case _InflaterState.unCompressedByte2:
return 17;
case _InflaterState.unCompressedByte3:
return 18;
case _InflaterState.unCompressedByte4:
return 19;
case _InflaterState.decodeUnCompressedBytes:
return 20;
case _InflaterState.srFooter:
return 21;
case _InflaterState.rFooter:
return 22;
case _InflaterState.vFooter:
return 23;
case _InflaterState.done:
return 24;
default:
return 0;
}
};
return _Inflater;
}());
export { _Inflater };