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