Process fonts on main thread again - font handling works again

This commit is contained in:
Julian Viereck 2011-10-08 22:22:30 +02:00
parent 75884a6160
commit 90e2546ea3
4 changed files with 289 additions and 129 deletions

118
fonts.js
View File

@ -146,7 +146,20 @@ var FontLoader = {
for (var i = 0; i < fonts.length; i++) { for (var i = 0; i < fonts.length; i++) {
var font = fonts[i]; var font = fonts[i];
// If there is already a fontObj on the font, then it was loaded/attached
// to the page already and we don't have to do anything for this font
// here future.
if (font.fontObj) {
console.log('got already', font.properties.loadedName);
continue;
}
var obj = new Font(font.name, font.file, font.properties); var obj = new Font(font.name, font.file, font.properties);
// Store the fontObj on the font such that `setFont` in CanvasGraphics
// can reuse it later again.
font.fontObj = obj;
objs.push(obj); objs.push(obj);
var str = ''; var str = '';
@ -403,6 +416,111 @@ 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];
}
var name = this.loadedName;
var bold = this.black ? (this.bold ? 'bolder' : 'bold') :
(this.bold ? 'bold' : 'normal');
var italic = this.italic ? 'italic' : 'normal';
this.fontFallback = this.serif ? 'serif' : 'sans-serif';
this.namePart1 = italic + ' ' + bold + ' ';
this.namePart2 = 'px "' + name + '", "';
this.supported = Object.keys(this.encoding).length != 0;
// Set the loading flag. Gets set to false in FontLoader.bind().
this.loading = true;
};
function int16(bytes) {
return (bytes[0] << 8) + (bytes[1] & 0xff);
};
constructor.prototype = {
getRule: function fonts_getRule(size, fallback) {
fallback = fallback || this.fontFallback;
return this.namePart1 + size + this.namePart2 + fallback + '"';
},
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).

266
pdf.js
View File

