Make font processing happen in a worker
This commit is contained in:
parent
e9fff5968e
commit
6dcf9f42a5
277
fonts.js
277
fonts.js
@ -161,68 +161,57 @@ if (!isWorker) {
|
|||||||
var FontLoader = {
|
var FontLoader = {
|
||||||
fonts: {},
|
fonts: {},
|
||||||
fontsLoading: false,
|
fontsLoading: false,
|
||||||
waitingNames: [],
|
waitingFontObjs: [],
|
||||||
waitingStr: [],
|
waitingFontIds: [],
|
||||||
|
|
||||||
bind: function(fonts, callback) {
|
|
||||||
var rules = [], names = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < fonts.length; i++) {
|
|
||||||
var font = fonts[i];
|
|
||||||
|
|
||||||
var obj = new Font(font.name, font.file, font.properties);
|
|
||||||
|
|
||||||
var str = '';
|
|
||||||
var data = obj.data;
|
|
||||||
var name = obj.loadedName;
|
|
||||||
if (data) {
|
|
||||||
var length = data.length;
|
|
||||||
for (var j = 0; j < length; j++)
|
|
||||||
str += String.fromCharCode(data[j]);
|
|
||||||
|
|
||||||
|
|
||||||
this.fonts[obj.loadedName] = obj;
|
|
||||||
|
|
||||||
this.waitingNames.push(name);
|
|
||||||
this.waitingStr.push(str);
|
|
||||||
} else {
|
|
||||||
// If there is no data, then there is nothing to load and we can
|
|
||||||
// resolve the object right away.
|
|
||||||
Objects.resolve(name, obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
bind: function(objId, fontObj) {
|
||||||
|
this.waitingFontObjs.push(fontObj);
|
||||||
|
this.waitingFontIds.push(objId);
|
||||||
|
|
||||||
if (!this.fontsLoading) {
|
if (!this.fontsLoading) {
|
||||||
this.executeWaiting();
|
this.executeWaiting();
|
||||||
} else {
|
|
||||||
console.log('There are currently some fonts getting loaded - waiting');
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
bindDOM: function font_bindDom(fontObj) {
|
||||||
|
var fontName = fontObj.loadedName;
|
||||||
|
// Add the font-face rule to the document
|
||||||
|
var url = ('url(data:' + fontObj.mimetype + ';base64,' +
|
||||||
|
window.btoa(fontObj.str) + ');');
|
||||||
|
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}';
|
||||||
|
var styleSheet = document.styleSheets[0];
|
||||||
|
styleSheet.insertRule(rule, styleSheet.cssRules.length);
|
||||||
|
return rule;
|
||||||
|
},
|
||||||
|
|
||||||
executeWaiting: function() {
|
executeWaiting: function() {
|
||||||
var names = this.waitingNames;
|
|
||||||
console.log('executing fonts', names.join(', '));
|
|
||||||
|
|
||||||
var rules = [];
|
var rules = [];
|
||||||
for (var i = 0; i < names.length; i++) {
|
var names = [];
|
||||||
var obj = this.fonts[names[i]];
|
var objIds = this.waitingFontIds;
|
||||||
var rule = obj.bindDOM(this.waitingStr[i]);
|
|
||||||
|
for (var i = 0; i < this.waitingFontObjs.length; i++) {
|
||||||
|
var fontObj = this.waitingFontObjs[i];
|
||||||
|
var rule = this.bindDOM(fontObj);
|
||||||
|
this.fonts[objIds[i]] = fontObj;
|
||||||
|
names.push(fontObj.loadedName);
|
||||||
rules.push(rule);
|
rules.push(rule);
|
||||||
}
|
}
|
||||||
this.prepareFontLoadEvent(rules, names);
|
|
||||||
this.waitingNames = [];
|
this.prepareFontLoadEvent(rules, names, objIds);
|
||||||
this.waitingStr = [];
|
this.waitingFontIds = [];
|
||||||
|
this.waitingFontObjs = [];
|
||||||
},
|
},
|
||||||
|
|
||||||
fontLoadEvent: function(names) {
|
fontLoadEvent: function(objIds) {
|
||||||
|
for (var i = 0; i < objIds.length; i++) {
|
||||||
|
var objId = objIds[i];
|
||||||
|
Objects.resolve(objId, this.fonts[objId]);
|
||||||
|
delete this.fonts[objId];
|
||||||
|
}
|
||||||
|
|
||||||
this.fontsLoading = false;
|
this.fontsLoading = false;
|
||||||
|
|
||||||
for (var i = 0; i < names.length; i++) {
|
if (this.waitingFontIds.length != 0) {
|
||||||
var name = names[i];
|
|
||||||
Objects.resolve(name, this.fonts[name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.waitingNames.length != 0) {
|
|
||||||
this.executeWaiting();
|
this.executeWaiting();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -232,7 +221,7 @@ var FontLoader = {
|
|||||||
// 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) {
|
prepareFontLoadEvent: function(rules, names, objIds) {
|
||||||
this.fontsLoading = true;
|
this.fontsLoading = true;
|
||||||
/** 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
|
||||||
@ -278,18 +267,16 @@ var FontLoader = {
|
|||||||
}
|
}
|
||||||
src += '</style>';
|
src += '</style>';
|
||||||
src += '<script type="application/javascript">';
|
src += '<script type="application/javascript">';
|
||||||
var fontNamesArray = '';
|
var objIdsArray = '';
|
||||||
for (var i = 0; i < names.length; ++i) {
|
for (var i = 0; i < objIds.length; ++i) {
|
||||||
fontNamesArray += '"' + names[i] + '", ';
|
objIdsArray += '"' + objIds[i] + '", ';
|
||||||
}
|
}
|
||||||
src += ' var fontNames=[' + fontNamesArray + '];\n';
|
src += ' var objIds=[' + objIdsArray + '];\n';
|
||||||
src += ' window.onload = function () {\n';
|
src += ' window.onload = function () {\n';
|
||||||
src += ' setTimeout(function(){parent.postMessage(JSON.stringify(fontNames), "*")},0);\n';
|
src += ' setTimeout(function(){parent.postMessage(JSON.stringify(objIds), "*")},0);\n';
|
||||||
src += ' }';
|
src += ' }';
|
||||||
src += '</script></head><body>';
|
src += '</script></head><body>';
|
||||||
for (var i = 0; i < names.length; ++i) {
|
src += '<p style="font-family:\'' + name + '\'">Hi</p>';
|
||||||
src += '<p style="font-family:\'' + names[i] + '\'">Hi</p>';
|
|
||||||
}
|
|
||||||
src += '</body></html>';
|
src += '</body></html>';
|
||||||
var frame = document.createElement('iframe');
|
var frame = document.createElement('iframe');
|
||||||
frame.src = 'data:text/html,' + src;
|
frame.src = 'data:text/html,' + src;
|
||||||
@ -447,6 +434,90 @@ function getUnicodeRangeFor(value) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FontShape is the minimal shape a FontObject can have to be useful during
|
||||||
|
* executing the IRQueue.
|
||||||
|
*/
|
||||||
|
var FontShape = (function FontShape() {
|
||||||
|
var constructor = function FontShape_constructor(obj) {
|
||||||
|
for (var name in obj) {
|
||||||
|
this[name] = obj[name];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function int16(bytes) {
|
||||||
|
return (bytes[0] << 8) + (bytes[1] & 0xff);
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor.prototype = {
|
||||||
|
charsToUnicode: function fonts_chars2Unicode(chars) {
|
||||||
|
var charsCache = this.charsCache;
|
||||||
|
var str;
|
||||||
|
|
||||||
|
// if we translated this string before, just grab it from the cache
|
||||||
|
if (charsCache) {
|
||||||
|
str = charsCache[chars];
|
||||||
|
if (str)
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lazily create the translation cache
|
||||||
|
if (!charsCache)
|
||||||
|
charsCache = this.charsCache = Object.create(null);
|
||||||
|
|
||||||
|
// translate the string using the font's encoding
|
||||||
|
var encoding = this.encoding;
|
||||||
|
if (!encoding)
|
||||||
|
return chars;
|
||||||
|
str = '';
|
||||||
|
|
||||||
|
if (this.composite) {
|
||||||
|
// composite fonts have multi-byte strings convert the string from
|
||||||
|
// single-byte to multi-byte
|
||||||
|
// XXX assuming CIDFonts are two-byte - later need to extract the
|
||||||
|
// correct byte encoding according to the PDF spec
|
||||||
|
var length = chars.length - 1; // looping over two bytes at a time so
|
||||||
|
// loop should never end on the last byte
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
var charcode = int16([chars.charCodeAt(i++), chars.charCodeAt(i)]);
|
||||||
|
var unicode = encoding[charcode];
|
||||||
|
if ('undefined' == typeof(unicode)) {
|
||||||
|
warn('Unencoded charcode ' + charcode);
|
||||||
|
unicode = charcode;
|
||||||
|
} else {
|
||||||
|
unicode = unicode.unicode;
|
||||||
|
}
|
||||||
|
str += String.fromCharCode(unicode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (var i = 0; i < chars.length; ++i) {
|
||||||
|
var charcode = chars.charCodeAt(i);
|
||||||
|
var unicode = encoding[charcode];
|
||||||
|
if ('undefined' == typeof(unicode)) {
|
||||||
|
warn('Unencoded charcode ' + charcode);
|
||||||
|
unicode = charcode;
|
||||||
|
} else {
|
||||||
|
unicode = unicode.unicode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle surrogate pairs
|
||||||
|
if (unicode > 0xFFFF) {
|
||||||
|
str += String.fromCharCode(unicode & 0xFFFF);
|
||||||
|
unicode >>= 16;
|
||||||
|
}
|
||||||
|
str += String.fromCharCode(unicode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enter the translated string into the cache
|
||||||
|
return (charsCache[chars] = str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return constructor;
|
||||||
|
})();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 'Font' is the class the outside world should use, it encapsulate all the font
|
* 'Font' is the class the outside world should use, it encapsulate all the font
|
||||||
* decoding logics whatever type it is (assuming the font type is supported).
|
* decoding logics whatever type it is (assuming the font type is supported).
|
||||||
@ -1322,98 +1393,6 @@ var Font = (function Font() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return stringToArray(otf.file);
|
return stringToArray(otf.file);
|
||||||
},
|
|
||||||
|
|
||||||
bindWorker: function font_bindWorker(data) {
|
|
||||||
postMessage({
|
|
||||||
action: 'font',
|
|
||||||
data: {
|
|
||||||
raw: data,
|
|
||||||
fontName: this.loadedName,
|
|
||||||
mimetype: this.mimetype
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
bindDOM: function font_bindDom(data) {
|
|
||||||
var fontName = this.loadedName;
|
|
||||||
|
|
||||||
// Add the font-face rule to the document
|
|
||||||
var url = ('url(data:' + this.mimetype + ';base64,' +
|
|
||||||
window.btoa(data) + ');');
|
|
||||||
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}';
|
|
||||||
var styleSheet = document.styleSheets[0];
|
|
||||||
if (!styleSheet) {
|
|
||||||
document.documentElement.firstChild.appendChild( document.createElement('style') );
|
|
||||||
styleSheet = document.styleSheets[0];
|
|
||||||
}
|
|
||||||
styleSheet.insertRule(rule, styleSheet.cssRules.length);
|
|
||||||
|
|
||||||
return rule;
|
|
||||||
},
|
|
||||||
|
|
||||||
charsToUnicode: function fonts_chars2Unicode(chars) {
|
|
||||||
var charsCache = this.charsCache;
|
|
||||||
var str;
|
|
||||||
|
|
||||||
// if we translated this string before, just grab it from the cache
|
|
||||||
if (charsCache) {
|
|
||||||
str = charsCache[chars];
|
|
||||||
if (str)
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
// lazily create the translation cache
|
|
||||||
if (!charsCache)
|
|
||||||
charsCache = this.charsCache = Object.create(null);
|
|
||||||
|
|
||||||
// translate the string using the font's encoding
|
|
||||||
var encoding = this.encoding;
|
|
||||||
if (!encoding)
|
|
||||||
return chars;
|
|
||||||
str = '';
|
|
||||||
|
|
||||||
if (this.composite) {
|
|
||||||
// composite fonts have multi-byte strings convert the string from
|
|
||||||
// single-byte to multi-byte
|
|
||||||
// XXX assuming CIDFonts are two-byte - later need to extract the
|
|
||||||
// correct byte encoding according to the PDF spec
|
|
||||||
var length = chars.length - 1; // looping over two bytes at a time so
|
|
||||||
// loop should never end on the last byte
|
|
||||||
for (var i = 0; i < length; i++) {
|
|
||||||
var charcode = int16([chars.charCodeAt(i++), chars.charCodeAt(i)]);
|
|
||||||
var unicode = encoding[charcode];
|
|
||||||
if ('undefined' == typeof(unicode)) {
|
|
||||||
warn('Unencoded charcode ' + charcode);
|
|
||||||
unicode = charcode;
|
|
||||||
} else {
|
|
||||||
unicode = unicode.unicode;
|
|
||||||
}
|
|
||||||
str += String.fromCharCode(unicode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (var i = 0; i < chars.length; ++i) {
|
|
||||||
var charcode = chars.charCodeAt(i);
|
|
||||||
var unicode = encoding[charcode];
|
|
||||||
if ('undefined' == typeof(unicode)) {
|
|
||||||
warn('Unencoded charcode ' + charcode);
|
|
||||||
unicode = charcode;
|
|
||||||
} else {
|
|
||||||
unicode = unicode.unicode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle surrogate pairs
|
|
||||||
if (unicode > 0xFFFF) {
|
|
||||||
str += String.fromCharCode(unicode & 0xFFFF);
|
|
||||||
unicode >>= 16;
|
|
||||||
}
|
|
||||||
str += String.fromCharCode(unicode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enter the translated string into the cache
|
|
||||||
return (charsCache[chars] = str);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var kDefaultURL = 'compressed.tracemonkey-pldi-09.pdf';
|
var kDefaultURL = 'compressed.tracemonkey-pldi-09.pdf';
|
||||||
var kDefaultScale = 1.5;
|
var kDefaultScale = 1;
|
||||||
var kDefaultScaleDelta = 1.1;
|
var kDefaultScaleDelta = 1.1;
|
||||||
var kCacheSize = 20;
|
var kCacheSize = 20;
|
||||||
var kCssUnits = 96.0 / 72.0;
|
var kCssUnits = 96.0 / 72.0;
|
||||||
|
46
worker.js
46
worker.js
@ -141,6 +141,9 @@ var WorkerPDFDoc = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var fontWorker = new Worker('../worker/boot_font.js');
|
||||||
|
var fontHandler = this.fontHandler = new MessageHandler('font', fontWorker);
|
||||||
|
|
||||||
var handler = this.handler = new MessageHandler("main", worker);
|
var handler = this.handler = new MessageHandler("main", worker);
|
||||||
handler.on("page", function(data) {
|
handler.on("page", function(data) {
|
||||||
var pageNum = data.pageNum;
|
var pageNum = data.pageNum;
|
||||||
@ -164,42 +167,23 @@ var WorkerPDFDoc = (function() {
|
|||||||
var file = data[3];
|
var file = data[3];
|
||||||
var properties = data[4];
|
var properties = data[4];
|
||||||
|
|
||||||
var font = {
|
fontHandler.send("font", [objId, name, file, properties]);
|
||||||
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;
|
break;
|
||||||
default:
|
default:
|
||||||
throw "Got unkown object type " + objType;
|
throw "Got unkown object type " + objType;
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
|
fontHandler.on('font_ready', function(data) {
|
||||||
|
var objId = data[0];
|
||||||
|
var fontObj = new FontShape(data[1]);
|
||||||
|
// If there is no string, then there is nothing to attach to the DOM.
|
||||||
|
if (!fontObj.str) {
|
||||||
|
Objects.resolve(objId, fontObj);
|
||||||
|
} else {
|
||||||
|
FontLoader.bind(objId, fontObj);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (!useWorker) {
|
if (!useWorker) {
|
||||||
// If the main thread is our worker, setup the handling for the messages
|
// If the main thread is our worker, setup the handling for the messages
|
||||||
|
Loading…
x
Reference in New Issue
Block a user