Merge branch 'master' of github.com:andreasgal/pdf.js
This commit is contained in:
commit
58cb3e76b2
311
pdf.js
311
pdf.js
@ -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;
|
||||||
|
})();
|
||||||
|
Loading…
Reference in New Issue
Block a user