Make the fonts decoding code works with asynchronous data url

This commit is contained in:
Vivien Nicolas 2011-06-14 04:35:46 +02:00
parent c03dd98075
commit b0ee046b31
3 changed files with 161 additions and 19 deletions

View File

@ -11,6 +11,16 @@ var kMaxFontFileSize = 40000;
*/ */
var kMaxGlyphsCount = 1024; 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 * 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 the font has already been decoded simply return
if (Fonts[aFontName]) { if (Fonts[aFontName]) {
this.font = Fonts[aFontName]; this.font = Fonts[aFontName].data;
return; return;
} }
fontCount++;
var start = Date.now(); var start = Date.now();
switch (aFontType) { switch (aFontType) {
@ -62,10 +73,13 @@ var Font = function(aFontName, aFontFile, aFontType) {
} }
var end = Date.now(); var end = Date.now();
Fonts[aFontName] = {
data: this.font,
loading: true
}
// Attach the font to the document // Attach the font to the document
this.bind(); this.bind();
Fonts[aFontName] = this.font;
}; };
Font.prototype = { Font.prototype = {
@ -84,10 +98,90 @@ Font.prototype = {
: String.fromCharCode(data[i])); : String.fromCharCode(data[i]));
var dataBase64 = window.btoa(str.join("")); 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 // Add the @font-face rule to the document
var url = "url(data:" + this.mimetype + ";base64," + dataBase64 + ");"; 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]; var styleSheet = document.styleSheets[0];
styleSheet.insertRule(rule, styleSheet.length); styleSheet.insertRule(rule, styleSheet.length);
}, },
@ -473,7 +567,6 @@ var TrueType = function(aFontName, aFontFile) {
*/ */
var PSFonts = new Dict(); var PSFonts = new Dict();
var Stack = function(aStackSize) { var Stack = function(aStackSize) {
var innerStack = new Array(aStackSize || 0); var innerStack = new Array(aStackSize || 0);
@ -1136,7 +1229,6 @@ var Type1Parser = function(aAsciiStream, aBinaryStream) {
} }
}; };
var CFF = function(aFontName, aFontFile) { var CFF = function(aFontName, aFontFile) {
var start = Date.now(); var start = Date.now();

15
pdf.js
View File

@ -1399,6 +1399,19 @@ var Page = (function() {
? obj ? obj
: null)); : 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) { display: function(gfx) {
var xref = this.xref; var xref = this.xref;
var contents = xref.fetchIfRef(this.contents); var contents = xref.fetchIfRef(this.contents);
@ -1843,7 +1856,7 @@ var CanvasGraphics = (function() {
var fontFile = this.xref.fetchIfRef(fontDescriptor.get("FontFile")); var fontFile = this.xref.fetchIfRef(fontDescriptor.get("FontFile"));
if (!fontFile) if (!fontFile)
fontFile = this.xref.fetchIfRef(fontDescriptor.get("FontFile2")); fontFile = this.xref.fetchIfRef(fontDescriptor.get("FontFile2"));
fontName = fontDescriptor.get("FontName").name.replace("+", " "); fontName = fontDescriptor.get("FontName").name.replace("+", "_");
new Font(fontName, fontFile, subtype); new Font(fontName, fontFile, subtype);
} }

61
test.js
View File

@ -21,7 +21,6 @@ function queryParams() {
return params; return params;
} }
function open(url) { function open(url) {
document.title = url; document.title = url;
req = new XMLHttpRequest(); req = new XMLHttpRequest();
@ -54,21 +53,59 @@ function displayPage(num) {
var page = pdfDocument.getPage(pageNum = 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"); var gfx = new CanvasGraphics(ctx);
ctx.save(); page.display(gfx);
ctx.fillStyle = "rgb(255, 255, 255)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
var gfx = new CanvasGraphics(ctx); var t2 = Date.now();
page.display(gfx); 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"); // If everything is ready do not delayed the page loading any more
infoDisplay.innerHTML = "Time to render: "+ (t1 - t0) + "/" + (t2 - t1) + " ms"; if (fontsReady)
display();
else
setTimeout(displayPage, 150, num);
} }
function nextPage() { function nextPage() {