]> git.parisson.com Git - pdf.js.git/commitdiff
Move client code into worker_client.js. Cleanup + comments + 2-space-indention
authorJulian Viereck <julian.viereck@gmail.com>
Wed, 22 Jun 2011 20:54:16 +0000 (22:54 +0200)
committerJulian Viereck <julian.viereck@gmail.com>
Thu, 23 Jun 2011 21:33:24 +0000 (23:33 +0200)
canvas_proxy.js
viewer_worker.html
worker.js
worker_client.js [new file with mode: 0644]

index 07ae31a631a94bcbacc50b8b918262d78e7ea177..83b57682fca3e254997a77cbc8a483134ee81f78 100644 (file)
-// 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;
-// }
 
 var JpegStreamProxyCounter = 0;
 // WebWorker Proxy for JpegStream.
 var JpegStreamProxy = (function() {
-    function constructor(bytes, dict) {
-        this.id = JpegStreamProxyCounter++;
-        this.dict = dict;
+  function constructor(bytes, dict) {
+    this.id = JpegStreamProxyCounter++;
+    this.dict = dict;
 
-        // create DOM image.
-        postMessage("jpeg_stream");
-        postMessage({
-            id:  this.id,
-            str: bytesToString(bytes)
-        });
-
-        // var img = new Image();
-        // img.src = "data:image/jpeg;base64," + window.btoa(bytesToString(bytes));
-        // this.domImage = img;
+    // Tell the main thread to create an image.
+    postMessage("jpeg_stream");
+    postMessage({
+      id:  this.id,
+      str: bytesToString(bytes)
+    });
+  }
+
+  constructor.prototype = {
+    getImage: function() {
+      return this;
+    },
+    getChar: function() {
+      error("internal error: getChar is not valid on JpegStream");
     }
+  };
 
-    constructor.prototype = {
-        getImage: function() {
-            return this;
-            // return this.domImage;
-        },
-        getChar: function() {
-            error("internal error: getChar is not valid on JpegStream");
-        }
-    };
-
-    return constructor;
+  return constructor;
 })();
 
 // 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]]);
-    this.addColorStop = function(i, rgba) {
-        stack.push(["$addColorStop", [i, rgba]]);
-    }
+  stack.push(["$createLinearGradient", [x0, y0, x1, y1]]);
+  this.addColorStop = function(i, rgba) {
+    stack.push(["$addColorStop", [i, rgba]]);
+  }
 }
 
+// Really simple PatternProxy.
 var patternProxyCounter = 0;
 function PatternProxy(stack, object, kind) {
-    this.id = patternProxyCounter++;
+  this.id = patternProxyCounter++;
 
-    if (!(object instanceof CanvasProxy) ) {
-        throw "unkown type to createPattern";
-    }
-    // Flush the object here to ensure it's available on the main thread.
-    // 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]]);
+  if (!(object instanceof CanvasProxy) ) {
+    throw "unkown type to createPattern";
+  }
+
+  // Flush the object here to ensure it's available on the main thread.
+  // 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]]);
 }
 
 var canvasProxyCounter = 0;
 function CanvasProxy(width, height) {
-    this.id = canvasProxyCounter++;
-
-    var stack = this.$stack = [];
-
-    // Dummy context exposed.
-    var ctx = {};
-    this.getContext = function(type) {
-        if (type != "2d") {
-            throw "CanvasProxy can only provide a 2d context.";
-        }
-        return ctx;
-    }
-
-    // Expose only the minimum of the canvas object - there is no dom to do
-    // more here.
-    this.width = width;
-    this.height = height;
-    ctx.canvas = this;
-
-    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",
-
-        "$setCurrentX",
-        "$addCurrentX",
-        "$saveCurrentX",
-        "$restoreCurrentX",
-        "$showText"
-    ];
-
-    ctx.createPattern = function(object, kind) {
-        return new PatternProxy(stack, object, kind);
-    }
+  this.id = canvasProxyCounter++;
 
-    ctx.createLinearGradient = function(x0, y0, x1, y1) {
-        return new GradientProxy(stack, x0, y0, x1, y1);
-    }
+  // The `stack` holds the rendering calls and gets flushed to the main thead.
+  var stack = this.$stack = [];
 
