From f346014c12a87eda4986c71497d4b0c3d1c8fdc9 Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Tue, 21 Jun 2011 23:33:11 +0200 Subject: [PATCH 01/24] Backup work --- canvas_proxy.js | 109 +++++++++++++++++++++++++++++++++++++++++++ pdf.js | 13 +++++- viewer_worker.html | 110 +++++++++++++++++++++++++++++++++++++++++++ worker.js | 113 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 343 insertions(+), 2 deletions(-) create mode 100644 canvas_proxy.js create mode 100644 viewer_worker.html create mode 100644 worker.js diff --git a/canvas_proxy.js b/canvas_proxy.js new file mode 100644 index 000000000..1b100beae --- /dev/null +++ b/canvas_proxy.js @@ -0,0 +1,109 @@ +function CanvasProxy(width, height) { + var stack = this.$stack = []; + + // Expose only the minimum of the canvas object - there is no dom to do + // more here. + this.canvas = { + width: width, + height: height + } + + var ctxFunc = [ + "createRadialGradient", + "arcTo", + "arc", + "fillText", + "strokeText", + "drawImage", + "getImageData", + "putImageData", + "createImageData", + "drawWindow", + "save", + "restore", + "scale", + "rotate", + "translate", + "transform", + "setTransform", + "createLinearGradient", + "createPattern", + "clearRect", + "fillRect", + "strokeRect", + "beginPath", + "closePath", + "moveTo", + "lineTo", + "quadraticCurveTo", + "bezierCurveTo", + "rect", + "fill", + "stroke", + "clip", + "measureText", + "isPointInPath" + ]; + function buildFuncCall(name) { + return function() { + console.log("funcCall", name) + stack.push([name, Array.prototype.slice.call(arguments)]); + } + } + var name; + for (var i = 0; i < ctxFunc.length; i++) { + name = ctxFunc[i]; + this[name] = buildFuncCall(name); + } + + var ctxProp = { + // "canvas" + "globalAlpha": "1", + "globalCompositeOperation": "source-over", + "strokeStyle": "#000000", + "fillStyle": "#000000", + "lineWidth": "1", + "lineCap": "butt", + "lineJoin": "miter", + "miterLimit": "10", + "shadowOffsetX": "0", + "shadowOffsetY": "0", + "shadowBlur": "0", + "shadowColor": "rgba(0, 0, 0, 0)", + "font": "10px sans-serif", + "textAlign": "start", + "textBaseline": "alphabetic", + "mozTextStyle": "10px sans-serif", + "mozImageSmoothingEnabled": "true", + "DRAWWINDOW_DRAW_CARET": "1", + "DRAWWINDOW_DO_NOT_FLUSH": "2", + "DRAWWINDOW_DRAW_VIEW": "4", + "DRAWWINDOW_USE_WIDGET_LAYERS": "8", + "DRAWWINDOW_ASYNC_DECODE_IMAGES": "16", + } + + function buildGetter(name) { + return function() { + return this["$" + name]; + } + } + + function buildSetter(name) { + return function(value) { + stack.push(["$", name, value]); + return this["$" + name] = value; + } + } + + for (var name in ctxProp) { + this["$" + name] = ctxProp[name]; + this.__defineGetter__(name, buildGetter(name)); + this.__defineSetter__(name, buildSetter(name)); + } +} + +CanvasProxy.prototype.flush = function() { + postMessage("canvas_proxy_stack"); + postMessage(JSON.stringify(this.$stack)); + this.$stack.length = 0; +} diff --git a/pdf.js b/pdf.js index 326c31234..09d1c874e 100644 --- a/pdf.js +++ b/pdf.js @@ -2277,7 +2277,10 @@ var CanvasGraphics = (function() { this.pendingClip = null; this.res = null; this.xobjs = null; - this.map = { + } + + constructor.prototype = { + map: { // Graphics state w: "setLineWidth", J: "setLineCap", @@ -2634,7 +2637,9 @@ var CanvasGraphics = (function() { } var fn = Function("objpool", src); - return function (gfx) { fn.call(gfx, objpool); }; + var ret = function (gfx) { fn.call(gfx, objpool); }; + ret.src = src; + return ret; }, endDrawing: function() { @@ -3041,6 +3046,7 @@ 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"); @@ -3468,6 +3474,7 @@ var ColorSpace = (function() { break; case "ICCBased": var dict = stream.dict; + this.stream = stream; this.dict = dict; this.numComps = dict.get("N"); @@ -3574,6 +3581,7 @@ var PDFFunction = (function() { 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); } @@ -3601,6 +3609,7 @@ var PDFFunction = (function() { // decode 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])); } diff --git a/viewer_worker.html b/viewer_worker.html new file mode 100644 index 000000000..f9e1f0b32 --- /dev/null +++ b/viewer_worker.html @@ -0,0 +1,110 @@ + + + Simple pdf.js page viewer worker + + + + + +
+ + + + + + -- + +
+ +
+ + + +
+ + + diff --git a/worker.js b/worker.js new file mode 100644 index 000000000..fdc762afd --- /dev/null +++ b/worker.js @@ -0,0 +1,113 @@ +"use strict"; + +function log() { + var args = Array.prototype.slice.call(arguments); + postMessage("log"); + postMessage(JSON.stringify(args)) +} + +var console = { + log: log +} + +importScripts("canvas_proxy.js"); +importScripts("pdf.js"); +importScripts("fonts.js"); +importScripts("glyphlist.js") + +// var array = new Uint8Array(2); +// array[0] = 1; +// array[1] = 300; +// postMessage(array); + +var timer = null; +function tic() { + timer = Date.now(); +} + +function toc(msg) { + log("Took ", (Date.now() - timer)); + timer = null; +} + + +var canvas = new CanvasProxy(1224, 1584); +// canvas.moveTo(0, 10); +// canvas.lineTo(0, 20); +// canvas.lineTo(500, 500); +// canvas.flush(); +// canvas.stroke(); +// canvas.flush(); +log("test"); + +onmessage = function(event) { + var data = event.data; + var pdfDocument = new PDFDoc(new Stream(data)); + var numPages = pdfDocument.numPages; + + tic(); + // Let's try to render the first page... + var page = pdfDocument.getPage(1); + + // page.compile will collect all fonts for us, once we have loaded them + // we can trigger the actual page rendering with page.display + var fonts = []; + + var gfx = new CanvasGraphics(canvas); + page.compile(gfx, fonts); + toc("compiled page"); + + // + var fontsReady = true; + // Inspect fonts and translate the missing one + var count = fonts.length; + for (var i = 0; i < count; i++) { + var font = fonts[i]; + if (Fonts[font.name]) { + fontsReady = fontsReady && !Fonts[font.name].loading; + continue; + } + + new Font(font.name, font.file, font.properties); + fontsReady = false; + } + + function delayLoadFont() { + for (var i = 0; i < count; i++) { + if (Fonts[font.name].loading) + return; + } + clearInterval(pageInterval); + page.display(gfx); + + canvas.flush(); + }; + + if (fontsReady) { + delayLoadFont(); + } else { + pageInterval = setInterval(delayLoadFont, 10); + } + postMessage(page.code.src); +} + +// function open(url) { +// var req = new XMLHttpRequest(); +// req.open("GET", url); +// // req.responseType = "arraybuffer"; +// req.expected = 0;//(document.URL.indexOf("file:") == 0) ? 0 : 200; +// req.onreadystatechange = function() { +// postMessage("loaded"); +// if (req.readyState == 4 && req.status == req.expected) { +// var data = req.mozResponseArrayBuffer || req.mozResponse || +// req.responseArrayBuffer || req.response; +// pdfDocument = new PDFDoc(new Stream(data)); +// numPages = pdfDocument.numPages; +// // document.getElementById("numPages").innerHTML = numPages.toString(); +// // goToPage(pageNum); +// } +// }; +// req.send(null); +// } +// +// open("compressed.tracemonkey-pldi-09.pdf") \ No newline at end of file From a8dcb0dcd68635a41875617d33b700d4a9467e1e Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Wed, 22 Jun 2011 01:28:17 +0200 Subject: [PATCH 02/24] Most working, but once you add the font-css file to the web page, there is no font drawn at all --- canvas_proxy.js | 12 +++- fonts.js | 146 +++++++++++++++++++++++++-------------------- pdf.js | 34 +++++++++-- viewer_worker.html | 95 +++++++++++++++++++++++++---- worker.js | 56 +++++++++-------- 5 files changed, 235 insertions(+), 108 deletions(-) diff --git a/canvas_proxy.js b/canvas_proxy.js index 1b100beae..433166aac 100644 --- a/canvas_proxy.js +++ b/canvas_proxy.js @@ -42,11 +42,17 @@ function CanvasProxy(width, height) { "stroke", "clip", "measureText", - "isPointInPath" + "isPointInPath", + + "$setCurrentX", + "$addCurrentX", + "$saveCurrentX", + "$restoreCurrentX", + "$showText" ]; function buildFuncCall(name) { return function() { - console.log("funcCall", name) + // console.log("funcCall", name) stack.push([name, Array.prototype.slice.call(arguments)]); } } @@ -103,6 +109,8 @@ function CanvasProxy(width, height) { } CanvasProxy.prototype.flush = function() { + // postMessage("log"); + // postMessage(JSON.stringify([this.$stack.length])); postMessage("canvas_proxy_stack"); postMessage(JSON.stringify(this.$stack)); this.$stack.length = 0; diff --git a/fonts.js b/fonts.js index d5943b7a3..8c0abbcec 100644 --- a/fonts.js +++ b/fonts.js @@ -759,91 +759,109 @@ var Font = (function () { var data = this.font; var fontName = this.name; + var isWorker = (typeof window == "undefined"); /** Hack begin */ + if (!isWorker) { - // Actually there is not event when a font has finished downloading so - // the following code are a dirty hack to 'guess' when a font is ready - var canvas = document.createElement("canvas"); - var style = "border: 1px solid black; position:absolute; top: " + - (debug ? (100 * fontCount) : "-200") + "px; left: 2px; width: 340px; height: 100px"; - canvas.setAttribute("style", style); - canvas.setAttribute("width", 340); - canvas.setAttribute("heigth", 100); - document.body.appendChild(canvas); + // Actually there is not event when a font has finished downloading so + // the following code are a dirty hack to 'guess' when a font is ready + var canvas = document.createElement("canvas"); + var style = "border: 1px solid black; position:absolute; top: " + + (debug ? (100 * fontCount) : "-200") + "px; left: 2px; width: 340px; height: 100px"; + canvas.setAttribute("style", style); + canvas.setAttribute("width", 340); + canvas.setAttribute("heigth", 100); + document.body.appendChild(canvas); - // Get the font size canvas think it will be for 'spaces' - var ctx = canvas.getContext("2d"); - ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial"; - var testString = " "; + // Get the font size canvas think it will be for 'spaces' + var ctx = canvas.getContext("2d"); + ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial"; + var testString = " "; - // When debugging use the characters provided by the charsets to visually - // see what's happening instead of 'spaces' - var debug = false; - if (debug) { - var name = document.createElement("font"); - name.setAttribute("style", "position: absolute; left: 20px; top: " + - (100 * fontCount + 60) + "px"); - name.innerHTML = fontName; - document.body.appendChild(name); + // When debugging use the characters provided by the charsets to visually + // see what's happening instead of 'spaces' + var debug = false; + if (debug) { + var name = document.createElement("font"); + name.setAttribute("style", "position: absolute; left: 20px; top: " + + (100 * fontCount + 60) + "px"); + name.innerHTML = fontName; + document.body.appendChild(name); - // Retrieve font charset - var charset = Fonts[fontName].properties.charset || []; + // Retrieve font charset + var charset = Fonts[fontName].properties.charset || []; - // if the charset is too small make it repeat a few times - var count = 30; - while (count-- && charset.length <= 30) - charset = charset.concat(charset.slice()); + // if the charset is too small make it repeat a few times + var count = 30; + while (count-- && charset.length <= 30) + charset = charset.concat(charset.slice()); - for (var i = 0; i < charset.length; i++) { - var unicode = GlyphsUnicode[charset[i]]; - if (!unicode) - continue; - testString += String.fromCharCode(unicode); - } + for (var i = 0; i < charset.length; i++) { + var unicode = GlyphsUnicode[charset[i]]; + if (!unicode) + continue; + testString += String.fromCharCode(unicode); + } - ctx.fillText(testString, 20, 20); - } + ctx.fillText(testString, 20, 20); + } - // Periodicaly check for the width of the testString, it will be - // different once the real font has loaded - var textWidth = ctx.measureText(testString).width; + // Periodicaly check for the width of the testString, it will be + // different once the real font has loaded + var textWidth = ctx.measureText(testString).width; - var interval = window.setInterval(function canvasInterval(self) { - this.start = this.start || Date.now(); - ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial"; + var interval = window.setInterval(function canvasInterval(self) { + this.start = this.start || Date.now(); + ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial"; - // For some reasons the font has not loaded, so mark it loaded for the - // page to proceed but cry - if ((Date.now() - this.start) >= kMaxWaitForFontFace) { - window.clearInterval(interval); - Fonts[fontName].loading = false; - warn("Is " + fontName + " for charset: " + charset + " loaded?"); - this.start = 0; - } else if (textWidth != ctx.measureText(testString).width) { - window.clearInterval(interval); - Fonts[fontName].loading = false; - this.start = 0; - } + // For some reasons the font has not loaded, so mark it loaded for the + // page to proceed but cry + if ((Date.now() - this.start) >= kMaxWaitForFontFace) { + window.clearInterval(interval); + Fonts[fontName].loading = false; + warn("Is " + fontName + " for charset: " + charset + " loaded?"); + this.start = 0; + } else if (textWidth != ctx.measureText(testString).width) { + window.clearInterval(interval); + Fonts[fontName].loading = false; + this.start = 0; + } - if (debug) - ctx.fillText(testString, 20, 50); - }, 30, this); + if (debug) + ctx.fillText(testString, 20, 50); + }, 30, this); + } /** Hack end */ - + // // Get the base64 encoding of the binary font data var str = ""; var length = data.length; for (var i = 0; i < length; ++i) str += String.fromCharCode(data[i]); - var base64 = window.btoa(str); + if (isWorker) { + postMessage("font"); + postMessage(JSON.stringify({ + str: str, + mimetype: this.mimetype, + fontName: fontName, + })); - // Add the @font-face rule to the document - var url = "url(data:" + this.mimetype + ";base64," + base64 + ");"; - var rule = "@font-face { font-family:'" + fontName + "';src:" + url + "}"; - var styleSheet = document.styleSheets[0]; - styleSheet.insertRule(rule, styleSheet.length); + setTimeout(function() { + Fonts[fontName].loading = false; + }, kMaxWaitForFontFace); + } else { + var base64 = window.btoa(str); + + // Add the @font-face rule to the document + var url = "url(data:" + this.mimetype + ";base64," + base64 + ");"; + var rule = "@font-face { font-family:'" + fontName + "';src:" + url + "}"; + var styleSheet = document.styleSheets[0]; + styleSheet.insertRule(rule, styleSheet.length); + console.log("added font", fontName); + console.log(rule); + } } }; diff --git a/pdf.js b/pdf.js index 09d1c874e..80e9c1930 100644 --- a/pdf.js +++ b/pdf.js @@ -2674,12 +2674,18 @@ var CanvasGraphics = (function() { }, save: function() { this.ctx.save(); + if (this.ctx.$saveCurrentX) { + this.ctx.$saveCurrentX(); + } this.stateStack.push(this.current); this.current = new CanvasExtraState(); }, restore: function() { var prev = this.stateStack.pop(); if (prev) { + if (this.ctx.$restoreCurrentX) { + this.ctx.$restoreCurrentX(); + } this.current = prev; this.ctx.restore(); } @@ -2760,6 +2766,9 @@ var CanvasGraphics = (function() { // Text beginText: function() { this.current.textMatrix = IDENTITY_MATRIX; + if (this.ctx.$setCurrentX) { + this.ctx.$setCurrentX(0) + } this.current.x = this.current.lineX = 0; this.current.y = this.current.lineY = 0; }, @@ -2814,6 +2823,9 @@ var CanvasGraphics = (function() { moveText: function (x, y) { this.current.x = this.current.lineX += x; this.current.y = this.current.lineY += y; + if (this.ctx.$setCurrentX) { + this.ctx.$setCurrentX(this.current.x) + } }, setLeadingMoveText: function(x, y) { this.setLeading(-y); @@ -2821,6 +2833,10 @@ var CanvasGraphics = (function() { }, setTextMatrix: function(a, b, c, d, e, f) { this.current.textMatrix = [ a, b, c, d, e, f ]; + + if (this.ctx.$setCurrentX) { + this.$setCurrentX(0) + } this.current.x = this.current.lineX = 0; this.current.y = this.current.lineY = 0; }, @@ -2831,11 +2847,15 @@ var CanvasGraphics = (function() { this.ctx.save(); this.ctx.transform.apply(this.ctx, this.current.textMatrix); this.ctx.scale(1, -1); - this.ctx.translate(0, -2 * this.current.y); - text = Fonts.charsToUnicode(text); - this.ctx.fillText(text, this.current.x, this.current.y); - this.current.x += this.ctx.measureText(text).width; + if (this.ctx.$showText) { + this.ctx.$showText(this.current.y, Fonts.charsToUnicode(text)); + } else { + console.log(text, this.current.x); + text = Fonts.charsToUnicode(text); + this.ctx.fillText(text, 0, 0); + this.current.x += this.ctx.measureText(text).width; + } this.ctx.restore(); }, @@ -2843,7 +2863,11 @@ var CanvasGraphics = (function() { for (var i = 0; i < arr.length; ++i) { var e = arr[i]; if (IsNum(e)) { - this.current.x -= e * 0.001 * this.current.fontSize; + if (this.ctx.$addCurrentX) { + this.ctx.$addCurrentX(-e * 0.001 * this.current.fontSize) + } else { + this.current.x -= e * 0.001 * this.current.fontSize; + } } else if (IsString(e)) { this.showText(e); } else { diff --git a/viewer_worker.html b/viewer_worker.html index f9e1f0b32..dde249e55 100644 --- a/viewer_worker.html +++ b/viewer_worker.html @@ -11,11 +11,63 @@ var myWorker = new Worker('worker.js'); const WAIT = 0; const CANVAS_PROXY_STACK = 1; const LOG = 2; +const FONT = 3; + +var currentX = 0; +var currentXStack = []; +var special = { + "$setCurrentX": function(value) { + currentX = value; + }, + + "$addCurrentX": function(value) { + currentX += value; + }, + + "$saveCurrentX": function() { + currentXStack.push(currentX); + }, + + "$restoreCurrentX": function() { + currentX = currentXStack.pop(); + }, + + "$showText": function(y, text) { + console.log(text, currentX, y, this.measureText(text).width); + + this.translate(currentX, -1 * y); + this.fillText(text, 0, 0); + currentX += this.measureText(text).width; + } +} + +function renderProxyCanvas(stack) { + // for (var i = 0; i < stack.length; i++) { + for (var i = 0; i < 1000; i++) { + var opp = stack[i]; + if (opp[0] == "$") { + // console.log("set property", opp[1], opp[2]); + if (opp[1] == "font") { + ctx[opp[1]] = opp[2]; + // console.log("font", opp[2]); + } else { + ctx[opp[1]] = opp[2]; + } + + } else if (opp[0] in special) { + // console.log("sepcial", opp[0], opp[1]) + special[opp[0]].apply(ctx, opp[1]); + } else { + // console.log("execute", opp[0], opp[1]); + ctx[opp[0]].apply(ctx, opp[1]); + } + } +} var onMessageState = WAIT; myWorker.onmessage = function(event) { var data = event.data; - console.log("onMessageRaw", data); + // console.log("onMessageRaw", data); switch (onMessageState) { case WAIT: if (typeof data != "string") { @@ -28,11 +80,31 @@ myWorker.onmessage = function(event) { case "canvas_proxy_stack": onMessageState = CANVAS_PROXY_STACK; return; + case "font": + onMessageState = FONT; + return; default: throw "unkown state: " + data } break; + case FONT: + data = JSON.parse(data); + var base64 = window.btoa(data.str); + + // Add the @font-face rule to the document + var url = "url(data:" + data.mimetype + ";base64," + base64 + ");"; + var rule = "@font-face { font-family:'" + data.fontName + "';src:" + url + "}"; + var styleSheet = document.styleSheets[0]; + + // ONCE you uncomment this, there is no font painted at all :( + // styleSheet.insertRule(rule, styleSheet.length); + + console.log("added font", data.fontName); + // console.log(rule); + onMessageState = WAIT; + break; + case LOG: console.log.apply(console, JSON.parse(data)); onMessageState = WAIT; @@ -40,17 +112,14 @@ myWorker.onmessage = function(event) { case CANVAS_PROXY_STACK: var stack = JSON.parse(data); - for (var i = 0; i < stack.length; i++) { - var opp = stack[i]; - if (opp[0] == "$") { - console.log("set property", opp[1], opp[2]); - ctx[opp[1]] = opp[2]; - } else { - console.log("execute", opp[0], opp[1]); - ctx[opp[0]].apply(ctx, opp[1]); - } - } + console.log("canvas stack", stack.length) + // console.log(stack.length); onMessageState = WAIT; + // return; + + setTimeout(function() { + renderProxyCanvas(stack); + }, 2000); break; } } @@ -75,6 +144,10 @@ function open(url) { window.onload = function() { var ctx = window.ctx = document.getElementById("canvas").getContext("2d"); + ctx.save(); + ctx.fillStyle = "rgb(255, 255, 255)"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.restore(); // for (var name in ctx) { // if (!(ctx[name] instanceof Function)) { // console.log('"' + name + '": "' + ctx[name] + '",'); diff --git a/worker.js b/worker.js index fdc762afd..9ee9409bd 100644 --- a/worker.js +++ b/worker.js @@ -40,6 +40,7 @@ var canvas = new CanvasProxy(1224, 1584); // canvas.flush(); log("test"); +var pageInterval; onmessage = function(event) { var data = event.data; var pdfDocument = new PDFDoc(new Stream(data)); @@ -59,36 +60,39 @@ onmessage = function(event) { // var fontsReady = true; - // Inspect fonts and translate the missing one - var count = fonts.length; - for (var i = 0; i < count; i++) { - var font = fonts[i]; - if (Fonts[font.name]) { - fontsReady = fontsReady && !Fonts[font.name].loading; - continue; - } + // Inspect fonts and translate the missing one + var count = fonts.length; + for (var i = 0; i < count; i++) { + var font = fonts[i]; + if (Fonts[font.name]) { + fontsReady = fontsReady && !Fonts[font.name].loading; + continue; + } - new Font(font.name, font.file, font.properties); - fontsReady = false; - } + new Font(font.name, font.file, font.properties); + fontsReady = false; + } - function delayLoadFont() { - for (var i = 0; i < count; i++) { - if (Fonts[font.name].loading) - return; - } - clearInterval(pageInterval); - page.display(gfx); + // function delayLoadFont() { + // for (var i = 0; i < count; i++) { + // if (Fonts[font.name].loading) + // return; + // } + // clearInterval(pageInterval); + // page.display(gfx); + // + // log("flush"); + // canvas.flush(); + // }; - canvas.flush(); - }; + // if (fontsReady) { + // delayLoadFont(); + // } else { + // pageInterval = setInterval(delayLoadFont, 10); + // } - if (fontsReady) { - delayLoadFont(); - } else { - pageInterval = setInterval(delayLoadFont, 10); - } - postMessage(page.code.src); + page.display(gfx); + canvas.flush(); } // function open(url) { From d9424a7135820fd902d50cadfd2150c6b6a1bdd7 Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Wed, 22 Jun 2011 09:15:55 +0200 Subject: [PATCH 03/24] Make fonts getting loaded by a very nasty hack --- fonts.js | 6 +----- pdf.js | 6 +++--- viewer_worker.html | 43 ++++++++++++++++++++++++++++--------------- worker.js | 2 +- 4 files changed, 33 insertions(+), 24 deletions(-) diff --git a/fonts.js b/fonts.js index 8c0abbcec..c7f54edf9 100644 --- a/fonts.js +++ b/fonts.js @@ -844,13 +844,9 @@ var Font = (function () { postMessage("font"); postMessage(JSON.stringify({ str: str, - mimetype: this.mimetype, fontName: fontName, + mimetype: this.mimetype })); - - setTimeout(function() { - Fonts[fontName].loading = false; - }, kMaxWaitForFontFace); } else { var base64 = window.btoa(str); diff --git a/pdf.js b/pdf.js index 80e9c1930..64b99a33e 100644 --- a/pdf.js +++ b/pdf.js @@ -2835,7 +2835,7 @@ var CanvasGraphics = (function() { this.current.textMatrix = [ a, b, c, d, e, f ]; if (this.ctx.$setCurrentX) { - this.$setCurrentX(0) + this.ctx.$setCurrentX(0) } this.current.x = this.current.lineX = 0; this.current.y = this.current.lineY = 0; @@ -2851,9 +2851,9 @@ var CanvasGraphics = (function() { if (this.ctx.$showText) { this.ctx.$showText(this.current.y, Fonts.charsToUnicode(text)); } else { - console.log(text, this.current.x); text = Fonts.charsToUnicode(text); - this.ctx.fillText(text, 0, 0); + this.ctx.translate(this.current.x, -1 * this.current.y); + this.ctx.fillText(Fonts.charsToUnicode(text), 0, 0); this.current.x += this.ctx.measureText(text).width; } diff --git a/viewer_worker.html b/viewer_worker.html index dde249e55..930fb6cd5 100644 --- a/viewer_worker.html +++ b/viewer_worker.html @@ -8,10 +8,6 @@ var myWorker = new Worker('worker.js'); // array[0] = 1; // array[1] = 300; // -const WAIT = 0; -const CANVAS_PROXY_STACK = 1; -const LOG = 2; -const FONT = 3; var currentX = 0; var currentXStack = []; @@ -33,7 +29,7 @@ var special = { }, "$showText": function(y, text) { - console.log(text, currentX, y, this.measureText(text).width); + // console.log(text, currentX, y, this.measureText(text).width); this.translate(currentX, -1 * y); this.fillText(text, 0, 0); @@ -41,14 +37,16 @@ var special = { } } +var gStack; function renderProxyCanvas(stack) { - // for (var i = 0; i < stack.length; i++) { - for (var i = 0; i < 1000; i++) { + for (var i = 0; i < stack.length; i++) { + // for (var i = 0; i < 1000; i++) { var opp = stack[i]; if (opp[0] == "$") { // console.log("set property", opp[1], opp[2]); if (opp[1] == "font") { ctx[opp[1]] = opp[2]; + // ctx.font = "10px 'Verdana Bold Italic'"; // console.log("font", opp[2]); } else { ctx[opp[1]] = opp[2]; @@ -64,7 +62,15 @@ function renderProxyCanvas(stack) { } } +const WAIT = 0; +const CANVAS_PROXY_STACK = 1; +const LOG = 2; +const FONT = 3; + var onMessageState = WAIT; +var fontStr = null; +var first = true; +var intervals = []; myWorker.onmessage = function(event) { var data = event.data; // console.log("onMessageRaw", data); @@ -96,12 +102,15 @@ myWorker.onmessage = function(event) { var url = "url(data:" + data.mimetype + ";base64," + base64 + ");"; var rule = "@font-face { font-family:'" + data.fontName + "';src:" + url + "}"; var styleSheet = document.styleSheets[0]; + styleSheet.insertRule(rule, styleSheet.length); - // ONCE you uncomment this, there is no font painted at all :( - // styleSheet.insertRule(rule, styleSheet.length); + // *HACK*: this makes the font get loaded on the page. WTF? We + // really have to set the fonts a few time... + var interval = setInterval(function() { + ctx.font = "bold italic 20px " + data.fontName + ", Symbol, Arial"; + }, 10); + intervals.push(interval); - console.log("added font", data.fontName); - // console.log(rule); onMessageState = WAIT; break; @@ -112,14 +121,18 @@ myWorker.onmessage = function(event) { case CANVAS_PROXY_STACK: var stack = JSON.parse(data); + gStack = stack; console.log("canvas stack", stack.length) - // console.log(stack.length); - onMessageState = WAIT; - // return; + // Shedule a timeout. Hoping the fonts are loaded after 100ms. setTimeout(function() { + // Remove all setIntervals to make the font load. + intervals.forEach(function(inter) { + clearInterval(inter); + }); renderProxyCanvas(stack); - }, 2000); + }, 100); + onMessageState = WAIT; break; } } diff --git a/worker.js b/worker.js index 9ee9409bd..6d34a9d62 100644 --- a/worker.js +++ b/worker.js @@ -48,7 +48,7 @@ onmessage = function(event) { tic(); // Let's try to render the first page... - var page = pdfDocument.getPage(1); + var page = pdfDocument.getPage(8); // page.compile will collect all fonts for us, once we have loaded them // we can trigger the actual page rendering with page.display From b151516416cf2bc84013cb39eeb1e361701e9892 Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Wed, 22 Jun 2011 09:46:11 +0200 Subject: [PATCH 04/24] Introduce ImageCanvas to handle canvas rendering in WebWorker --- canvas_proxy.js | 7 ++++--- fonts.js | 2 -- pdf.js | 41 +++++++++++++++++++++++++++++++++-------- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/canvas_proxy.js b/canvas_proxy.js index 433166aac..ccc95c74a 100644 --- a/canvas_proxy.js +++ b/canvas_proxy.js @@ -14,9 +14,9 @@ function CanvasProxy(width, height) { "arc", "fillText", "strokeText", - "drawImage", - "getImageData", - "putImageData", + // "drawImage", + // "getImageData", + // "putImageData", "createImageData", "drawWindow", "save", @@ -50,6 +50,7 @@ function CanvasProxy(width, height) { "$restoreCurrentX", "$showText" ]; + function buildFuncCall(name) { return function() { // console.log("funcCall", name) diff --git a/fonts.js b/fonts.js index c7f54edf9..9c9201b72 100644 --- a/fonts.js +++ b/fonts.js @@ -855,8 +855,6 @@ var Font = (function () { var rule = "@font-face { font-family:'" + fontName + "';src:" + url + "}"; var styleSheet = document.styleSheets[0]; styleSheet.insertRule(rule, styleSheet.length); - console.log("added font", fontName); - console.log(rule); } } }; diff --git a/pdf.js b/pdf.js index 64b99a33e..273550084 100644 --- a/pdf.js +++ b/pdf.js @@ -2269,14 +2269,32 @@ var Encodings = { } }; +function ImageCanvas(width, height) { + var tmpCanvas = this.canvas = document.createElement("canvas"); + tmpCanvas.width = width; + tmpCanvas.height = height; + + this.ctx = tmpCanvas.getContext("2d"); + this.imgData = this.ctx.getImageData(0, 0, width, height); +} + +ImageCanvas.prototype.putImageData = function(imgData) { + this.ctx.putImageData(imgData, 0, 0); +} + +ImageCanvas.prototype.getCanvas = function() { + return this.canvas; +} + var CanvasGraphics = (function() { - function constructor(canvasCtx) { + function constructor(canvasCtx, imageCanvas) { this.ctx = canvasCtx; this.current = new CanvasExtraState(); this.stateStack = [ ]; this.pendingClip = null; this.res = null; this.xobjs = null; + this.ImageCanvas = imageCanvas || ImageCanvas; } constructor.prototype = { @@ -3009,6 +3027,7 @@ var CanvasGraphics = (function() { var tmpCanvas = document.createElement("canvas"); tmpCanvas.width = Math.ceil(botRight[0] - topLeft[0]); tmpCanvas.height = Math.ceil(botRight[1] - topLeft[1]); + console.log("tilingFill", tmpCanvas.width, tmpCanvas.height); // set the new canvas element context as the graphics context var tmpCtx = tmpCanvas.getContext("2d"); @@ -3249,6 +3268,7 @@ var CanvasGraphics = (function() { ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height, 0, -h, w, h); this.restore(); + console.log("drawImage"); return; } @@ -3328,11 +3348,15 @@ var CanvasGraphics = (function() { // handle matte object } - var tmpCanvas = document.createElement("canvas"); - tmpCanvas.width = w; - tmpCanvas.height = h; - var tmpCtx = tmpCanvas.getContext("2d"); - var imgData = tmpCtx.getImageData(0, 0, w, h); + var tmpCanvas = new this.ImageCanvas(w, h); + // var tmpCanvas = document.createElement("canvas"); + // tmpCanvas.width = w; + // tmpCanvas.height = h; + // + // var tmpCtx = tmpCanvas.getContext("2d"); + // var imgData = tmpCtx.getImageData(0, 0, w, h); + // var pixels = imgData.data; + var imgData = tmpCanvas.imgData; var pixels = imgData.data; if (bitsPerComponent != 8) @@ -3399,8 +3423,9 @@ var CanvasGraphics = (function() { TODO("Images with "+ numComps + " components per pixel"); } } - tmpCtx.putImageData(imgData, 0, 0); - ctx.drawImage(tmpCanvas, 0, -h); + console.log("paintImageXObject", w, h); + tmpCanvas.putImageData(imgData, 0, 0); + ctx.drawImage(tmpCanvas.getCanvas(), 0, -h); this.restore(); }, From d99dc718c5fd5e22a88c1c98c8cc6f02c51cfd63 Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Wed, 22 Jun 2011 10:40:51 +0200 Subject: [PATCH 05/24] Get working for not real images --- canvas_proxy.js | 29 +++++++++++++++++++++++++++++ viewer_worker.html | 15 ++++++++++----- worker.js | 37 +++---------------------------------- 3 files changed, 42 insertions(+), 39 deletions(-) diff --git a/canvas_proxy.js b/canvas_proxy.js index ccc95c74a..610cdcdba 100644 --- a/canvas_proxy.js +++ b/canvas_proxy.js @@ -1,3 +1,24 @@ +var ImageCanvasProxyCounter = 0; +function ImageCanvasProxy(width, height) { + this.id = ImageCanvasProxyCounter++; + this.width = width; + this.height = height; + + // Using `Uint8ClampedArray` seems to be the type of ImageData - at least + // Firebug tells me so. + this.imgData = { + data: Uint8ClampedArray(width * height * 4) + }; +} + +ImageCanvasProxy.prototype.putImageData = function(imgData) { + // this.ctx.putImageData(imgData, 0, 0); +} + +ImageCanvasProxy.prototype.getCanvas = function() { + return this; +} + function CanvasProxy(width, height) { var stack = this.$stack = []; @@ -51,6 +72,14 @@ function CanvasProxy(width, height) { "$showText" ]; + this.drawImage = function(canvas, x, y) { + if (canvas instanceof ImageCanvasProxy) { + stack.push(["$drawCanvas", [canvas.imgData, x, y, canvas.width, canvas.height]]); + } else { + throw "unkown type to drawImage"; + } + } + function buildFuncCall(name) { return function() { // console.log("funcCall", name) diff --git a/viewer_worker.html b/viewer_worker.html index 930fb6cd5..07623c50c 100644 --- a/viewer_worker.html +++ b/viewer_worker.html @@ -4,11 +4,6 @@ @@ -184,7 +214,7 @@ window.onload = function() { -- Can we use JSONP to overcome the same-origin restrictions? --> - -- diff --git a/worker.js b/worker.js index 33b34f350..dcb87a811 100644 --- a/worker.js +++ b/worker.js @@ -26,7 +26,7 @@ function tic() { } function toc(msg) { - log("Took ", (Date.now() - timer)); + log(msg + ": " + (Date.now() - timer) + "ms"); timer = null; } @@ -41,46 +41,41 @@ var canvas = new CanvasProxy(1224, 1584); log("test"); var pageInterval; - +var pdfDocument = null; onmessage = function(event) { var data = event.data; - var pdfDocument = new PDFDoc(new Stream(data)); - var numPages = pdfDocument.numPages; + if (!pdfDocument) { + pdfDocument = new PDFDoc(new Stream(data)); + postMessage("pdf_num_page"); + postMessage(pdfDocument.numPages) + return; + } else { + tic(); - tic(); - // Let's try to render the first page... - var page = pdfDocument.getPage(2); + // Let's try to render the first page... + var page = pdfDocument.getPage(parseInt(data)); - // page.compile will collect all fonts for us, once we have loaded them - // we can trigger the actual page rendering with page.display - 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 + var fonts = []; + var gfx = new CanvasGraphics(canvas, ImageCanvasProxy); + page.compile(gfx, fonts); - var gfx = new CanvasGraphics(canvas, ImageCanvasProxy); - page.compile(gfx, fonts); - toc("compiled page"); + // Inspect fonts and translate the missing one. + var count = fonts.length; + for (var i = 0; i < count; i++) { + var font = fonts[i]; + if (Fonts[font.name]) { + fontsReady = fontsReady && !Fonts[font.name].loading; + continue; + } + // This "builds" the font and sents it over to the main thread. + new Font(font.name, font.file, font.properties); + } + toc("compiled page"); - page.display(gfx); - canvas.flush(); + page.display(gfx); + canvas.flush(); + } } - -// function open(url) { -// var req = new XMLHttpRequest(); -// req.open("GET", url); -// // req.responseType = "arraybuffer"; -// req.expected = 0;//(document.URL.indexOf("file:") == 0) ? 0 : 200; -// req.onreadystatechange = function() { -// postMessage("loaded"); -// if (req.readyState == 4 && req.status == req.expected) { -// var data = req.mozResponseArrayBuffer || req.mozResponse || -// req.responseArrayBuffer || req.response; -// pdfDocument = new PDFDoc(new Stream(data)); -// numPages = pdfDocument.numPages; -// // document.getElementById("numPages").innerHTML = numPages.toString(); -// // goToPage(pageNum); -// } -// }; -// req.send(null); -// } -// -// open("compressed.tracemonkey-pldi-09.pdf") \ No newline at end of file From ddd8aeffb9483b8ccc1f236052e55714a93b69bb Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Wed, 22 Jun 2011 13:19:25 +0200 Subject: [PATCH 07/24] Fix font loading issue by using a hidden DOM font node --- viewer_worker.html | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/viewer_worker.html b/viewer_worker.html index bba694f21..ced71679e 100644 --- a/viewer_worker.html +++ b/viewer_worker.html @@ -118,12 +118,10 @@ myWorker.onmessage = function(event) { var styleSheet = document.styleSheets[0]; styleSheet.insertRule(rule, styleSheet.length); - // *HACK*: this makes the font get loaded on the page. WTF? We - // really have to set the fonts a few time... - var interval = setInterval(function() { - ctx.font = "bold italic 20px " + data.fontName + ", Symbol, Arial"; - }, 10); - intervals.push(interval); + // Just adding the font-face to the DOM doesn't make it load. It + // seems it's loaded once Gecko notices it's used. Therefore, + // add a div on the page using the loaded font. + document.getElementById("fonts").innerHTML += "
j
"; console.log("setup font", data.fontName); onMessageState = WAIT; @@ -139,16 +137,13 @@ myWorker.onmessage = function(event) { gStack = stack; console.log("canvas stack size", stack.length) - // Shedule a timeout. Hoping the fonts are loaded after 100ms. + // There might be fonts that need to get loaded. Shedule the + // rendering at the end of the event queue ensures this. setTimeout(function() { - // Remove all setIntervals to make the font load. - intervals.forEach(function(inter) { - clearInterval(inter); - }); tic(); renderProxyCanvas(stack); toc("canvas rendering") - }, 100); + }, 0); onMessageState = WAIT; break; } @@ -208,6 +203,7 @@ window.onload = function() { +
- - - Previous + + -- diff --git a/worker.js b/worker.js index e59e37155..09e2b8145 100644 --- a/worker.js +++ b/worker.js @@ -1,15 +1,26 @@ "use strict"; +var timer = null; +function tic() { + timer = Date.now(); +} + +function toc(msg) { + log(msg + ": " + (Date.now() - timer) + "ms"); + timer = null; +} + function log() { - var args = Array.prototype.slice.call(arguments); - postMessage("log"); - postMessage(JSON.stringify(args)) + var args = Array.prototype.slice.call(arguments); + postMessage("log"); + postMessage(JSON.stringify(args)) } var console = { - log: log + log: log } +// importScripts("canvas_proxy.js"); importScripts("pdf.js"); importScripts("fonts.js"); @@ -18,55 +29,50 @@ importScripts("glyphlist.js") // Use the JpegStreamProxy proxy. JpegStream = JpegStreamProxy; -var timer = null; -function tic() { - timer = Date.now(); -} - -function toc(msg) { - log(msg + ": " + (Date.now() - timer) + "ms"); - timer = null; -} - // Create the WebWorkerProxyCanvas. var canvas = new CanvasProxy(1224, 1584); -var pageInterval; +// Listen for messages from the main thread. var pdfDocument = null; onmessage = function(event) { - var data = event.data; - if (!pdfDocument) { - pdfDocument = new PDFDoc(new Stream(data)); - postMessage("pdf_num_page"); - postMessage(pdfDocument.numPages) - return; - } else { - tic(); + var data = event.data; + // If there is no pdfDocument yet, then the sent data is the PDFDocument. + if (!pdfDocument) { + pdfDocument = new PDFDoc(new Stream(data)); + postMessage("pdf_num_page"); + postMessage(pdfDocument.numPages) + return; + } + // User requested to render a certain page. + else { + tic(); - // Let's try to render the first page... - var page = pdfDocument.getPage(parseInt(data)); + // Let's try to render the first page... + var page = pdfDocument.getPage(parseInt(data)); - // page.compile will collect all fonts for us, once we have loaded them - // we can trigger the actual page rendering with page.display - var fonts = []; - var gfx = new CanvasGraphics(canvas.getContext("2d"), CanvasProxy); - page.compile(gfx, fonts); + // page.compile will collect all fonts for us, once we have loaded them + // we can trigger the actual page rendering with page.display + var fonts = []; + var gfx = new CanvasGraphics(canvas.getContext("2d"), CanvasProxy); + page.compile(gfx, fonts); - // Inspect fonts and translate the missing one. - var count = fonts.length; - for (var i = 0; i < count; i++) { - var font = fonts[i]; - if (Fonts[font.name]) { - fontsReady = fontsReady && !Fonts[font.name].loading; - continue; - } + // Inspect fonts and translate the missing one. + var count = fonts.length; + for (var i = 0; i < count; i++) { + var font = fonts[i]; + if (Fonts[font.name]) { + fontsReady = fontsReady && !Fonts[font.name].loading; + continue; + } - // This "builds" the font and sents it over to the main thread. - new Font(font.name, font.file, font.properties); - } - toc("compiled page"); - - page.display(gfx); - canvas.flush(); + // This "builds" the font and sents it over to the main thread. + new Font(font.name, font.file, font.properties); } + toc("compiled page"); + + tic() + page.display(gfx); + canvas.flush(); + toc("displayed page"); + } } diff --git a/worker_client.js b/worker_client.js new file mode 100644 index 000000000..316ef1fc0 --- /dev/null +++ b/worker_client.js @@ -0,0 +1,294 @@ +"use strict"; + +function WorkerPDFDoc(canvas) { + var timer = null + function tic() { + timer = Date.now(); + } + + function toc(msg) { + console.log(msg + ": " + (Date.now() - timer) + "ms"); + } + + this.ctx = canvas.getContext("2d"); + this.canvas = canvas; + this.worker = new Worker('worker.js'); + + this.numPage = 1; + this.numPages = null; + + var imagesList = {}; + var canvasList = { + 0: canvas + }; + var patternList = {}; + var gradient; + + var currentX = 0; + var currentXStack = []; + + var ctxSpecial = { + "$setCurrentX": function(value) { + currentX = value; + }, + + "$addCurrentX": function(value) { + currentX += value; + }, + + "$saveCurrentX": function() { + currentXStack.push(currentX); + }, + + "$restoreCurrentX": function() { + currentX = currentXStack.pop(); + }, + + "$showText": function(y, text, uniText) { + this.translate(currentX, -1 * y); + this.fillText(uniText, 0, 0); + currentX += this.measureText(text).width; + }, + + "$putImageData": function(imageData, x, y) { + var imgData = this.getImageData(0, 0, imageData.width, imageData.height); + + // Store the .data property to avaid property lookups. + var imageRealData = imageData.data; + var imgRealData = imgData.data; + + // Copy over the imageData. + var len = imageRealData.length; + while (len--) + imgRealData[len] = imageRealData[len] + + this.putImageData(imgData, x, y); + }, + + "$drawImage": function(id, x, y, sx, sy, swidth, sheight) { + var image = imagesList[id]; + if (!image) { + throw "Image not found"; + } + this.drawImage(image, x, y, image.width, image.height, + sx, sy, swidth, sheight); + }, + + "$drawCanvas": function(id, x, y, sx, sy, swidth, sheight) { + var canvas = canvasList[id]; + if (!canvas) { + throw "Canvas not found"; + } + if (sheight != null) { + this.drawImage(canvas, x, y, canvas.width, canvas.height, + sx, sy, swidth, sheight); + } else { + this.drawImage(canvas, x, y, canvas.width, canvas.height); + } + }, + + "$createLinearGradient": function(x0, y0, x1, y1) { + gradient = this.createLinearGradient(x0, y0, x1, y1); + }, + + "$createPatternFromCanvas": function(patternId, canvasId, kind) { + var canvas = canvasList[canvasId]; + if (!canvas) { + throw "Canvas not found"; + } + patternList[patternId] = this.createPattern(canvas, kind); + }, + + "$addColorStop": function(i, rgba) { + gradient.addColorStop(i, rgba); + }, + + "$fillStyleGradient": function() { + this.fillStyle = gradient; + }, + + "$fillStylePattern": function(id) { + var pattern = patternList[id]; + if (!pattern) { + throw "Pattern not found"; + } + this.fillStyle = pattern; + }, + + "$strokeStyleGradient": function() { + this.strokeStyle = gradient; + }, + + "$strokeStylePattern": function(id) { + var pattern = patternList[id]; + if (!pattern) { + throw "Pattern not found"; + } + this.strokeStyle = pattern; + } + } + + function renderProxyCanvas(canvas, stack) { + var ctx = canvas.getContext("2d"); + for (var i = 0; i < stack.length; i++) { + var opp = stack[i]; + if (opp[0] == "$") { + ctx[opp[1]] = opp[2]; + } else if (opp[0] in ctxSpecial) { + ctxSpecial[opp[0]].apply(ctx, opp[1]); + } else { + ctx[opp[0]].apply(ctx, opp[1]); + } + } + } + + /** + * onMessage state machine. + */ + const WAIT = 0; + const CANVAS_PROXY_STACK = 1; + const LOG = 2; + const FONT = 3; + const PDF_NUM_PAGE = 4; + const JPEG_STREAM = 5; + + var onMessageState = WAIT; + this.worker.onmessage = function(event) { + var data = event.data; + // console.log("onMessageRaw", data); + switch (onMessageState) { + case WAIT: + if (typeof data != "string") { + throw "expecting to get an string"; + } + switch (data) { + case "pdf_num_page": + onMessageState = PDF_NUM_PAGE; + return; + + case "log": + onMessageState = LOG; + return; + + case "canvas_proxy_stack": + onMessageState = CANVAS_PROXY_STACK; + return; + + case "font": + onMessageState = FONT; + return; + + case "jpeg_stream": + onMessageState = JPEG_STREAM; + return; + + default: + throw "unkown state: " + data + } + break; + + case JPEG_STREAM: + var img = new Image(); + img.src = "data:image/jpeg;base64," + window.btoa(data.str); + imagesList[data.id] = img; + console.log("got image", data.id) + break; + + case PDF_NUM_PAGE: + this.numPages = parseInt(data); + if (this.loadCallback) { + this.loadCallback(); + } + onMessageState = WAIT; + break; + + case FONT: + data = JSON.parse(data); + var base64 = window.btoa(data.str); + + // Add the @font-face rule to the document + var url = "url(data:" + data.mimetype + ";base64," + base64 + ");"; + var rule = "@font-face { font-family:'" + data.fontName + "';src:" + url + "}"; + var styleSheet = document.styleSheets[0]; + styleSheet.insertRule(rule, styleSheet.length); + + // Just adding the font-face to the DOM doesn't make it load. It + // seems it's loaded once Gecko notices it's used. Therefore, + // add a div on the page using the loaded font. + document.getElementById("fonts").innerHTML += "
j
"; + + onMessageState = WAIT; + break; + + case LOG: + console.log.apply(console, JSON.parse(data)); + onMessageState = WAIT; + break; + + case CANVAS_PROXY_STACK: + var id = data.id; + var stack = data.stack; + + // Check if there is already a canvas with the given id. If not, + // create a new canvas. + if (!canvasList[id]) { + var newCanvas = document.createElement("canvas"); + newCanvas.width = data.width; + newCanvas.height = data.height; + canvasList[id] = newCanvas; + } + + // There might be fonts that need to get loaded. Shedule the + // rendering at the end of the event queue ensures this. + setTimeout(function() { + if (id == 0) tic(); + renderProxyCanvas(canvasList[id], stack); + if (id == 0) toc("canvas rendering") + }, 0); + onMessageState = WAIT; + break; + } + }.bind(this); +} + + WorkerPDFDoc.prototype.open = function(url, callback) { + var req = new XMLHttpRequest(); + req.open("GET", url); + req.mozResponseType = req.responseType = "arraybuffer"; + req.expected = (document.URL.indexOf("file:") == 0) ? 0 : 200; + req.onreadystatechange = function() { + if (req.readyState == 4 && req.status == req.expected) { + var data = req.mozResponseArrayBuffer || req.mozResponse || + req.responseArrayBuffer || req.response; + + this.loadCallback = callback; + this.worker.postMessage(data); + this.showPage(this.numPage); + } + }.bind(this); + req.send(null); +} + +WorkerPDFDoc.prototype.showPage = function(numPage) { + var ctx = this.ctx; + ctx.save(); + ctx.fillStyle = "rgb(255, 255, 255)"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.restore(); + + this.numPage = parseInt(numPage); + this.worker.postMessage(numPage); + if (this.onChangePage) { + this.onChangePage(numPage); + } +} + +WorkerPDFDoc.prototype.nextPage = function() { + if (this.numPage == this.numPages) return; + this.showPage(++this.numPage); +} + +WorkerPDFDoc.prototype.prevPage = function() { + if (this.numPage == 1) return; + this.showPage(--this.numPage); +} From 229edf24d4586f39bb25b65a947aceb06b8a72ad Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Thu, 23 Jun 2011 13:09:36 +0200 Subject: [PATCH 16/24] First pass on review: worker.js -> pdf_worker.js, Font.bind cleanup + other stuff --- canvas_proxy.js | 73 ++++++++++++++++-------------- fonts.js | 92 +++++++------------------------------- pdf.js | 8 ++-- worker.js => pdf_worker.js | 3 ++ viewer_worker.html | 1 + worker_client.js | 23 ++++++---- 6 files changed, 77 insertions(+), 123 deletions(-) rename worker.js => pdf_worker.js (92%) diff --git a/canvas_proxy.js b/canvas_proxy.js index 83b57682f..0b7681bfe 100644 --- a/canvas_proxy.js +++ b/canvas_proxy.js @@ -1,3 +1,7 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + +"use strict"; var JpegStreamProxyCounter = 0; // WebWorker Proxy for JpegStream. @@ -29,16 +33,16 @@ var JpegStreamProxy = (function() { // Really simple GradientProxy. There is currently only one active gradient at // the time, meaning you can't create a gradient, create a second one and then // use the first one again. As this isn't used in pdf.js right now, it's okay. -function GradientProxy(stack, x0, y0, x1, y1) { - stack.push(["$createLinearGradient", [x0, y0, x1, y1]]); +function GradientProxy(cmdQueue, x0, y0, x1, y1) { + cmdQueue.push(["$createLinearGradient", [x0, y0, x1, y1]]); this.addColorStop = function(i, rgba) { - stack.push(["$addColorStop", [i, rgba]]); + cmdQueue.push(["$addColorStop", [i, rgba]]); } } // Really simple PatternProxy. var patternProxyCounter = 0; -function PatternProxy(stack, object, kind) { +function PatternProxy(cmdQueue, object, kind) { this.id = patternProxyCounter++; if (!(object instanceof CanvasProxy) ) { @@ -49,7 +53,7 @@ function PatternProxy(stack, object, kind) { // TODO: Make some kind of dependency management, such that the object // gets flushed only if needed. object.flush(); - stack.push(["$createPatternFromCanvas", [this.id, object.id, kind]]); + cmdQueue.push(["$createPatternFromCanvas", [this.id, object.id, kind]]); } var canvasProxyCounter = 0; @@ -57,7 +61,7 @@ function CanvasProxy(width, height) { this.id = canvasProxyCounter++; // The `stack` holds the rendering calls and gets flushed to the main thead. - var stack = this.$stack = []; + var cmdQueue = this.cmdQueue = []; // Dummy context that gets exposed. var ctx = {}; @@ -119,7 +123,7 @@ function CanvasProxy(width, height) { function buildFuncCall(name) { return function() { // console.log("funcCall", name) - stack.push([name, Array.prototype.slice.call(arguments)]); + cmdQueue.push([name, Array.prototype.slice.call(arguments)]); } } var name; @@ -131,11 +135,11 @@ function CanvasProxy(width, height) { // Some function calls that need more work. ctx.createPattern = function(object, kind) { - return new PatternProxy(stack, object, kind); + return new PatternProxy(cmdQueue, object, kind); } ctx.createLinearGradient = function(x0, y0, x1, y1) { - return new GradientProxy(stack, x0, y0, x1, y1); + return new GradientProxy(cmdQueue, x0, y0, x1, y1); } ctx.getImageData = function(x, y, w, h) { @@ -147,16 +151,16 @@ function CanvasProxy(width, height) { } ctx.putImageData = function(data, x, y, width, height) { - stack.push(["$putImageData", [data, x, y, width, height]]); + cmdQueue.push(["$putImageData", [data, x, y, width, height]]); } ctx.drawImage = function(image, x, y, width, height, sx, sy, swidth, sheight) { if (image instanceof CanvasProxy) { // Send the image/CanvasProxy to the main thread. image.flush(); - stack.push(["$drawCanvas", [image.id, x, y, sx, sy, swidth, sheight]]); + cmdQueue.push(["$drawCanvas", [image.id, x, y, sx, sy, swidth, sheight]]); } else if(image instanceof JpegStreamProxy) { - stack.push(["$drawImage", [image.id, x, y, sx, sy, swidth, sheight]]) + cmdQueue.push(["$drawImage", [image.id, x, y, sx, sy, swidth, sheight]]) } else { throw "unkown type to drawImage"; } @@ -192,11 +196,26 @@ function CanvasProxy(width, height) { function buildSetter(name) { return function(value) { - stack.push(["$", name, value]); + cmdQueue.push(["$", name, value]); return ctx["$" + name] = value; } } + // Setting the value to `stroke|fillStyle` needs special handling, as it + // might gets an gradient/pattern. + function buildSetterStyle(name) { + return function(value) { + if (value instanceof GradientProxy) { + cmdQueue.push(["$" + name + "Gradient"]); + } else if (value instanceof PatternProxy) { + cmdQueue.push(["$" + name + "Pattern", [value.id]]); + } else { + cmdQueue.push(["$", name, value]); + return ctx["$" + name] = value; + } + } + } + for (var name in ctxProp) { ctx["$" + name] = ctxProp[name]; ctx.__defineGetter__(name, buildGetter(name)); @@ -204,18 +223,6 @@ function CanvasProxy(width, height) { // Special treatment for `fillStyle` and `strokeStyle`: The passed style // might be a gradient. Need to check for that. if (name == "fillStyle" || name == "strokeStyle") { - function buildSetterStyle(name) { - return function(value) { - if (value instanceof GradientProxy) { - stack.push(["$" + name + "Gradient"]); - } else if (value instanceof PatternProxy) { - stack.push(["$" + name + "Pattern", [value.id]]); - } else { - stack.push(["$", name, value]); - return ctx["$" + name] = value; - } - } - } ctx.__defineSetter__(name, buildSetterStyle(name)); } else { ctx.__defineSetter__(name, buildSetter(name)); @@ -224,16 +231,16 @@ function CanvasProxy(width, height) { } /** -* Sends the current stack of the CanvasProxy over to the main thread and -* resets the stack. +* Sends the current cmdQueue of the CanvasProxy over to the main thread and +* resets the cmdQueue. */ CanvasProxy.prototype.flush = function() { - postMessage("canvas_proxy_stack"); + postMessage("canvas_proxy_cmd_queue"); postMessage({ - id: this.id, - stack: this.$stack, - width: this.width, - height: this.height + id: this.id, + cmdQueue: this.cmdQueue, + width: this.width, + height: this.height }); - this.$stack.length = 0; + this.cmdQueue.length = 0; } diff --git a/fonts.js b/fonts.js index 9c9201b72..a3604c6b9 100644 --- a/fonts.js +++ b/fonts.js @@ -759,88 +759,15 @@ var Font = (function () { var data = this.font; var fontName = this.name; - var isWorker = (typeof window == "undefined"); - /** Hack begin */ - if (!isWorker) { - - // Actually there is not event when a font has finished downloading so - // the following code are a dirty hack to 'guess' when a font is ready - var canvas = document.createElement("canvas"); - var style = "border: 1px solid black; position:absolute; top: " + - (debug ? (100 * fontCount) : "-200") + "px; left: 2px; width: 340px; height: 100px"; - canvas.setAttribute("style", style); - canvas.setAttribute("width", 340); - canvas.setAttribute("heigth", 100); - document.body.appendChild(canvas); - - // Get the font size canvas think it will be for 'spaces' - var ctx = canvas.getContext("2d"); - ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial"; - var testString = " "; - - // When debugging use the characters provided by the charsets to visually - // see what's happening instead of 'spaces' - var debug = false; - if (debug) { - var name = document.createElement("font"); - name.setAttribute("style", "position: absolute; left: 20px; top: " + - (100 * fontCount + 60) + "px"); - name.innerHTML = fontName; - document.body.appendChild(name); - - // Retrieve font charset - var charset = Fonts[fontName].properties.charset || []; - - // if the charset is too small make it repeat a few times - var count = 30; - while (count-- && charset.length <= 30) - charset = charset.concat(charset.slice()); - - for (var i = 0; i < charset.length; i++) { - var unicode = GlyphsUnicode[charset[i]]; - if (!unicode) - continue; - testString += String.fromCharCode(unicode); - } - - ctx.fillText(testString, 20, 20); - } - - // Periodicaly check for the width of the testString, it will be - // different once the real font has loaded - var textWidth = ctx.measureText(testString).width; - - var interval = window.setInterval(function canvasInterval(self) { - this.start = this.start || Date.now(); - ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial"; - - // For some reasons the font has not loaded, so mark it loaded for the - // page to proceed but cry - if ((Date.now() - this.start) >= kMaxWaitForFontFace) { - window.clearInterval(interval); - Fonts[fontName].loading = false; - warn("Is " + fontName + " for charset: " + charset + " loaded?"); - this.start = 0; - } else if (textWidth != ctx.measureText(testString).width) { - window.clearInterval(interval); - Fonts[fontName].loading = false; - this.start = 0; - } - - if (debug) - ctx.fillText(testString, 20, 50); - }, 30, this); - } - - /** Hack end */ - // // Get the base64 encoding of the binary font data var str = ""; var length = data.length; for (var i = 0; i < length; ++i) str += String.fromCharCode(data[i]); - if (isWorker) { + // Insert the font-face css on the page. In a web worker, this needs to + // be forwareded on the main thread. + if (typeof window == "undefined") { postMessage("font"); postMessage(JSON.stringify({ str: str, @@ -855,6 +782,19 @@ var Font = (function () { var rule = "@font-face { font-family:'" + fontName + "';src:" + url + "}"; var styleSheet = document.styleSheets[0]; styleSheet.insertRule(rule, styleSheet.length); + + var div = document.createElement("div"); + div.innerHTML += "
j
"; + document.body.appendChild(div); + + Fonts[fontName].loading = true; + window.setTimeout(function() { + Fonts[fontName].loading = false; + // Timeout of just `0`, `10` doesn't work here, but for me all values + // above work. Setting value to 50ms. + }, 50); } } }; diff --git a/pdf.js b/pdf.js index 1223a2bb6..847067946 100644 --- a/pdf.js +++ b/pdf.js @@ -2645,9 +2645,7 @@ var CanvasGraphics = (function() { } var fn = Function("objpool", src); - var ret = function (gfx) { fn.call(gfx, objpool); }; - ret.src = src; - return ret; + return function (gfx) { fn.call(gfx, objpool); }; }, endDrawing: function() { @@ -3015,8 +3013,8 @@ var CanvasGraphics = (function() { var botRight = applyMatrix([x0 + xstep, y0 + ystep], matrix); var tmpCanvas = new this.ScratchCanvas( - Math.ceil(botRight[0] - topLeft[0]), // WIDTH - Math.ceil(botRight[1] - topLeft[1]) // HEIGHT + Math.ceil(botRight[0] - topLeft[0]), // width + Math.ceil(botRight[1] - topLeft[1]) // height ); // set the new canvas element context as the graphics context diff --git a/worker.js b/pdf_worker.js similarity index 92% rename from worker.js rename to pdf_worker.js index 09e2b8145..91245aedb 100644 --- a/worker.js +++ b/pdf_worker.js @@ -1,3 +1,6 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + "use strict"; var timer = null; diff --git a/viewer_worker.html b/viewer_worker.html index a9f08388f..a5ffc6a6e 100644 --- a/viewer_worker.html +++ b/viewer_worker.html @@ -14,6 +14,7 @@ window.onload = function() { pdfDoc.onChangePage = function(numPage) { document.getElementById("pageNumber").value = numPage; } + // pdfDoc.open("canvas.pdf", function() { pdfDoc.open("compressed.tracemonkey-pldi-09.pdf", function() { document.getElementById("numPages").innerHTML = "/" + pdfDoc.numPages; }) diff --git a/worker_client.js b/worker_client.js index 316ef1fc0..f69f4f682 100644 --- a/worker_client.js +++ b/worker_client.js @@ -1,3 +1,6 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + "use strict"; function WorkerPDFDoc(canvas) { @@ -128,10 +131,11 @@ function WorkerPDFDoc(canvas) { } } - function renderProxyCanvas(canvas, stack) { + function renderProxyCanvas(canvas, cmdQueue) { var ctx = canvas.getContext("2d"); - for (var i = 0; i < stack.length; i++) { - var opp = stack[i]; + var cmdQueueLength = cmdQueue.length; + for (var i = 0; i < cmdQueueLength; i++) { + var opp = cmdQueue[i]; if (opp[0] == "$") { ctx[opp[1]] = opp[2]; } else if (opp[0] in ctxSpecial) { @@ -146,7 +150,7 @@ function WorkerPDFDoc(canvas) { * onMessage state machine. */ const WAIT = 0; - const CANVAS_PROXY_STACK = 1; + const CANVAS_PROXY_CMD_QUEUE = 1; const LOG = 2; const FONT = 3; const PDF_NUM_PAGE = 4; @@ -170,8 +174,8 @@ function WorkerPDFDoc(canvas) { onMessageState = LOG; return; - case "canvas_proxy_stack": - onMessageState = CANVAS_PROXY_STACK; + case "canvas_proxy_cmd_queue": + onMessageState = CANVAS_PROXY_CMD_QUEUE; return; case "font": @@ -215,6 +219,7 @@ function WorkerPDFDoc(canvas) { // Just adding the font-face to the DOM doesn't make it load. It // seems it's loaded once Gecko notices it's used. Therefore, // add a div on the page using the loaded font. + var div = document.createElement("div"); document.getElementById("fonts").innerHTML += "
j
"; onMessageState = WAIT; @@ -225,9 +230,9 @@ function WorkerPDFDoc(canvas) { onMessageState = WAIT; break; - case CANVAS_PROXY_STACK: + case CANVAS_PROXY_CMD_QUEUE: var id = data.id; - var stack = data.stack; + var cmdQueue = data.cmdQueue; // Check if there is already a canvas with the given id. If not, // create a new canvas. @@ -242,7 +247,7 @@ function WorkerPDFDoc(canvas) { // rendering at the end of the event queue ensures this. setTimeout(function() { if (id == 0) tic(); - renderProxyCanvas(canvasList[id], stack); + renderProxyCanvas(canvasList[id], cmdQueue); if (id == 0) toc("canvas rendering") }, 0); onMessageState = WAIT; From 78129970c6b70992e7d8316a1978f0be79a86604 Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Thu, 23 Jun 2011 13:25:59 +0200 Subject: [PATCH 17/24] Change postMessage to send only one object that holds the action and data. --- canvas_proxy.js | 17 +++--- fonts.js | 10 ++-- pdf_worker.js | 12 +++-- worker_client.js | 132 +++++++++++++++++------------------------------ 4 files changed, 70 insertions(+), 101 deletions(-) diff --git a/canvas_proxy.js b/canvas_proxy.js index 0b7681bfe..e2795bd00 100644 --- a/canvas_proxy.js +++ b/canvas_proxy.js @@ -11,10 +11,9 @@ var JpegStreamProxy = (function() { this.dict = dict; // Tell the main thread to create an image. - postMessage("jpeg_stream"); postMessage({ - id: this.id, - str: bytesToString(bytes) + action: jpeg_stream, + data: bytesToString(bytes) }); } @@ -235,12 +234,14 @@ function CanvasProxy(width, height) { * resets the cmdQueue. */ CanvasProxy.prototype.flush = function() { - postMessage("canvas_proxy_cmd_queue"); postMessage({ - id: this.id, - cmdQueue: this.cmdQueue, - width: this.width, - height: this.height + action: "canvas_proxy_cmd_queue", + data: { + id: this.id, + cmdQueue: this.cmdQueue, + width: this.width, + height: this.height + } }); this.cmdQueue.length = 0; } diff --git a/fonts.js b/fonts.js index a3604c6b9..7f4958caf 100644 --- a/fonts.js +++ b/fonts.js @@ -768,12 +768,14 @@ var Font = (function () { // Insert the font-face css on the page. In a web worker, this needs to // be forwareded on the main thread. if (typeof window == "undefined") { - postMessage("font"); - postMessage(JSON.stringify({ - str: str, + postMessage({ + action: "font", + data: { + raw: str, fontName: fontName, mimetype: this.mimetype - })); + } + }); } else { var base64 = window.btoa(str); diff --git a/pdf_worker.js b/pdf_worker.js index 91245aedb..13a1f3f28 100644 --- a/pdf_worker.js +++ b/pdf_worker.js @@ -15,8 +15,10 @@ function toc(msg) { function log() { var args = Array.prototype.slice.call(arguments); - postMessage("log"); - postMessage(JSON.stringify(args)) + postMessage({ + action: "log", + args: args + }); } var console = { @@ -42,8 +44,10 @@ onmessage = function(event) { // If there is no pdfDocument yet, then the sent data is the PDFDocument. if (!pdfDocument) { pdfDocument = new PDFDoc(new Stream(data)); - postMessage("pdf_num_page"); - postMessage(pdfDocument.numPages) + postMessage({ + action: "pdf_num_pages", + data: pdfDocument.numPages + }); return; } // User requested to render a certain page. diff --git a/worker_client.js b/worker_client.js index f69f4f682..4af0d9764 100644 --- a/worker_client.js +++ b/worker_client.js @@ -15,7 +15,7 @@ function WorkerPDFDoc(canvas) { this.ctx = canvas.getContext("2d"); this.canvas = canvas; - this.worker = new Worker('worker.js'); + this.worker = new Worker('pdf_worker.js'); this.numPage = 1; this.numPages = null; @@ -147,90 +147,44 @@ function WorkerPDFDoc(canvas) { } /** - * onMessage state machine. + * Functions to handle data sent by the WebWorker. */ - const WAIT = 0; - const CANVAS_PROXY_CMD_QUEUE = 1; - const LOG = 2; - const FONT = 3; - const PDF_NUM_PAGE = 4; - const JPEG_STREAM = 5; + var actionHandler = { + "log": function(data) { + console.log.apply(console, data); + }, + + "pdf_num_pages": function(data) { + this.numPages = parseInt(data); + if (this.loadCallback) { + this.loadCallback(); + } + }, + + "font": function(data) { + var base64 = window.btoa(data.raw); - var onMessageState = WAIT; - this.worker.onmessage = function(event) { - var data = event.data; - // console.log("onMessageRaw", data); - switch (onMessageState) { - case WAIT: - if (typeof data != "string") { - throw "expecting to get an string"; - } - switch (data) { - case "pdf_num_page": - onMessageState = PDF_NUM_PAGE; - return; + // Add the @font-face rule to the document + var url = "url(data:" + data.mimetype + ";base64," + base64 + ");"; + var rule = "@font-face { font-family:'" + data.fontName + "';src:" + url + "}"; + var styleSheet = document.styleSheets[0]; + styleSheet.insertRule(rule, styleSheet.length); - case "log": - onMessageState = LOG; - return; + // Just adding the font-face to the DOM doesn't make it load. It + // seems it's loaded once Gecko notices it's used. Therefore, + // add a div on the page using the loaded font. + var div = document.createElement("div"); + document.getElementById("fonts").innerHTML += "
j
"; + }, - case "canvas_proxy_cmd_queue": - onMessageState = CANVAS_PROXY_CMD_QUEUE; - return; - - case "font": - onMessageState = FONT; - return; - - case "jpeg_stream": - onMessageState = JPEG_STREAM; - return; - - default: - throw "unkown state: " + data - } - break; - - case JPEG_STREAM: - var img = new Image(); - img.src = "data:image/jpeg;base64," + window.btoa(data.str); - imagesList[data.id] = img; - console.log("got image", data.id) - break; - - case PDF_NUM_PAGE: - this.numPages = parseInt(data); - if (this.loadCallback) { - this.loadCallback(); - } - onMessageState = WAIT; - break; - - case FONT: - data = JSON.parse(data); - var base64 = window.btoa(data.str); - - // Add the @font-face rule to the document - var url = "url(data:" + data.mimetype + ";base64," + base64 + ");"; - var rule = "@font-face { font-family:'" + data.fontName + "';src:" + url + "}"; - var styleSheet = document.styleSheets[0]; - styleSheet.insertRule(rule, styleSheet.length); - - // Just adding the font-face to the DOM doesn't make it load. It - // seems it's loaded once Gecko notices it's used. Therefore, - // add a div on the page using the loaded font. - var div = document.createElement("div"); - document.getElementById("fonts").innerHTML += "
j
"; - - onMessageState = WAIT; - break; - - case LOG: - console.log.apply(console, JSON.parse(data)); - onMessageState = WAIT; - break; - - case CANVAS_PROXY_CMD_QUEUE: + "jpeg_stream": function(data) { + var img = new Image(); + img.src = "data:image/jpeg;base64," + window.btoa(data); + imagesList[data.id] = img; + console.log("got image", data.id) + }, + + "canvas_proxy_cmd_queue": function(data) { var id = data.id; var cmdQueue = data.cmdQueue; @@ -250,13 +204,21 @@ function WorkerPDFDoc(canvas) { renderProxyCanvas(canvasList[id], cmdQueue); if (id == 0) toc("canvas rendering") }, 0); - onMessageState = WAIT; - break; } - }.bind(this); + } + + // List to the WebWorker for data and call actionHandler on it. + this.worker.onmessage = function(event) { + var data = event.data; + if (data.action in actionHandler) { + actionHandler[data.action].call(this, data.data); + } else { + throw "Unkown action from worker: " + data.action; + } + } } - WorkerPDFDoc.prototype.open = function(url, callback) { +WorkerPDFDoc.prototype.open = function(url, callback) { var req = new XMLHttpRequest(); req.open("GET", url); req.mozResponseType = req.responseType = "arraybuffer"; From 37ee56a705d88b360e67b1cb3d2e064bd6614fb6 Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Thu, 23 Jun 2011 14:36:45 +0200 Subject: [PATCH 18/24] Fix sending image data to main thread --- canvas_proxy.js | 7 +++++-- viewer.js | 2 +- worker_client.js | 5 ++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/canvas_proxy.js b/canvas_proxy.js index e2795bd00..d6f5a0a25 100644 --- a/canvas_proxy.js +++ b/canvas_proxy.js @@ -12,8 +12,11 @@ var JpegStreamProxy = (function() { // Tell the main thread to create an image. postMessage({ - action: jpeg_stream, - data: bytesToString(bytes) + action: "jpeg_stream", + data: { + id: this.id, + raw: bytesToString(bytes) + } }); } diff --git a/viewer.js b/viewer.js index 41aaf354c..d0aeb0b2d 100644 --- a/viewer.js +++ b/viewer.js @@ -10,7 +10,7 @@ function load(userInput) { pageNum = parseInt(queryParams().page) || 1; var fileName = userInput; if (!userInput) { - fileName = queryParams().file || "compressed.tracemonkey-pldi-09.pdf"; + fileName = "canvas.pdf"; } open(fileName); } diff --git a/worker_client.js b/worker_client.js index 4af0d9764..385103c30 100644 --- a/worker_client.js +++ b/worker_client.js @@ -71,7 +71,7 @@ function WorkerPDFDoc(canvas) { "$drawImage": function(id, x, y, sx, sy, swidth, sheight) { var image = imagesList[id]; if (!image) { - throw "Image not found"; + throw "Image not found: " + id; } this.drawImage(image, x, y, image.width, image.height, sx, sy, swidth, sheight); @@ -179,9 +179,8 @@ function WorkerPDFDoc(canvas) { "jpeg_stream": function(data) { var img = new Image(); - img.src = "data:image/jpeg;base64," + window.btoa(data); + img.src = "data:image/jpeg;base64," + window.btoa(data.raw); imagesList[data.id] = img; - console.log("got image", data.id) }, "canvas_proxy_cmd_queue": function(data) { From c35e1e75522313faae4cfaa63dde10b903c93c07 Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Thu, 23 Jun 2011 15:24:55 +0200 Subject: [PATCH 19/24] Fix WebWorker logging and add separate timing for `fonts`. --- pdf_worker.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pdf_worker.js b/pdf_worker.js index 13a1f3f28..86dfec2dd 100644 --- a/pdf_worker.js +++ b/pdf_worker.js @@ -17,7 +17,7 @@ function log() { var args = Array.prototype.slice.call(arguments); postMessage({ action: "log", - args: args + data: args }); } @@ -62,7 +62,9 @@ onmessage = function(event) { var fonts = []; var gfx = new CanvasGraphics(canvas.getContext("2d"), CanvasProxy); page.compile(gfx, fonts); + toc("compiled page"); + tic() // Inspect fonts and translate the missing one. var count = fonts.length; for (var i = 0; i < count; i++) { @@ -75,7 +77,7 @@ onmessage = function(event) { // This "builds" the font and sents it over to the main thread. new Font(font.name, font.file, font.properties); } - toc("compiled page"); + toc("fonts"); tic() page.display(gfx); From 171ab51c569182bf41d4f1de74d55cc81a346abf Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Thu, 23 Jun 2011 19:43:01 +0200 Subject: [PATCH 20/24] Ensure divs used to make fonts load are not visible --- fonts.js | 6 +++--- viewer.js | 2 +- viewer_worker.html | 2 -- worker_client.js | 5 ++++- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/fonts.js b/fonts.js index 7f4958caf..ba40ef8e1 100644 --- a/fonts.js +++ b/fonts.js @@ -786,9 +786,9 @@ var Font = (function () { styleSheet.insertRule(rule, styleSheet.length); var div = document.createElement("div"); - div.innerHTML += "
j
"; + var style = 'font-family:"' + fontName + + '";position: absolute;top:-99999;left:-99999;z-index:-99999'; + div.setAttribute("style", style); document.body.appendChild(div); Fonts[fontName].loading = true; diff --git a/viewer.js b/viewer.js index d0aeb0b2d..41aaf354c 100644 --- a/viewer.js +++ b/viewer.js @@ -10,7 +10,7 @@ function load(userInput) { pageNum = parseInt(queryParams().page) || 1; var fileName = userInput; if (!userInput) { - fileName = "canvas.pdf"; + fileName = queryParams().file || "compressed.tracemonkey-pldi-09.pdf"; } open(fileName); } diff --git a/viewer_worker.html b/viewer_worker.html index a5ffc6a6e..d13935f13 100644 --- a/viewer_worker.html +++ b/viewer_worker.html @@ -22,9 +22,7 @@ window.onload = function() { - -