From 60989ee3b9e9fc590de5153490e4d288eb61b3ae Mon Sep 17 00:00:00 2001 From: sbarman Date: Thu, 16 Jun 2011 17:03:15 -0700 Subject: [PATCH 01/46] initial implementation of predictor --- pdf.js | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 175 insertions(+), 1 deletion(-) diff --git a/pdf.js b/pdf.js index 904ff3906..c2b8f160f 100644 --- a/pdf.js +++ b/pdf.js @@ -230,7 +230,24 @@ var FlateStream = (function() { 0x50007, 0x50017, 0x5000f, 0x00000 ]), 5]; - function constructor(stream) { + function constructor(stream, params) { + if (IsDict(params)) { + var predType = params.get("Predictor"); + if (predType && predType > 1) { + var colors = params.get("Colors"); + if (!colors) + colors = 1; + var bpc = params.get("BitsPerComponent"); + if (!bpc) + bpc = 8; + var cols = params.get("Columns"); + if (!cols) + cols = 1; + + this.pred = new FilterPredictor(this, predType, cols, + colors, bpc); + } + } this.stream = stream; this.dict = stream.dict; var cmf = stream.getByte(); @@ -506,6 +523,163 @@ var FlateStream = (function() { return constructor; })(); +var FilterPredictor = (function() { + function constructor(str, type, width, colors, bits) { + this.str = str; + this.type = type; + this.width = width; + this.colors = colors; + this.bits = bits; + + this.nVals = width * colors; + this.pixBytes = (colors * bits + 7) >> 3; + var rowBytes = (width * colors * bits + 7) >> 3; + this.rowBytes = rowBytes; + + if (width < 0 || colors < 0 || bits < 0 ||bits > 16) + error("Invalid predictor"); + + var prevLine = []; + for (var i = 0; i < rowBytes; ++i) + prevLine.push(0); + this.prevLine = prevLine; + this.prevIdx = rowBytes; + } + + constructor.prototype = { + getByte: function() { + if (this.prevIdx >= this.rowBytes) { + if(!this.getNextLine()) + return; + } + return this.prevLine[this.prevIdx]; + }, + getNextLine: function() { + if (this.type >= 10) { + var curType = this.str.getRawByte(); + if (!curType) + return; + curType += 10; + } else { + var curType = this.type; + } + + var line = []; + for (var i = 0; i < this.rowBytes - this.pixBytes; i++) + line.push(this.str.getRawByte()); + + var pixBytes = this.pixBytes; + var rowBytes = this.rowBytes; + var prevLine = this.prevLine; + + var upLeftBuf = []; + for (var i = 0, ii = pixBytes + 1; i < ii; ++i) + upLeftBuf.push(0); + + for (var i = pixBytes, ii = rowBybtes; i < ii; ++i) { + for (var j = pixBytes; j > 0; --j) { + upLeftBuf[j] = upLeftBuf[j - 1]; + upLeftBuf[0] = prevLine[i]; + + var c = line[i - pixBytes]; + if (!c) { + if (i > pixBytes) + break; + return; + } + switch (curType) { + case 11: + prevLine[i] = prevLine[i - pixBytes] + c; + break; + case 12: + prevLine[i] = prevLine[i] + c; + break; + case 13: + prevLine[i] = ((prevLine[i - pixBytes] + + prevLine[i]) >> 1) + c; + break; + case 14: + var left = prevLine[i - pixBytes]; + var up = prevLine[i]; + var upLeft = upLeftBuf[pixBytes]; + var p = left + up - upLeft; + + var pa = p - left; + if (pa < 0) + pa = -pa; + var pb = p - up; + if (pb < 0) + pb = -pb; + var pc = p - upLeft; + if (pc < 0) + pc = -pc; + + if (pa <= pb && pa <= pc) + prevLine[i] = left + c; + else if (pb <= pc) + prevLine[i] = up + c; + else + prevLine[i] = upLeft + c; + break; + case 10: + default: + prevLine[i] = c; + break; + } + } + } + var bits = this.bits; + var colors = this.colors; + + if (curPred === 2) { + if (bits === 1) { + var inbuf = prevLine[pixBytes - 1]; + for (var i = pixBytes; i < rowBytes; i+= 8) { + inBuf = (inBuf << 8) | prevLine[i]; + prevLine[i] ^= inBuf >> colors; + } + } else if (bits === 8) { + for (var i = pixBytes; i < rowBytes; ++i) + prevLine[i] += prevLine[i - colors]; + } else { + for (var i = 0, ii = colors + 1; i < ii; ++i) + upLeftBuf[i] = 0; + var bitMask = (1 << bits) - 1; + var inbuf = 0, outbut = 0; + var inbits = 0, outbits = 0; + var j = pixBytes, k = pixBytes; + var width = this.width; + for (var i = 0; i < width; ++i) { + for (var kk = 0; kk < colors; ++kk) { + if (inbits < bits) { + inbuf = (inbuf << 8) | (prevLine[j++] & 255); + inbits += 8; + } + upLeftBuf[kk] = (upLeftBuf[kk] + (inbuf >> + (inbits - bits))) & bitMask; + inbits -= bits; + outbuf = (outbuf << bits) | upLeftBuf[kk]; + outbits += bits; + if (outbits >= 8) { + prevLine[k++] = (outbuf >> (outbits - 8)); + outbits -= 8; + } + } + } + if (outbits > 0) { + prevLine[k++] = (outbuf << (8 - outbits)) + + (inbuf & ((1 << (8 - outbits)) - 1)) + } + } + } + prevIdx = pixBytes; + return true; + } + }; + + return constructor; +})(); + var DecryptStream = (function() { function constructor(str, fileKey, encAlgorithm, keyLength) { // TODO From ce09870e2b9e1420d33af3be3b2bc076383ed233 Mon Sep 17 00:00:00 2001 From: sbarman Date: Fri, 17 Jun 2011 13:32:26 -0700 Subject: [PATCH 02/46] Rearranged predictor interface --- pdf.js | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/pdf.js b/pdf.js index 256e56adf..b591c20f1 100644 --- a/pdf.js +++ b/pdf.js @@ -232,23 +232,6 @@ var FlateStream = (function() { ]), 5]; function constructor(stream, params) { - if (IsDict(params)) { - var predType = params.get("Predictor"); - if (predType && predType > 1) { - var colors = params.get("Colors"); - if (!colors) - colors = 1; - var bpc = params.get("BitsPerComponent"); - if (!bpc) - bpc = 8; - var cols = params.get("Columns"); - if (!cols) - cols = 1; - - this.pred = new FilterPredictor(this, predType, cols, - colors, bpc); - } - } this.stream = stream; this.dict = stream.dict; var cmf = stream.getByte(); @@ -1297,9 +1280,25 @@ var Parser = (function() { }, makeFilter: function(stream, name, params) { if (name == "FlateDecode" || name == "Fl") { - if (params) - error("params not supported yet for FlateDecode"); - return new FlateStream(stream); + var flateStr = new FlateStream(stream); + if (IsDict(params)) { + var predType = params.get("Predictor"); + if (predType && predType > 1) { + var colors = params.get("Colors"); + if (!colors) + colors = 1; + var bpc = params.get("BitsPerComponent"); + if (!bpc) + bpc = 8; + var cols = params.get("Columns"); + if (!cols) + cols = 1; + + flateStr = new FilterPredictor(flateStr, predType, cols, + colors, bpc); + } + } + return flateStr; } else { error("filter '" + name + "' not supported yet"); } From 3da5245abfa43482e2ebe55dad67ef3d99bb102a Mon Sep 17 00:00:00 2001 From: sbarman Date: Fri, 17 Jun 2011 16:06:02 -0700 Subject: [PATCH 03/46] Working version of predictors --- pdf.js | 154 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 82 insertions(+), 72 deletions(-) diff --git a/pdf.js b/pdf.js index b591c20f1..e39fcf6ff 100644 --- a/pdf.js +++ b/pdf.js @@ -18,6 +18,7 @@ function warn(msg) { } function error(msg) { + console.trace(); throw new Error(msg); } @@ -231,7 +232,7 @@ var FlateStream = (function() { 0x50007, 0x50017, 0x5000f, 0x00000 ]), 5]; - function constructor(stream, params) { + function constructor(stream) { this.stream = stream; this.dict = stream.dict; var cmf = stream.getByte(); @@ -510,6 +511,8 @@ var FlateStream = (function() { var FilterPredictor = (function() { function constructor(str, type, width, colors, bits) { this.str = str; + this.dict = str.dict; + this.type = type; this.width = width; this.colors = colors; @@ -536,11 +539,17 @@ var FilterPredictor = (function() { if(!this.getNextLine()) return; } - return this.prevLine[this.prevIdx]; + return this.prevLine[this.prevIdx++]; + }, + getBytes: function(length) { + var buf = new Uint8Array(length); + for (var i = 0; i < length; ++i) + buf[i] = this.getByte(); + return buf; }, getNextLine: function() { if (this.type >= 10) { - var curType = this.str.getRawByte(); + var curType = this.str.getByte(); if (!curType) return; curType += 10; @@ -550,7 +559,7 @@ var FilterPredictor = (function() { var line = []; for (var i = 0; i < this.rowBytes - this.pixBytes; i++) - line.push(this.str.getRawByte()); + line.push(this.str.getByte()); var pixBytes = this.pixBytes; var rowBytes = this.rowBytes; @@ -560,62 +569,62 @@ var FilterPredictor = (function() { for (var i = 0, ii = pixBytes + 1; i < ii; ++i) upLeftBuf.push(0); - for (var i = pixBytes, ii = rowBybtes; i < ii; ++i) { + for (var i = pixBytes, ii = rowBytes; i < ii; ++i) { for (var j = pixBytes; j > 0; --j) { upLeftBuf[j] = upLeftBuf[j - 1]; - upLeftBuf[0] = prevLine[i]; + } + upLeftBuf[0] = prevLine[i]; - var c = line[i - pixBytes]; - if (!c) { - if (i > pixBytes) - break; - return; - } - switch (curType) { - case 11: - prevLine[i] = prevLine[i - pixBytes] + c; + var c = line[i - pixBytes]; + if (c == undefined) { + if (i > pixBytes) break; - case 12: - prevLine[i] = prevLine[i] + c; - break; - case 13: - prevLine[i] = ((prevLine[i - pixBytes] - + prevLine[i]) >> 1) + c; - break; - case 14: - var left = prevLine[i - pixBytes]; - var up = prevLine[i]; - var upLeft = upLeftBuf[pixBytes]; - var p = left + up - upLeft; + return; + } + switch (curType) { + case 11: + prevLine[i] = prevLine[i - pixBytes] + c; + break; + case 12: + prevLine[i] = prevLine[i] + c; + break; + case 13: + prevLine[i] = ((prevLine[i - pixBytes] + + prevLine[i]) >> 1) + c; + break; + case 14: + var left = prevLine[i - pixBytes]; + var up = prevLine[i]; + var upLeft = upLeftBuf[pixBytes]; + var p = left + up - upLeft; - var pa = p - left; - if (pa < 0) - pa = -pa; - var pb = p - up; - if (pb < 0) + var pa = p - left; + if (pa < 0) + pa = -pa; + var pb = p - up; + if (pb < 0) pb = -pb; - var pc = p - upLeft; - if (pc < 0) - pc = -pc; + var pc = p - upLeft; + if (pc < 0) + pc = -pc; - if (pa <= pb && pa <= pc) - prevLine[i] = left + c; - else if (pb <= pc) - prevLine[i] = up + c; - else - prevLine[i] = upLeft + c; - break; - case 10: - default: - prevLine[i] = c; - break; - } + if (pa <= pb && pa <= pc) + prevLine[i] = left + c; + else if (pb <= pc) + prevLine[i] = up + c; + else + prevLine[i] = upLeft + c; + break; + case 10: + default: + prevLine[i] = c; + break; } } var bits = this.bits; var colors = this.colors; - if (curPred === 2) { + if (curType === 2) { if (bits === 1) { var inbuf = prevLine[pixBytes - 1]; for (var i = pixBytes; i < rowBytes; i+= 8) { @@ -656,7 +665,7 @@ var FilterPredictor = (function() { } } } - prevIdx = pixBytes; + this.prevIdx = pixBytes; return true; } }; @@ -680,9 +689,9 @@ var Name = (function() { } constructor.prototype = { - toString: function() { - return this.name; - } + toString: function() { + return this.name; + } }; return constructor; @@ -694,9 +703,9 @@ var Cmd = (function() { } constructor.prototype = { - toString: function() { - return this.cmd; - } + toString: function() { + return this.cmd; + } }; return constructor; @@ -709,18 +718,18 @@ var Dict = (function() { constructor.prototype = { get: function(key) { - return this.map[key]; - }, - get2: function(key1, key2) { - return this.get(key1) || this.get(key2); - }, - has: function(key) { - return key in this.map; - }, - set: function(key, value) { - this.map[key] = value; - }, - forEach: function(aCallback) { + return this.map[key]; + }, + get2: function(key1, key2) { + return this.get(key1) || this.get(key2); + }, + has: function(key) { + return key in this.map; + }, + set: function(key, value) { + this.map[key] = value; + }, + forEach: function(aCallback) { for (var key in this.map) aCallback(key, this.map[key]); }, @@ -784,7 +793,7 @@ function IsArray(v) { } function IsStream(v) { - return typeof v == "object" && "getChar" in v; + return typeof v == "object" && "getByte" in v; } function IsRef(v) { @@ -1294,6 +1303,7 @@ var Parser = (function() { if (!cols) cols = 1; + log("Predictor being used"); flateStr = new FilterPredictor(flateStr, predType, cols, colors, bpc); } @@ -2529,7 +2539,7 @@ var CanvasGraphics = (function() { var smask = image.dict.get("SMask"); smask = xref.fetchIfRef(smask); - if (IsStream(smask)) { + if (smask) { if (inline) error("cannot combine smask and inlining"); @@ -2559,8 +2569,6 @@ var CanvasGraphics = (function() { if (maskDecode) TODO("Handle mask decode"); // handle matte object - } else { - smask = null; } var tmpCanvas = document.createElement("canvas"); @@ -2573,7 +2581,7 @@ var CanvasGraphics = (function() { if (bitsPerComponent != 8) error("unhandled number of bits per component"); - if (smask) { + if (false && smask) { if (maskColorSpace.numComps != 1) error("Incorrect number of components in smask"); @@ -2711,6 +2719,8 @@ var ColorSpace = (function() { case "DeviceGray": case "G": this.numComps = 1; + case "DeviceRGB": + this.numComps = 3; break; } TODO("fill in color space constructor"); From ab6ff77b41a463f89507018ad4ddf3eacc18e93b Mon Sep 17 00:00:00 2001 From: sbarman Date: Sat, 18 Jun 2011 13:06:54 -0700 Subject: [PATCH 04/46] Fixed predictor --- pdf.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pdf.js b/pdf.js index b75ce91ec..6a1589dde 100644 --- a/pdf.js +++ b/pdf.js @@ -519,8 +519,9 @@ var FilterPredictor = (function() { this.bits = bits; this.nVals = width * colors; - this.pixBytes = (colors * bits + 7) >> 3; - var rowBytes = (width * colors * bits + 7) >> 3; + var pixBytes = (colors * bits + 7) >> 3; + this.pixBytes = pixBytes; + var rowBytes = ((width * colors * bits + 7) >> 3) + pixBytes; this.rowBytes = rowBytes; if (width < 0 || colors < 0 || bits < 0 ||bits > 16) @@ -569,7 +570,7 @@ var FilterPredictor = (function() { for (var i = 0, ii = pixBytes + 1; i < ii; ++i) upLeftBuf.push(0); - for (var i = pixBytes, ii = rowBytes; i < ii; ++i) { + for (var i = pixBytes, ii = rowBytes + pixBytes + 1; i < ii; ++i) { for (var j = pixBytes; j > 0; --j) { upLeftBuf[j] = upLeftBuf[j - 1]; } @@ -2118,7 +2119,7 @@ var CanvasGraphics = (function() { var widths = xref.fetchIfRef(fontDict.get("Widths")); var firstChar = xref.fetchIfRef(fontDict.get("FirstChar")); - assertWellFormed(IsArray(widths) && IsInteger(firstChar), + assertWellFormed(IsArray(widths) && IsInt(firstChar), "invalid font Widths or FirstChar"); var charset = []; for (var j = 0; j < widths.length; j++) { @@ -2775,9 +2776,9 @@ var CanvasGraphics = (function() { if (bitsPerComponent != 8) error("unhandled number of bits per component"); - if (false && smask) { - if (maskColorSpace.numComps != 1) - error("Incorrect number of components in smask"); + if (smask) { + //if (maskColorSpace.numComps != 1) + // error("Incorrect number of components in smask"); var numComps = colorSpace.numComps; var imgArray = image.getBytes(numComps * w * h); From 11408201180c25f45071c3527aac7b66049edaa3 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sun, 19 Jun 2011 23:23:21 -0500 Subject: [PATCH 05/46] Multi-color predictor; row predictor for sub and average --- pdf.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/pdf.js b/pdf.js index a051e57f3..f22d7c70d 100644 --- a/pdf.js +++ b/pdf.js @@ -522,13 +522,13 @@ var PredictorStream = (function() { this.colors = params.get("Colors") || 1; this.bitsPerComponent = params.get("BitsPerComponent") || 8; this.columns = params.get("Columns") || 1; - if (this.colors !== 1 || this.bitsPerComponent !== 8) { - error("Multi-color and multi-byte predictors are not supported"); + if (this.bitsPerComponent !== 8) { + error("Multi-byte predictors are not supported"); } if (this.predictor < 10 || this.predictor > 15) { error("Unsupported predictor"); } - this.currentRow = new Uint8Array(this.columns); + this.currentRow = new Uint8Array(this.columns * this.colors); this.pos = 0; this.bufferLength = 0; } @@ -536,19 +536,33 @@ var PredictorStream = (function() { constructor.prototype = { readRow : function() { var lastRow = this.currentRow; + var colors = this.colors; var predictor = this.stream.getByte(); - var currentRow = this.stream.getBytes(this.columns), i; + var currentRow = this.stream.getBytes(this.columns * colors), i; switch (predictor) { default: error("Unsupported predictor"); break; case 0: break; + case 1: + for (i = colors; i < currentRow.length; ++i) { + currentRow[i] = (currentRow[i - colors] + currentRow[i]) & 0xFF; + } + break; case 2: for (i = 0; i < currentRow.length; ++i) { currentRow[i] = (lastRow[i] + currentRow[i]) & 0xFF; } break; + case 3: + for (i = 0; i < color; ++i) { + currentRow[i] = ((lastRow[i] >> 1) + currentRow[i]) & 0xFF; + } + for (; i < currentRow.length; ++i) { + currentRow[i] = (((lastRow[i] + currentRow[i]) >> 1) + currentRow[i]) & 0xFF; + } + break; } this.pos = 0; this.bufferLength = currentRow.length; From d37af0a800927f1273b31776c0203345567fe880 Mon Sep 17 00:00:00 2001 From: Justin D'Arcangelo Date: Mon, 20 Jun 2011 21:08:50 -0400 Subject: [PATCH 06/46] Replaced zoom control with standard HTML + Zoom -
-
    -
  • 50%
  • -
  • 75%
  • -
  • 100%
  • -
  • 125%
  • -
  • 150%
  • -
  • 200%
  • -
-
diff --git a/multi-page-viewer.js b/multi-page-viewer.js index 6cb46a08a..9d9cec702 100644 --- a/multi-page-viewer.js +++ b/multi-page-viewer.js @@ -11,7 +11,7 @@ var PDFViewer = { previousPageButton: null, nextPageButton: null, pageNumberInput: null, - scaleInput: null, + scaleSelect: null, willJumpToPage: false, @@ -66,92 +66,103 @@ var PDFViewer = { removePage: function(num) { var div = document.getElementById('pageContainer' + num); - if (div && div.hasChildNodes()) { - while (div.childNodes.length > 0) { + if (div) { + while (div.hasChildNodes()) { div.removeChild(div.firstChild); } } }, drawPage: function(num) { - if (PDFViewer.pdf) { - var page = PDFViewer.pdf.getPage(num); - var div = document.getElementById('pageContainer' + num); - - if (div && !div.hasChildNodes()) { - var canvas = document.createElement('canvas'); - canvas.id = 'page' + num; - canvas.mozOpaque = true; - - // Canvas dimensions must be specified in CSS pixels. CSS pixels - // are always 96 dpi. These dimensions are 8.5in x 11in at 96dpi. - canvas.width = PDFViewer.pageWidth(); - canvas.height = PDFViewer.pageHeight(); - - var ctx = canvas.getContext('2d'); - ctx.save(); - ctx.fillStyle = 'rgb(255, 255, 255)'; - ctx.fillRect(0, 0, canvas.width, canvas.height); - ctx.restore(); - - var gfx = new CanvasGraphics(ctx); - var fonts = []; + if (!PDFViewer.pdf) { + return; + } - // page.compile will collect all fonts for us, once we have loaded them - // we can trigger the actual page rendering with page.display - page.compile(gfx, fonts); - - var fontsReady = true; - - // Inspect fonts and translate the missing one - var fontCount = fonts.length; - - for (var i = 0; i < fontCount; i++) { - var font = fonts[i]; - - if (Fonts[font.name]) { - fontsReady = fontsReady && !Fonts[font.name].loading; - continue; - } + var div = document.getElementById('pageContainer' + num); + var canvas = document.createElement('canvas'); + + if (div && !div.hasChildNodes()) { + div.appendChild(canvas); + + var page = PDFViewer.pdf.getPage(num); - new Font(font.name, font.file, font.properties); - - fontsReady = false; + canvas.id = 'page' + num; + canvas.mozOpaque = true; + + // Canvas dimensions must be specified in CSS pixels. CSS pixels + // are always 96 dpi. These dimensions are 8.5in x 11in at 96dpi. + canvas.width = PDFViewer.pageWidth(); + canvas.height = PDFViewer.pageHeight(); + + var ctx = canvas.getContext('2d'); + ctx.save(); + ctx.fillStyle = 'rgb(255, 255, 255)'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.restore(); + + var gfx = new CanvasGraphics(ctx); + var fonts = []; + + // page.compile will collect all fonts for us, once we have loaded them + // we can trigger the actual page rendering with page.display + page.compile(gfx, fonts); + + var areFontsReady = true; + + // Inspect fonts and translate the missing one + var fontCount = fonts.length; + + for (var i = 0; i < fontCount; i++) { + var font = fonts[i]; + + if (Fonts[font.name]) { + areFontsReady = areFontsReady && !Fonts[font.name].loading; + continue; } - var pageInterval; - var delayLoadFont = function() { - for (var i = 0; i < fontCount; i++) { - if (Fonts[font.name].loading) { - return; - } - } - - clearInterval(pageInterval); - - PDFViewer.drawPage(num); - } - - if (!fontsReady) { - pageInterval = setInterval(delayLoadFont, 10); - return; - } - - page.display(gfx); - div.appendChild(canvas); + new Font(font.name, font.file, font.properties); + + areFontsReady = false; } + + var pageInterval; + + var delayLoadFont = function() { + for (var i = 0; i < fontCount; i++) { + if (Fonts[font.name].loading) { + return; + } + } + + clearInterval(pageInterval); + + while (div.hasChildNodes()) { + div.removeChild(div.firstChild); + } + + PDFViewer.drawPage(num); + } + + if (!areFontsReady) { + pageInterval = setInterval(delayLoadFont, 10); + return; + } + + page.display(gfx); } }, changeScale: function(num) { - while (PDFViewer.element.childNodes.length > 0) { + while (PDFViewer.element.hasChildNodes()) { PDFViewer.element.removeChild(PDFViewer.element.firstChild); } PDFViewer.scale = num / 100; + var i; + if (PDFViewer.pdf) { - for (var i = 1; i <= PDFViewer.numberOfPages; i++) { + for (i = 1; i <= PDFViewer.numberOfPages; i++) { PDFViewer.createPage(i); } @@ -160,7 +171,21 @@ var PDFViewer = { } } - PDFViewer.scaleInput.value = Math.floor(PDFViewer.scale * 100) + '%'; + for (i = 0; i < PDFViewer.scaleSelect.childNodes; i++) { + var option = PDFViewer.scaleSelect.childNodes[i]; + + if (option.value == num) { + if (!option.selected) { + option.selected = 'selected'; + } + } else { + if (option.selected) { + option.removeAttribute('selected'); + } + } + } + + PDFViewer.scaleSelect.value = Math.floor(PDFViewer.scale * 100) + '%'; }, goToPage: function(num) { @@ -217,6 +242,11 @@ var PDFViewer = { if (PDFViewer.numberOfPages > 0) { PDFViewer.drawPage(1); } + + PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ? + 'disabled' : ''; + PDFViewer.nextPageButton.className = (PDFViewer.pageNumber === PDFViewer.numberOfPages) ? + 'disabled' : ''; } }; @@ -320,40 +350,14 @@ window.onload = function() { this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : ''; }; - PDFViewer.scaleInput = document.getElementById('scale'); - PDFViewer.scaleInput.buttonElement = document.getElementById('scaleComboBoxButton'); - PDFViewer.scaleInput.buttonElement.listElement = document.getElementById('scaleComboBoxList'); - PDFViewer.scaleInput.onchange = function(evt) { + PDFViewer.scaleSelect = document.getElementById('scaleSelect'); + PDFViewer.scaleSelect.onchange = function(evt) { PDFViewer.changeScale(parseInt(this.value)); }; - PDFViewer.scaleInput.buttonElement.onclick = function(evt) { - this.listElement.style.display = (this.listElement.style.display === 'block') ? 'none' : 'block'; - }; - PDFViewer.scaleInput.buttonElement.onmousedown = function(evt) { - if (this.className.indexOf('disabled') === -1) { - this.className = 'down'; - } - }; - PDFViewer.scaleInput.buttonElement.onmouseup = function(evt) { - this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : ''; - }; - PDFViewer.scaleInput.buttonElement.onmouseout = function(evt) { - this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : ''; - }; - - var listItems = PDFViewer.scaleInput.buttonElement.listElement.getElementsByTagName('LI'); - - for (var i = 0; i < listItems.length; i++) { - var listItem = listItems[i]; - listItem.onclick = function(evt) { - PDFViewer.changeScale(parseInt(this.innerHTML)); - PDFViewer.scaleInput.buttonElement.listElement.style.display = 'none'; - }; - } - PDFViewer.pageNumber = parseInt(PDFViewer.queryParams.page) || PDFViewer.pageNumber; - PDFViewer.scale = parseInt(PDFViewer.scaleInput.value) / 100 || 1.0; + PDFViewer.scale = parseInt(PDFViewer.scaleSelect.value) / 100 || 1.0; + PDFViewer.open(PDFViewer.queryParams.file || PDFViewer.url); window.onscroll = function(evt) { From 825f9249b23d247b78ab8eb6fa685c373362e4fa Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Mon, 20 Jun 2011 22:39:49 -0400 Subject: [PATCH 07/46] restore getChar in JpegStream --- pdf.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pdf.js b/pdf.js index 40044300c..1beeb6ca4 100644 --- a/pdf.js +++ b/pdf.js @@ -542,6 +542,9 @@ var JpegStream = (function() { constructor.prototype = { getImage: function() { return this.domImage; + }, + getChar: function() { + error("internal error: getChar is not valid on JpegStream"); } }; From 4635091c43d714a136b4d1752a7c249b34074bff Mon Sep 17 00:00:00 2001 From: sbarman Date: Mon, 20 Jun 2011 19:51:36 -0700 Subject: [PATCH 08/46] working implementation of png and tiff predictors --- pdf.js | 105 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 91 insertions(+), 14 deletions(-) diff --git a/pdf.js b/pdf.js index 488db9a9a..1e112e9ee 100644 --- a/pdf.js +++ b/pdf.js @@ -752,14 +752,66 @@ var PredictorStream = (function() { this.rowBytes = rowBytes; this.currentRow = new Uint8Array(rowBytes); + this.bufferLength = rowBytes; this.pos = rowBytes; } constructor.prototype = { readRowTiff : function() { + var currentRow = this.currentRow; + var rowBytes = this.rowBytes; + var pixBytes = this.pixBytes; + var bits = this.bits; + var colors = this.colors; + + var rawBytes = this.stream.getBytes(rowBytes - pixBytes); + + if (bits === 1) { + var inbuf = 0; + for (var i = pixBytes, j = 0; i < rowBytes; ++i, ++j) { + var c = rawBytes[j]; + inBuf = (inBuf << 8) | c; + // bitwise addition is exclusive or + // first shift inBuf and then add + currentRow[i] = (c ^ (inBuf >> colors)) & 0xFF; + // truncate inBuf (assumes colors < 16) + inBuf &= 0xFFFF; + } + } else if (bits === 8) { + for (var i = pixBytes, j = 0; i < rowBytes; ++i, ++j) + currentRow[i] = currentRow[i - colors] + rawBytes[j]; + } else { + var compArray = new Uint8Array(colors + 1); + var bitMask = (1 << bits) - 1; + var inbuf = 0, outbut = 0; + var inbits = 0, outbits = 0; + var j = 0, k = pixBytes; + var columns = this.columns; + for (var i = 0; i < columns; ++i) { + for (var kk = 0; kk < colors; ++kk) { + if (inbits < bits) { + inbuf = (inbuf << 8) | (rawBytes[j++] & 0xFF); + inbits += 8; + } + compArray[kk] = (compArray[kk] + (inbuf >> + (inbits - bits))) & bitMask; + inbits -= bits; + outbuf = (outbuf << bits) | compArray[kk]; + outbits += bits; + if (outbits >= 8) { + currentRow[k++] = (outbuf >> (outbits - 8)) & 0xFF; + outbits -= 8; + } + } + } + if (outbits > 0) { + currentRow[k++] = (outbuf << (8 - outbits)) + + (inbuf & ((1 << (8 - outbits)) - 1)) + } + } + this.pos = pixBytes; }, readRowPng : function() { - // swap the buffers var currentRow = this.currentRow; var rowBytes = this.rowBytes; @@ -773,31 +825,56 @@ var PredictorStream = (function() { case 0: break; case 1: - for (i = pixBytes; i < rowBytes; ++i) - currentRow[i] = (currentRow[i - pixBytes] + rawBytes[i]) & 0xFF; + // set the first pixel + for (var i = pixBytes, j = 0; i < rowBytes; ++i, ++j) + currentRow[i] = (currentRow[i - pixBytes] + rawBytes[j]) & 0xFF; break; case 2: - for (i = pixBytes; i < rowBytes; ++i) - currentRow[i] = (currentRow[i] + rawBytes[i]) & 0xFF; + for (var i = pixBytes, j = 0; i < rowBytes; ++i, ++j) + currentRow[i] = (currentRow[i] + rawBytes[j]) & 0xFF; break; case 3: - for (i = pixBytes; i < rowBytes; ++i) - currentRow[i] = (((currentRow[i] + currentRow[i - pixBytes]) - >> 1) + rawBytes[i]) & 0xFF; + // set the first pixel + for (i = pixBytes, j = 0; i < rowBytes; ++i, ++j) + currentRow[i] = (((currentRow[i] + currentRow[i - pixBytes]) + >> 1) + rawBytes[j]) & 0xFF; break; case 4: - for (i = pixBytes; i < rowBytes; ++i) { + // we need to save the up left pixels values. the simplest way + // is to create a new buffer + var lastRow = currentRow; + var currentRow = new Uint8Array(rowBytes); + for (var i = pixBytes, j = 0; i < rowBytes; ++i, ++j) { + var up = lastRow[i]; + var upLeft = lastRow[i - pixBytes]; var left = currentRow[i - pixBytes]; - var up = currentRow[i]; - var upLeft = + var p = left + up - upLeft; + + var pa = p - left; + if (pa < 0) + pa = -pa; + var pb = p - up; + if (pb < 0) + pb = -pb; + var pc = p - upLeft; + if (pc < 0) + pc = -pc; + + var c = rawBytes[j]; + if (pa <= pb && pa <= pc) + currentRow[i] = left + c; + else if (pb <= pc) + currentRow[i] = up + c; + else + currentRow[i] = upLeft + c; + break; + this.currentRow = currentRow; } default: error("Unsupported predictor"); break; } - this.pos = 0; - this.bufferLength = currentRow.length; - this.currentRow = currentRow; + this.pos = pixBytes; }, getByte : function() { if (this.pos >= this.bufferLength) { From 2fbd6859f22fd84c7708eebc9f18c26f3489bfd7 Mon Sep 17 00:00:00 2001 From: sbarman Date: Mon, 20 Jun 2011 20:19:13 -0700 Subject: [PATCH 09/46] Bug fix when an array of filters is passed in --- pdf.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pdf.js b/pdf.js index 40044300c..00e504a6c 100644 --- a/pdf.js +++ b/pdf.js @@ -1229,7 +1229,8 @@ var Parser = (function() { if (IsArray(filter)) { var filterArray = filter; var paramsArray = params; - for (filter in filterArray) { + for (var i = 0, ii = filter.length; i < ii; ++i) { + filter = filter[i]; if (!IsName(filter)) error("Bad filter name"); else { From 75bffdd61366c1c21697f0597eadb8aeb731fc37 Mon Sep 17 00:00:00 2001 From: sbarman Date: Mon, 20 Jun 2011 20:28:48 -0700 Subject: [PATCH 10/46] cleaned up code --- pdf.js | 170 +-------------------------------------------------------- 1 file changed, 1 insertion(+), 169 deletions(-) diff --git a/pdf.js b/pdf.js index 1e112e9ee..91d6476a2 100644 --- a/pdf.js +++ b/pdf.js @@ -526,172 +526,6 @@ var FlateStream = (function() { return constructor; })(); -/* -var FilterPredictor = (function() { - function constructor(str, type, width, colors, bits) { - this.str = str; - this.dict = str.dict; - - this.type = type; - this.width = width; - this.colors = colors; - this.bits = bits; - - this.nVals = width * colors; - var pixBytes = (colors * bits + 7) >> 3; - this.pixBytes = pixBytes; - var rowBytes = ((width * colors * bits + 7) >> 3) + pixBytes; - this.rowBytes = rowBytes; - - if (width < 0 || colors < 0 || bits < 0 ||bits > 16) - error("Invalid predictor"); - - var prevLine = []; - for (var i = 0; i < rowBytes; ++i) - prevLine.push(0); - this.prevLine = prevLine; - this.prevIdx = rowBytes; - } - - constructor.prototype = { - getByte: function() { - if (this.prevIdx >= this.rowBytes) { - if(!this.getNextLine()) - return; - } - return this.prevLine[this.prevIdx++]; - }, - getBytes: function(length) { - var buf = new Uint8Array(length); - for (var i = 0; i < length; ++i) - buf[i] = this.getByte(); - return buf; - }, - getNextLine: function() { - if (this.type >= 10) { - var curType = this.str.getByte(); - if (!curType) - return; - curType += 10; - } else { - var curType = this.type; - } - - var line = []; - for (var i = 0; i < this.rowBytes - this.pixBytes; i++) - line.push(this.str.getByte()); - - var pixBytes = this.pixBytes; - var rowBytes = this.rowBytes; - var prevLine = this.prevLine; - - var upLeftBuf = []; - for (var i = 0, ii = pixBytes + 1; i < ii; ++i) - upLeftBuf.push(0); - - for (var i = pixBytes, ii = rowBytes + pixBytes + 1; i < ii; ++i) { - for (var j = pixBytes; j > 0; --j) { - upLeftBuf[j] = upLeftBuf[j - 1]; - } - upLeftBuf[0] = prevLine[i]; - - var c = line[i - pixBytes]; - if (c == undefined) { - if (i > pixBytes) - break; - return; - } - switch (curType) { - case 11: - prevLine[i] = prevLine[i - pixBytes] + c; - break; - case 12: - prevLine[i] = prevLine[i] + c; - break; - case 13: - prevLine[i] = ((prevLine[i - pixBytes] - + prevLine[i]) >> 1) + c; - break; - case 14: - var left = prevLine[i - pixBytes]; - var up = prevLine[i]; - var upLeft = upLeftBuf[pixBytes]; - var p = left + up - upLeft; - - var pa = p - left; - if (pa < 0) - pa = -pa; - var pb = p - up; - if (pb < 0) - pb = -pb; - var pc = p - upLeft; - if (pc < 0) - pc = -pc; - - if (pa <= pb && pa <= pc) - prevLine[i] = left + c; - else if (pb <= pc) - prevLine[i] = up + c; - else - prevLine[i] = upLeft + c; - break; - case 10: - default: - prevLine[i] = c; - break; - } - } - var bits = this.bits; - var colors = this.colors; - - if (curType === 2) { - if (bits === 1) { - var inbuf = prevLine[pixBytes - 1]; - for (var i = pixBytes; i < rowBytes; i+= 8) { - inBuf = (inBuf << 8) | prevLine[i]; - prevLine[i] ^= inBuf >> colors; - } - } else if (bits === 8) { - for (var i = pixBytes; i < rowBytes; ++i) - prevLine[i] += prevLine[i - colors]; - } else { - for (var i = 0, ii = colors + 1; i < ii; ++i) - upLeftBuf[i] = 0; - var bitMask = (1 << bits) - 1; - var inbuf = 0, outbut = 0; - var inbits = 0, outbits = 0; - var j = pixBytes, k = pixBytes; - var width = this.width; - for (var i = 0; i < width; ++i) { - for (var kk = 0; kk < colors; ++kk) { - if (inbits < bits) { - inbuf = (inbuf << 8) | (prevLine[j++] & 255); - inbits += 8; - } - upLeftBuf[kk] = (upLeftBuf[kk] + (inbuf >> - (inbits - bits))) & bitMask; - inbits -= bits; - outbuf = (outbuf << bits) | upLeftBuf[kk]; - outbits += bits; - if (outbits >= 8) { - prevLine[k++] = (outbuf >> (outbits - 8)); - outbits -= 8; - } - } - } - if (outbits > 0) { - prevLine[k++] = (outbuf << (8 - outbits)) + - (inbuf & ((1 << (8 - outbits)) - 1)) - } - } - } - this.prevIdx = pixBytes; - return true; - } - }; - return constructor; -})(); -*/ // A JpegStream can't be read directly. We use the platform to render the underlying // JPEG data for us. var JpegStream = (function() { @@ -747,7 +581,7 @@ var PredictorStream = (function() { var pixBytes = (colors * bits + 7) >> 3; this.pixBytes = pixBytes; - // add an extra pixByte to represent the pixel left column 0 + // add an extra pixByte to represent the pixel left of column 0 var rowBytes = ((columns * colors * bits + 7) >> 3) + pixBytes; this.rowBytes = rowBytes; @@ -825,7 +659,6 @@ var PredictorStream = (function() { case 0: break; case 1: - // set the first pixel for (var i = pixBytes, j = 0; i < rowBytes; ++i, ++j) currentRow[i] = (currentRow[i - pixBytes] + rawBytes[j]) & 0xFF; break; @@ -834,7 +667,6 @@ var PredictorStream = (function() { currentRow[i] = (currentRow[i] + rawBytes[j]) & 0xFF; break; case 3: - // set the first pixel for (i = pixBytes, j = 0; i < rowBytes; ++i, ++j) currentRow[i] = (((currentRow[i] + currentRow[i - pixBytes]) >> 1) + rawBytes[j]) & 0xFF; From 3b38313b7a88a91961cd61d857d9e96088262d03 Mon Sep 17 00:00:00 2001 From: sbarman Date: Mon, 20 Jun 2011 20:41:24 -0700 Subject: [PATCH 11/46] cleaned up code --- pdf.js | 73 +++++++++++++++++----------------------------------------- 1 file changed, 21 insertions(+), 52 deletions(-) diff --git a/pdf.js b/pdf.js index 91d6476a2..96c0c5438 100644 --- a/pdf.js +++ b/pdf.js @@ -19,7 +19,6 @@ function warn(msg) { } function error(msg) { - console.trace(); throw new Error(msg); } @@ -540,10 +539,9 @@ var JpegStream = (function() { } constructor.prototype = { - getByte: function() { - // dummy method to pass IsStream test - error("shouldnt be called"); - }, + getChar: function() { + }, + getImage: function() { return this.domImage; } @@ -653,7 +651,6 @@ var PredictorStream = (function() { var predictor = this.stream.getByte(); var rawBytes = this.stream.getBytes(rowBytes - pixBytes); - var i; switch (predictor) { case 0: @@ -667,7 +664,7 @@ var PredictorStream = (function() { currentRow[i] = (currentRow[i] + rawBytes[j]) & 0xFF; break; case 3: - for (i = pixBytes, j = 0; i < rowBytes; ++i, ++j) + for (var i = pixBytes, j = 0; i < rowBytes; ++i, ++j) currentRow[i] = (((currentRow[i] + currentRow[i - pixBytes]) >> 1) + rawBytes[j]) & 0xFF; break; @@ -767,9 +764,6 @@ var Name = (function() { } constructor.prototype = { - toString: function() { - return this.name; - } }; return constructor; @@ -781,9 +775,6 @@ var Cmd = (function() { } constructor.prototype = { - toString: function() { - return this.cmd; - } }; return constructor; @@ -796,23 +787,22 @@ var Dict = (function() { constructor.prototype = { get: function(key) { - return this.map[key]; - }, - get2: function(key1, key2) { - return this.get(key1) || this.get(key2); - }, - has: function(key) { - return key in this.map; - }, - set: function(key, value) { - this.map[key] = value; - }, - forEach: function(aCallback) { - for (var key in this.map) - aCallback(key, this.map[key]); + return this.map[key]; + }, + get2: function(key1, key2) { + return this.get(key1) || this.get(key2); + }, + has: function(key) { + return key in this.map; + }, + set: function(key, value) { + this.map[key] = value; + }, + forEach: function(aCallback) { + for (var key in this.map) + aCallback(key, this.map[key]); } }; - return constructor; })(); @@ -865,7 +855,7 @@ function IsArray(v) { } function IsStream(v) { - return typeof v == "object" && "getByte" in v; + return typeof v == "object" && "getChar" in v; } function IsRef(v) { @@ -1364,27 +1354,6 @@ var Parser = (function() { }, makeFilter: function(stream, name, length, params) { if (name == "FlateDecode" || name == "Fl") { -/* var flateStr = new FlateStream(stream); - if (IsDict(params)) { - var predType = params.get("Predictor"); - if (predType && predType > 1) { - var colors = params.get("Colors"); - if (!colors) - colors = 1; - var bpc = params.get("BitsPerComponent"); - if (!bpc) - bpc = 8; - var cols = params.get("Columns"); - if (!cols) - cols = 1; - - log("Predictor being used"); - flateStr = new FilterPredictor(flateStr, predType, cols, - colors, bpc); - } - } - return flateStr; -*/ if (params) { return new PredictorStream(new FlateStream(stream), params); } @@ -3096,8 +3065,8 @@ var CanvasGraphics = (function() { error("unhandled number of bits per component"); if (smask) { - //if (maskColorSpace.numComps != 1) - // error("Incorrect number of components in smask"); + if (maskColorSpace.numComps != 1) + error("Incorrect number of components in smask"); var numComps = colorSpace.numComps; var imgArray = image.getBytes(numComps * w * h); From 7a3cdcdd06fdd3f41383042bde199939681936f9 Mon Sep 17 00:00:00 2001 From: sbarman Date: Mon, 20 Jun 2011 20:45:29 -0700 Subject: [PATCH 12/46] working implementation of png and tiff predictors --- pdf.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pdf.js b/pdf.js index 96c0c5438..7a38b9729 100644 --- a/pdf.js +++ b/pdf.js @@ -539,8 +539,9 @@ var JpegStream = (function() { } constructor.prototype = { + // Needed to pass IsStream test getChar: function() { - }, + }, getImage: function() { return this.domImage; @@ -3022,7 +3023,7 @@ var CanvasGraphics = (function() { var smask = image.dict.get("SMask"); smask = xref.fetchIfRef(smask); - if (smask) { + if (IsStream(smask)) { if (inline) error("cannot combine smask and inlining"); From cfda084714a466e7ac060aca678492f809a9a856 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Mon, 20 Jun 2011 22:55:32 -0500 Subject: [PATCH 13/46] Fixes 09061a2cc48da67768b regression, spaces, and default value for type --- pdf.js | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/pdf.js b/pdf.js index 1beeb6ca4..40e05cf98 100644 --- a/pdf.js +++ b/pdf.js @@ -1444,26 +1444,29 @@ var XRef = (function() { for (i = 0; i < n; ++i) { var type = 0, offset = 0, generation = 0; for (j = 0; j < typeFieldWidth; ++j) - type = (type << 8) | stream.getByte(); + type = (type << 8) | stream.getByte(); + // if type field is absent, its default value = 1 + if (typeFieldWidth == 0) + type = 1; for (j = 0; j < offsetFieldWidth; ++j) - offset = (offset << 8) | stream.getByte(); + offset = (offset << 8) | stream.getByte(); for (j = 0; j < generationFieldWidth; ++j) - generation = (generation << 8) | stream.getByte(); - var entry = new Ref(offset, generation); - if (typeFieldWidth > 0) { - switch (type) { - case 0: - entry.free = true; - break; - case 1: - entry.uncompressed = true; - break; - case 2: - break; - default: - error("Invalid XRef entry type"); - break; - } + generation = (generation << 8) | stream.getByte(); + var entry = {} + entry.offset = offset; + entry.gen = generation; + switch (type) { + case 0: + entry.free = true; + break; + case 1: + entry.uncompressed = true; + break; + case 2: + break; + default: + error("Invalid XRef entry type"); + break; } if (!this.entries[first + i]) this.entries[first + i] = entry; From a96c7830a3da452d18d6e6eb09d341b7fa390bd0 Mon Sep 17 00:00:00 2001 From: sbarman Date: Mon, 20 Jun 2011 22:44:08 -0700 Subject: [PATCH 14/46] removed redundant getChar --- pdf.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pdf.js b/pdf.js index be324d54f..fe52b5c4e 100644 --- a/pdf.js +++ b/pdf.js @@ -540,10 +540,6 @@ var JpegStream = (function() { } constructor.prototype = { - // Needed to pass IsStream test - getChar: function() { - }, - getImage: function() { return this.domImage; }, From 2069f3f241603e8510750b58d25c88144345296a Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Tue, 21 Jun 2011 03:04:33 -0400 Subject: [PATCH 15/46] style fixes and fix bug in numComps calculation (missing break) --- pdf.js | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/pdf.js b/pdf.js index fe52b5c4e..57e53061f 100644 --- a/pdf.js +++ b/pdf.js @@ -553,8 +553,7 @@ var JpegStream = (function() { var PredictorStream = (function() { function constructor(stream, params) { - var predictor = params.get("Predictor") || 1; - this.predictor = predictor; + var predictor = this.predictor = params.get("Predictor") || 1; if (predictor <= 1) return stream; // no prediction @@ -571,18 +570,13 @@ var PredictorStream = (function() { if (params.has("EarlyChange")) { error("EarlyChange predictor parameter is not supported"); } - var colors = params.get("Colors") || 1; - this.colors = colors; - var bits = params.get("BitsPerComponent") || 8; - this.bits = bits; - var columns = params.get("Columns") || 1; - this.columns = columns; + var colors = this.colors = params.get("Colors") || 1; + var bits = this.bits = params.get("BitsPerComponent") || 8; + var columns = this.columns = params.get("Columns") || 1; - var pixBytes = (colors * bits + 7) >> 3; - this.pixBytes = pixBytes; + var pixBytes = this.pixBytes = (colors * bits + 7) >> 3; // add an extra pixByte to represent the pixel left of column 0 - var rowBytes = ((columns * colors * bits + 7) >> 3) + pixBytes; - this.rowBytes = rowBytes; + var rowBytes = this.rowBytes = ((columns * colors * bits + 7) >> 3) + pixBytes; this.currentRow = new Uint8Array(rowBytes); this.bufferLength = rowBytes; @@ -626,8 +620,8 @@ var PredictorStream = (function() { inbuf = (inbuf << 8) | (rawBytes[j++] & 0xFF); inbits += 8; } - compArray[kk] = (compArray[kk] + (inbuf >> - (inbits - bits))) & bitMask; + compArray[kk] = (compArray[kk] + + (inbuf >> (inbits - bits))) & bitMask; inbits -= bits; outbuf = (outbuf << bits) | compArray[kk]; outbits += bits; @@ -639,7 +633,7 @@ var PredictorStream = (function() { } if (outbits > 0) { currentRow[k++] = (outbuf << (8 - outbits)) + - (inbuf & ((1 << (8 - outbits)) - 1)) + (inbuf & ((1 << (8 - outbits)) - 1)) } } this.pos = pixBytes; @@ -707,18 +701,16 @@ var PredictorStream = (function() { this.pos = pixBytes; }, getByte : function() { - if (this.pos >= this.bufferLength) { + 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) { + if (this.pos >= this.bufferLength) this.readRow(); - } bytes[i] = this.currentRow[this.pos++]; } return bytes; @@ -727,9 +719,8 @@ var PredictorStream = (function() { return String.formCharCode(this.getByte()); }, lookChar : function() { - if (this.pos >= this.bufferLength) { + if (this.pos >= this.bufferLength) this.readRow(); - } return String.formCharCode(this.currentRow[this.pos]); }, skip : function(n) { @@ -3288,6 +3279,7 @@ var ColorSpace = (function() { case "DeviceGray": case "G": this.numComps = 1; + break; case "DeviceRGB": this.numComps = 3; break; From 4b118d361cfb12a6c220704dbea022077f462f80 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Tue, 21 Jun 2011 03:42:01 -0400 Subject: [PATCH 16/46] don't die when translating fonts without a charset --- pdf.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pdf.js b/pdf.js index 57e53061f..fda5b512e 100644 --- a/pdf.js +++ b/pdf.js @@ -2267,10 +2267,10 @@ var CanvasGraphics = (function() { // Get the font charset if any var charset = descriptor.get("CharSet"); - if (charset) + if (charset) { assertWellFormed(IsString(charset), "invalid charset"); - - charset = charset.split("/"); + charset = charset.split("/"); + } } else if (IsName(encoding)) { var encoding = Encodings[encoding.name]; if (!encoding) From 9f995645e5d6de4ae9d917b696c1efd21fee38c9 Mon Sep 17 00:00:00 2001 From: sbarman Date: Tue, 21 Jun 2011 10:49:02 -0700 Subject: [PATCH 17/46] minor fix to render page, add F command, added inhertable dictionary entries --- pdf.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/pdf.js b/pdf.js index fda5b512e..21a131bfd 100644 --- a/pdf.js +++ b/pdf.js @@ -784,6 +784,16 @@ var Dict = (function() { get2: function(key1, key2) { return this.get(key1) || this.get(key2); }, + getOrInherit: function(key, xref) { + var obj = this.map[key]; + var dict = this; + while (!obj && dict) { + dict = xref.fetchIfRef(dict.get("Parent")); + if (dict) + obj = dict.get(key); + } + return obj; + }, has: function(key) { return key in this.map; }, @@ -1701,10 +1711,11 @@ var Page = (function() { return shadow(this, "content", this.pageDict.get("Contents")); }, get resources() { - return shadow(this, "resources", this.pageDict.get("Resources")); + return shadow(this, "resources", + this.pageDict.getOrInherit("Resources", this.xref)); }, get mediaBox() { - var obj = this.pageDict.get("MediaBox"); + var obj = this.pageDict.getOrInherit("MediaBox", this.xref); return shadow(this, "mediaBox", ((IsArray(obj) && obj.length == 4) ? obj : null)); @@ -2154,6 +2165,7 @@ var CanvasGraphics = (function() { S: "stroke", s: "closeStroke", f: "fill", + F: "fill", "f*": "eoFill", B: "fillStroke", "B*": "eoFillStroke", From bad69c984cc1f2dee8c1a4162d3a0c2c096ebaf0 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Tue, 21 Jun 2011 14:42:41 -0400 Subject: [PATCH 18/46] indentation fix --- pdf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdf.js b/pdf.js index 21a131bfd..c12cb2b08 100644 --- a/pdf.js +++ b/pdf.js @@ -1712,7 +1712,7 @@ var Page = (function() { }, get resources() { return shadow(this, "resources", - this.pageDict.getOrInherit("Resources", this.xref)); + this.pageDict.getOrInherit("Resources", this.xref)); }, get mediaBox() { var obj = this.pageDict.getOrInherit("MediaBox", this.xref); From 5a96404705f88bab1975a86aedae7c59aacfdea1 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Tue, 21 Jun 2011 15:05:33 -0400 Subject: [PATCH 19/46] whitespace fixes and move inheriting of dict props into the page since it only happens there --- pdf.js | 128 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 65 insertions(+), 63 deletions(-) diff --git a/pdf.js b/pdf.js index c12cb2b08..fff135816 100644 --- a/pdf.js +++ b/pdf.js @@ -554,12 +554,12 @@ var JpegStream = (function() { var PredictorStream = (function() { function constructor(stream, params) { var predictor = this.predictor = params.get("Predictor") || 1; - + if (predictor <= 1) return stream; // no prediction if (predictor !== 2 && (predictor < 10 || predictor > 15)) error("Unsupported predictor"); - + if (predictor === 2) this.readRow = this.readRowTiff; else @@ -573,7 +573,7 @@ var PredictorStream = (function() { var colors = this.colors = params.get("Colors") || 1; var bits = this.bits = params.get("BitsPerComponent") || 8; var columns = this.columns = params.get("Columns") || 1; - + var pixBytes = this.pixBytes = (colors * bits + 7) >> 3; // add an extra pixByte to represent the pixel left of column 0 var rowBytes = this.rowBytes = ((columns * colors * bits + 7) >> 3) + pixBytes; @@ -784,16 +784,6 @@ var Dict = (function() { get2: function(key1, key2) { return this.get(key1) || this.get(key2); }, - getOrInherit: function(key, xref) { - var obj = this.map[key]; - var dict = this; - while (!obj && dict) { - dict = xref.fetchIfRef(dict.get("Parent")); - if (dict) - obj = dict.get(key); - } - return obj; - }, has: function(key) { return key in this.map; }, @@ -1116,7 +1106,7 @@ var Lexer = (function() { break; } } - + // start reading token switch (ch) { case '0': case '1': case '2': case '3': case '4': @@ -1290,7 +1280,7 @@ var Parser = (function() { } return str; } - + // simple object var obj = this.buf1; this.shift(); @@ -1303,7 +1293,7 @@ var Parser = (function() { // get stream start position lexer.skipToNextLine(); var pos = stream.pos; - + // get length var length = dict.get("Length"); var xref = this.xref; @@ -1373,7 +1363,7 @@ var Parser = (function() { return constructor; })(); - + var Linearization = (function() { function constructor(stream) { this.parser = new Parser(new Lexer(stream), false); @@ -1707,15 +1697,28 @@ var Page = (function() { } constructor.prototype = { + getPageProp: function(key) { + return this.pageDict.get(key); + }, + inheritPageProp: function(key) { + var dict = this.pageDict; + var obj = dict.get(key); + while (!obj) { + dict = this.xref.fetchIfRef(dict.get("Parent")); + if (!dict) + break; + obj = dict.get(key); + } + return obj; + }, get content() { - return shadow(this, "content", this.pageDict.get("Contents")); + return shadow(this, "content", this.getPageProp("Contents")); }, get resources() { - return shadow(this, "resources", - this.pageDict.getOrInherit("Resources", this.xref)); + return shadow(this, "resources", this.inheritPageProp("Resources")); }, get mediaBox() { - var obj = this.pageDict.getOrInherit("MediaBox", this.xref); + var obj = this.inheritPageProp("MediaBox"); return shadow(this, "mediaBox", ((IsArray(obj) && obj.length == 4) ? obj : null)); @@ -1804,7 +1807,7 @@ var Catalog = (function() { pageCache.push(new Page(this.xref, pageCache.length, obj)); } else { // must be a child page dictionary assertWellFormed(IsDict(obj), - "page dictionary kid reference points to wrong type of object"); + "page dictionary kid reference points to wrong type of object"); this.traverseKids(obj); } } @@ -2890,7 +2893,6 @@ var CanvasGraphics = (function() { shadingFill: function(entryRef) { var xref = this.xref; var res = this.res; - var shadingRes = xref.fetchIfRef(res.get("Shading")); if (!shadingRes) error("No shading resource found"); @@ -2915,12 +2917,14 @@ var CanvasGraphics = (function() { if (background) TODO("handle background colors"); - const types = [null, this.fillFunctionShading, - this.fillAxialShading, this.fillRadialShading]; - + const types = [null, + this.fillFunctionShading, + this.fillAxialShading, + this.fillRadialShading]; + var typeNum = shading.get("ShadingType"); var fillFn = types[typeNum]; - if (!fillFn) + if (!fillFn) error("Unknown type of shading"); fillFn.apply(this, [shading]); @@ -2931,7 +2935,7 @@ var CanvasGraphics = (function() { var coordsArr = sh.get("Coords"); var x0 = coordsArr[0], y0 = coordsArr[1], x1 = coordsArr[2], y1 = coordsArr[3]; - + var t0 = 0.0, t1 = 1.0; if (sh.has("Domain")) { var domainArr = sh.get("Domain"); @@ -2990,12 +2994,12 @@ 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"); @@ -3097,15 +3101,14 @@ var CanvasGraphics = (function() { // actual image var csStream = dict.get2("ColorSpace", "CS"); csStream = xref.fetchIfRef(csStream); - if (IsName(csStream) && inline) + if (IsName(csStream) && inline) csStream = colorSpaces.get(csStream); - - var colorSpace = new ColorSpace(xref, 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"); @@ -3130,7 +3133,7 @@ var CanvasGraphics = (function() { 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); @@ -3140,7 +3143,7 @@ var CanvasGraphics = (function() { var maskDecode = maskDict.get2("Decode", "D"); if (maskDecode) TODO("Handle mask decode"); - // handle matte object + // handle matte object } var tmpCanvas = document.createElement("canvas"); @@ -3149,21 +3152,21 @@ var CanvasGraphics = (function() { var tmpCtx = tmpCanvas.getContext("2d"); var imgData = tmpCtx.getImageData(0, 0, w, h); var pixels = imgData.data; - + if (bitsPerComponent != 8) - error("unhandled number of bits per component"); - + error("unhandled number of bits per component"); + if (smask) { 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: @@ -3190,7 +3193,7 @@ var CanvasGraphics = (function() { var numComps = colorSpace.numComps; var imgArray = image.getBytes(numComps * w * h); var imgIdx = 0; - + var length = 4 * w * h; switch (numComps) { case 1: @@ -3300,7 +3303,7 @@ var ColorSpace = (function() { } else if (IsArray(cs)) { var mode = cs[0].name; this.mode = mode; - + var stream = cs[1]; stream = xref.fetchIfRef(stream); @@ -3313,7 +3316,6 @@ var ColorSpace = (function() { break; case "ICCBased": var dict = stream.dict; - this.stream = stream; this.dict = dict; this.numComps = dict.get("N"); @@ -3325,7 +3327,7 @@ var ColorSpace = (function() { error("unrecognized color space object"); } }; - + constructor.prototype = { }; @@ -3338,13 +3340,15 @@ var PDFFunction = (function() { if (!dict) dict = fn; - const types = [this.constructSampled, null, - this.constructInterpolated, this.constructStiched, - this.constructPostScript]; - + const types = [this.constructSampled, + null, + this.constructInterpolated, + this.constructStiched, + this.constructPostScript]; + var typeNum = dict.get("FunctionType"); var typeFn = types[typeNum]; - if (!typeFn) + if (!typeFn) error("Unknown type of function"); typeFn.apply(this, [fn, dict]); @@ -3357,7 +3361,7 @@ var PDFFunction = (function() { if (!domain || !range) error("No domain or range"); - + var inputSize = domain.length / 2; var outputSize = range.length / 2; @@ -3371,7 +3375,7 @@ var PDFFunction = (function() { order = 1; if (order !== 1) error ("No support for cubic spline interpolation"); - + var encode = dict.get("Encode"); if (!encode) { encode = []; @@ -3400,15 +3404,14 @@ var PDFFunction = (function() { for (var i = 0; i < inputSize; i++) { var i2 = i * 2; - + // clip to the domain var v = clip(args[i], domain[i2], domain[i2 + 1]); // encode - v = encode[i2] + ((v - domain[i2]) * - (encode[i2 + 1] - encode[i2]) / - (domain[i2 + 1] - domain[i2])); - + v = encode[i2] + ((v - domain[i2]) * + (encode[i2 + 1] - encode[i2]) / + (domain[i2 + 1] - domain[i2])); // clip to the size args[i] = clip(v, 0, size[i] - 1); } @@ -3431,12 +3434,11 @@ var PDFFunction = (function() { var high = samples[ceil + i]; var v = low * scale + high * (1 - scale); } - + var i2 = i * 2; // decode - v = decode[i2] + (v * (decode[i2 + 1] - decode[i2]) / - ((1 << bps) - 1)); - + v = decode[i2] + (v * (decode[i2 + 1] - decode[i2]) / + ((1 << bps) - 1)); // clip to the domain output.push(clip(v, range[i2], range[i2 + 1])); } @@ -3471,10 +3473,10 @@ var PDFFunction = (function() { }, constructInterpolated: function() { error("unhandled type of function"); - }, + }, constructStiched: function() { error("unhandled type of function"); - }, + }, constructPostScript: function() { error("unhandled type of function"); } From beb048b2178fd85c539962fb8dd0631a7dc91082 Mon Sep 17 00:00:00 2001 From: sbarman Date: Tue, 21 Jun 2011 13:34:35 -0700 Subject: [PATCH 20/46] initial impl of ASCII85 Decoding --- pdf.js | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/pdf.js b/pdf.js index fff135816..dfb47ea16 100644 --- a/pdf.js +++ b/pdf.js @@ -750,6 +750,116 @@ var DecryptStream = (function() { return constructor; })(); +var Ascii85Stream = (function() { + function constructor(str) { + this.str = str; + this.dict = str.dict; + this.eof = false; + this.pos = 0; + this.bufferLength = 0; + this.buffer = new Uint8Array(4); + } + constructor.prototype = { + getByte: function() { + if (this.pos >= this.bufferLength) + this.readBlock(); + return this.buffer[this.pos++]; + }, + getBytes: function(n) { + var i, bytes; + bytes = new Uint8Array(n); + for (i = 0; i < n; ++i) { + if (this.pos >= this.bufferLength) + this.readBlock(); + if (this.eof) + break; + bytes[i] = this.buffer[this.pos++]; + } + return bytes; + }, + getChar : function() { + return String.fromCharCode(this.getByte()); + }, + lookChar : function() { + if (this.pos >= this.bufferLength) + this.readRow(); + return String.fromCharCode(this.currentRow[this.pos]); + }, + skip : function(n) { + var i; + if (!n) { + n = 1; + } + while (n > this.bufferLength - this.pos) { + n -= this.bufferLength - this.pos; + this.readBlock(); + if (this.bufferLength === 0) break; + } + this.pos += n; + }, + readBlock: function() { + if (this.eof) { + this.bufferLength = 0; + this.buffer = []; + this.pos = 0; + return; + } + + const tildaCode = "~".charCodeAt(0); + const zCode = "z".charCodeAt(0); + var str = this.str; + + var c = str.getByte(); + while (Lexer.isSpace(String.fromCharCode(c))) + c = str.getByte(); + if (!c || c === tildaCode) { + this.eof = true; + return; + } + + var buffer = this.buffer; + // special code for z + if (c == zCode) { + buffer[0] = 0; + buffer[1] = 0; + buffer[2] = 0; + buffer[3] = 0; + this.bufferLength = 4; + } else { + var input = new Uint8Array(5); + input[0] = c; + for (var i = 1; i < 5; ++i){ + c = str.getByte(); + while (Lexer.isSpace(String.fromCharCode(c))) + c = str.getByte(); + + input[i] = c; + + if (!c || c == tildaCode) + break; + } + this.bufferLength = i - 1; + // partial ending; + if (i < 5) { + for (++i; i < 5; ++i) + input[i] = 0x21 + 84; + this.eof = true; + } + var t = 0; + for (var i = 0; i < 5; ++i) + t = t * 85 + (input[i] - 0x21); + + for (var i = 3; i >= 0; --i){ + buffer[i] = t & 0xFF; + t >>= 8; + } + } + } + }; + + return constructor; +})(); + var Name = (function() { function constructor(name) { this.name = name; @@ -1354,6 +1464,8 @@ var Parser = (function() { } else if (name == "DCTDecode") { var bytes = stream.getBytes(length); return new JpegStream(bytes, stream.dict); + } else if (name == "ASCII85Decode") { + return new Ascii85Stream(stream); } else { error("filter '" + name + "' not supported yet"); } From bf8d49ef1a7fac0190a1bfbbea704ca469e605fc Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Tue, 21 Jun 2011 13:40:21 -0700 Subject: [PATCH 21/46] Don't error out for FontFile3 descriptors. --- pdf.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pdf.js b/pdf.js index fff135816..cffb1c0fa 100644 --- a/pdf.js +++ b/pdf.js @@ -784,6 +784,9 @@ var Dict = (function() { get2: function(key1, key2) { return this.get(key1) || this.get(key2); }, + get3: function(key1, key2, key3) { + return this.get(key1) || this.get(key2) || this.get(key3); + }, has: function(key) { return key in this.map; }, @@ -2255,7 +2258,7 @@ var CanvasGraphics = (function() { assertWellFormed(IsName(fontName), "invalid font name"); fontName = fontName.name.replace("+", "_"); - var fontFile = descriptor.get2("FontFile", "FontFile2"); + var fontFile = descriptor.get3("FontFile", "FontFile2", "FontFile3"); if (!fontFile) error("FontFile not found for font: " + fontName); fontFile = xref.fetchIfRef(fontFile); From f7fb8e63489b4948ac04aef90ec3b64313da0375 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Tue, 21 Jun 2011 13:56:49 -0700 Subject: [PATCH 22/46] fix typo --- pdf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdf.js b/pdf.js index cffb1c0fa..52a65f1e3 100644 --- a/pdf.js +++ b/pdf.js @@ -2624,7 +2624,7 @@ var CanvasGraphics = (function() { setWordSpacing: function(spacing) { TODO("word spacing"); }, - setHSpacing: function(scale) { + setHScale: function(scale) { TODO("horizontal text scale"); }, setLeading: function(leading) { From 240fcf650b28e3495ffdb79677c4e019a9e3796c Mon Sep 17 00:00:00 2001 From: sbarman Date: Tue, 21 Jun 2011 14:34:13 -0700 Subject: [PATCH 23/46] fixed getBytes in Ascii85Stream --- pdf.js | 44 +++++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/pdf.js b/pdf.js index dfb47ea16..5ff97fa91 100644 --- a/pdf.js +++ b/pdf.js @@ -766,16 +766,38 @@ var Ascii85Stream = (function() { return this.buffer[this.pos++]; }, getBytes: function(n) { - var i, bytes; - bytes = new Uint8Array(n); - for (i = 0; i < n; ++i) { - if (this.pos >= this.bufferLength) - this.readBlock(); - if (this.eof) - break; - bytes[i] = this.buffer[this.pos++]; + if (n) { + var i, bytes; + bytes = new Uint8Array(n); + for (i = 0; i < n; ++i) { + if (this.pos >= this.bufferLength) + this.readBlock(); + if (this.eof) + break; + bytes[i] = this.buffer[this.pos++]; + } + return bytes; + } else { + var length = 0; + var size = 1 << 8; + var bytes = new Uint8Array(size); + while (true) { + if (this.pos >= this.bufferLength) + this.readBlock(); + if (this.eof) + break; + if (length == size) { + var oldSize = size; + size <<= 1; + var oldBytes = bytes; + bytes = new Uint8Array(size); + for (var i = 0; i < oldSize; ++i) + bytes[i] = oldBytes[i]; + } + bytes[length++] = this.buffer[this.pos++]; + } + return bytes.subarray(0, length); } - return bytes; }, getChar : function() { return String.fromCharCode(this.getByte()); @@ -1441,8 +1463,8 @@ var Parser = (function() { if (IsArray(filter)) { var filterArray = filter; var paramsArray = params; - for (var i = 0, ii = filter.length; i < ii; ++i) { - filter = filter[i]; + for (var i = 0, ii = filterArray.length; i < ii; ++i) { + filter = filterArray[i]; if (!IsName(filter)) error("Bad filter name"); else { From 2dff6d818cedb2f482aa2060e4160b3e8130156a Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Tue, 21 Jun 2011 14:53:57 -0700 Subject: [PATCH 24/46] test-harness improvements --- test.py | 59 ++++++++++++++++++++++++++++++++++------------ test_manifest.json | 4 ++-- test_slave.html | 23 +++++++++++++++--- 3 files changed, 66 insertions(+), 20 deletions(-) diff --git a/test.py b/test.py index 46d30fef5..7c3c4048a 100644 --- a/test.py +++ b/test.py @@ -1,7 +1,10 @@ import json, os, sys, subprocess from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer +from urlparse import urlparse ANAL = True +DEFAULT_MANIFEST_FILE = 'test_manifest.json' +REFDIR = 'ref' VERBOSE = False MIMEs = { @@ -34,8 +37,11 @@ class PDFTestHandler(BaseHTTPRequestHandler): BaseHTTPRequestHandler.log_request(code, size) def do_GET(self): + url = urlparse(self.path) + # Ignore query string + path, _ = url.path, url.query cwd = os.getcwd() - path = os.path.abspath(os.path.realpath(cwd + os.sep + self.path)) + path = os.path.abspath(os.path.realpath(cwd + os.sep + path)) cwd = os.path.abspath(cwd) prefix = os.path.commonprefix(( path, cwd )) _, ext = os.path.splitext(path) @@ -69,10 +75,10 @@ class PDFTestHandler(BaseHTTPRequestHandler): self.end_headers() result = json.loads(self.rfile.read(numBytes)) - browser = 'firefox4' - id, failure, round, page, snapshot = result['id'], result['failure'], result['round'], result['page'], result['snapshot'] + browser, id, failure, round, page, snapshot = result['browser'], result['id'], result['failure'], result['round'], result['page'], result['snapshot'] taskResults = State.taskResults[browser][id] - taskResults[round][page - 1] = Result(snapshot, failure) + taskResults[round].append(Result(snapshot, failure)) + assert len(taskResults[round]) == page if result['taskDone']: check(State.manifest[id], taskResults, browser) @@ -81,7 +87,7 @@ class PDFTestHandler(BaseHTTPRequestHandler): State.done = (0 == State.remaining) -def set_up(): +def set_up(manifestFile): # Only serve files from a pdf.js clone assert not ANAL or os.path.isfile('pdf.js') and os.path.isdir('.git') @@ -90,7 +96,7 @@ def set_up(): #'chrome12', 'chrome13', 'firefox5', 'firefox6','opera11' ): if os.access(b, os.R_OK | os.X_OK) ] - mf = open('test_manifest.json') + mf = open(manifestFile) manifestList = json.load(mf) mf.close() @@ -101,15 +107,16 @@ def set_up(): State.manifest[id] = item taskResults = [ ] for r in xrange(rounds): - taskResults.append([ None ] * 100) + taskResults.append([ ]) State.taskResults[b][id] = taskResults State.remaining = len(manifestList) for b in testBrowsers: print 'Launching', b + qs = 'browser='+ b +'&manifestFile='+ manifestFile subprocess.Popen(( os.path.abspath(os.path.realpath(b)), - 'http://localhost:8080/test_slave.html' )) + 'http://localhost:8080/test_slave.html?'+ qs)) def check(task, results, browser): @@ -129,7 +136,7 @@ def check(task, results, browser): return kind = task['type'] - if '==' == kind: + if 'eq' == kind: checkEq(task, results, browser) elif 'fbf' == kind: checkFBF(task, results, browser) @@ -140,8 +147,26 @@ def check(task, results, browser): def checkEq(task, results, browser): - print ' !!! [TODO: == tests] !!!' - print 'TEST-PASS | == test', task['id'], '| in', browser + pfx = os.path.join(REFDIR, sys.platform, browser, task['id']) + results = results[0] + + passed = True + for page in xrange(len(results)): + ref = None + try: + path = os.path.join(pfx, str(page + 1)) + f = open(path) + ref = f.read() + f.close() + except IOError, ioe: + continue + + snapshot = results[page] + if ref != snapshot: + print 'TEST-UNEXPECTED-FAIL | eq', task['id'], '| in', browser, '| rendering of page', page + 1, '!= reference rendering' + passed = False + if passed: + print 'TEST-PASS | eq test', task['id'], '| in', browser printed = [False] @@ -150,13 +175,16 @@ def checkFBF(task, results, browser): round0, round1 = results[0], results[1] assert len(round0) == len(round1) + passed = True for page in xrange(len(round1)): r0Page, r1Page = round0[page], round1[page] if r0Page is None: break if r0Page.snapshot != r1Page.snapshot: print 'TEST-UNEXPECTED-FAIL | forward-back-forward test', task['id'], '| in', browser, '| first rendering of page', page + 1, '!= second' - print 'TEST-PASS | forward-back-forward test', task['id'], '| in', browser + passed = False + if passed: + print 'TEST-PASS | forward-back-forward test', task['id'], '| in', browser def checkLoad(task, results, browser): @@ -165,11 +193,12 @@ def checkLoad(task, results, browser): print 'TEST-PASS | load test', task['id'], '| in', browser -def main(): - set_up() +def main(args): + manifestFile = args[0] if len(args) == 1 else DEFAULT_MANIFEST_FILE + set_up(manifestFile) server = HTTPServer(('127.0.0.1', 8080), PDFTestHandler) while not State.done: server.handle_request() if __name__ == '__main__': - main() + main(sys.argv[1:]) diff --git a/test_manifest.json b/test_manifest.json index 2f45a026c..e31b8b2b4 100644 --- a/test_manifest.json +++ b/test_manifest.json @@ -1,8 +1,8 @@ [ - { "id": "tracemonkey-==", + { "id": "tracemonkey-eq", "file": "tests/tracemonkey.pdf", "rounds": 1, - "type": "==" + "type": "eq" }, { "id": "tracemonkey-fbf", "file": "tests/tracemonkey.pdf", diff --git a/test_slave.html b/test_slave.html index c560d90d0..cff9b3f7d 100644 --- a/test_slave.html +++ b/test_slave.html @@ -5,9 +5,24 @@