-    ctx.getImageData = function(x, y, w, h) {
-        return {
-            width: w,
-            height: h,
-            data: Uint8ClampedArray(w * h * 4)
-        };
+  // Dummy context that gets exposed.
+  var ctx = {};
+  this.getContext = function(type) {
+    if (type != "2d") {
+      throw "CanvasProxy can only provide a 2d context.";
     }
-
-    ctx.putImageData = function(data, x, y, width, height) {
-        stack.push(["$putImageData", [data, x, y, width, height]]);
+    return ctx;
+  }
+
+  // Expose only the minimum of the canvas object - there is no dom to do
+  // more here.
+  this.width = width;
+  this.height = height;
+  ctx.canvas = this;
+
+  // Setup function calls to `ctx`.
+  var ctxFunc = [
+  "createRadialGradient",
+  "arcTo",
+  "arc",
+  "fillText",
+  "strokeText",
+  "createImageData",
+  "drawWindow",
+  "save",
+  "restore",
+  "scale",
+  "rotate",
+  "translate",
+  "transform",
+  "setTransform",
+  "clearRect",
+  "fillRect",
+  "strokeRect",
+  "beginPath",
+  "closePath",
+  "moveTo",
+  "lineTo",
+  "quadraticCurveTo",
+  "bezierCurveTo",
+  "rect",
+  "fill",
+  "stroke",
+  "clip",
+  "measureText",
+  "isPointInPath",
+
+  // These functions are necessary to track the rendering currentX state.
+  // The exact values can be computed on the main thread only, as the
+  // worker has no idea about text width.
+  "$setCurrentX",
+  "$addCurrentX",
+  "$saveCurrentX",
+  "$restoreCurrentX",
+  "$showText"
+  ];
+
+  function buildFuncCall(name) {
+    return function() {
+      // console.log("funcCall", name)
+      stack.push([name, Array.prototype.slice.call(arguments)]);
     }
-
-    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]]);
-        } else if(image instanceof JpegStreamProxy) {
-            stack.push(["$drawImage", [image.id, x, y, sx, sy, swidth, sheight]])
-        } else {
-            throw "unkown type to drawImage";
-        }
-    }
-
-    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];
+    ctx[name] = buildFuncCall(name);
+  }
+
+  // Some function calls that need more work.
+
+  ctx.createPattern = function(object, kind) {
+    return new PatternProxy(stack, object, kind);
+  }
+
+  ctx.createLinearGradient = function(x0, y0, x1, y1) {
+    return new GradientProxy(stack, x0, y0, x1, y1);
+  }
+
+  ctx.getImageData = function(x, y, w, h) {
+    return {
+      width: w,
+      height: h,
+      data: Uint8ClampedArray(w * h * 4)
+    };
+  }
+
+  ctx.putImageData = function(data, x, y, width, height) {
+    stack.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]]);
+    } else if(image instanceof JpegStreamProxy) {
+      stack.push(["$drawImage", [image.id, x, y, sx, sy, swidth, sheight]])
+    } else {
+      throw "unkown type to drawImage";
     }
