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();