|
|
|
@ -65,6 +65,19 @@ var Stream = (function() {
@@ -65,6 +65,19 @@ var Stream = (function() {
|
|
|
|
|
return -1; |
|
|
|
|
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() { |
|
|
|
|
var bytes = this.bytes; |
|
|
|
|
if (this.pos >= this.end) |
|
|
|
@ -78,15 +91,6 @@ var Stream = (function() {
@@ -78,15 +91,6 @@ var Stream = (function() {
|
|
|
|
|
this.pos++; |
|
|
|
|
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) { |
|
|
|
|
if (!n) |
|
|
|
|
n = 1; |
|
|
|
@ -242,8 +246,8 @@ var FlateStream = (function() {
@@ -242,8 +246,8 @@ var FlateStream = (function() {
|
|
|
|
|
this.eof = false; |
|
|
|
|
this.codeSize = 0; |
|
|
|
|
this.codeBuf = 0; |
|
|
|
|
|
|
|
|
|
this.pos = 0; |
|
|
|
|
this.bufferPos = 0; |
|
|
|
|
this.bufferLength = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -299,38 +303,48 @@ var FlateStream = (function() {
@@ -299,38 +303,48 @@ var FlateStream = (function() {
|
|
|
|
|
buffer2[i] = buffer[i]; |
|
|
|
|
return this.buffer = buffer2; |
|
|
|
|
}, |
|
|
|
|
getByte: function() { |
|
|
|
|
var bufferLength = this.bufferLength; |
|
|
|
|
var pos = this.pos; |
|
|
|
|
if (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 bufferLength = this.bufferLength; |
|
|
|
|
var bufferPos = this.bufferPos; |
|
|
|
|
if (bufferLength == bufferPos) { |
|
|
|
|
var pos = this.pos; |
|
|
|
|
if (bufferLength == pos) { |
|
|
|
|
if (this.eof) |
|
|
|
|
return; |
|
|
|
|
this.readBlock(); |
|
|
|
|
} |
|
|
|
|
return String.fromCharCode(this.buffer[bufferPos]); |
|
|
|
|
return String.fromCharCode(this.buffer[pos]); |
|
|
|
|
}, |
|
|
|
|
getChar: function() { |
|
|
|
|
var ch = this.lookChar(); |
|
|
|
|
if (!ch) |
|
|
|
|
return; |
|
|
|
|
this.pos++; |
|
|
|
|
this.bufferPos++; |
|
|
|
|
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) { |
|
|
|
|
if (!n) |
|
|
|
|
n = 1; |
|
|
|
@ -1053,7 +1067,7 @@ var Parser = (function() {
@@ -1053,7 +1067,7 @@ var Parser = (function() {
|
|
|
|
|
length = xref.fetchIfRef(length); |
|
|
|
|
if (!IsInt(length)) { |
|
|
|
|
error("Bad 'Length' attribute in stream"); |
|
|
|
|
lenght = 0; |
|
|
|
|
length = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// skip over the stream data
|
|
|
|
@ -1982,10 +1996,21 @@ var CanvasGraphics = (function() {
@@ -1982,10 +1996,21 @@ var CanvasGraphics = (function() {
|
|
|
|
|
return; |
|
|
|
|
xobj = this.xref.fetchIfRef(xobj); |
|
|
|
|
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"); |
|
|
|
|
assertWellFormed(IsName(type), "XObject should have a Name subtype"); |
|
|
|
|
if ("Image" == type.name) { |
|
|
|
|
this.paintImageXObject(xobj); |
|
|
|
|
this.paintImageXObject(xobj, false); |
|
|
|
|
} else if ("Form" == type.name) { |
|
|
|
|
this.paintFormXObject(xobj); |
|
|
|
|
} else if ("PS" == type.name) { |
|
|
|
@ -2015,9 +2040,15 @@ var CanvasGraphics = (function() {
@@ -2015,9 +2040,15 @@ var CanvasGraphics = (function() {
|
|
|
|
|
this.restore(); |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
paintImageXObject: function(image) { |
|
|
|
|
paintImageXObject: function(image, inline) { |
|
|
|
|
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?
|
|
|
|
|
|
|
|
|
|
var dict = image.dict; |
|
|
|
@ -2026,6 +2057,10 @@ var CanvasGraphics = (function() {
@@ -2026,6 +2057,10 @@ var CanvasGraphics = (function() {
|
|
|
|
|
|
|
|
|
|
if (w < 1 || h < 1) |
|
|
|
|
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"); |
|
|
|
|
if (!IsBool(interpolate)) |
|
|
|
@ -2034,13 +2069,9 @@ var CanvasGraphics = (function() {
@@ -2034,13 +2069,9 @@ var CanvasGraphics = (function() {
|
|
|
|
|
if (!IsBool(imageMask)) |
|
|
|
|
imageMask = false; |
|
|
|
|
|
|
|
|
|
// JPX/JPEG2000 streams directly contain bits per component
|
|
|
|
|
// and color space mode information.
|
|
|
|
|
var bitsPerComponent = image.bitsPerComponent; |
|
|
|
|
var csMode = image.csMode; |
|
|
|
|
|
|
|
|
|
if (!bitsPerComponent) { |
|
|
|
|
bitsPerComponent = dict.get("BitsPerComponent", "BPC"); |
|
|
|
|
bitsPerComponent = dict.get2("BitsPerComponent", "BPC"); |
|
|
|
|
if (!bitsPerComponent) { |
|
|
|
|
if (imageMask) |
|
|
|
|
bitsPerComponent = 1; |
|
|
|
@ -2049,57 +2080,140 @@ var CanvasGraphics = (function() {
@@ -2049,57 +2080,140 @@ 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"); |
|
|
|
|
tmpCanvas.width = w; |
|
|
|
|
tmpCanvas.height = h; |
|
|
|
|
var tmpCtx = tmpCanvas.getContext("2d"); |
|
|
|
|
var imgData = tmpCtx.getImageData(0, 0, w, h); |
|
|
|
|
var pixels = imgData.data; |
|
|
|
|
|
|
|
|
|
// Read in image data
|
|
|
|
|
image.snarf(pixels); |
|
|
|
|
|
|
|
|
|
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 (bitsPerComponent != 8) |
|
|
|
|
error("unhandled number of bits per component"); |
|
|
|
|
|
|
|
|
|
if (smask) { |
|
|
|
|
var smaskDict = smask.dict; |
|
|
|
|
if (!smaskDict) |
|
|
|
|
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"); |
|
|
|
|
if (matte) { |
|
|
|
|
TODO(matte); |
|
|
|
|
if (maskColorSpace.numComps != 1) |
|
|
|
|
error("Incorrect number of components in smask"); |
|
|
|
|
|
|
|
|
|
var numComps = colorSpace.numComps; |
|
|
|
|
var imgArray = image.getBytes(numComps * w * h); |
|
|
|
|
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 { |
|
|
|
|
// TODO blend if SMask is a mask image
|
|
|
|
|
var stop = 4 * w * h; |
|
|
|
|
for (var i = 3; i < stop; i += 4) |
|
|
|
|
pixels[i] = alpha; |
|
|
|
|
var numComps = colorSpace.numComps; |
|
|
|
|
var imgArray = image.getBytes(numComps * w * h); |
|
|
|
|
var imgIdx = 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] = 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); |
|
|
|
|
|
|
|
|
|
this.ctx.drawImage(tmpCanvas, 0, 0); |
|
|
|
|
|
|
|
|
|
ctx.drawImage(tmpCanvas, 0, 0); |
|
|
|
|
this.restore(); |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
@ -2138,3 +2252,50 @@ var CanvasGraphics = (function() {
@@ -2138,3 +2252,50 @@ var CanvasGraphics = (function() {
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
})(); |
|
|
|
|