From 31e9bea5c9de2eef2e1661a20873bedb19ba5d1a Mon Sep 17 00:00:00 2001
From: Julian Viereck <julian.viereck@gmail.com>
Date: Wed, 29 Jun 2011 14:26:38 +0200
Subject: [PATCH 1/7] Make worker code work with latest font code changes

---
 pdf.js           | 12 +++++-------
 worker/client.js |  7 ++++---
 2 files changed, 9 insertions(+), 10 deletions(-)

diff --git a/pdf.js b/pdf.js
index 33a21e38c..349589016 100644
--- a/pdf.js
+++ b/pdf.js
@@ -3807,23 +3807,21 @@ var CanvasGraphics = (function() {
             if (fontDescriptor && fontDescriptor.num) {
                 var fontDescriptor = this.xref.fetchIfRef(fontDescriptor);
                 fontName = fontDescriptor.get("FontName").name.replace("+", "_");
-                Fonts.setActive(fontName, size);
             }
 
             if (!fontName) {
                 // TODO: fontDescriptor is not available, fallback to default font
-                this.current.fontSize = size;
-                this.ctx.font = this.current.fontSize + 'px sans-serif';
-                Fonts.setActive("sans-serif", this.current.fontSize);
-                return;
+                fontName = "sans-serif";
             }
 
             this.current.fontName = fontName;
             this.current.fontSize = size;
 
-            this.ctx.font = this.current.fontSize + 'px "' + fontName + '"';
             if (this.ctx.$setFont) {
-              this.ctx.$setFont(fontName);
+              this.ctx.$setFont(fontName, size);
+            } else {
+              this.ctx.font = size + 'px "' + fontName + '"';
+              Fonts.setActive(fontName, size);
             }
         },
         setTextRenderingMode: function(mode) {
diff --git a/worker/client.js b/worker/client.js
index 84e44b94c..960e97496 100644
--- a/worker/client.js
+++ b/worker/client.js
@@ -250,9 +250,10 @@ function WorkerPDFDoc(canvas) {
       }
       this.strokeStyle = pattern;
     },
-    
-    "$setFont": function(name) {
-      Fonts.active = name;
+
+    "$setFont": function(name, size) {
+      this.font = size + 'px "' + name + '"';
+      Fonts.setActive(name, size);
     }
   }
 

From 57d4053f6275726fb706052b980d17f38d3fe775 Mon Sep 17 00:00:00 2001
From: Chris Jones <jones.chris.g@gmail.com>
Date: Wed, 29 Jun 2011 18:35:00 -0700
Subject: [PATCH 2/7] try to make sidebar a bit more discoverable by leaving a
 little edge of it sticking out

---
 multi_page_viewer.css | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/multi_page_viewer.css b/multi_page_viewer.css
index 2eaca4870..17b2537be 100644
--- a/multi_page_viewer.css
+++ b/multi_page_viewer.css
@@ -181,7 +181,7 @@ span {
   width: 200px;
   top: 62px;
   bottom: 18px;
-  left: -170px;
+  left: -140px;
   transition: left 0.25s ease-in-out 1s;
   -moz-transition: left 0.25s ease-in-out 1s;
   -webkit-transition: left 0.25s ease-in-out 1s;

From ab3bb9726e0bef242c6637cd4771eef75351a48a Mon Sep 17 00:00:00 2001
From: Chris Jones <jones.chris.g@gmail.com>
Date: Wed, 29 Jun 2011 18:52:27 -0700
Subject: [PATCH 3/7] redraw on zoom changes

---
 multi_page_viewer.js | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/multi_page_viewer.js b/multi_page_viewer.js
index b2c0dc3ed..4e9ca79d5 100644
--- a/multi_page_viewer.js
+++ b/multi_page_viewer.js
@@ -249,7 +249,10 @@ var PDFViewer = {
       PDFViewer.pageNumber = num;
       PDFViewer.pageNumberInput.value = PDFViewer.pageNumber;
       PDFViewer.willJumpToPage = true;
-      
+
+      if (document.location.hash.substr(1) == PDFViewer.pageNumber)
+        // Force a "scroll event" to redraw
+        setTimeout(window.onscroll, 0);
       document.location.hash = PDFViewer.pageNumber;
       
       PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ? 'disabled' : '';

From ca63ad8a4d91c0c445d2abe41365c5b6213d6fc4 Mon Sep 17 00:00:00 2001
From: Chris Jones <jones.chris.g@gmail.com>
Date: Wed, 29 Jun 2011 19:01:43 -0700
Subject: [PATCH 4/7] no need to recreate thumbnails after zooming, thumbnails
 don't change size

---
 multi_page_viewer.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/multi_page_viewer.js b/multi_page_viewer.js
index b2c0dc3ed..1bde03cc6 100644
--- a/multi_page_viewer.js
+++ b/multi_page_viewer.js
@@ -216,7 +216,6 @@ var PDFViewer = {
     
     if (PDFViewer.pdf) {
       for (i = 1; i <= PDFViewer.numberOfPages; i++) {
-        PDFViewer.createThumbnail(i);
         PDFViewer.createPage(i);
       }
     }

From 5ece4da552533a28827f9a0b9088e14b11f1866c Mon Sep 17 00:00:00 2001
From: notmasteryet <async.processingjs@yahoo.com>
Date: Wed, 29 Jun 2011 22:43:59 -0500
Subject: [PATCH 5/7] the "hieght" and the 72dpi<->96dpi fixes

---
 multi_page_viewer.js | 19 +++++++++++--------
 test/test_slave.html | 10 ++++++----
 viewer.css           |  7 ++++---
 viewer.html          |  4 +---
 viewer.js            | 10 +++++-----
 viewer_worker.html   |  5 +----
 worker/client.js     |  7 +++++++
 worker/pdf.js        | 13 +++++++++++++
 8 files changed, 48 insertions(+), 27 deletions(-)

diff --git a/multi_page_viewer.js b/multi_page_viewer.js
index 87e2c8f14..7bd59873f 100644
--- a/multi_page_viewer.js
+++ b/multi_page_viewer.js
@@ -29,11 +29,15 @@ var PDFViewer = {
   scale: 1.0,
   
   pageWidth: function(page) {
-    return page.mediaBox[2] * PDFViewer.scale;
+    var pdfToCssUnitsCoef = 96.0 / 72.0;
+    var width = (page.mediaBox[2] - page.mediaBox[0]);
+    return width * PDFViewer.scale * pdfToCssUnitsCoef;
   },
   
   pageHeight: function(page) {
-    return page.mediaBox[3] * PDFViewer.scale;
+    var pdfToCssUnitsCoef = 96.0 / 72.0;
+    var height = (page.mediaBox[3] - page.mediaBox[1]);
+    return height * PDFViewer.scale * pdfToCssUnitsCoef;
   },
   
   lastPagesDrawn: [],
@@ -106,10 +110,11 @@ var PDFViewer = {
       canvas.id = 'thumbnail' + num;
       canvas.mozOpaque = true;
 
-      // Canvas dimensions must be specified in CSS pixels. CSS pixels
-      // are always 96 dpi. These dimensions are 8.5in x 11in at 96dpi.
-      canvas.width = 104;
-      canvas.height = 134;
+      var pageWidth = PDFViewer.pageWidth(page);
+      var pageHeight = PDFViewer.pageHeight(page);
+      var thumbScale = Math.min(104 / pageWidth, 134 / pageHeight);
+      canvas.width = pageWidth * thumbScale;
+      canvas.height = pageHeight * thumbScale;
       div.appendChild(canvas);
 
       var ctx = canvas.getContext('2d');
@@ -175,8 +180,6 @@ var PDFViewer = {
       canvas.id = 'page' + num;
       canvas.mozOpaque = true;
 
-      // Canvas dimensions must be specified in CSS pixels. CSS pixels
-      // are always 96 dpi. These dimensions are 8.5in x 11in at 96dpi.
       canvas.width = PDFViewer.pageWidth(page);
       canvas.height = PDFViewer.pageHeight(page);
       div.appendChild(canvas);
diff --git a/test/test_slave.html b/test/test_slave.html
index d70e362af..0a330e703 100644
--- a/test/test_slave.html
+++ b/test/test_slave.html
@@ -4,6 +4,7 @@
   <style type="text/css"></style>
   <script type="text/javascript" src="/pdf.js"></script>
   <script type="text/javascript" src="/fonts.js"></script>
+  <script type="text/javascript" src="/crypto.js"></script>
   <script type="text/javascript" src="/glyphlist.js"></script>
   <script type="application/javascript">
 var appPath, browser, canvas, currentTask, currentTaskIdx, failure, manifest, numPages, pdfDoc, stdout;
@@ -103,11 +104,12 @@ function nextPage() {
   }
 
   try {
+    var pdfToCssUnitsCoef = 96.0 / 72.0;
     // using mediaBox for the canvas size
-    var wTwips = (currentPage.mediaBox[2] - currentPage.mediaBox[0]);
-    var hTwips = (currentPage.mediaBox[3] - currentPage.mediaBox[1]);
-    canvas.width = wTwips * 96.0 / 72.0;
-    canvas.height = hTwips * 96.0 / 72.0;
+    var pageWidth = (currentPage.mediaBox[2] - currentPage.mediaBox[0]);
+    var pageHeight = (currentPage.mediaBox[3] - currentPage.mediaBox[1]);
+    canvas.width = pageWidth * pdfToCssUnitsCoef;
+    canvas.height = pageHeight * pdfToCssUnitsCoef;
     clear(ctx);
   } catch(e) {
     failure = 'page setup: '+ e.toString();
diff --git a/viewer.css b/viewer.css
index 857eed101..9987492dc 100644
--- a/viewer.css
+++ b/viewer.css
@@ -24,10 +24,11 @@ span#info {
 }
 
 #viewer {
+}
+
+#canvas {
     margin: auto;
-    border: 1px solid black;
-    width: 12.75in;
-    height: 16.5in;
+    display: block;
 }
 
 #pageNumber {
diff --git a/viewer.html b/viewer.html
index 24213f7d6..c600547f0 100644
--- a/viewer.html
+++ b/viewer.html
@@ -25,9 +25,7 @@
     </div>
 
     <div id="viewer">
-      <!-- Canvas dimensions must be specified in CSS pixels.  CSS pixels
-           are always 96 dpi.  816x1056 is 8.5x11in at 96dpi. -->
-      <canvas id="canvas" width="816" height="1056" defaultwidth="816" defaultheight="1056"></canvas>
+      <canvas id="canvas"></canvas>
     </div>
   </body>
 </html>
diff --git a/viewer.js b/viewer.js
index 5db2effda..38b0537b1 100644
--- a/viewer.js
+++ b/viewer.js
@@ -60,12 +60,12 @@ function displayPage(num) {
     var t0 = Date.now();
 
     var page = pdfDocument.getPage(pageNum = num);
-    canvas.width = parseInt(canvas.getAttribute("defaultwidth")) * pageScale;
-    canvas.height = parseInt(canvas.getAttribute("defaultheight")) * pageScale;
 
-    // scale canvas by 2
-    canvas.width = 2 * page.mediaBox[2];
-    canvas.hieght = 2 * page.mediaBox[3];
+    var pdfToCssUnitsCoef = 96.0 / 72.0;
+    var pageWidth = (page.mediaBox[2] - page.mediaBox[0]);
+    var pageHeight = (page.mediaBox[3] - page.mediaBox[1]);
+    canvas.width = pageScale * pageWidth * pdfToCssUnitsCoef;
+    canvas.height = pageScale * pageHeight * pdfToCssUnitsCoef;
 
     var t1 = Date.now();
     var ctx = canvas.getContext("2d");
diff --git a/viewer_worker.html b/viewer_worker.html
index fe3319d62..89fb8a087 100644
--- a/viewer_worker.html
+++ b/viewer_worker.html
@@ -39,10 +39,7 @@ window.onload = function() {
     </div>
 
     <div id="viewer">
-      <!-- Canvas dimensions must be specified in CSS pixels.  CSS pixels
-           are always 96 dpi.  816x1056 is 8.5x11in at 96dpi. -->
-      <!-- We're rendering here at 1.5x scale. -->
-      <canvas id="canvas" width="1224" height="1584"></canvas>
+      <canvas id="canvas"></canvas>
     </div>
   </body>
 </html>
diff --git a/worker/client.js b/worker/client.js
index 960e97496..73fb12f09 100644
--- a/worker/client.js
+++ b/worker/client.js
@@ -306,6 +306,13 @@ function WorkerPDFDoc(canvas) {
       document.body.appendChild(div);
     },
     
+    "setup_page": function(data) {
+      var size = data.split(",");
+      var canvas = this.canvas, ctx = this.ctx;
+      canvas.width = parseInt(size[0]);
+      canvas.height = parseInt(size[1]);
+    },
+
     "fonts": function(data) {
       this.waitingForFonts = true;
       this.fontWorker.ensureFonts(data, function() {
diff --git a/worker/pdf.js b/worker/pdf.js
index d9a7b5319..a1f18f694 100644
--- a/worker/pdf.js
+++ b/worker/pdf.js
@@ -31,6 +31,7 @@ importScripts("console.js")
 importScripts("canvas.js");
 importScripts("../pdf.js");
 importScripts("../fonts.js");
+importScripts("../crypto.js");
 importScripts("../glyphlist.js")
 
 // Use the JpegStreamProxy proxy.
@@ -59,6 +60,18 @@ onmessage = function(event) {
     // Let's try to render the first page...
     var page = pdfDocument.getPage(parseInt(data));
 
+    var pdfToCssUnitsCoef = 96.0 / 72.0;
+    var pageWidth = (page.mediaBox[2] - page.mediaBox[0]) * pdfToCssUnitsCoef;
+    var pageHeight = (page.mediaBox[3] - page.mediaBox[1]) * pdfToCssUnitsCoef;
+    postMessage({
+      action: "setup_page",
+      data: pageWidth + "," + pageHeight
+    });
+
+    // Set canvas size.
+    canvas.width = pageWidth;
+    canvas.height = pageHeight;
+
     // 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 = [];

From 32f681cfcf37e0a326a8aea69ae21596f561536f Mon Sep 17 00:00:00 2001
From: notmasteryet <async.processingjs@yahoo.com>
Date: Wed, 29 Jun 2011 23:18:27 -0500
Subject: [PATCH 6/7] loading the max 15 thumbnails first time

---
 multi_page_viewer.js | 30 ++++++++++++++++++++++++------
 1 file changed, 24 insertions(+), 6 deletions(-)

diff --git a/multi_page_viewer.js b/multi_page_viewer.js
index 87e2c8f14..f92f450dc 100644
--- a/multi_page_viewer.js
+++ b/multi_page_viewer.js
@@ -274,6 +274,12 @@ var PDFViewer = {
   openURL: function(url) {
     PDFViewer.url = url;
     document.title = url;
+
+    if (this.thumbsLoadingInterval) {
+      // cancel thumbs loading operations
+      clearInterval(this.thumbsLoadingInterval);
+      this.thumbsLoadingInterval = null;
+    }
     
     var req = new XMLHttpRequest();
     req.open('GET', url);
@@ -290,7 +296,9 @@ var PDFViewer = {
     
     req.send(null);
   },
-  
+
+  thumbsLoadingInterval: null,
+
   readPDF: function(data) {
     while (PDFViewer.element.hasChildNodes()) {
       PDFViewer.element.removeChild(PDFViewer.element.firstChild);
@@ -312,12 +320,22 @@ var PDFViewer = {
       PDFViewer.drawPage(1);
       document.location.hash = 1;
       
-      setTimeout(function() {
-        for (var i = 1; i <= PDFViewer.numberOfPages; i++) {
-          PDFViewer.createThumbnail(i);
-          PDFViewer.drawThumbnail(i);
+      // slowly loading the thumbs (few per second)
+      // first time we are loading more images than subsequent
+      var currentPageIndex = 1, imagesToLoad = 15;
+      this.thumbsLoadingInterval = setInterval((function() {
+        while (imagesToLoad-- > 0) {
+          if (currentPageIndex > PDFViewer.numberOfPages) {
+            clearInterval(this.thumbsLoadingInterval);
+            this.thumbsLoadingInterval = null;
+            return;
+          }
+          PDFViewer.createThumbnail(currentPageIndex);
+          PDFViewer.drawThumbnail(currentPageIndex);
+          ++currentPageIndex;
         }
-      }, 500);
+        imagesToLoad = 3; // next time loading less images
+      }).bind(this), 500);
     }
     
     PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ? 'disabled' : '';

From 05381cc53115aa0bd9040fb82fd41c1a5aad0301 Mon Sep 17 00:00:00 2001
From: Andreas Gal <andreas.gal@gmail.com>
Date: Thu, 30 Jun 2011 01:00:58 -0700
Subject: [PATCH 7/7] cache font measurements

---
 fonts.js | 153 ++++++++++++++++++++++++++++++-------------------------
 1 file changed, 83 insertions(+), 70 deletions(-)

diff --git a/fonts.js b/fonts.js
index e25b2ae2d..f154cddf4 100644
--- a/fonts.js
+++ b/fonts.js
@@ -34,65 +34,89 @@ var kDisableFonts = false;
  *      http://cgit.freedesktop.org/poppler/poppler/tree/poppler/GfxFont.cc#n65
  */
 
-var kScalePrecision = 40;
-var Fonts = {
-  _active: null,
+var Fonts = (function () {
+  var kScalePrecision = 40;
+  var fonts = Object.create(null);
+  var ctx = document.createElement("canvas").getContext("2d");
+  ctx.scale(1 / kScalePrecision, 1);
 
-  get active() {
-    return this._active;
-  },
-
-  setActive: function fonts_setActive(name, size) {
-    this._active = this[name];
-    this.ctx.font = (size * kScalePrecision) + 'px "' + name + '"';
-  },
-
-  charsToUnicode: function fonts_chars2Unicode(chars) {
-    var active = this._active;
-    if (!active)
-      return chars;
-
-    // if we translated this string before, just grab it from the cache
-    var str = active.cache[chars];
-    if (str)
-      return str;
-
-    // translate the string using the font's encoding
-    var encoding = active.properties.encoding;
-    if (!encoding)
-      return chars;
+  function Font(name, data, properties) {
+    this.name = name;
+    this.data = data;
+    this.properties = properties;
+    this.loading = true;
+    this.charsCache = Object.create(null);
+    this.sizes = [];
+  }
 
-    str = "";
-    for (var i = 0; i < chars.length; ++i) {
-      var charcode = chars.charCodeAt(i);
-      var unicode = encoding[charcode];
+  var current;
+  var charsCache;
+  var measureCache;
 
-      // Check if the glyph has already been converted
-      if (!IsNum(unicode))
+  return {
+    registerFont: function fonts_registerFont(fontName, data, properties) {
+      fonts[fontName] = new Font(fontName, data, properties);
+    },
+    blacklistFont: function fonts_blacklistFont(fontName) {
+      registerFont(fontName, null, {});
+      markLoaded(fontName);
+    },
+    lookup: function fonts_lookup(fontName) {
+      return fonts[fontName];
+    },
+    setActive: function fonts_setActive(fontName, size) {
+      current = fonts[fontName];
+      charsCache = current.charsCache;
+      var sizes = current.sizes;
+      if (!(measureCache = sizes[size]))
+        measureCache = sizes[size] = Object.create(null);
+      ctx.font = (size * kScalePrecision) + 'px "' + fontName + '"';
+    },
+    charsToUnicode: function fonts_chars2Unicode(chars) {
+      if (!charsCache)
+        return chars;
+
+      // if we translated this string before, just grab it from the cache
+      var str = charsCache[chars];
+      if (str)
+        return str;
+
+      // translate the string using the font's encoding
+      var encoding = current.properties.encoding;
+      if (!encoding)
+        return chars;
+
+      str = "";
+      for (var i = 0; i < chars.length; ++i) {
+        var charcode = chars.charCodeAt(i);
+        var unicode = encoding[charcode];
+
+        // Check if the glyph has already been converted
+        if (!IsNum(unicode))
           unicode = encoding[unicode] = GlyphsUnicode[unicode.name];
 
-      // Handle surrogate pairs
-      if (unicode > 0xFFFF) {
-        str += String.fromCharCode(unicode & 0xFFFF);
-        unicode >>= 16;
+        // Handle surrogate pairs
+        if (unicode > 0xFFFF) {
+          str += String.fromCharCode(unicode & 0xFFFF);
+          unicode >>= 16;
+        }
+        str += String.fromCharCode(unicode);
       }
-      str += String.fromCharCode(unicode);
-    }
-
-    // Enter the translated string into the cache
-    return active.cache[chars] = str;
-  },
 
-  get ctx() {
-    var ctx = document.createElement("canvas").getContext("2d");
-    ctx.scale(1 / kScalePrecision, 1);
-    return shadow(this, "ctx", ctx);
-  },
-
-  measureText: function fonts_measureText(text) {
-    return this.ctx.measureText(text).width / kScalePrecision;
+      // Enter the translated string into the cache
+      return charsCache[chars] = str;
+    },
+    measureText: function fonts_measureText(text) {
+      var width;
+      if (measureCache && (width = measureCache[text]))
+        return width;
+      width = ctx.measureText(text).width / kScalePrecision;
+      if (measureCache)
+        measureCache[text] = width;
+      return width;
+    }
   }
-};
+})();
 
 var FontLoader = {
   bind: function(fonts) {
@@ -101,8 +125,8 @@ var FontLoader = {
 
     for (var i = 0; i < fonts.length; i++) {
       var font = fonts[i];
-      if (Fonts[font.name]) {
-        ready = ready && !Fonts[font.name].loading;
+      if (Fonts.lookup(font.name)) {
+        ready = ready && !Fonts.lookup(font.name).loading;
         continue;
       }
 
@@ -111,7 +135,7 @@ var FontLoader = {
       var obj = new Font(font.name, font.file, font.properties);
 
       var str = "";
-      var data = Fonts[font.name].data;
+      var data = Fonts.lookup(font.name).data;
       var length = data.length;
       for (var j = 0; j < length; j++)
         str += String.fromCharCode(data[j]);
@@ -138,8 +162,8 @@ var Font = (function () {
     this.encoding = properties.encoding;
 
     // If the font has already been decoded simply return it
-    if (Fonts[name]) {
-      this.font = Fonts[name].data;
+    if (Fonts.lookup(name)) {
+      this.font = Fonts.lookup(name).data;
       return;
     }
     fontCount++;
@@ -148,12 +172,7 @@ var Font = (function () {
     // If the font is to be ignored, register it like an already loaded font
     // to avoid the cost of waiting for it be be loaded by the platform.
     if (properties.ignore || kDisableFonts) {
-      Fonts[name] = {
-        data: file,
-        loading: false,
-        properties: {},
-        cache: Object.create(null)
-      }
+      Fonts.blacklistFont(name);
       return;
     }
 
@@ -180,13 +199,7 @@ var Font = (function () {
         break;
     }
     this.data = data;
-
-    Fonts[name] = {
-      data: data,
-      properties: properties,
-      loading: true,
-      cache: Object.create(null)
-    };
+    Fonts.registerFont(name, data, properties);
   };
 
   function stringToArray(str) {
@@ -833,7 +846,7 @@ var Font = (function () {
         }
         
         window.clearInterval(interval);
-        Fonts[fontName].loading = false;
+        Fonts.lookup(fontName).loading = false;
         this.start = 0;
         if (callback) {
           callback();