-    var name;
-    for (var i = 0; i < ctxFunc.length; i++) {
-        name = ctxFunc[i];
-        ctx[name] = buildFuncCall(name);
+  }
+
+  // Setup property access to `ctx`.
+  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"
+  }
+
+  function buildGetter(name) {
+    return function() {
+      return ctx["$" + 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 buildSetter(name) {
+    return function(value) {
+      stack.push(["$", name, value]);
+      return ctx["$" + name] = value;
     }
+  }
 
-    function buildGetter(name) {
-        return function() {
-            return ctx["$" + name];
-        }
-    }
+  for (var name in ctxProp) {
+    ctx["$" + name] = ctxProp[name];
+    ctx.__defineGetter__(name, buildGetter(name));
 
-    function buildSetter(name) {
+    // 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));
     }
-
-    for (var name in ctxProp) {
-        ctx["$" + name] = ctxProp[name];
-        ctx.__defineGetter__(name, buildGetter(name));
-
-        // 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));
-        }
-    }
+  }
 }
 
+/**
+* Sends the current stack of the CanvasProxy over to the main thread and
+* resets the stack.
+*/
 CanvasProxy.prototype.flush = function() {
-    postMessage("canvas_proxy_stack");
-    postMessage({
-        id:     this.id,
-        stack:  this.$stack,
-        width:  this.width,
-        height: this.height
-    });
-    this.$stack.length = 0;
+  postMessage("canvas_proxy_stack");
+  postMessage({
+    id:     this.id,
+    stack:  this.$stack,
+    width:  this.width,
+    height: this.height
+  });
+  this.$stack.length = 0;
 }
index 92806bc994957a58fa2a45b8724218458fd31d21..a9f08388f5b42116fda309ab090d48c1f4ddcf9b 100644 (file)
 <html>
     <head>
         <title>Simple pdf.js page viewer worker</title>
+        <script type="text/javascript" src="worker_client.js"></script>
 <script>
 
-var timer = null
-function tic() {
-    timer = Date.now();
-}
-
-function toc(msg) {
-    console.log(msg + ": " + (Date.now() - timer) + "ms");
-}
-
-var myWorker = new Worker('worker.js');
-var imagesList = {};
-var canvasList = {};
-var patternList = {};
-var gradient;
-
-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, 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;
-    }
-}
-
-var gStack;
-function renderProxyCanvas(canvas, stack) {
-    var ctx = canvas.getContext("2d");
-    for (var i = 0; i < stack.length; i++) {
-    // for (var i = 0; i < 1000; i++) {
-        var opp = stack[i];
-        if (opp[0] == "$") {
-            ctx[opp[1]] = opp[2];
-        } else if (opp[0] in special) {
-            special[opp[0]].apply(ctx, opp[1]);
-        } else {
-            ctx[opp[0]].apply(ctx, opp[1]);
-        }
-    }
-}
-
-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;
-var fontStr = null;
-var first = true;
-var intervals = [];
-myWorker.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:
-            console.log(data);
-            maxPages = parseInt(data);
-            document.getElementById("numPages").innerHTML = "/" + data;
-            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 += "<div style='font-family:" + data.fontName + "'>j</div>";
-            console.log("setup font", data.fontName);
-
-            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;
-            gStack = 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;
-    }
-}
-//
-// myWorker.postMessage(array);
-
-var currentPage = 1;
-var maxPages = 1;
-function showPage(num) {
-    ctx.save();
-    ctx.fillStyle = "rgb(255, 255, 255)";
-    ctx.fillRect(0, 0, canvas.width, canvas.height);
-    ctx.restore();
-    console.log("worker: page=" + num)
-    document.getElementById('pageNumber').value = num;
-    currentPage = parseInt(num);
-    myWorker.postMessage(num);
-}
-
-function open(url) {
-    document.title = url;
-    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;
-        myWorker.postMessage(data);
-        showPage("2");
-      }
-    };
-    req.send(null);
-}
-
-function nextPage() {
-    if (currentPage == maxPages) return;
-    currentPage++;
-    showPage(currentPage);
-}
-
-function prevPage() {
-    if (currentPage == 1) return;
-    currentPage--;
-    showPage(currentPage);
-}
 
+var pdfDoc;
 window.onload = function() {
     window.canvas = document.getElementById("canvas");
     window.ctx = canvas.getContext("2d");
-    canvasList[0] = window.canvas;
-    open("compressed.tracemonkey-pldi-09.pdf");
+
+    pdfDoc = new WorkerPDFDoc(window.canvas);
+    pdfDoc.onChangePage = function(numPage) {
+        document.getElementById("pageNumber").value = numPage;
+    }
+    pdfDoc.open("compressed.tracemonkey-pldi-09.pdf", function() {
+        document.getElementById("numPages").innerHTML = "/" + pdfDoc.numPages;
+    })
 }
 </script>
         <link rel="stylesheet" href="viewer.css"></link>
@@ -301,9 +28,9 @@ window.onload = function() {
     <input type="file" style="float: right; margin: auto 32px;" onChange="load(this.value.toString());"></input>
     <!-- This only opens supported PDFs from the source path...
       -- Can we use JSONP to overcome the same-origin restrictions? -->
-      <button onclick="prevPage();">Previous</button>
-      <button onclick="nextPage();">Next</button>
-      <input type="text" id="pageNumber" onchange="showPage(this.value);"
+      <button onclick="pdfDoc.prevPage();">Previous</button>
+      <button onclick="pdfDoc.nextPage();">Next</button>
+      <input type="text" id="pageNumber" onchange="pdfDoc.showPage(this.value);"
              value="1" size="4"></input>
       <span id="numPages">--</span>
       <span id="info"></span>
index e59e37155ea555681f8065070a8a7ad82163ca76..09e2b814553d38a89802bcfdbc3e4ffc8dfc9495 100644 (file)
--- 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 (file)
index 0000000..316ef1f
--- /dev/null
@@ -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 += "<div style='font-family:" + data.fontName + "'>j</div>";
+
+        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);
+}