Browse Source

rewrote streams to be object oriented

sbarman 14 years ago
parent
commit
99ffc9991e
  1. 343
      pdf.js

343
pdf.js

@ -71,11 +71,6 @@ var Stream = (function() {
get length() { get length() {
return this.end - this.start; return this.end - this.start;
}, },
lookByte: function() {
if (this.pos >= this.end)
return;
return this.bytes[this.pos];
},
getByte: function() { getByte: function() {
if (this.pos >= this.end) if (this.pos >= this.end)
return; return;
@ -99,10 +94,14 @@ var Stream = (function() {
return bytes.subarray(pos, end); return bytes.subarray(pos, end);
}, },
lookChar: function() { lookChar: function() {
return String.fromCharCode(this.lookByte()); if (this.pos >= this.end)
return;
return String.fromCharCode(this.bytes[this.pos]);
}, },
getChar: function() { getChar: function() {
return String.fromCharCode(this.getByte()); if (this.pos >= this.end)
return;
return String.fromCharCode(this.bytes[this.pos++]);
}, },
skip: function(n) { skip: function(n) {
if (!n) if (!n)
@ -137,6 +136,84 @@ var StringStream = (function() {
return constructor; return constructor;
})(); })();
// super class for the decoding streams
var DecodeStream = (function() {
function constructor() {
this.pos = 0;
this.bufferLength = 0;
this.eof = false;
this.buffer = null;
}
constructor.prototype = {
ensureBuffer: function(requested) {
var buffer = this.buffer;
var current = buffer ? buffer.byteLength : 0;
if (requested < current)
return buffer;
var size = 512;
while (size < requested)
size <<= 1;
var buffer2 = Uint8Array(size);
for (var i = 0; i < current; ++i)
buffer2[i] = buffer[i];
return this.buffer = buffer2;
},
getByte: function() {
var pos = this.pos;
while (this.bufferLength <= pos) {
if (this.eof)
return;
this.readBlock();
}
return this.buffer[this.pos++];
},
getBytes: function(length) {
var pos = this.pos;
this.ensureBuffer(pos + length);
while (!this.eof && this.bufferLength < pos + length)
this.readBlock();
var end = pos + length;
var bufEnd = this.bufferLength;
if (end > bufEnd)
end = bufEnd;
this.pos = end;
return this.buffer.subarray(pos, end)
},
lookChar: function() {
var pos = this.pos;
while (this.bufferLength <= pos) {
if (this.eof)
return;
this.readBlock();
}
return String.fromCharCode(this.buffer[this.pos]);
},
getChar: function() {
var pos = this.pos;
while (this.bufferLength <= pos) {
if (this.eof)
return;
this.readBlock();
}
return String.fromCharCode(this.buffer[this.pos++]);
},
skip: function(n) {
if (!n)
n = 1;
this.pos += n;
}
};
return constructor;
})();
var FlateStream = (function() { var FlateStream = (function() {
const codeLenCodeMap = Uint32Array([ const codeLenCodeMap = Uint32Array([
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
@ -261,12 +338,11 @@ var FlateStream = (function() {
this.bytes = bytes; this.bytes = bytes;
this.bytesPos = bytesPos; this.bytesPos = bytesPos;
this.eof = false;
this.codeSize = 0; this.codeSize = 0;
this.codeBuf = 0; this.codeBuf = 0;
this.pos = 0; DecodeStream.call(this);
this.bufferLength = 0;
} }
constructor.prototype = { constructor.prototype = {
@ -314,64 +390,6 @@ var FlateStream = (function() {
this.bytesPos = bytesPos; this.bytesPos = bytesPos;
return codeVal; return codeVal;
}, },
ensureBuffer: function(requested) {
var buffer = this.buffer;
var current = buffer ? buffer.byteLength : 0;
if (requested < current)
return buffer;
var size = 512;
while (size < requested)
size <<= 1;
var buffer2 = Uint8Array(size);
for (var i = 0; i < current; ++i)
buffer2[i] = buffer[i];
return this.buffer = buffer2;
},
getByte: function() {
var pos = this.pos;
while (this.bufferLength <= pos) {
if (this.eof)
return;
this.readBlock();
}
return this.buffer[this.pos++];
},
getBytes: function(length) {
var pos = this.pos;
while (!this.eof && this.bufferLength < pos + length)
this.readBlock();
var end = pos + length;
var bufEnd = this.bufferLength;
if (end > bufEnd)
end = bufEnd;
this.pos = end;
return this.buffer.subarray(pos, end)
},
lookChar: function() {
var pos = this.pos;
while (this.bufferLength <= pos) {
if (this.eof)
return;
this.readBlock();
}
return String.fromCharCode(this.buffer[pos]);
},
getChar: function() {
var ch = this.lookChar();
// shouldnt matter what the position is if we get past the eof
// so no need to check if ch is undefined
this.pos++;
return ch;
},
skip: function(n) {
if (!n)
n = 1;
this.pos += n;
},
generateHuffmanTable: function(lengths) { generateHuffmanTable: function(lengths) {
var n = lengths.length; var n = lengths.length;
@ -489,8 +507,10 @@ var FlateStream = (function() {
} }
} }
litCodeTable = this.generateHuffmanTable(codeLengths.slice(0, numLitCodes)); litCodeTable = this.generateHuffmanTable(
distCodeTable = this.generateHuffmanTable(codeLengths.slice(numLitCodes, codes)); codeLengths.slice(0, numLitCodes));
distCodeTable = this.generateHuffmanTable(
codeLengths.slice(numLitCodes, codes));
} else { } else {
error("Unknown block type in flate stream"); error("Unknown block type in flate stream");
} }
@ -526,30 +546,13 @@ var FlateStream = (function() {
} }
}; };
return constructor; var thisPrototype = constructor.prototype;
})(); var superPrototype = DecodeStream.prototype;
// A JpegStream can't be read directly. We use the platform to render the underlying for (var i in superPrototype) {
// JPEG data for us. if (!thisPrototype[i])
var JpegStream = (function() { thisPrototype[i] = superPrototype[i];
function constructor(bytes, dict) {
// TODO: per poppler, some images may have "junk" before that need to be removed
this.dict = dict;
// create DOM image
var img = new Image();
img.src = "data:image/jpeg;base64," + window.btoa(bytesToString(bytes));
this.domImage = img;
} }
constructor.prototype = {
getImage: function() {
return this.domImage;
},
getChar: function() {
error("internal error: getChar is not valid on JpegStream");
}
};
return constructor; return constructor;
})(); })();
@ -563,9 +566,9 @@ var PredictorStream = (function() {
error("Unsupported predictor"); error("Unsupported predictor");
if (predictor === 2) if (predictor === 2)
this.readRow = this.readRowTiff; this.readBlock = this.readBlockTiff;
else else
this.readRow = this.readRowPng; this.readBlock = this.readBlockPng;
this.stream = stream; this.stream = stream;
this.dict = stream.dict; this.dict = stream.dict;
@ -578,27 +581,34 @@ var PredictorStream = (function() {
var pixBytes = this.pixBytes = (colors * bits + 7) >> 3; var pixBytes = this.pixBytes = (colors * bits + 7) >> 3;
// add an extra pixByte to represent the pixel left of column 0 // add an extra pixByte to represent the pixel left of column 0
var rowBytes = this.rowBytes = ((columns * colors * bits + 7) >> 3) + pixBytes; var rowBytes = this.rowBytes = (columns * colors * bits + 7) >> 3;
this.currentRow = new Uint8Array(rowBytes); DecodeStream.call(this);
this.bufferLength = rowBytes;
this.pos = rowBytes;
} }
constructor.prototype = { constructor.prototype = {
readRowTiff : function() { readBlockTiff : function() {
var currentRow = this.currentRow; var buffer = this.buffer;
var pos = this.pos;
var rowBytes = this.rowBytes; var rowBytes = this.rowBytes;
var pixBytes = this.pixBytes; var pixBytes = this.pixBytes;
var buffer = this.buffer;
var bufferLength = this.bufferLength;
this.ensureBuffer(bufferLength + rowBytes);
var currentRow = buffer.subarray(bufferLength, bufferLength + rowBytes);
var bits = this.bits; var bits = this.bits;
var colors = this.colors; var colors = this.colors;
var rawBytes = this.stream.getBytes(rowBytes - pixBytes); var rawBytes = this.stream.getBytes(rowBytes);
if (bits === 1) { if (bits === 1) {
var inbuf = 0; var inbuf = 0;
for (var i = pixBytes, j = 0; i < rowBytes; ++i, ++j) { for (var i = 0; i < rowBytes; ++i) {
var c = rawBytes[j]; var c = rawBytes[i];
inBuf = (inBuf << 8) | c; inBuf = (inBuf << 8) | c;
// bitwise addition is exclusive or // bitwise addition is exclusive or
// first shift inBuf and then add // first shift inBuf and then add
@ -607,14 +617,16 @@ var PredictorStream = (function() {
inBuf &= 0xFFFF; inBuf &= 0xFFFF;
} }
} else if (bits === 8) { } else if (bits === 8) {
for (var i = pixBytes, j = 0; i < rowBytes; ++i, ++j) for (var i = 0; i < colors; ++i)
currentRow[i] = currentRow[i - colors] + rawBytes[j]; currentRow[i] = rawBytes[i];
for (; i < rowBytes; ++i)
currentRow[i] = currentRow[i - colors] + rawBytes[i];
} else { } else {
var compArray = new Uint8Array(colors + 1); var compArray = new Uint8Array(colors + 1);
var bitMask = (1 << bits) - 1; var bitMask = (1 << bits) - 1;
var inbuf = 0, outbut = 0; var inbuf = 0, outbut = 0;
var inbits = 0, outbits = 0; var inbits = 0, outbits = 0;
var j = 0, k = pixBytes; var j = 0, k = 0;
var columns = this.columns; var columns = this.columns;
for (var i = 0; i < columns; ++i) { for (var i = 0; i < columns; ++i) {
for (var kk = 0; kk < colors; ++kk) { for (var kk = 0; kk < colors; ++kk) {
@ -638,40 +650,54 @@ var PredictorStream = (function() {
(inbuf & ((1 << (8 - outbits)) - 1)) (inbuf & ((1 << (8 - outbits)) - 1))
} }
} }
this.pos = pixBytes; this.bufferLength += rowBytes;
}, },
readRowPng : function() { readBlockPng : function() {
var currentRow = this.currentRow; var buffer = this.buffer;
var pos = this.pos;
var rowBytes = this.rowBytes; var rowBytes = this.rowBytes;
var pixBytes = this.pixBytes; var pixBytes = this.pixBytes;
var predictor = this.stream.getByte(); var predictor = this.stream.getByte();
var rawBytes = this.stream.getBytes(rowBytes - pixBytes); var rawBytes = this.stream.getBytes(rowBytes);
var buffer = this.buffer;
var bufferLength = this.bufferLength;
this.ensureBuffer(bufferLength + pixBytes);
var currentRow = buffer.subarray(bufferLength, bufferLength + rowBytes);
var prevRow = buffer.subarray(bufferLength - rowBytes, bufferLength);
if (prevRow.length == 0)
prevRow = currentRow;
switch (predictor) { switch (predictor) {
case 0: case 0:
break; break;
case 1: case 1:
for (var i = pixBytes, j = 0; i < rowBytes; ++i, ++j) for (var i = 0; i < pixBytes; ++i)
currentRow[i] = (currentRow[i - pixBytes] + rawBytes[j]) & 0xFF; currentRow[i] = rawBytes[i];
for (; i < rowBytes; ++i)
currentRow[i] = (currentRow[i - pixBytes] + rawBytes[i]) & 0xFF;
break; break;
case 2: case 2:
for (var i = pixBytes, j = 0; i < rowBytes; ++i, ++j) for (var i = 0; i < rowBytes; ++i)
currentRow[i] = (currentRow[i] + rawBytes[j]) & 0xFF; currentRow[i] = (prevRow[i] + rawBytes[i]) & 0xFF;
break; break;
case 3: case 3:
for (var i = pixBytes, j = 0; i < rowBytes; ++i, ++j) for (var i = 0; i < pixBytes; ++i)
currentRow[i] = (((currentRow[i] + currentRow[i - pixBytes]) currentRow[i] = (prevRow[i] >> 1) + rawBytes[i];
>> 1) + rawBytes[j]) & 0xFF; for (; i < rowBytes; ++i)
currentRow[i] = (((prevRow[i] + currentRow[i - pixBytes])
>> 1) + rawBytes[i]) & 0xFF;
break; break;
case 4: case 4:
// we need to save the up left pixels values. the simplest way // we need to save the up left pixels values. the simplest way
// is to create a new buffer // is to create a new buffer
var lastRow = currentRow; for (var i = 0; i < pixBytes; ++i)
var currentRow = new Uint8Array(rowBytes); currentRow[i] = rawBytes[i];
for (var i = pixBytes, j = 0; i < rowBytes; ++i, ++j) { for (; i < rowBytes; ++i) {
var up = lastRow[i]; var up = prevRow[i];
var upLeft = lastRow[i - pixBytes]; var upLeft = lastRow[i - pixBytes];
var left = currentRow[i - pixBytes]; var left = currentRow[i - pixBytes];
var p = left + up - upLeft; var p = left + up - upLeft;
@ -686,7 +712,7 @@ var PredictorStream = (function() {
if (pc < 0) if (pc < 0)
pc = -pc; pc = -pc;
var c = rawBytes[j]; var c = rawBytes[i];
if (pa <= pb && pa <= pc) if (pa <= pb && pa <= pc)
currentRow[i] = left + c; currentRow[i] = left + c;
else if (pb <= pc) else if (pb <= pc)
@ -694,54 +720,49 @@ var PredictorStream = (function() {
else else
currentRow[i] = upLeft + c; currentRow[i] = upLeft + c;
break; break;
this.currentRow = currentRow;
} }
default: default:
error("Unsupported predictor"); error("Unsupported predictor");
break; break;
} }
this.pos = pixBytes; this.bufferLength += rowBytes;
},
getByte : function() {
if (this.pos >= this.bufferLength)
this.readRow();
return this.currentRow[this.pos++];
},
getBytes : function(n) {
var i, bytes;
bytes = new Uint8Array(n);
for (i = 0; i < n; ++i) {
if (this.pos >= this.bufferLength)
this.readRow();
bytes[i] = this.currentRow[this.pos++];
}
return bytes;
},
getChar : function() {
return String.formCharCode(this.getByte());
}, },
lookChar : function() { };
if (this.pos >= this.bufferLength)
this.readRow(); var thisPrototype = constructor.prototype;
return String.formCharCode(this.currentRow[this.pos]); var superPrototype = DecodeStream.prototype;
for (var i in superPrototype) {
if (!thisPrototype[i])
thisPrototype[i] = superPrototype[i];
}
return constructor;
})();
// A JpegStream can't be read directly. We use the platform to render the underlying
// JPEG data for us.
var JpegStream = (function() {
function constructor(bytes, dict) {
// TODO: per poppler, some images may have "junk" before that need to be removed
this.dict = dict;
// create DOM image
var img = new Image();
img.src = "data:image/jpeg;base64," + window.btoa(bytesToString(bytes));
this.domImage = img;
}
constructor.prototype = {
getImage: function() {
return this.domImage;
}, },
skip : function(n) { getChar: function() {
var i; error("internal error: getChar is not valid on JpegStream");
if (!n) {
n = 1;
}
while (n > this.bufferLength - this.pos) {
n -= this.bufferLength - this.pos;
this.readRow();
if (this.bufferLength === 0) break;
}
this.pos += n;
} }
}; };
return constructor; return constructor;
})(); })();
var DecryptStream = (function() { var DecryptStream = (function() {
function constructor(str, fileKey, encAlgorithm, keyLength) { function constructor(str, fileKey, encAlgorithm, keyLength) {
TODO("decrypt stream is not implemented"); TODO("decrypt stream is not implemented");

Loading…
Cancel
Save