]> git.parisson.com Git - pdf.js.git/commitdiff
Move all worker related files under worker/
authorJulian Viereck <julian.viereck@gmail.com>
Sun, 26 Jun 2011 18:44:13 +0000 (20:44 +0200)
committerJulian Viereck <julian.viereck@gmail.com>
Tue, 28 Jun 2011 06:06:39 +0000 (08:06 +0200)
canvas_proxy.js [deleted file]
pdf_worker.js [deleted file]
viewer_worker.html
worker/canvas.js [new file with mode: 0644]
worker/client.js [new file with mode: 0644]
worker/pdf.js [new file with mode: 0644]
worker_client.js [deleted file]

diff --git a/canvas_proxy.js b/canvas_proxy.js
deleted file mode 100644 (file)
index d6f5a0a..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-/* -*- 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.
-var JpegStreamProxy = (function() {
-  function constructor(bytes, dict) {
-    this.id = JpegStreamProxyCounter++;
-    this.dict = dict;
-
-    // Tell the main thread to create an image.
-    postMessage({
-      action: "jpeg_stream",
-      data: {
-        id: this.id,
-        raw: bytesToString(bytes)
-      }
-    });
-  }
-
-  constructor.prototype = {
-    getImage: function() {
-      return this;
-    },
-    getChar: function() {
-      error("internal error: getChar is not valid on JpegStream");
-    }
-  };
-
-  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(cmdQueue, x0, y0, x1, y1) {
-  cmdQueue.push(["$createLinearGradient", [x0, y0, x1, y1]]);
-  this.addColorStop = function(i, rgba) {
-    cmdQueue.push(["$addColorStop", [i, rgba]]);
-  }
-}
-
-// Really simple PatternProxy.
-var patternProxyCounter = 0;
-function PatternProxy(cmdQueue, object, kind) {
-  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();
-  cmdQueue.push(["$createPatternFromCanvas", [this.id, object.id, kind]]);
-}
-
-var canvasProxyCounter = 0;
-function CanvasProxy(width, height) {
-  this.id = canvasProxyCounter++;
-
-  // The `stack` holds the rendering calls and gets flushed to the main thead.
-  var cmdQueue = this.cmdQueue = [];
-
-  // Dummy context that gets 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;
-
-  // 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)
-      cmdQueue.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(cmdQueue, object, kind);
-  }
-
-  ctx.createLinearGradient = function(x0, y0, x1, y1) {
-    return new GradientProxy(cmdQueue, 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) {
-    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();
-      cmdQueue.push(["$drawCanvas", [image.id, x, y, sx, sy, swidth, sheight]]);
-    } else if(image instanceof JpegStreamProxy) {
-      cmdQueue.push(["$drawImage", [image.id, x, y, sx, sy, swidth, sheight]])
-    } else {
-      throw "unkown type to drawImage";
-    }
-  }
-
-  // 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];
-    }
-  }
-
-  function buildSetter(name) {
-    return function(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));
-
-    // Special treatment for `fillStyle` and `strokeStyle`: The passed style
-    // might be a gradient. Need to check for that.
-    if (name == "fillStyle" || name == "strokeStyle") {
-      ctx.__defineSetter__(name, buildSetterStyle(name));
-    } else {
-      ctx.__defineSetter__(name, buildSetter(name));
-    }
-  }
-}
-
-/**
-* Sends the current cmdQueue of the CanvasProxy over to the main thread and
-* resets the cmdQueue.
-*/
-CanvasProxy.prototype.flush = function() {
-  postMessage({
-    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/pdf_worker.js b/pdf_worker.js
deleted file mode 100644 (file)
index fa29428..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/* -*- 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 consoleTimer = {};
-var console = {
-  log: function log() {
-    var args = Array.prototype.slice.call(arguments);
-    postMessage({
-      action: "log",
-      data: args
-    });
-  },
-  
-  time: function(name) {
-    consoleTimer[name] = Date.now();
-  },
-  
-  timeEnd: function(name) {
-    var time = consoleTimer[name];
-    if (time == null) {
-      throw "Unkown timer name " + name;
-    }
-    this.log("Timer:", name, Date.now() - time);
-  }
-}
-
-//
-importScripts("canvas_proxy.js");
-importScripts("pdf.js");
-importScripts("fonts.js");
-importScripts("glyphlist.js")
-
-// Use the JpegStreamProxy proxy.
-JpegStream = JpegStreamProxy;
-
-// Create the WebWorkerProxyCanvas.
-var canvas = new CanvasProxy(1224, 1584);
-
-// Listen for messages from the main thread.
-var pdfDocument = null;
-onmessage = function(event) {
-  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({
-      action: "pdf_num_pages",
-      data: pdfDocument.numPages
-    });
-    return;
-  }
-  // User requested to render a certain page.
-  else {
-    console.time("compile");
-
-    // 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);
-    console.timeEnd("compile");
-
-    console.time("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;
-      }
-
-      // This "builds" the font and sents it over to the main thread.
-      new Font(font.name, font.file, font.properties);
-    }
-    console.timeEnd("fonts");
-
-    console.time("display");
-    page.display(gfx);
-    canvas.flush();
-    console.timeEnd("display");
-  }
-}
index 51f2b9d8a9618539fb20ed7baf7f97380fc72ba1..6443b22c10a32c0f26ea35e66ac25bd37ceaad89 100644 (file)
@@ -1,7 +1,7 @@
 <html>
     <head>
         <title>Simple pdf.js page worker viewer</title>
-        <script type="text/javascript" src="worker_client.js"></script>
+        <script type="text/javascript" src="worker/client.js"></script>
 <script>
 
 
diff --git a/worker/canvas.js b/worker/canvas.js
new file mode 100644 (file)
index 0000000..d6f5a0a
--- /dev/null
@@ -0,0 +1,250 @@
+/* -*- 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.
+var JpegStreamProxy = (function() {
+  function constructor(bytes, dict) {
+    this.id = JpegStreamProxyCounter++;
+    this.dict = dict;
+
+    // Tell the main thread to create an image.
+    postMessage({
+      action: "jpeg_stream",
+      data: {
+        id: this.id,
+        raw: bytesToString(bytes)
+      }
+    });
+  }
+
+  constructor.prototype = {
+    getImage: function() {
+      return this;
+    },
+    getChar: function() {
+      error("internal error: getChar is not valid on JpegStream");
+    }
+  };
+
+  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(cmdQueue, x0, y0, x1, y1) {
+  cmdQueue.push(["$createLinearGradient", [x0, y0, x1, y1]]);
+  this.addColorStop = function(i, rgba) {
+    cmdQueue.push(["$addColorStop", [i, rgba]]);
+  }
+}
+
+// Really simple PatternProxy.
+var patternProxyCounter = 0;
+function PatternProxy(cmdQueue, object, kind) {
+  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();
+  cmdQueue.push(["$createPatternFromCanvas", [this.id, object.id, kind]]);
+}
+
+var canvasProxyCounter = 0;
+function CanvasProxy(width, height) {
+  this.id = canvasProxyCounter++;
+
+  // The `stack` holds the rendering calls and gets flushed to the main thead.
+  var cmdQueue = this.cmdQueue = [];
+
+  // Dummy context that gets 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;
+
+  // 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)
+      cmdQueue.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(cmdQueue, object, kind);
+  }
+
+  ctx.createLinearGradient = function(x0, y0, x1, y1) {
+    return new GradientProxy(cmdQueue, 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) {
+    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();
+      cmdQueue.push(["$drawCanvas", [image.id, x, y, sx, sy, swidth, sheight]]);
+    } else if(image instanceof JpegStreamProxy) {
+      cmdQueue.push(["$drawImage", [image.id, x, y, sx, sy, swidth, sheight]])
+    } else {
+      throw "unkown type to drawImage";
+    }
+  }
+
+  // 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];
+    }
+  }
+
+  function buildSetter(name) {
+    return function(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));
+
+    // Special treatment for `fillStyle` and `strokeStyle`: The passed style
+    // might be a gradient. Need to check for that.
+    if (name == "fillStyle" || name == "strokeStyle") {
+      ctx.__defineSetter__(name, buildSetterStyle(name));
+    } else {
+      ctx.__defineSetter__(name, buildSetter(name));
+    }
+  }
+}
+
+/**
+* Sends the current cmdQueue of the CanvasProxy over to the main thread and
+* resets the cmdQueue.
+*/
+CanvasProxy.prototype.flush = function() {
+  postMessage({
+    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/worker/client.js b/worker/client.js
new file mode 100644 (file)
index 0000000..5d0cae0
--- /dev/null
@@ -0,0 +1,272 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+
+"use strict";
+
+if (typeof console.time == "undefined") {
+  var consoleTimer = {};
+  console.time = function(name) {
+    consoleTimer[name] = Date.now();
+  };
+  
+  console.timeEnd = function(name) {
+    var time = consoleTimer[name];
+    if (time == null) {
+      throw "Unkown timer name " + name;
+    }
+    this.log("Timer:", name, Date.now() - time);
+  };
+}
+
+function WorkerPDFDoc(canvas) {
+  var timer = null
+
+  this.ctx = canvas.getContext("2d");
+  this.canvas = canvas;
+  this.worker = new Worker('worker/pdf.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) {
+      this.translate(currentX, -1 * y);
+      this.fillText(text, 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: " + id;
+      }
+      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, cmdQueue) {
+    var ctx = canvas.getContext("2d");
+    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) {
+        ctxSpecial[opp[0]].apply(ctx, opp[1]);
+      } else {
+        ctx[opp[0]].apply(ctx, opp[1]);
+      }
+    }
+  }
+
+  /**
+  * Functions to handle data sent by the WebWorker.
+  */
+  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);
+
+      // 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");
+      var style = 'font-family:"' + data.fontName + 
+        '";position: absolute;top:-99999;left:-99999;z-index:-99999';
+      div.setAttribute("style", style);
+      document.body.appendChild(div);
+    },
+
+    "jpeg_stream": function(data) {
+      var img = new Image();
+      img.src = "data:image/jpeg;base64," + window.btoa(data.raw);
+      imagesList[data.id] = img;
+    },
+    
+    "canvas_proxy_cmd_queue": function(data) {
+        var id = data.id;
+        var cmdQueue = data.cmdQueue;
+
+        // 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) {
+            console.time("canvas rendering");
+            var ctx = this.ctx;
+            ctx.save();
+            ctx.fillStyle = "rgb(255, 255, 255)";
+            ctx.fillRect(0, 0, canvas.width, canvas.height);
+            ctx.restore();
+          }
+          renderProxyCanvas(canvasList[id], cmdQueue);
+          if (id == 0) console.timeEnd("canvas rendering")
+        }, 0, 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) {
+  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) {
+  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);
+}
diff --git a/worker/pdf.js b/worker/pdf.js
new file mode 100644 (file)
index 0000000..c4d16d8
--- /dev/null
@@ -0,0 +1,88 @@
+/* -*- 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 consoleTimer = {};
+var console = {
+  log: function log() {
+    var args = Array.prototype.slice.call(arguments);
+    postMessage({
+      action: "log",
+      data: args
+    });
+  },
+  
+  time: function(name) {
+    consoleTimer[name] = Date.now();
+  },
+  
+  timeEnd: function(name) {
+    var time = consoleTimer[name];
+    if (time == null) {
+      throw "Unkown timer name " + name;
+    }
+    this.log("Timer:", name, Date.now() - time);
+  }
+}
+
+//
+importScripts("canvas.js");
+importScripts("../pdf.js");
+importScripts("../fonts.js");
+importScripts("../glyphlist.js")
+
+// Use the JpegStreamProxy proxy.
+JpegStream = JpegStreamProxy;
+
+// Create the WebWorkerProxyCanvas.
+var canvas = new CanvasProxy(1224, 1584);
+
+// Listen for messages from the main thread.
+var pdfDocument = null;
+onmessage = function(event) {
+  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({
+      action: "pdf_num_pages",
+      data: pdfDocument.numPages
+    });
+    return;
+  }
+  // User requested to render a certain page.
+  else {
+    console.time("compile");
+
+    // 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);
+    console.timeEnd("compile");
+
+    console.time("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;
+      }
+
+      // This "builds" the font and sents it over to the main thread.
+      new Font(font.name, font.file, font.properties);
+    }
+    console.timeEnd("fonts");
+
+    console.time("display");
+    page.display(gfx);
+    canvas.flush();
+    console.timeEnd("display");
+  }
+}
diff --git a/worker_client.js b/worker_client.js
deleted file mode 100644 (file)
index 359a5f7..0000000
+++ /dev/null
@@ -1,272 +0,0 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
-/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
-
-"use strict";
-
-if (typeof console.time == "undefined") {
-  var consoleTimer = {};
-  console.time = function(name) {
-    consoleTimer[name] = Date.now();
-  };
-  
-  console.timeEnd = function(name) {
-    var time = consoleTimer[name];
-    if (time == null) {
-      throw "Unkown timer name " + name;
-    }
-    this.log("Timer:", name, Date.now() - time);
-  };
-}
-
-function WorkerPDFDoc(canvas) {
-  var timer = null
-
-  this.ctx = canvas.getContext("2d");
-  this.canvas = canvas;
-  this.worker = new Worker('pdf_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) {
-      this.translate(currentX, -1 * y);
-      this.fillText(text, 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: " + id;
-      }
-      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, cmdQueue) {
-    var ctx = canvas.getContext("2d");
-    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) {
-        ctxSpecial[opp[0]].apply(ctx, opp[1]);
-      } else {
-        ctx[opp[0]].apply(ctx, opp[1]);
-      }
-    }
-  }
-
-  /**
-  * Functions to handle data sent by the WebWorker.
-  */
-  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);
-
-      // 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");
-      var style = 'font-family:"' + data.fontName + 
-        '";position: absolute;top:-99999;left:-99999;z-index:-99999';
-      div.setAttribute("style", style);
-      document.body.appendChild(div);
-    },
-
-    "jpeg_stream": function(data) {
-      var img = new Image();
-      img.src = "data:image/jpeg;base64," + window.btoa(data.raw);
-      imagesList[data.id] = img;
-    },
-    
-    "canvas_proxy_cmd_queue": function(data) {
-        var id = data.id;
-        var cmdQueue = data.cmdQueue;
-
-        // 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) {
-            console.time("canvas rendering");
-            var ctx = this.ctx;
-            ctx.save();
-            ctx.fillStyle = "rgb(255, 255, 255)";
-            ctx.fillRect(0, 0, canvas.width, canvas.height);
-            ctx.restore();
-          }
-          renderProxyCanvas(canvasList[id], cmdQueue);
-          if (id == 0) console.timeEnd("canvas rendering")
-        }, 0, 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) {
-  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) {
-  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);
-}