Trying to implement progressive font rendering. Works on FF, but Chrome doesn't catchup the fonts

This commit is contained in:
Julian Viereck 2011-09-09 16:15:51 -07:00
parent ac11f30ae9
commit dd9aea21e9
4 changed files with 115 additions and 98 deletions

View File

@ -159,24 +159,12 @@ if (!isWorker) {
}
var FontLoader = {
listeningForFontLoad: false,
fontLoadData: {},
fonts: {},
bind: function(fonts, callback) {
function checkFontsLoaded() {
for (var i = 0; i < objs.length; i++) {
var fontObj = objs[i];
if (fontObj.loading) {
return false;
}
}
document.documentElement.removeEventListener(
'pdfjsFontLoad', checkFontsLoaded, false);
callback(objs);
return true;
}
console.log("requesting fonts", fonts[0].properties.loadedName, fonts[0].name);
var rules = [], names = [], objs = [];
for (var i = 0; i < fonts.length; i++) {
@ -196,28 +184,33 @@ var FontLoader = {
if (rule) {
rules.push(rule);
names.push(obj.loadedName);
this.fonts[obj.loadedName] = obj;
this.fontLoadData[obj.loadedName] = obj;
}
}
}
this.listeningForFontLoad = false;
if (!isWorker && rules.length) {
FontLoader.prepareFontLoadEvent(rules, names, objs);
}
if (!checkFontsLoaded()) {
document.documentElement.addEventListener(
'pdfjsFontLoad', checkFontsLoaded, false);
if (rules.length) {
this.fontsLoading += rules.length;
FontLoader.prepareFontLoadEvent(rules, names);
}
return objs;
},
postFontLoadEvent: function(names) {
for (var i = 0; i < names.length; i++) {
var name = names[i];
Objects.resolve(name, this.fontLoadData[name]);
}
},
// Set things up so that at least one pdfjsFontLoad event is
// dispatched when all the @font-face |rules| for |names| have been
// loaded in a subdocument. It's expected that the load of |rules|
// has already started in this (outer) document, so that they should
// be ordered before the load in the subdocument.
prepareFontLoadEvent: function(rules, names, objs) {
prepareFontLoadEvent: function(rules, names, callback) {
/** Hack begin */
// There's no event when a font has finished downloading so the
// following code is a dirty hack to 'guess' when a font is
@ -253,23 +246,6 @@ var FontLoader = {
div.innerHTML = html;
document.body.appendChild(div);
if (!this.listeningForFontLoad) {
window.addEventListener(
'message',
function(e) {
var fontNames = JSON.parse(e.data);
for (var i = 0; i < objs.length; ++i) {
var font = objs[i];
font.loading = false;
}
var evt = document.createEvent('Events');
evt.initEvent('pdfjsFontLoad', true, false);
document.documentElement.dispatchEvent(evt);
},
false);
this.listeningForFontLoad = true;
}
// XXX we should have a time-out here too, and maybe fire
// pdfjsFontLoadFailed?
var src = '<!DOCTYPE HTML><html><head>';
@ -303,6 +279,16 @@ var FontLoader = {
}
};
if (!isWorker) {
window.addEventListener(
'message',
function(e) {
FontLoader.postFontLoadEvent(JSON.parse(e.data));
}.bind(this),
false);
}
var UnicodeRanges = [
{ 'begin': 0x0000, 'end': 0x007F }, // Basic Latin
{ 'begin': 0x0080, 'end': 0x00FF }, // Latin-1 Supplement

48
pdf.js
View File

@ -3353,16 +3353,17 @@ var Page = (function() {
} catch (e) {
exc = e.toString();
continuation(exc);
throw e;
}
});
};
this.ensureFonts(fonts, function() {
// this.ensureFonts(fonts, function() {
displayContinuation();
});
// });
},
getIRQueue: function(handler, fonts) {
getIRQueue: function(handler) {
if (this.IRQueue) {
// content was compiled
return this.IRQueue;
@ -3381,7 +3382,7 @@ var Page = (function() {
var pe = this.pe = new PartialEvaluator();
var IRQueue = {};
return this.IRQueue = pe.getIRQueue(content, xref, resources, IRQueue, handler, fonts, "p" + this.pageNumber + "_");
return this.IRQueue = pe.getIRQueue(content, xref, resources, IRQueue, handler, "p" + this.pageNumber + "_");
},
ensureFonts: function(fonts, callback) {
@ -4161,7 +4162,13 @@ var PartialEvaluator = (function() {
};
constructor.prototype = {
getIRQueue: function(stream, xref, resources, queue, handler, fonts, uniquePrefix) {
getIRQueue: function(stream, xref, resources, queue, handler, uniquePrefix) {
function insertDependency(depList) {
fnArray.push("dependency");
argsArray.push(depList);
}
function buildPaintImageXObject(image, inline) {
var dict = image.dict;
var w = dict.get('Width', 'W');
@ -4172,9 +4179,8 @@ var PartialEvaluator = (function() {
handler.send("obj", [objId, "JpegStream", image.getIR()]);
// Add the dependency on the image object.
fnArray.push("dependency");
argsArray.push([ objId ]);
insertDependency([objId]);
// The normal fn.
fn = 'paintJpegXObject';
args = [ objId, w, h ];
@ -4265,7 +4271,7 @@ var PartialEvaluator = (function() {
// TODO: Add dependency here.
// Create an IR of the pattern code.
var codeIR = this.getIRQueue(pattern, xref,
dict.get('Resources'), {}, handler, fonts, uniquePrefix);
dict.get('Resources'), {}, handler, uniquePrefix);
args = TilingPattern.getIR(codeIR, dict, args);
}
@ -4303,7 +4309,7 @@ var PartialEvaluator = (function() {
// This adds the IRQueue of the xObj to the current queue.
this.getIRQueue(xobj, xref, xobj.dict.get('Resources'), queue,
handler, fonts, uniquePrefix);
handler, uniquePrefix);
fn = "paintFormXObjectEnd";
@ -4324,14 +4330,24 @@ var PartialEvaluator = (function() {
assertWellFormed(IsDict(font));
if (!font.translated) {
font.translated = this.translateFont(font, xref, resources);
if (fonts && font.translated) {
if (font.translated) {
// keep track of each font we translated so the caller can
// load them asynchronously before calling display on a page
fonts.push(font.translated);
var loadedName = uniquePrefix + "font_" + (FontLoadedCounter++);
font.translated.properties.loadedName = loadedName;
FontsMap[loadedName] = font;
handler.send("obj", [
loadedName,
"Font",
font.translated.name,
font.translated.file,
font.translated.properties
]);
// Ensure the font is ready before the font is set
// and later on used for drawing.
insertDependency([loadedName]);
}
}
args[0].name = font.translated.properties.loadedName;
@ -4870,13 +4886,14 @@ var CanvasGraphics = (function() {
var depObjId = deps[n];
var promise;
if (!Objects[depObjId]) {
promise = Objects[depObjId] = new Promise();
promise = Objects[depObjId] = new Promise(depObjId);
} else {
promise = Objects[depObjId];
}
// If the promise isn't resolved yet, add the continueCallback
// to the promise and bail out.
if (!promise.isResolved) {
console.log("depending on obj", depObjId);
promise.then(continueCallback);
return i;
}
@ -5096,13 +5113,14 @@ var CanvasGraphics = (function() {
setFont: function(fontRef, size) {
// Lookup the fontObj using fontRef only.
var fontRefName = fontRef.name;
var fontObj = FontsMap[fontRefName].fontObj;
var fontObj = Objects.get(fontRefName);
if (!fontObj) {
throw "Can't find font for " + fontRefName;
}
var name = fontObj.loadedName;
console.log("setFont", name);
if (!name) {
// TODO: fontDescriptor is not available, fallback to default font
name = 'sans-serif';

View File

@ -62,16 +62,25 @@ var WorkerPage = (function() {
var Objects = {
resolve: function(objId, data) {
// In case there is a promise already on this object, just resolve it.
if (Objects[objId] instanceof Promise) {
if (Objects[objId]) {
Objects[objId].resolve(data);
} else {
Objects[objId] = new Promise(data);
Objects[objId] = new Promise(objId, data);
}
},
get: function(objId) {
var obj = Objects[objId];
if (!obj || !obj.isResolved) {
throw "Requesting object that isn't resolved yet";
}
return obj.data;
}
};
var Promise = (function() {
function Promise(data) {
function Promise(name, data) {
this.name = name;
// If you build a promise and pass in some data it's already resolved.
if (data != null) {
this.isResolved = true;
@ -84,6 +93,8 @@ var Promise = (function() {
Promise.prototype = {
resolve: function(data) {
console.log("resolve", this.name);
if (this.isResolved) {
throw "A Promise can be resolved only once";
}
@ -137,29 +148,6 @@ var WorkerPDFDoc = (function() {
var pageNum = data.pageNum;
var page = this.pageCache[pageNum];
// Add necessary shape back to fonts.
var fonts = data.fonts;
for (var i = 0; i < fonts.length; i++) {
var font = fonts[i];
// Some fonts don't have a file, e.g. the build in ones like Arial.
if (font.file) {
var fontFileDict = new Dict();
fontFileDict.map = font.file.dict.map;
var fontFile = new Stream(font.file.bytes, font.file.start,
font.file.end - font.file.start, fontFileDict);
// Check if this is a FlateStream. Otherwise just use the created
// Stream one. This makes complex_ttf_font.pdf work.
var cmf = font.file.bytes[0];
if ((cmf & 0x0f) == 0x08) {
font.file = new FlateStream(fontFile);
} else {
font.file = fontFile;
}
}
}
page.startRenderingFromIRQueue(data.IRQueue, data.fonts);
}, this);
@ -173,6 +161,45 @@ var WorkerPDFDoc = (function() {
var IR = data[2];
new JpegStreamIR(objId, IR);
break;
case "Font":
var name = data[2];
var file = data[3];
var properties = data[4];
console.log("got new font", name);
var font = {
name: name,
file: file,
properties: properties
};
// Some fonts don't have a file, e.g. the build in ones like Arial.
if (file) {
var fontFileDict = new Dict();
fontFileDict.map = file.dict.map;
var fontFile = new Stream(file.bytes, file.start,
file.end - file.start, fontFileDict);
// Check if this is a FlateStream. Otherwise just use the created
// Stream one. This makes complex_ttf_font.pdf work.
var cmf = file.bytes[0];
if ((cmf & 0x0f) == 0x08) {
font.file = new FlateStream(fontFile);
} else {
font.file = fontFile;
}
}
FontLoader.bind(
[ font ],
function(fontObjs) {
var fontObj = fontObjs[0];
Objects.resolve(objId, fontObj);
}
);
break;
default:
throw "Got unkown object type " + objType;
}

View File

@ -19,25 +19,12 @@ var WorkerHandler = {
// The following code does quite the same as Page.prototype.startRendering,
// but stops at one point and sends the result back to the main thread.
var gfx = new CanvasGraphics(null);
var fonts = [];
var start = Date.now();
// Pre compile the pdf page and fetch the fonts/images.
var IRQueue = page.getIRQueue(handler, fonts);
var IRQueue = page.getIRQueue(handler);
console.log("page=%d - getIRQueue: time=%dms, len=%d", pageNum, Date.now() - start, IRQueue.fnArray.length);
// Extract the minimum of font data that is required to build all required
// font stuff on the main thread.
var fontsMin = [];
for (var i = 0; i < fonts.length; i++) {
var font = fonts[i];
fontsMin.push({
name: font.name,
file: font.file,
properties: font.properties
});
}
if (false /* show used commands */) {
var cmdMap = {};
@ -59,7 +46,6 @@ var WorkerHandler = {
handler.send("page", {
pageNum: pageNum,
fonts: fontsMin,
IRQueue: IRQueue,
});
}, this);