From e15328800aad995b1a2957ba4f98f9a618a6208f Mon Sep 17 00:00:00 2001
From: Julian Viereck <julian.viereck@gmail.com>
Date: Wed, 22 Jun 2011 01:28:17 +0200
Subject: [PATCH] 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) {