@ -3594,11 +3594,11 @@ var Page = (function pagePage() {
ensureFonts: function(fonts, callback) { ensureFonts: function(fonts, callback) {
console.log('--ensureFonts--', '' + fonts); console.log('--ensureFonts--', '' + fonts);
// Convert the font names to the corresponding font obj. // Convert the font names to the corresponding font obj.
// for (var i = 0; i < fonts.length; i++) { for (var i = 0; i < fonts.length; i++) {
// // HACK FOR NOW. Access the data directly. This isn't allowed as the // HACK FOR NOW. Access the data directly. This isn't allowed as the
// // font object isn't resolved yet. // font object isn't resolved yet.
// fonts[i] = this.objs.objs[fonts[i]].data; fonts[i] = this.objs.objs[fonts[i]].data;
// } }
// Load all the fonts // Load all the fonts
var fontObjs = FontLoader.bind( var fontObjs = FontLoader.bind(
@ -3610,9 +3610,6 @@ var Page = (function pagePage() {
}.bind(this), }.bind(this),
this.objs this.objs
); );
for (var i = 0, ii = fonts.length; i < ii; ++i)
fonts[i].dict.fontObj = fontObjs[i];
}, },
display: function(gfx, callback) { display: function(gfx, callback) {
@ -4059,42 +4056,49 @@ var PDFDoc = (function() {
var i = 0; var i = 0;
function checkFontData() { // function checkFontData() {
// Check if all fontObjs have been processed. If not, shedule a // // Check if all fontObjs have been processed. If not, shedule a
// callback that is called once the data arrives and that checks // // callback that is called once the data arrives and that checks
// the next fonts. // // the next fonts.
for (i; i < depFonts.length; i++) { // for (i; i < depFonts.length; i++) {
var fontName = depFonts[i]; // var fontName = depFonts[i];
if (!objs.hasData(fontName)) { // if (!objs.hasData(fontName)) {
console.log('need to wait for fontData', fontName); // console.log('need to wait for fontData', fontName);
objs.onData(fontName, checkFontData); // objs.onData(fontName, checkFontData);
return; // return;
} else if (!objs.isResolved(fontName)) { // } else if (!objs.isResolved(fontName)) {
fontsToLoad.push(fontName); // fontsToLoad.push(fontName);
} // }
} // }
//
// // There can be edge cases where two pages wait for one font and then
// // call startRenderingFromIRQueue twice with the same font. That makes
// // the font getting loaded twice and throw an error later as the font
// // promise gets resolved twice.
// // This prevents thats fonts are loaded really only once.
// for (var j = 0; j < fontsToLoad.length; j++) {
// var fontName = fontsToLoad[j];
// if (fontsLoading[fontName]) {
// fontsToLoad.splice(j, 1);
// j--;
// } else {
// fontsLoading[fontName] = true;
// }
// }
//
// for (var i = 0; i < depFonts.lenght; i++) {
// // Get fonts from the objects.
// fontsToLoad.push(this.objs.get(depFonts[i]));
// }
//
// // At this point, all font data ia loaded. Start the actuall rendering.
// page.startRenderingFromIRQueue(data.IRQueue, fontsToLoad);
// }
//
// checkFontData();
// There can be edge cases where two pages wait for one font and then // Start rendering directly for now, as the fonts are
// call startRenderingFromIRQueue twice with the same font. That makes page.startRenderingFromIRQueue(data.IRQueue, Object.keys(data.depFonts));
// the font getting loaded twice and throw an error later as the font
// promise gets resolved twice.
// This prevents thats fonts are loaded really only once.
for (var j = 0; j < fontsToLoad.length; j++) {
var fontName = fontsToLoad[j];
if (fontsLoading[fontName]) {
fontsToLoad.splice(j, 1);
j--;
} else {
fontsLoading[fontName] = true;
}
}
// At this point, all font data ia loaded. Start the actuall rendering.
page.startRenderingFromIRQueue(data.IRQueue, fontsToLoad);
}
// checkFontData();
page.startRenderingFromIRQueue(data.IRQueue, data.depFonts);
}, this); }, this);
processorHandler.on("obj", function(data) { processorHandler.on("obj", function(data) {
@ -4112,21 +4116,6 @@ var PDFDoc = (function() {
var file = data[3]; var file = data[3];
var properties = data[4]; var properties = data[4];
// << CODE TAKEN FROM WORKER >>
data = [objId, name, file, properties];
var objId = data[0];
var name = data[1];
var file = data[2];
var properties = data[3];
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) { if (file) {
var fontFileDict = new Dict(); var fontFileDict = new Dict();
fontFileDict.map = file.dict.map; fontFileDict.map = file.dict.map;
@ -4138,34 +4127,76 @@ var PDFDoc = (function() {
// Stream one. This makes complex_ttf_font.pdf work. // Stream one. This makes complex_ttf_font.pdf work.
var cmf = file.bytes[0]; var cmf = file.bytes[0];
if ((cmf & 0x0f) == 0x08) { if ((cmf & 0x0f) == 0x08) {
font.file = new FlateStream(fontFile); file = new FlateStream(fontFile);
} else { } else {
font.file = fontFile; file = fontFile;
} }
} }
var obj = new Font(font.name, font.file, font.properties);
var str = ''; // For now, resolve the font object here direclty. The real font object
var data = obj.data; // is then created in FontLoader.bind().
if (data) { this.objs.resolve(objId, {
var length = data.length; name: name,
for (var j = 0; j < length; j++) file: file,
str += String.fromCharCode(data[j]); properties: properties
} });
obj.str = str; //
//
var fontObj = new FontShape(obj); // // << CODE TAKEN FROM WORKER >>
for (var prop in obj) { // data = [objId, name, file, properties];
fontObj[prop] = obj[prop]; // var objId = data[0];
} // var name = data[1];
// var file = data[2];
if (!str) { // var properties = data[3];
this.objs.resolve(objId, fontObj); //
} else { // var font = {
this.objs.setData(objId, fontObj); // 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;
// }
// }
//
// var obj = new Font(font.name, font.file, font.properties);
//
// var str = '';
// var data = obj.data;
// if (data) {
// var length = data.length;
// for (var j = 0; j < length; j++)
// str += String.fromCharCode(data[j]);
// }
//
// obj.str = str;
//
// var fontObj = new FontShape(obj);
// for (var prop in obj) {
// fontObj[prop] = obj[prop];
// }
//
// if (!str) {
// this.objs.resolve(objId, fontObj);
// } else {
// this.objs.setData(objId, fontObj);
// }
// processorHandler.send("font", [objId, name, file, properties]); // processorHandler.send("font", [objId, name, file, properties]);
break; break;
@ -4194,7 +4225,7 @@ var PDFDoc = (function() {
WorkerProcessorHandler.setup(processorHandler); WorkerProcessorHandler.setup(processorHandler);
} }
processorHandler.send("doc", this.pdf); processorHandler.send("doc", this.data);
} }
constructor.prototype = { constructor.prototype = {
@ -4872,22 +4903,23 @@ var PartialEvaluator = (function partialEvaluator() {
} }
} }
} else if (cmd == 'Tf') { // eagerly collect all fonts } else if (cmd == 'Tf') { // eagerly collect all fonts
var fontRes = resources.get('Font'); args[0].name = handleSetFont(args[0].name);
if (fontRes) { // var fontRes = resources.get('Font');
fontRes = xref.fetchIfRef(fontRes); // if (fontRes) {
var font = xref.fetchIfRef(fontRes.get(args[0].name)); // fontRes = xref.fetchIfRef(fontRes);
assertWellFormed(isDict(font)); // var font = xref.fetchIfRef(fontRes.get(args[0].name));
if (!font.translated) { // assertWellFormed(isDict(font));
font.translated = this.translateFont(font, xref, resources); // if (!font.translated) {
if (font.translated) { // font.translated = this.translateFont(font, xref, resources);
// keep track of each font we translated so the caller can // if (font.translated) {
// load them asynchronously before calling display on a page // // keep track of each font we translated so the caller can
// fonts.push(font.translated); // // load them asynchronously before calling display on a page
dependency.push(font.translated); // // fonts.push(font.translated);
} // dependency.push(font.translated);
} // }
} // }
// }
//
} else if (cmd == 'EI') { } else if (cmd == 'EI') {
buildPaintImageXObject(args[0], true); buildPaintImageXObject(args[0], true);
} }
@ -5789,22 +5821,32 @@ var CanvasGraphics = (function canvasGraphics() {
this.current.leading = -leading; this.current.leading = -leading;
}, },
setFont: function canvasGraphicsSetFont(fontRef, size) { setFont: function canvasGraphicsSetFont(fontRef, size) {
var font; // Lookup the fontObj using fontRef only.
// the tf command uses a name, but graphics state uses a reference var fontRefName = fontRef.name;
if (isName(fontRef)) { var fontObj = this.objs.get(fontRefName).fontObj;
font = this.xref.fetchIfRef(this.res.get('Font'));
if (!isDict(font))
return;
font = font.get(fontRef.name); if (!fontObj) {
} else if (isRef(fontRef)) { throw "Can't find font for " + fontRefName;
font = fontRef;
} }
font = this.xref.fetchIfRef(font);
if (!font)
error('Referenced font is not found');
var fontObj = font.fontObj; var name = fontObj.loadedName || 'sans-serif';
// var font;
// // the tf command uses a name, but graphics state uses a reference
// if (isName(fontRef)) {
// font = this.xref.fetchIfRef(this.res.get('Font'));
// if (!isDict(font))
// return;
//
// font = font.get(fontRef.name);
// } else if (isRef(fontRef)) {
// font = fontRef;
// }
// font = this.xref.fetchIfRef(font);
// if (!font)
// error('Referenced font is not found');
//
// var fontObj = font.fontObj;
this.current.font = fontObj; this.current.font = fontObj;
this.current.fontSize = size; this.current.fontSize = size;
@ -5860,7 +5902,7 @@ var CanvasGraphics = (function canvasGraphics() {
// return; // return;
// } // }
console.log("showText", text); // console.log("showText", text);
var ctx = this.ctx; var ctx = this.ctx;
var current = this.current; var current = this.current;
@ -5942,7 +5984,7 @@ var CanvasGraphics = (function canvasGraphics() {
// return; // return;
// } // }
console.log("showSpacedText", arr); // console.log("showSpacedText", arr);
var ctx = this.ctx; var ctx = this.ctx;
var current = this.current; var current = this.current;

View File

@ -4,7 +4,7 @@
'use strict'; 'use strict';
// Set this to true if you want to use workers. // Set this to true if you want to use workers.
var useWorker = false; var useWorker = true;
var WorkerPage = (function() { var WorkerPage = (function() {
function constructor(workerPDF, page, objs) { function constructor(workerPDF, page, objs) {

View File

@ -10,7 +10,7 @@ var WorkerProcessorHandler = {
handler.on("doc", function(data) { handler.on("doc", function(data) {
// Create only the model of the PDFDoc, which is enough for // Create only the model of the PDFDoc, which is enough for
// processing the content of the pdf. // processing the content of the pdf.
pdfDoc = data;//new PDFDocModel(new Stream(data)); pdfDoc = new PDFDocModel(new Stream(data));
}); });
handler.on("page_request", function(pageNum) { handler.on("page_request", function(pageNum) {
@ -50,22 +50,22 @@ var WorkerProcessorHandler = {
} }
// Filter the dependecies for fonts. // Filter the dependecies for fonts.
// var fonts = {}; var fonts = {};
// for (var i = 0; i < dependency.length; i++) {
// var dep = dependency[i];
// if (dep.indexOf('font_') == 0) {
// fonts[dep] = true;
// }
// }
var fonts = [];
for (var i = 0; i < dependency.length; i++) { for (var i = 0; i < dependency.length; i++) {
var dep = dependency[i]; var dep = dependency[i];
if (typeof dep === "object") { if (dep.indexOf('font_') == 0) {
fonts.push(dep); fonts[dep] = true;
} }
} }
// var fonts = [];
// for (var i = 0; i < dependency.length; i++) {
// var dep = dependency[i];
// if (typeof dep === "object") {
// fonts.push(dep);
// }
// }
handler.send("page", { handler.send("page", {
pageNum: pageNum, pageNum: pageNum,
IRQueue: IRQueue, IRQueue: IRQueue,