Merge branch 'master' of github.com:andreasgal/pdf.js

This commit is contained in:
Chris Jones 2011-06-10 12:17:00 -07:00
commit 58cb3e76b2

311
pdf.js
View File

@ -65,6 +65,19 @@ var Stream = (function() {
return -1; return -1;
return bytes[this.pos++]; return bytes[this.pos++];
}, },
// returns subarray of original buffer
// should only be read
getBytes: function(length) {
var bytes = this.bytes;
var pos = this.pos;
var end = pos + length;
var strEnd = this.end;
if (end > strEnd)
end = strEnd;
this.pos = end;
return bytes.subarray(pos, end);
},
lookChar: function() { lookChar: function() {
var bytes = this.bytes; var bytes = this.bytes;
if (this.pos >= this.end) if (this.pos >= this.end)
@ -78,15 +91,6 @@ var Stream = (function() {
this.pos++; this.pos++;
return ch; return ch;
}, },
snarf: function(dest) {
var bytes = this.bytes;
var pos = this.pos;
var end = this.end;
var n = 0;
while (pos < end)
dest[n++] = bytes[pos++];
this.pos = this.end;
},
skip: function(n) { skip: function(n) {
if (!n) if (!n)
n = 1; n = 1;
@ -242,8 +246,8 @@ var FlateStream = (function() {
this.eof = false; this.eof = false;
this.codeSize = 0; this.codeSize = 0;
this.codeBuf = 0; this.codeBuf = 0;
this.pos = 0; this.pos = 0;
this.bufferPos = 0;
this.bufferLength = 0; this.bufferLength = 0;
} }
@ -299,38 +303,48 @@ var FlateStream = (function() {
buffer2[i] = buffer[i]; buffer2[i] = buffer[i];
return this.buffer = buffer2; return this.buffer = buffer2;
}, },
lookChar: function() { getByte: function() {
var bufferLength = this.bufferLength; var bufferLength = this.bufferLength;
var bufferPos = this.bufferPos; var pos = this.pos;
if (bufferLength == bufferPos) { if (bufferLength == pos) {
if (this.eof) if (this.eof)
return; return;
this.readBlock(); this.readBlock();
} }
return String.fromCharCode(this.buffer[bufferPos]); 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 bufferLength = this.bufferLength;
var pos = this.pos;
if (bufferLength == pos) {
if (this.eof)
return;
this.readBlock();
}
return String.fromCharCode(this.buffer[pos]);
}, },
getChar: function() { getChar: function() {
var ch = this.lookChar(); var ch = this.lookChar();
if (!ch) if (!ch)
return; return;
this.pos++; this.pos++;
this.bufferPos++;
return ch; return ch;
}, },
snarf: function(dest) {
// copy the leftover data in the buffer into dest
var bufferLength = this.bufferLength;
var bufferPos = this.bufferPos;
var n = 0;
while (bufferPos < bufferLength)
dest[n++] = this.buffer[bufferPos++];
// now use dest as our buffer and fill it
this.buffer = dest;
while (!this.eof)
this.readBlock();
// update stream position
this.pos = n;
},
skip: function(n) { skip: function(n) {
if (!n) if (!n)
n = 1; n = 1;
@ -1053,7 +1067,7 @@ var Parser = (function() {
length = xref.fetchIfRef(length); length = xref.fetchIfRef(length);
if (!IsInt(length)) { if (!IsInt(length)) {
error("Bad 'Length' attribute in stream"); error("Bad 'Length' attribute in stream");
lenght = 0; length = 0;
} }
// skip over the stream data // skip over the stream data
@ -1982,10 +1996,21 @@ var CanvasGraphics = (function() {
return; return;
xobj = this.xref.fetchIfRef(xobj); xobj = this.xref.fetchIfRef(xobj);
assertWellFormed(IsStream(xobj), "XObject should be a stream"); assertWellFormed(IsStream(xobj), "XObject should be a stream");
var oc = xobj.dict.get("OC");
if (oc) {
TODO("oc for xobject");
}
var opi = xobj.dict.get("OPI");
if (opi) {
TODO("opi for xobject");
}
var type = xobj.dict.get("Subtype"); var type = xobj.dict.get("Subtype");
assertWellFormed(IsName(type), "XObject should have a Name subtype"); assertWellFormed(IsName(type), "XObject should have a Name subtype");
if ("Image" == type.name) { if ("Image" == type.name) {
this.paintImageXObject(xobj); this.paintImageXObject(xobj, false);
} else if ("Form" == type.name) { } else if ("Form" == type.name) {
this.paintFormXObject(xobj); this.paintFormXObject(xobj);
} else if ("PS" == type.name) { } else if ("PS" == type.name) {
@ -2015,9 +2040,15 @@ var CanvasGraphics = (function() {
this.restore(); this.restore();
}, },
paintImageXObject: function(image) { paintImageXObject: function(image, inline) {
this.save(); this.save();
if (image.getParams) {
// JPX/JPEG2000 streams directly contain bits per component
// and color space mode information.
TODO("get params from actual stream");
// var bits = ...
// var colorspace = ...
}
// TODO cache rendered images? // TODO cache rendered images?
var dict = image.dict; var dict = image.dict;
@ -2027,6 +2058,10 @@ var CanvasGraphics = (function() {
if (w < 1 || h < 1) if (w < 1 || h < 1)
error("Invalid image width or height"); error("Invalid image width or height");
var ctx = this.ctx;
// scale the image to the unit square
ctx.scale(1/w, 1/h);
var interpolate = dict.get2("Interpolate", "I"); var interpolate = dict.get2("Interpolate", "I");
if (!IsBool(interpolate)) if (!IsBool(interpolate))
interpolate = false; interpolate = false;
@ -2034,13 +2069,9 @@ var CanvasGraphics = (function() {
if (!IsBool(imageMask)) if (!IsBool(imageMask))
imageMask = false; imageMask = false;
// JPX/JPEG2000 streams directly contain bits per component
// and color space mode information.
var bitsPerComponent = image.bitsPerComponent; var bitsPerComponent = image.bitsPerComponent;
var csMode = image.csMode;
if (!bitsPerComponent) { if (!bitsPerComponent) {
bitsPerComponent = dict.get("BitsPerComponent", "BPC"); bitsPerComponent = dict.get2("BitsPerComponent", "BPC");
if (!bitsPerComponent) { if (!bitsPerComponent) {
if (imageMask) if (imageMask)
bitsPerComponent = 1; bitsPerComponent = 1;
@ -2049,6 +2080,67 @@ var CanvasGraphics = (function() {
} }
} }
if (bitsPerComponent !== 8)
error("Unsupported bpc");
var xref = this.xref;
var colorSpaces = this.colorSpaces;
if (imageMask) {
error("support image masks");
}
// actual image
var csStream = dict.get2("ColorSpace", "CS");
csStream = xref.fetchIfRef(csStream);
if (IsName(csStream) && inline)
csStream = colorSpaces.get(csStream);
var colorSpace = new ColorSpace(xref, csStream);
var decode = dict.get2("Decode", "D");
TODO("create color map");
var mask = image.dict.get("Mask");
mask = xref.fetchIfRef(mask);
var smask = image.dict.get("SMask");
smask = xref.fetchIfRef(smask);
if (IsStream(smask)) {
if (inline)
error("cannot combine smask and inlining");
var maskDict = smask.dict;
var maskW = maskDict.get2("Width", "W");
var maskH = maskDict.get2("Height", "H");
if (!IsNum(maskW) || !IsNum(maskH) || maskW < 1 || maskH < 1)
error("Invalid image width or height");
if (maskW !== w || maskH !== h)
error("Invalid image width or height");
var maskInterpolate = maskDict.get2("Interpolate", "I");
if (!IsBool(maskInterpolate))
maskInterpolate = false;
var maskBPC = maskDict.get2("BitsPerComponent", "BPC");
if (!maskBPC)
error("Invalid image mask bpc");
var maskCsStream = maskDict.get2("ColorSpace", "CS");
maskCsStream = xref.fetchIfRef(maskCsStream);
var maskColorSpace = new ColorSpace(xref, maskCsStream);
if (maskColorSpace.mode !== "DeviceGray")
error("Invalid color space for smask");
var maskDecode = maskDict.get2("Decode", "D");
if (maskDecode)
TODO("Handle mask decode");
// handle matte object
} else {
smask = null;
}
var tmpCanvas = document.createElement("canvas"); var tmpCanvas = document.createElement("canvas");
tmpCanvas.width = w; tmpCanvas.width = w;
tmpCanvas.height = h; tmpCanvas.height = h;
@ -2056,50 +2148,72 @@ var CanvasGraphics = (function() {
var imgData = tmpCtx.getImageData(0, 0, w, h); var imgData = tmpCtx.getImageData(0, 0, w, h);
var pixels = imgData.data; var pixels = imgData.data;
// Read in image data if (bitsPerComponent != 8)
image.snarf(pixels); error("unhandled number of bits per component");
var alpha = 25;
if (image.dict.has("SMask")) {
var smask = image.dict.get("SMask");
smask = this.xref.fetchIfRef(smask);
// Specifies either a shape or opacity mask to be
// applied to the image samples
TODO("SMask");
}
if (smask) { if (smask) {
var smaskDict = smask.dict; if (maskColorSpace.numComps != 1)
if (!smaskDict) error("Incorrect number of components in smask");
error("No dictionary for smask");
var smaskBitsPerComponent = smaskDict.get2("BitsPerComponent", "BPC");
if (!smaskBitsPerComponent)
error("Bad BPC for smask");
var max = (1 << bitsPerComponent) - 1;
var matte = smaskDict.get("Matte"); var numComps = colorSpace.numComps;
if (matte) { var imgArray = image.getBytes(numComps * w * h);
TODO(matte); var imgIdx = 0;
var smArray = smask.getBytes(w * h);
var smIdx = 0;
var length = 4 * w * h;
switch (numComps) {
case 1:
for (var i = 0; i < length; i += 4) {
var p = imgArray[imageIdx++];
pixels[i] = p;
pixels[i+1] = p;
pixels[i+2] = p;
pixels[i+3] = smArray[smIdx++];
}
break;
case 3:
for (var i = 0; i < length; i += 4) {
pixels[i] = imgArray[imgIdx++];
pixels[i+1] = imgArray[imgIdx++];
pixels[i+2] = imgArray[imgIdx++];
pixels[i+3] = smArray[smIdx++];
}
break;
default:
error("unhandled amount of components per pixel: " + numComps);
} }
// read in mask data
var mask = new Uint8Array(4 * w * h);
smask.snarf(mask);
// and blend images with it
var stop = 4 * w * h;
for (var i = 0; i < stop; ++i)
pixels[i] = pixels[i] * mask[i] / max;
} else { } else {
// TODO blend if SMask is a mask image var numComps = colorSpace.numComps;
var stop = 4 * w * h; var imgArray = image.getBytes(numComps * w * h);
for (var i = 3; i < stop; i += 4) var imgIdx = 0;
pixels[i] = alpha;
var length = 4 * w * h;
switch (numComps) {
case 1:
for (var i = 0; i < length; i += 4) {
var p = imgArray[imageIdx++];
pixels[i] = p;
pixels[i+1] = p;
pixels[i+2] = p;
pixels[i+3] = 255;
}
break;
case 3:
for (var i = 0; i < length; i += 4) {
pixels[i] = imgArray[imgIdx++];
pixels[i+1] = imgArray[imgIdx++];
pixels[i+2] = imgArray[imgIdx++];
pixels[i+3] = 255;
}
break;
default:
error("unhandled amount of components per pixel: " + numComps);
}
} }
tmpCtx.putImageData(imgData, 0, 0); tmpCtx.putImageData(imgData, 0, 0);
ctx.drawImage(tmpCanvas, 0, 0);
this.ctx.drawImage(tmpCanvas, 0, 0);
this.restore(); this.restore();
}, },
@ -2138,3 +2252,50 @@ var CanvasGraphics = (function() {
return constructor; return constructor;
})(); })();
var ColorSpace = (function() {
function constructor(xref, cs) {
if (IsName(cs)) {
var mode = cs.name;
this.mode = mode;
switch(mode) {
case "DeviceGray":
case "G":
this.numComps = 1;
break;
}
TODO("fill in color space constructor");
} else if (IsArray(cs)) {
var mode = cs[0].name;
this.mode = mode;
var stream = cs[1];
stream = xref.fetchIfRef(stream);
switch (mode) {
case "DeviceGray":
case "G":
this.stream = stream;
this.dict = stream.dict;
this.numComps = 1;
break;
case "ICCBased":
var dict = stream.dict;
this.stream = stream;
this.dict = dict;
this.numComps = dict.get("N");
break;
default:
error("unrecognized color space object");
}
} else {
error("unrecognized color space object");
}
};
constructor.prototype = {
};
return constructor;
})();