Trying to implement progressive font rendering. Works on FF, but Chrome doesn't catchup the fonts
This commit is contained in:
parent
ac11f30ae9
commit
dd9aea21e9
70
fonts.js
70
fonts.js
@ -159,24 +159,12 @@ if (!isWorker) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var FontLoader = {
|
var FontLoader = {
|
||||||
listeningForFontLoad: false,
|
fontLoadData: {},
|
||||||
|
fonts: {},
|
||||||
|
|
||||||
bind: function(fonts, callback) {
|
bind: function(fonts, callback) {
|
||||||
function checkFontsLoaded() {
|
console.log("requesting fonts", fonts[0].properties.loadedName, fonts[0].name);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
var rules = [], names = [], objs = [];
|
var rules = [], names = [], objs = [];
|
||||||
|
|
||||||
for (var i = 0; i < fonts.length; i++) {
|
for (var i = 0; i < fonts.length; i++) {
|
||||||
@ -196,28 +184,33 @@ var FontLoader = {
|
|||||||
if (rule) {
|
if (rule) {
|
||||||
rules.push(rule);
|
rules.push(rule);
|
||||||
names.push(obj.loadedName);
|
names.push(obj.loadedName);
|
||||||
|
this.fonts[obj.loadedName] = obj;
|
||||||
|
this.fontLoadData[obj.loadedName] = obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.listeningForFontLoad = false;
|
if (rules.length) {
|
||||||
if (!isWorker && rules.length) {
|
this.fontsLoading += rules.length;
|
||||||
FontLoader.prepareFontLoadEvent(rules, names, objs);
|
FontLoader.prepareFontLoadEvent(rules, names);
|
||||||
}
|
|
||||||
|
|
||||||
if (!checkFontsLoaded()) {
|
|
||||||
document.documentElement.addEventListener(
|
|
||||||
'pdfjsFontLoad', checkFontsLoaded, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return objs;
|
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
|
// Set things up so that at least one pdfjsFontLoad event is
|
||||||
// dispatched when all the @font-face |rules| for |names| have been
|
// dispatched when all the @font-face |rules| for |names| have been
|
||||||
// loaded in a subdocument. It's expected that the load of |rules|
|
// loaded in a subdocument. It's expected that the load of |rules|
|
||||||
// has already started in this (outer) document, so that they should
|
// has already started in this (outer) document, so that they should
|
||||||
// be ordered before the load in the subdocument.
|
// be ordered before the load in the subdocument.
|
||||||
prepareFontLoadEvent: function(rules, names, objs) {
|
prepareFontLoadEvent: function(rules, names, callback) {
|
||||||
/** Hack begin */
|
/** Hack begin */
|
||||||
// There's no event when a font has finished downloading so the
|
// There's no event when a font has finished downloading so the
|
||||||
// following code is a dirty hack to 'guess' when a font is
|
// following code is a dirty hack to 'guess' when a font is
|
||||||
@ -253,23 +246,6 @@ var FontLoader = {
|
|||||||
div.innerHTML = html;
|
div.innerHTML = html;
|
||||||
document.body.appendChild(div);
|
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
|
// XXX we should have a time-out here too, and maybe fire
|
||||||
// pdfjsFontLoadFailed?
|
// pdfjsFontLoadFailed?
|
||||||
var src = '<!DOCTYPE HTML><html><head>';
|
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 = [
|
var UnicodeRanges = [
|
||||||
{ 'begin': 0x0000, 'end': 0x007F }, // Basic Latin
|
{ 'begin': 0x0000, 'end': 0x007F }, // Basic Latin
|
||||||
{ 'begin': 0x0080, 'end': 0x00FF }, // Latin-1 Supplement
|
{ 'begin': 0x0080, 'end': 0x00FF }, // Latin-1 Supplement
|
||||||
|
48
pdf.js
48
pdf.js
@ -3353,16 +3353,17 @@ var Page = (function() {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
exc = e.toString();
|
exc = e.toString();
|
||||||
continuation(exc);
|
continuation(exc);
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
this.ensureFonts(fonts, function() {
|
// this.ensureFonts(fonts, function() {
|
||||||
displayContinuation();
|
displayContinuation();
|
||||||
});
|
// });
|
||||||
},
|
},
|
||||||
|
|
||||||
getIRQueue: function(handler, fonts) {
|
getIRQueue: function(handler) {
|
||||||
if (this.IRQueue) {
|
if (this.IRQueue) {
|
||||||
// content was compiled
|
// content was compiled
|
||||||
return this.IRQueue;
|
return this.IRQueue;
|
||||||
@ -3381,7 +3382,7 @@ var Page = (function() {
|
|||||||
|
|
||||||
var pe = this.pe = new PartialEvaluator();
|
var pe = this.pe = new PartialEvaluator();
|
||||||
var IRQueue = {};
|
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) {
|
ensureFonts: function(fonts, callback) {
|
||||||
@ -4161,7 +4162,13 @@ var PartialEvaluator = (function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
constructor.prototype = {
|
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) {
|
function buildPaintImageXObject(image, inline) {
|
||||||
var dict = image.dict;
|
var dict = image.dict;
|
||||||
var w = dict.get('Width', 'W');
|
var w = dict.get('Width', 'W');
|
||||||
@ -4172,9 +4179,8 @@ var PartialEvaluator = (function() {
|
|||||||
handler.send("obj", [objId, "JpegStream", image.getIR()]);
|
handler.send("obj", [objId, "JpegStream", image.getIR()]);
|
||||||
|
|
||||||
// Add the dependency on the image object.
|
// Add the dependency on the image object.
|
||||||
fnArray.push("dependency");
|
insertDependency([objId]);
|
||||||
argsArray.push([ objId ]);
|
|
||||||
|
|
||||||
// The normal fn.
|
// The normal fn.
|
||||||
fn = 'paintJpegXObject';
|
fn = 'paintJpegXObject';
|
||||||
args = [ objId, w, h ];
|
args = [ objId, w, h ];
|
||||||
@ -4265,7 +4271,7 @@ var PartialEvaluator = (function() {
|
|||||||
// TODO: Add dependency here.
|
// TODO: Add dependency here.
|
||||||
// Create an IR of the pattern code.
|
// Create an IR of the pattern code.
|
||||||
var codeIR = this.getIRQueue(pattern, xref,
|
var codeIR = this.getIRQueue(pattern, xref,
|
||||||
dict.get('Resources'), {}, handler, fonts, uniquePrefix);
|
dict.get('Resources'), {}, handler, uniquePrefix);
|
||||||
|
|
||||||
args = TilingPattern.getIR(codeIR, dict, args);
|
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 adds the IRQueue of the xObj to the current queue.
|
||||||
this.getIRQueue(xobj, xref, xobj.dict.get('Resources'), queue,
|
this.getIRQueue(xobj, xref, xobj.dict.get('Resources'), queue,
|
||||||
handler, fonts, uniquePrefix);
|
handler, uniquePrefix);
|
||||||
|
|
||||||
|
|
||||||
fn = "paintFormXObjectEnd";
|
fn = "paintFormXObjectEnd";
|
||||||
@ -4324,14 +4330,24 @@ var PartialEvaluator = (function() {
|
|||||||
assertWellFormed(IsDict(font));
|
assertWellFormed(IsDict(font));
|
||||||
if (!font.translated) {
|
if (!font.translated) {
|
||||||
font.translated = this.translateFont(font, xref, resources);
|
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
|
// keep track of each font we translated so the caller can
|
||||||
// load them asynchronously before calling display on a page
|
// load them asynchronously before calling display on a page
|
||||||
fonts.push(font.translated);
|
|
||||||
|
|
||||||
var loadedName = uniquePrefix + "font_" + (FontLoadedCounter++);
|
var loadedName = uniquePrefix + "font_" + (FontLoadedCounter++);
|
||||||
font.translated.properties.loadedName = loadedName;
|
font.translated.properties.loadedName = loadedName;
|
||||||
FontsMap[loadedName] = font;
|
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;
|
args[0].name = font.translated.properties.loadedName;
|
||||||
@ -4870,13 +4886,14 @@ var CanvasGraphics = (function() {
|
|||||||
var depObjId = deps[n];
|
var depObjId = deps[n];
|
||||||
var promise;
|
var promise;
|
||||||
if (!Objects[depObjId]) {
|
if (!Objects[depObjId]) {
|
||||||
promise = Objects[depObjId] = new Promise();
|
promise = Objects[depObjId] = new Promise(depObjId);
|
||||||
} else {
|
} else {
|
||||||
promise = Objects[depObjId];
|
promise = Objects[depObjId];
|
||||||
}
|
}
|
||||||
// If the promise isn't resolved yet, add the continueCallback
|
// If the promise isn't resolved yet, add the continueCallback
|
||||||
// to the promise and bail out.
|
// to the promise and bail out.
|
||||||
if (!promise.isResolved) {
|
if (!promise.isResolved) {
|
||||||
|
console.log("depending on obj", depObjId);
|
||||||
promise.then(continueCallback);
|
promise.then(continueCallback);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@ -5096,13 +5113,14 @@ var CanvasGraphics = (function() {
|
|||||||
setFont: function(fontRef, size) {
|
setFont: function(fontRef, size) {
|
||||||
// Lookup the fontObj using fontRef only.
|
// Lookup the fontObj using fontRef only.
|
||||||
var fontRefName = fontRef.name;
|
var fontRefName = fontRef.name;
|
||||||
var fontObj = FontsMap[fontRefName].fontObj;
|
var fontObj = Objects.get(fontRefName);
|
||||||
|
|
||||||
if (!fontObj) {
|
if (!fontObj) {
|
||||||
throw "Can't find font for " + fontRefName;
|
throw "Can't find font for " + fontRefName;
|
||||||
}
|
}
|
||||||
|
|
||||||
var name = fontObj.loadedName;
|
var name = fontObj.loadedName;
|
||||||
|
console.log("setFont", name);
|
||||||
if (!name) {
|
if (!name) {
|
||||||
// TODO: fontDescriptor is not available, fallback to default font
|
// TODO: fontDescriptor is not available, fallback to default font
|
||||||
name = 'sans-serif';
|
name = 'sans-serif';
|
||||||
|
79
worker.js
79
worker.js
@ -62,16 +62,25 @@ var WorkerPage = (function() {
|
|||||||
var Objects = {
|
var Objects = {
|
||||||
resolve: function(objId, data) {
|
resolve: function(objId, data) {
|
||||||
// In case there is a promise already on this object, just resolve it.
|
// 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);
|
Objects[objId].resolve(data);
|
||||||
} else {
|
} 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() {
|
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 you build a promise and pass in some data it's already resolved.
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
this.isResolved = true;
|
this.isResolved = true;
|
||||||
@ -84,6 +93,8 @@ var Promise = (function() {
|
|||||||
|
|
||||||
Promise.prototype = {
|
Promise.prototype = {
|
||||||
resolve: function(data) {
|
resolve: function(data) {
|
||||||
|
console.log("resolve", this.name);
|
||||||
|
|
||||||
if (this.isResolved) {
|
if (this.isResolved) {
|
||||||
throw "A Promise can be resolved only once";
|
throw "A Promise can be resolved only once";
|
||||||
}
|
}
|
||||||
@ -137,29 +148,6 @@ var WorkerPDFDoc = (function() {
|
|||||||
var pageNum = data.pageNum;
|
var pageNum = data.pageNum;
|
||||||
var page = this.pageCache[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);
|
page.startRenderingFromIRQueue(data.IRQueue, data.fonts);
|
||||||
}, this);
|
}, this);
|
||||||
@ -173,6 +161,45 @@ var WorkerPDFDoc = (function() {
|
|||||||
var IR = data[2];
|
var IR = data[2];
|
||||||
new JpegStreamIR(objId, IR);
|
new JpegStreamIR(objId, IR);
|
||||||
break;
|
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:
|
default:
|
||||||
throw "Got unkown object type " + objType;
|
throw "Got unkown object type " + objType;
|
||||||
}
|
}
|
||||||
|
@ -19,25 +19,12 @@ var WorkerHandler = {
|
|||||||
// The following code does quite the same as Page.prototype.startRendering,
|
// 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.
|
// but stops at one point and sends the result back to the main thread.
|
||||||
var gfx = new CanvasGraphics(null);
|
var gfx = new CanvasGraphics(null);
|
||||||
var fonts = [];
|
|
||||||
|
|
||||||
var start = Date.now();
|
var start = Date.now();
|
||||||
// Pre compile the pdf page and fetch the fonts/images.
|
// 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);
|
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 */) {
|
if (false /* show used commands */) {
|
||||||
var cmdMap = {};
|
var cmdMap = {};
|
||||||
@ -59,7 +46,6 @@ var WorkerHandler = {
|
|||||||
|
|
||||||
handler.send("page", {
|
handler.send("page", {
|
||||||
pageNum: pageNum,
|
pageNum: pageNum,
|
||||||
fonts: fontsMin,
|
|
||||||
IRQueue: IRQueue,
|
IRQueue: IRQueue,
|
||||||
});
|
});
|
||||||
}, this);
|
}, this);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user