diff --git a/PDFFont.js b/PDFFont.js index 34fb28fc4..4e81187b9 100644 --- a/PDFFont.js +++ b/PDFFont.js @@ -11,6 +11,16 @@ var kMaxFontFileSize = 40000; */ var kMaxGlyphsCount = 1024; +/** + * Maximum time to wait for a font to be loaded by @font-face + */ +var kMaxWaitForFontFace = 2000; + + /* + * Useful for debugging when you want to certains operations depending on how + * many fonts are loaded. + */ +var fontCount = 0; /** * Hold a map of decoded fonts and of the standard fourteen Type1 fonts and @@ -36,9 +46,10 @@ var Font = function(aFontName, aFontFile, aFontType) { // If the font has already been decoded simply return if (Fonts[aFontName]) { - this.font = Fonts[aFontName]; + this.font = Fonts[aFontName].data; return; } + fontCount++; var start = Date.now(); switch (aFontType) { @@ -62,10 +73,13 @@ var Font = function(aFontName, aFontFile, aFontType) { } var end = Date.now(); + Fonts[aFontName] = { + data: this.font, + loading: true + } + // Attach the font to the document this.bind(); - - Fonts[aFontName] = this.font; }; Font.prototype = { @@ -84,10 +98,90 @@ Font.prototype = { : String.fromCharCode(data[i])); var dataBase64 = window.btoa(str.join("")); + var fontName = this.name; + + /** Hack begin */ + + // Actually there is not event when a font has finished downloading so + // the following tons of code are a dirty hack to 'guess' when a font is + // ready + var debug = false; + + var canvas = document.createElement("canvas"); + var style = "position:absolute; left: " + + (debug ? (100 * fontCount) : "-200") + "px; top: -200px;"; + canvas.setAttribute("style", style); + canvas.setAttribute("width", 100); + canvas.setAttribute("heigth", 100); + document.body.appendChild(canvas); + + // Get the first character of the font + var page = pdfDocument.getPage(pageNum); + var xref = page.xref; + var resources = xref.fetchIfRef(page.resources); + var fontResource = resources.get("Font"); + var charset = ""; + for (var id in fontResource.map) { + var res = xref.fetch(fontResource.get(id)); + var descriptor = xref.fetch(res.get("FontDescriptor")); + var name = descriptor.get("FontName").toString(); + var font = Fonts[name.replace("+", "_")]; + if (font && font.loading && name == fontName.replace("_", "+")) { + charset = descriptor.get("CharSet").split("/"); + break; + } + } + + // Warn if the charset is not found, this is likely a bug! + var testCharset = charset; + if (!charset) { + warn("No charset found for: " + fontName); + } else { + // if the charset is too small make it repeat a few times + var count = 30; + while (count-- && testCharset.length <= 30) + testCharset = testCharset.concat(charset.slice()); + } + + // Get the font size canvas think it will be + var ctx = canvas.getContext("2d"); + var testString = ""; + for (var i = 0; i < testCharset.length; i++) { + var unicode = new Number("0x" + GlyphsUnicode[testCharset[i]]); + if (!unicode) + error("Unicode for " + testCharset[i] + " is has not been found in the glyphs list"); + testString += String.fromCharCode(unicode); + } + ctx.font = "20px " + fontName + ", Symbol"; + var textWidth = ctx.mozMeasureText(testString); + + if (debug) + ctx.fillText(testString, 20, 20); + + var start = Date.now(); + var interval = window.setInterval(function(self) { + ctx.font = "20px " + fontName + ", Symbol"; + + // For some reasons the font has not loaded, so mark it loaded for the + // page to proceed but cry + if ((Date.now() - start) >= kMaxWaitForFontFace) { + window.clearInterval(interval); + Fonts[fontName].loading = false; + warn("Is " + fontName + " for charset: " + charset + " loaded?"); + } else if (textWidth != ctx.mozMeasureText(testString)) { + window.clearInterval(interval); + Fonts[fontName].loading = false; + } + + if (debug) + ctx.fillText(testString, 20, 60); + }, 150, this); + + /** Hack end */ // Add the @font-face rule to the document var url = "url(data:" + this.mimetype + ";base64," + dataBase64 + ");"; - var rule = "@font-face { font-family:'" + this.name + "';src:" + url + "}"; + var rule = "@font-face { font-family:'" + fontName + "';src:" + url + "}"; var styleSheet = document.styleSheets[0]; styleSheet.insertRule(rule, styleSheet.length); }, @@ -473,7 +567,6 @@ var TrueType = function(aFontName, aFontFile) { */ var PSFonts = new Dict(); - var Stack = function(aStackSize) { var innerStack = new Array(aStackSize || 0); @@ -1136,7 +1229,6 @@ var Type1Parser = function(aAsciiStream, aBinaryStream) { } }; - var CFF = function(aFontName, aFontFile) { var start = Date.now(); diff --git a/pdf.js b/pdf.js index 8b06f5582..697e6c3ad 100644 --- a/pdf.js +++ b/pdf.js @@ -1399,6 +1399,19 @@ var Page = (function() { ? obj : null)); }, + get fonts() { + var xref = this.xref; + var fonts = []; + + var resources = xref.fetchIfRef(this.resources); + var fontResource = resources.get("Font"); + for (var id in fontResource.map) { + var res = xref.fetch(fontResource.get(id)); + var descriptor = xref.fetch(res.get("FontDescriptor")); + fonts.push(descriptor.get("FontName").toString()); + } + return shadow(this, "fonts", fonts); + }, display: function(gfx) { var xref = this.xref; var contents = xref.fetchIfRef(this.contents); @@ -1843,7 +1856,7 @@ var CanvasGraphics = (function() { var fontFile = this.xref.fetchIfRef(fontDescriptor.get("FontFile")); if (!fontFile) fontFile = this.xref.fetchIfRef(fontDescriptor.get("FontFile2")); - fontName = fontDescriptor.get("FontName").name.replace("+", " "); + fontName = fontDescriptor.get("FontName").name.replace("+", "_"); new Font(fontName, fontFile, subtype); } diff --git a/test.js b/test.js index ec784ea62..bd6c812e8 100644 --- a/test.js +++ b/test.js @@ -21,7 +21,6 @@ function queryParams() { return params; } - function open(url) { document.title = url; req = new XMLHttpRequest(); @@ -54,21 +53,59 @@ function displayPage(num) { var page = pdfDocument.getPage(pageNum = num); - var t1 = Date.now(); + function display() { + var t1 = Date.now(); + var ctx = canvas.getContext("2d"); + ctx.save(); + ctx.fillStyle = "rgb(255, 255, 255)"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.restore(); - var ctx = canvas.getContext("2d"); - ctx.save(); - ctx.fillStyle = "rgb(255, 255, 255)"; - ctx.fillRect(0, 0, canvas.width, canvas.height); - ctx.restore(); + var gfx = new CanvasGraphics(ctx); + page.display(gfx); - var gfx = new CanvasGraphics(ctx); - page.display(gfx); + var t2 = Date.now(); + var infoDisplay = document.getElementById("info"); + infoDisplay.innerHTML = "Time to render: "+ (t1 - t0) + "/" + (t2 - t1) + " ms"; + } - var t2 = Date.now(); + // Loading a font via data uri is asynchronous, so wait for all font + // of the page to be fully loaded before loading the page + var fontsReady = true; + var fonts = page.fonts; + for (var i = 0; i < fonts.length; i++) { + var fontName = fonts[i].replace("+", "_"); + var font = Fonts[fontName]; + if (!font) { + // load the new font + var xref = page.xref; + var resources = xref.fetchIfRef(page.resources); + var fontResource = resources.get("Font"); + for (var id in fontResource.map) { + var res = xref.fetch(fontResource.get(id)); + var descriptor = xref.fetch(res.get("FontDescriptor")); + var name = descriptor.get("FontName").toString(); + if (name == fontName.replace("_", "+")) { + var subtype = res.get("Subtype").name; + var fontFile = page.xref.fetchIfRef(descriptor.get("FontFile")); + if (!fontFile) + fontFile = page.xref.fetchIfRef(descriptor.get("FontFile2")); + new Font(fontName, fontFile, subtype); + fontsReady = false; + break; + } + } + } else if (font.loading) { + fontsReady = false; + break; + } + } - var infoDisplay = document.getElementById("info"); - infoDisplay.innerHTML = "Time to render: "+ (t1 - t0) + "/" + (t2 - t1) + " ms"; + // If everything is ready do not delayed the page loading any more + if (fontsReady) + display(); + else + setTimeout(displayPage, 150, num); } function nextPage() {