From 1e3147d0d7fd622db89388fe1cafbe7c6fce4862 Mon Sep 17 00:00:00 2001 From: sbarman Date: Thu, 16 Jun 2011 11:26:50 -0700 Subject: [PATCH 01/14] working version of tiling --- pdf.js | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 1 deletion(-) diff --git a/pdf.js b/pdf.js index 17537d233..771b18c30 100644 --- a/pdf.js +++ b/pdf.js @@ -1590,8 +1590,38 @@ var CanvasExtraState = (function() { // Start of text line (in text coordinates) this.lineX = 0.0; this.lineY = 0.0; + + this.transMatrix = IDENTITY_MATRIX; } constructor.prototype = { + applyTransform: function(point) { + var m = this.transMatrix + var x = point[0] * m[0] + point[1] * m[2] + m[4]; + var y = point[0] * m[1] + point[1] * m[3] + m[5]; + return [x,y]; + }, + concatTransform: function(m) { + var tm = this.transMatrix; + + var a = m[0] * tm[0] + m[1] * tm[2]; + var b = m[0] * tm[1] + m[1] * tm[3]; + var c = m[2] * tm[0] + m[3] * tm[2]; + var d = m[2] * tm[1] + m[3] * tm[3]; + var e = m[4] * tm[0] + m[5] * tm[2] + tm[4]; + var f = m[4] * tm[1] + m[5] * tm[3] + tm[5]; + this.transMatrix = [a, b, c, d, e, f] + }, + getInvTransform: function(matrix) { + var m = this.transMatrix; + var det = 1 / (m[0] * m[3] - m[1] * m[2]); + var a = m[3] * det; + var b = -m[1] * det; + var c = -m[2] * det; + var d = m[0] * det; + var e = (m[2] * m[5] - m[3] * m[4]) * det; + var f = (m[1] * m[4] - m[0] * m[5]) * det; + return [a, b, c, d, e, f] + } }; return constructor; })(); @@ -1851,6 +1881,7 @@ var CanvasGraphics = (function() { }, transform: function(a, b, c, d, e, f) { this.ctx.transform(a, b, c, d, e, f); + this.current.concatTransform([a,b,c,d,e,f]); }, // Path @@ -2023,6 +2054,11 @@ var CanvasGraphics = (function() { }, setFillColorSpace: function(space) { // TODO real impl + if (space.name === "Pattern") { + this.colorspace = "Pattern"; + } else { + this.colorspace = null; + } }, setStrokeColor: function(/*...*/) { // TODO real impl @@ -2046,7 +2082,104 @@ var CanvasGraphics = (function() { }, setFillColorN: function(/*...*/) { // TODO real impl - this.setFillColor.apply(this, arguments); + var args = arguments; + if (this.colorspace == "Pattern") { + var patternName = args[0]; + if (IsName(patternName)) { + var xref = this.xref; + var patternRes = xref.fetchIfRef(this.res.get("Pattern")); + if (!patternRes) + error("Unable to find pattern resource"); + + var pattern = xref.fetchIfRef(patternRes.get(patternName.name)); + + var type = pattern.dict.get("PatternType"); + if (type === 1) { + this.tilingFill(pattern); + } else { + error("Unhandled pattern type"); + } + } + } else { + // TODO real impl + this.setFillColor.apply(this, arguments); + } + }, + tilingFill: function(pattern) { + this.save(); + var dict = pattern.dict; + + var paintType = dict.get("PaintType"); + if (paintType == 2) { + error("Unsupported paint type"); + } else { + // should go to default for color space + this.ctx.fillStyle = this.makeCssRgb(1, 1, 1); + this.ctx.strokeStyle = this.makeCssRgb(0, 0, 0); + } + + // not sure what to do with this + var tilingType = dict.get("TilingType"); + + var tempExtra = new CanvasExtraState(); + var matrix = dict.get("Matrix"); + if (matrix && IsArray(matrix) && 6 == matrix.length) + tempExtra.transMatrix = matrix; + + var bbox = dict.get("BBox"); + var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3]; + + var xstep = dict.get("XStep"); + var ystep = dict.get("YStep"); + + // top left corner should correspond to the top left of the bbox + var topLeft = tempExtra.applyTransform([x0,y0]); + // we want the canvas to be as large as the step size + var botRight = tempExtra.applyTransform([x0 + xstep, y0 + ystep]); + + var tmpCanvas = document.createElement("canvas"); + tmpCanvas.width = Math.ceil(botRight[0] - topLeft[0]); + tmpCanvas.height = Math.ceil(botRight[1] - topLeft[1]); + + // set the new canvas element context as the graphics context + var tmpCtx = tmpCanvas.getContext("2d"); + var oldCtx = this.ctx; + this.ctx = tmpCtx; + + // normalize matrix transform so each step + // takes up the entire tmpCanvas + if (matrix[1] === 0 && matrix[2] === 0) { + matrix[0] = tmpCanvas.width / xstep; + matrix[3] = tmpCanvas.height / ystep; + tempExtra.transMatrix = matrix; + topLeft = tempExtra.applyTransform([x0,y0]); + } + + // move the top left corner of bounding box to [0,0] + tempExtra.transMatrix = [1, 0, 0, 1, -topLeft[0], -topLeft[1]]; + tempExtra.concatTransform(matrix); + matrix = tempExtra.transMatrix; + + this.transform.apply(this, matrix); + + if (bbox && IsArray(bbox) && 4 == bbox.length) { + this.rectangle.apply(this, bbox); + this.clip(); + this.endPath(); + } + + var xref = this.xref; + var res = xref.fetchIfRef(dict.get("Resources")); + if (!pattern.code) + pattern.code = this.compile(pattern, xref, res, []); + this.execute(pattern.code, xref, res); + + // set the old context + this.ctx = oldCtx; + this.restore(); + + var pattern = this.ctx.createPattern(tmpCanvas, "repeat"); + this.ctx.fillStyle = pattern; }, setStrokeGray: function(gray) { this.setStrokeRGBColor(gray, gray, gray); From de184e05302467cd36f2758cc5fef547e6f5f0c2 Mon Sep 17 00:00:00 2001 From: sbarman Date: Fri, 17 Jun 2011 10:10:29 -0700 Subject: [PATCH 02/14] stored bytes in flatestream --- pdf.js | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/pdf.js b/pdf.js index 5bf559fcf..656453524 100644 --- a/pdf.js +++ b/pdf.js @@ -73,7 +73,7 @@ var Stream = (function() { var pos = this.pos; var end = pos + length; var strEnd = this.end; - if (end > strEnd) + if (!end || end > strEnd) end = strEnd; this.pos = end; @@ -232,10 +232,12 @@ var FlateStream = (function() { ]), 5]; function constructor(stream) { - this.stream = stream; + var bytes = stream.getBytes(); + var bytesIdx = 0; + this.dict = stream.dict; - var cmf = stream.getByte(); - var flg = stream.getByte(); + var cmf = bytes[bytesIdx++]; + var flg = bytes[bytesIdx++]; if (cmf == -1 || flg == -1) error("Invalid header in flate stream"); if ((cmf & 0x0f) != 0x08) @@ -244,6 +246,9 @@ var FlateStream = (function() { error("Bad FCHECK in flate stream"); if (flg & 0x20) error("FDICT bit set in flate stream"); + + this.bytes = bytes; + this.bytesIdx = bytesIdx; this.eof = false; this.codeSize = 0; this.codeBuf = 0; @@ -254,12 +259,14 @@ var FlateStream = (function() { constructor.prototype = { getBits: function(bits) { - var stream = this.stream; var codeSize = this.codeSize; var codeBuf = this.codeBuf; + var bytes = this.bytes; + var bytesIdx = this.bytesIdx; + var b; while (codeSize < bits) { - if ((b = stream.getByte()) == -1) + if ((b = bytes[bytesIdx++]) == undefined) error("Bad encoding in flate stream"); codeBuf |= b << codeSize; codeSize += 8; @@ -267,6 +274,7 @@ var FlateStream = (function() { b = codeBuf & ((1 << bits) - 1); this.codeBuf = codeBuf >> bits; this.codeSize = codeSize -= bits; + this.bytesIdx = bytesIdx; return b; }, getCode: function(table) { @@ -274,10 +282,12 @@ var FlateStream = (function() { var maxLen = table[1]; var codeSize = this.codeSize; var codeBuf = this.codeBuf; - var stream = this.stream; + var bytes = this.bytes; + var bytesIdx = this.bytesIdx; + while (codeSize < maxLen) { var b; - if ((b = stream.getByte()) == -1) + if ((b = bytes[bytesIdx++]) == undefined) error("Bad encoding in flate stream"); codeBuf |= (b << codeSize); codeSize += 8; @@ -289,6 +299,7 @@ var FlateStream = (function() { error("Bad encoding in flate stream"); this.codeBuf = (codeBuf >> codeLen); this.codeSize = (codeSize - codeLen); + this.bytesIdx = bytesIdx; return codeVal; }, ensureBuffer: function(requested) { @@ -390,7 +401,8 @@ var FlateStream = (function() { return [codes, maxLen]; }, readBlock: function() { - var stream = this.stream; + var bytes = this.bytes; + var bytesIdx = this.bytesIdx; // read block header var hdr = this.getBits(3); @@ -400,16 +412,16 @@ var FlateStream = (function() { var b; if (hdr == 0) { // uncompressed block - if ((b = stream.getByte()) == -1) + if ((b = bytes[bytesIdx++]) == undefined) error("Bad block header in flate stream"); var blockLen = b; - if ((b = stream.getByte()) == -1) + if ((b = bytes[bytesIdx++]) == undefined) error("Bad block header in flate stream"); blockLen |= (b << 8); - if ((b = stream.getByte()) == -1) + if ((b = bytes[bytesIdx++]) == undefined) error("Bad block header in flate stream"); var check = b; - if ((b = stream.getByte()) == -1) + if ((b = bytes[bytesIdx++]) == undefined) error("Bad block header in flate stream"); check |= (b << 8); if (check != (~this.blockLen & 0xffff)) @@ -418,7 +430,7 @@ var FlateStream = (function() { var buffer = this.ensureBuffer(bufferLength + blockLen); this.bufferLength = bufferLength + blockLen; for (var n = bufferLength; n < blockLen; ++n) { - if ((b = stream.getByte()) == -1) { + if ((b = bytes[bytesIdx++]) == undefined) { this.eof = true; break; } From 558ac50d72b1c8dae5aa84ae4b6c34527ce57f1a Mon Sep 17 00:00:00 2001 From: sbarman Date: Fri, 17 Jun 2011 13:13:25 -0700 Subject: [PATCH 03/14] changed skip in FlateStream to not call getChar --- pdf.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/pdf.js b/pdf.js index 03cf6598a..e6dc776df 100644 --- a/pdf.js +++ b/pdf.js @@ -316,9 +316,8 @@ var FlateStream = (function() { return this.buffer = buffer2; }, getByte: function() { - var bufferLength = this.bufferLength; var pos = this.pos; - if (bufferLength == pos) { + while (this.bufferLength <= pos) { if (this.eof) return; this.readBlock(); @@ -341,9 +340,8 @@ var FlateStream = (function() { return this.buffer.subarray(pos, end) }, lookChar: function() { - var bufferLength = this.bufferLength; var pos = this.pos; - if (bufferLength == pos) { + while (this.bufferLength <= pos) { if (this.eof) return; this.readBlock(); @@ -352,16 +350,15 @@ var FlateStream = (function() { }, getChar: function() { var ch = this.lookChar(); - if (!ch) - return; + // 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; - while (n-- > 0) - this.getChar(); + this.pos += n; }, generateHuffmanTable: function(lengths) { var n = lengths.length; From 4593fb3949842171e7dd7988a124f80b20902d77 Mon Sep 17 00:00:00 2001 From: sbarman Date: Fri, 17 Jun 2011 17:35:56 -0700 Subject: [PATCH 04/14] Cleaned up code for tiling --- pdf.js | 70 ++++++++++++++++++++++------------------------------------ 1 file changed, 26 insertions(+), 44 deletions(-) diff --git a/pdf.js b/pdf.js index 85c9489ae..0d18b272f 100644 --- a/pdf.js +++ b/pdf.js @@ -1609,38 +1609,8 @@ var CanvasExtraState = (function() { // Start of text line (in text coordinates) this.lineX = 0.0; this.lineY = 0.0; - - this.transMatrix = IDENTITY_MATRIX; } constructor.prototype = { - applyTransform: function(point) { - var m = this.transMatrix - var x = point[0] * m[0] + point[1] * m[2] + m[4]; - var y = point[0] * m[1] + point[1] * m[3] + m[5]; - return [x,y]; - }, - concatTransform: function(m) { - var tm = this.transMatrix; - - var a = m[0] * tm[0] + m[1] * tm[2]; - var b = m[0] * tm[1] + m[1] * tm[3]; - var c = m[2] * tm[0] + m[3] * tm[2]; - var d = m[2] * tm[1] + m[3] * tm[3]; - var e = m[4] * tm[0] + m[5] * tm[2] + tm[4]; - var f = m[4] * tm[1] + m[5] * tm[3] + tm[5]; - this.transMatrix = [a, b, c, d, e, f] - }, - getInvTransform: function(matrix) { - var m = this.transMatrix; - var det = 1 / (m[0] * m[3] - m[1] * m[2]); - var a = m[3] * det; - var b = -m[1] * det; - var c = -m[2] * det; - var d = m[0] * det; - var e = (m[2] * m[5] - m[3] * m[4]) * det; - var f = (m[1] * m[4] - m[0] * m[5]) * det; - return [a, b, c, d, e, f] - } }; return constructor; })(); @@ -1942,7 +1912,6 @@ var CanvasGraphics = (function() { }, transform: function(a, b, c, d, e, f) { this.ctx.transform(a, b, c, d, e, f); - this.current.concatTransform([a,b,c,d,e,f]); }, // Path @@ -2181,6 +2150,22 @@ var CanvasGraphics = (function() { } }, tilingFill: function(pattern) { + function applyMatrix(point, m) { + var x = point[0] * m[0] + point[1] * m[2] + m[4]; + var y = point[0] * m[1] + point[1] * m[3] + m[5]; + return [x,y]; + }; + + function multiply(m, tm) { + var a = m[0] * tm[0] + m[1] * tm[2]; + var b = m[0] * tm[1] + m[1] * tm[3]; + var c = m[2] * tm[0] + m[3] * tm[2]; + var d = m[2] * tm[1] + m[3] * tm[3]; + var e = m[4] * tm[0] + m[5] * tm[2] + tm[4]; + var f = m[4] * tm[1] + m[5] * tm[3] + tm[5]; + return [a, b, c, d, e, f] + }; + this.save(); var dict = pattern.dict; @@ -2196,10 +2181,9 @@ var CanvasGraphics = (function() { // not sure what to do with this var tilingType = dict.get("TilingType"); - var tempExtra = new CanvasExtraState(); var matrix = dict.get("Matrix"); - if (matrix && IsArray(matrix) && 6 == matrix.length) - tempExtra.transMatrix = matrix; + if (!matrix) + matrix = [1, 0, 0, 1, 0, 0]; var bbox = dict.get("BBox"); var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3]; @@ -2208,9 +2192,9 @@ var CanvasGraphics = (function() { var ystep = dict.get("YStep"); // top left corner should correspond to the top left of the bbox - var topLeft = tempExtra.applyTransform([x0,y0]); + var topLeft = applyMatrix([x0,y0], matrix); // we want the canvas to be as large as the step size - var botRight = tempExtra.applyTransform([x0 + xstep, y0 + ystep]); + var botRight = applyMatrix([x0 + xstep, y0 + ystep], matrix); var tmpCanvas = document.createElement("canvas"); tmpCanvas.width = Math.ceil(botRight[0] - topLeft[0]); @@ -2221,20 +2205,17 @@ var CanvasGraphics = (function() { var oldCtx = this.ctx; this.ctx = tmpCtx; - // normalize matrix transform so each step - // takes up the entire tmpCanvas + // normalize transform matrix so each step + // takes up the entire tmpCanvas (no white borders) if (matrix[1] === 0 && matrix[2] === 0) { matrix[0] = tmpCanvas.width / xstep; matrix[3] = tmpCanvas.height / ystep; - tempExtra.transMatrix = matrix; - topLeft = tempExtra.applyTransform([x0,y0]); + topLeft = applyMatrix([x0,y0], matrix); } // move the top left corner of bounding box to [0,0] - tempExtra.transMatrix = [1, 0, 0, 1, -topLeft[0], -topLeft[1]]; - tempExtra.concatTransform(matrix); - matrix = tempExtra.transMatrix; - + matrix = multiply(matrix, [1, 0, 0, 1, -topLeft[0], -topLeft[1]]); + this.transform.apply(this, matrix); if (bbox && IsArray(bbox) && 4 == bbox.length) { @@ -2253,6 +2234,7 @@ var CanvasGraphics = (function() { this.ctx = oldCtx; this.restore(); + warn("Inverse pattern is painted"); var pattern = this.ctx.createPattern(tmpCanvas, "repeat"); this.ctx.fillStyle = pattern; }, From 569e8c1d02079811a10c523f8b1446c5c2f6e083 Mon Sep 17 00:00:00 2001 From: sbarman Date: Fri, 17 Jun 2011 17:46:02 -0700 Subject: [PATCH 05/14] clean up tiling --- pdf.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/pdf.js b/pdf.js index 2fc51a21c..9c8b7e6a0 100644 --- a/pdf.js +++ b/pdf.js @@ -2110,11 +2110,10 @@ var CanvasGraphics = (function() { }, setFillColorSpace: function(space) { // TODO real impl - if (space.name === "Pattern") { + if (space.name === "Pattern") this.colorspace = "Pattern"; - } else { + else this.colorspace = null; - } }, setStrokeColor: function(/*...*/) { // TODO real impl @@ -2150,11 +2149,10 @@ var CanvasGraphics = (function() { var pattern = xref.fetchIfRef(patternRes.get(patternName.name)); var type = pattern.dict.get("PatternType"); - if (type === 1) { + if (type === 1) this.tilingFill(pattern); - } else { + else error("Unhandled pattern type"); - } } } else { // TODO real impl @@ -2218,7 +2216,7 @@ var CanvasGraphics = (function() { this.ctx = tmpCtx; // normalize transform matrix so each step - // takes up the entire tmpCanvas (no white borders) + // takes up the entire tmpCanvas (need to remove white borders) if (matrix[1] === 0 && matrix[2] === 0) { matrix[0] = tmpCanvas.width / xstep; matrix[3] = tmpCanvas.height / ystep; From 74024df89a4fdcc70fd8311a4dc3639b9dcdef2f Mon Sep 17 00:00:00 2001 From: sbarman Date: Fri, 17 Jun 2011 17:48:44 -0700 Subject: [PATCH 06/14] clean up tiling --- pdf.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pdf.js b/pdf.js index 9c8b7e6a0..20a91d9af 100644 --- a/pdf.js +++ b/pdf.js @@ -2184,8 +2184,9 @@ var CanvasGraphics = (function() { error("Unsupported paint type"); } else { // should go to default for color space - this.ctx.fillStyle = this.makeCssRgb(1, 1, 1); - this.ctx.strokeStyle = this.makeCssRgb(0, 0, 0); + var ctx = this.ctx; + ctx.fillStyle = this.makeCssRgb(1, 1, 1); + ctx.strokeStyle = this.makeCssRgb(0, 0, 0); } // not sure what to do with this From 9d286d789e4a7233f0d9f358fd14207fd53584e5 Mon Sep 17 00:00:00 2001 From: sbarman Date: Sun, 19 Jun 2011 15:01:52 -0500 Subject: [PATCH 07/14] cleanup --- pdf.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/pdf.js b/pdf.js index 233773cb4..bf8ff1d97 100644 --- a/pdf.js +++ b/pdf.js @@ -1738,6 +1738,7 @@ var CanvasExtraState = (function() { this.fontSize = 0.0; this.textMatrix = IDENTITY_MATRIX; this.leading = 0.0; + this.colorSpace = "DeviceRGB"; // Current point (in user coordinates) this.x = 0.0; this.y = 0.0; @@ -2424,9 +2425,9 @@ var CanvasGraphics = (function() { setFillColorSpace: function(space) { // TODO real impl if (space.name === "Pattern") - this.colorspace = "Pattern"; + this.current.colorSpace = "Pattern"; else - this.colorspace = null; + this.current.colorSpace = "DeviceRGB"; }, setStrokeColor: function(/*...*/) { // TODO real impl @@ -2451,7 +2452,7 @@ var CanvasGraphics = (function() { setFillColorN: function(/*...*/) { // TODO real impl var args = arguments; - if (this.colorspace == "Pattern") { + if (this.current.colorSpace == "Pattern") { var patternName = args[0]; if (IsName(patternName)) { var xref = this.xref; @@ -2502,12 +2503,9 @@ var CanvasGraphics = (function() { ctx.strokeStyle = this.makeCssRgb(0, 0, 0); } - // not sure what to do with this - var tilingType = dict.get("TilingType"); + TODO("TilingType"); - var matrix = dict.get("Matrix"); - if (!matrix) - matrix = [1, 0, 0, 1, 0, 0]; + var matrix = dict.get("Matrix") || IDENTITY_MATRIX; var bbox = dict.get("BBox"); var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3]; @@ -2526,7 +2524,7 @@ var CanvasGraphics = (function() { // set the new canvas element context as the graphics context var tmpCtx = tmpCanvas.getContext("2d"); - var oldCtx = this.ctx; + var savedCtx = this.ctx; this.ctx = tmpCtx; // normalize transform matrix so each step @@ -2554,11 +2552,10 @@ var CanvasGraphics = (function() { pattern.code = this.compile(pattern, xref, res, []); this.execute(pattern.code, xref, res); - // set the old context - this.ctx = oldCtx; + this.ctx = savedCtx; this.restore(); - warn("Inverse pattern is painted"); + TODO("Inverse pattern is painted"); var pattern = this.ctx.createPattern(tmpCanvas, "repeat"); this.ctx.fillStyle = pattern; }, @@ -2650,8 +2647,8 @@ var CanvasGraphics = (function() { var gradient = this.ctx.createLinearGradient(x0, y0, x1, y1); // 10 samples seems good enough for now, but probably won't work - // if there are sharp color changes. Ideally, we could see the - // current image size and base the # samples on that. + // if there are sharp color changes. Ideally, we would implement + // the spec faithfully and add lossless optimizations. var step = (t1 - t0) / 10; for (var i = t0; i <= t1; i += step) { @@ -2664,7 +2661,10 @@ var CanvasGraphics = (function() { // HACK to draw the gradient onto an infinite rectangle. // PDF gradients are drawn across the entire image while // Canvas only allows gradients to be drawn in a rectangle - // Also, larger numbers seem to cause overflow which causes + // The following bug should allow us to remove this. + // https://bugzilla.mozilla.org/show_bug.cgi?id=664884 + // + // Also, larg numbers seem to cause overflow which causes // nothing to be drawn. this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10); }, From e2933b55836e121e720aa71bb5807eb8ed812859 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sun, 19 Jun 2011 22:04:45 -0500 Subject: [PATCH 08/14] DCTDecode; text and images vertical inversion fix --- pdf.js | 71 ++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 12 deletions(-) diff --git a/pdf.js b/pdf.js index f03615a64..ab9998264 100644 --- a/pdf.js +++ b/pdf.js @@ -509,9 +509,38 @@ var FlateStream = (function() { return constructor; })(); +var JpegStream = (function() { + function constructor(bytes, dict) { + // TODO per poppler, some images may have "junk" before that need to be removed + this.bytes = bytes; + this.dict = dict; + + // create DOM image + var buffer = "", i, n = bytes.length; + for (i = 0; i < n; ++i) { + buffer += String.fromCharCode(bytes[i]); + } + var img = new Image(); + img.src = "data:image/jpeg;base64," + window.btoa(buffer); + this.domImage = img; + } + + constructor.prototype = { + getChar: function() { + TODO("read direct pixels data"); + }, + getImage: function() { + return this.domImage; + } + }; + + return constructor; +})(); + var PredictorStream = (function() { function constructor(stream, params) { this.stream = stream; + this.dict = stream.dict; this.predictor = params.get("Predictor") || 1; if (this.predictor <= 1) { return stream; // no prediction @@ -1177,15 +1206,15 @@ var Parser = (function() { this.encAlgorithm, this.keyLength); } - stream = this.filter(stream, dict); + stream = this.filter(stream, dict, length); stream.parameters = dict; return stream; }, - filter: function(stream, dict) { + filter: function(stream, dict, length) { var filter = dict.get2("Filter", "F"); var params = dict.get2("DecodeParms", "DP"); if (IsName(filter)) - return this.makeFilter(stream, filter.name, params); + return this.makeFilter(stream, filter.name, length, params); if (IsArray(filter)) { var filterArray = filter; var paramsArray = params; @@ -1196,18 +1225,21 @@ var Parser = (function() { params = null; if (IsArray(paramsArray) && (i in paramsArray)) params = paramsArray[i]; - stream = this.makeFilter(stream, filter.name, params); + stream = this.makeFilter(stream, filter.name, length, params); } } } return stream; }, - makeFilter: function(stream, name, params) { + makeFilter: function(stream, name, length, params) { if (name == "FlateDecode" || name == "Fl") { if (params) { return new PredictorStream(new FlateStream(stream), params); } return new FlateStream(stream); + } else if (name == "DCTDecode") { + var bytes = stream.getBytes(length); + return new JpegStream(bytes, stream.dict); } else { error("filter '" + name + "' not supported yet"); } @@ -2398,11 +2430,17 @@ var CanvasGraphics = (function() { var fontName = ""; var fontDescriptor = font.get("FontDescriptor"); - if (fontDescriptor.num) { + if (fontDescriptor && fontDescriptor.num) { var fontDescriptor = this.xref.fetchIfRef(fontDescriptor); fontName = fontDescriptor.get("FontName").name.replace("+", "_"); Fonts.active = fontName; } + if (!fontName) { + // fontDescriptor is not available, fallback to default font (TODO ?) + this.current.fontSize = size; + this.ctx.font = this.current.fontSize + 'px sans-serif'; + return; + } this.current.fontSize = size; this.ctx.font = this.current.fontSize +'px "' + fontName + '"'; @@ -2431,9 +2469,9 @@ var CanvasGraphics = (function() { }, showText: function(text) { this.ctx.save(); - this.ctx.translate(0, 2 * this.current.y); - this.ctx.scale(1, -1); this.ctx.transform.apply(this.ctx, this.current.textMatrix); + this.ctx.scale(1, -1); + this.ctx.translate(0, -2 * this.current.y); this.ctx.fillText(Fonts.chars2Unicode(text), this.current.x, this.current.y); this.current.x += this.ctx.measureText(text).width; @@ -2679,8 +2717,17 @@ var CanvasGraphics = (function() { error("Invalid image width or height"); var ctx = this.ctx; + // scale the image to the unit square - ctx.scale(1/w, 1/h); + ctx.scale(1/w, -1/h); + + if (image instanceof JpegStream) { + var domImage = image.getImage(); + ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height, + 0, -h, w, h); + this.restore(); + return; + } var interpolate = dict.get2("Interpolate", "I"); if (!IsBool(interpolate)) @@ -2786,7 +2833,7 @@ var CanvasGraphics = (function() { switch (numComps) { case 1: for (var i = 0; i < length; i += 4) { - var p = imgArray[imageIdx++]; + var p = imgArray[imgIdx++]; pixels[i] = p; pixels[i+1] = p; pixels[i+2] = p; @@ -2813,7 +2860,7 @@ var CanvasGraphics = (function() { switch (numComps) { case 1: for (var i = 0; i < length; i += 4) { - var p = imgArray[imageIdx++]; + var p = imgArray[imgIdx++]; pixels[i] = p; pixels[i+1] = p; pixels[i+2] = p; @@ -2833,7 +2880,7 @@ var CanvasGraphics = (function() { } } tmpCtx.putImageData(imgData, 0, 0); - ctx.drawImage(tmpCanvas, 0, 0); + ctx.drawImage(tmpCanvas, 0, -h); this.restore(); }, From 9419a0d4b87afaa0d7aeb0d749f1621feac612dc Mon Sep 17 00:00:00 2001 From: sbarman Date: Mon, 20 Jun 2011 13:38:27 -0700 Subject: [PATCH 09/14] Used symbolic constants --- pdf.js | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/pdf.js b/pdf.js index bf8ff1d97..15b63c1ba 100644 --- a/pdf.js +++ b/pdf.js @@ -2033,6 +2033,9 @@ var CanvasGraphics = (function() { const NORMAL_CLIP = {}; const EO_CLIP = {}; + // Used for tiling patterns + const PAINT_TYPE = [null, "colored", "uncolored"]; + constructor.prototype = { translateFont: function(fontDict, xref, resources) { var descriptor = xref.fetch(fontDict.get("FontDescriptor")); @@ -2461,12 +2464,13 @@ var CanvasGraphics = (function() { error("Unable to find pattern resource"); var pattern = xref.fetchIfRef(patternRes.get(patternName.name)); - - var type = pattern.dict.get("PatternType"); - if (type === 1) - this.tilingFill(pattern); - else + + const types = [null, this.tilingFill]; + var typeNum = pattern.dict.get("PatternType"); + var patternFn = types[typeNum]; + if (!patternFn) error("Unhandled pattern type"); + patternFn.call(this, pattern); } } else { // TODO real impl @@ -2492,15 +2496,15 @@ var CanvasGraphics = (function() { this.save(); var dict = pattern.dict; + var ctx = this.ctx; - var paintType = dict.get("PaintType"); - if (paintType == 2) { - error("Unsupported paint type"); - } else { + var paintType = PAINT_TYPE[dict.get("PaintType")]; + if (paintType == "colored") { // should go to default for color space - var ctx = this.ctx; ctx.fillStyle = this.makeCssRgb(1, 1, 1); ctx.strokeStyle = this.makeCssRgb(0, 0, 0); + } else { + error("Unsupported paint type"); } TODO("TilingType"); @@ -2524,7 +2528,7 @@ var CanvasGraphics = (function() { // set the new canvas element context as the graphics context var tmpCtx = tmpCanvas.getContext("2d"); - var savedCtx = this.ctx; + var savedCtx = ctx; this.ctx = tmpCtx; // normalize transform matrix so each step @@ -2663,9 +2667,6 @@ var CanvasGraphics = (function() { // Canvas only allows gradients to be drawn in a rectangle // The following bug should allow us to remove this. // https://bugzilla.mozilla.org/show_bug.cgi?id=664884 - // - // Also, larg numbers seem to cause overflow which causes - // nothing to be drawn. this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10); }, From ac2378ce2ca3f4909a477111010fdab6aaa8ceed Mon Sep 17 00:00:00 2001 From: sbarman Date: Mon, 20 Jun 2011 13:47:42 -0700 Subject: [PATCH 10/14] fixed ExtraStateContext.colorSpace impl --- pdf.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/pdf.js b/pdf.js index 15b63c1ba..001784eea 100644 --- a/pdf.js +++ b/pdf.js @@ -1738,7 +1738,7 @@ var CanvasExtraState = (function() { this.fontSize = 0.0; this.textMatrix = IDENTITY_MATRIX; this.leading = 0.0; - this.colorSpace = "DeviceRGB"; + this.colorSpace = null; // Current point (in user coordinates) this.x = 0.0; this.y = 0.0; @@ -2454,9 +2454,17 @@ var CanvasGraphics = (function() { }, setFillColorN: function(/*...*/) { // TODO real impl - var args = arguments; + var colorSpace = this.current.colorSpace; + if (!colorSpace) { + var stateStack = this.stateStack; + var i = stateStack.length - 1; + while (!colorSpace && i >= 0) { + colorSpace = stateStack[i--].colorSpace; + } + } + if (this.current.colorSpace == "Pattern") { - var patternName = args[0]; + var patternName = arguments[0]; if (IsName(patternName)) { var xref = this.xref; var patternRes = xref.fetchIfRef(this.res.get("Pattern")); From 011f7129bec57b29233bc96cbce358b3a4098a6f Mon Sep 17 00:00:00 2001 From: sbarman Date: Mon, 20 Jun 2011 14:10:10 -0700 Subject: [PATCH 11/14] used typeof b == undefined --- pdf.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pdf.js b/pdf.js index 4fd3fedce..5b57d0db6 100644 --- a/pdf.js +++ b/pdf.js @@ -288,7 +288,7 @@ var FlateStream = (function() { while (codeSize < maxLen) { var b; - if ((b = bytes[bytesPos++]) == undefined) + if (typeof (b = bytes[bytesPos++]) == "undefined") error("Bad encoding in flate stream"); codeBuf |= (b << codeSize); codeSize += 8; @@ -416,16 +416,16 @@ var FlateStream = (function() { var b; if (hdr == 0) { // uncompressed block - if ((b = bytes[bytesPos++]) == undefined) + if (typeof (b = bytes[bytesPos++]) == "undefined") error("Bad block header in flate stream"); var blockLen = b; - if ((b = bytes[bytesPos++]) == undefined) + if (typeof (b = bytes[bytesPos++]) == "undefined") error("Bad block header in flate stream"); blockLen |= (b << 8); - if ((b = bytes[bytesPos++]) == undefined) + if (typeof (b = bytes[bytesPos++]) == "undefined") error("Bad block header in flate stream"); var check = b; - if ((b = bytes[bytesPos++]) == undefined) + if (typeof (b = bytes[bytesPos++]) == "undefined") error("Bad block header in flate stream"); check |= (b << 8); if (check != (~this.blockLen & 0xffff)) @@ -434,7 +434,7 @@ var FlateStream = (function() { var buffer = this.ensureBuffer(bufferLength + blockLen); this.bufferLength = bufferLength + blockLen; for (var n = bufferLength; n < blockLen; ++n) { - if ((b = bytes[bytesPos++]) == undefined) { + if (typeof (b = bytes[bytesPos++]) == "undefined") { this.eof = true; break; } From 96e82daf16f5491cfa02b6591db2c9bccb9b885f Mon Sep 17 00:00:00 2001 From: sbarman Date: Mon, 20 Jun 2011 14:14:28 -0700 Subject: [PATCH 12/14] forgot to changed to typeof b == undefined at one location --- pdf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdf.js b/pdf.js index 5b57d0db6..8a844be6c 100644 --- a/pdf.js +++ b/pdf.js @@ -267,7 +267,7 @@ var FlateStream = (function() { var b; while (codeSize < bits) { - if ((b = bytes[bytesPos++]) == undefined) + if (typeof (b = bytes[bytesPos++]) == "undefined") error("Bad encoding in flate stream"); codeBuf |= b << codeSize; codeSize += 8; From 2b79f5bcb0d2814916774e1df9c6895e3aa97cb9 Mon Sep 17 00:00:00 2001 From: sbarman Date: Mon, 20 Jun 2011 14:22:11 -0700 Subject: [PATCH 13/14] switched to using const enums --- pdf.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pdf.js b/pdf.js index 001784eea..aca7e0b5a 100644 --- a/pdf.js +++ b/pdf.js @@ -2034,7 +2034,7 @@ var CanvasGraphics = (function() { const EO_CLIP = {}; // Used for tiling patterns - const PAINT_TYPE = [null, "colored", "uncolored"]; + const PAINT_TYPE_COLORED = 1, PAINT_TYPE_UNCOLORED = 2; constructor.prototype = { translateFont: function(fontDict, xref, resources) { @@ -2506,12 +2506,15 @@ var CanvasGraphics = (function() { var dict = pattern.dict; var ctx = this.ctx; - var paintType = PAINT_TYPE[dict.get("PaintType")]; - if (paintType == "colored") { + var paintType = dict.get("PaintType"); + switch (paintType) { + case PAINT_TYPE_COLORED: // should go to default for color space ctx.fillStyle = this.makeCssRgb(1, 1, 1); ctx.strokeStyle = this.makeCssRgb(0, 0, 0); - } else { + break; + case PAINT_TYPE_UNCOLORED: + default: error("Unsupported paint type"); } From a6c48e3bc2abc0a6ed274d1f7b1014bc12fd4f0d Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Mon, 20 Jun 2011 19:36:40 -0400 Subject: [PATCH 14/14] cleanup DCTStream rendering code a tad --- pdf.js | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/pdf.js b/pdf.js index 758ea8297..40044300c 100644 --- a/pdf.js +++ b/pdf.js @@ -48,6 +48,14 @@ function shadow(obj, prop, value) { return value; } +function bytesToString(bytes) { + var str = ""; + var length = bytes.length; + for (var n = 0; n < length; ++n) + str += String.fromCharCode(bytes[n]); + return str; +} + var Stream = (function() { function constructor(arrayBuffer, start, length, dict) { this.bytes = Uint8Array(arrayBuffer); @@ -518,26 +526,20 @@ var FlateStream = (function() { 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.bytes = bytes; + // TODO: per poppler, some images may have "junk" before that need to be removed this.dict = dict; // create DOM image - var buffer = "", i, n = bytes.length; - for (i = 0; i < n; ++i) { - buffer += String.fromCharCode(bytes[i]); - } var img = new Image(); - img.src = "data:image/jpeg;base64," + window.btoa(buffer); + img.src = "data:image/jpeg;base64," + window.btoa(bytesToString(bytes)); this.domImage = img; } constructor.prototype = { - getChar: function() { - TODO("read direct pixels data"); - }, getImage: function() { return this.domImage; } @@ -2441,7 +2443,7 @@ var CanvasGraphics = (function() { Fonts.active = fontName; } if (!fontName) { - // fontDescriptor is not available, fallback to default font (TODO ?) + // TODO: fontDescriptor is not available, fallback to default font this.current.fontSize = size; this.ctx.font = this.current.fontSize + 'px sans-serif'; return; @@ -2854,10 +2856,13 @@ var CanvasGraphics = (function() { // scale the image to the unit square ctx.scale(1/w, -1/h); - if (image instanceof JpegStream) { + // If the platform can render the image format directly, the + // stream has a getImage property which directly returns a + // suitable DOM Image object. + if (image.getImage) { var domImage = image.getImage(); ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height, - 0, -h, w, h); + 0, -h, w, h); this.restore(); return; }