diff --git a/fonts.js b/fonts.js index 607e2aab9..f33cab9c3 100755 --- a/fonts.js +++ b/fonts.js @@ -21,47 +21,18 @@ var kMaxWaitForFontFace = 1000; * http://cgit.freedesktop.org/poppler/poppler/tree/poppler/GfxFont.cc#n65 */ -var Fonts = (function Fonts() { +var FontMeasure = (function FontMeasure() { var kScalePrecision = 40; - var fonts = []; - if (!isWorker) { - var ctx = document.createElement('canvas').getContext('2d'); - ctx.scale(1 / kScalePrecision, 1); - } - - var fontCount = 0; - - function FontInfo(name, data, properties) { - this.name = name; - this.data = data; - this.properties = properties; - this.id = fontCount++; - this.loading = true; - this.sizes = []; - } + var ctx = document.createElement('canvas').getContext('2d'); + ctx.scale(1 / kScalePrecision, 1); var current; var measureCache; return { - registerFont: function fonts_registerFont(fontName, data, properties) { - var font = new FontInfo(fontName, data, properties); - fonts.push(font); - return font.id; - }, - blacklistFont: function fonts_blacklistFont(fontName) { - var id = registerFont(fontName, null, {}); - markLoaded(fontName); - return id; - }, - lookupById: function fonts_lookupById(id) { - return fonts[id]; - }, - setActive: function fonts_setActive(fontName, fontObj, size) { - // |current| can be null is fontName is a built-in font - // (e.g. "sans-serif") - if (fontObj && (current = fonts[fontObj.id])) { + setActive: function fonts_setActive(font, size) { + if (current = font) { var sizes = current.sizes; if (!(measureCache = sizes[size])) measureCache = sizes[size] = Object.create(null); @@ -69,7 +40,7 @@ var Fonts = (function Fonts() { measureCache = null } - ctx.font = (size * kScalePrecision) + 'px "' + fontName + '"'; + ctx.font = (size * kScalePrecision) + 'px "' + font.loadedName + '"'; }, measureText: function fonts_measureText(text) { var width; @@ -88,9 +59,9 @@ var FontLoader = { bind: function(fonts, callback) { function checkFontsLoaded() { - for (var i = 0; i < allIds.length; i++) { - var id = allIds[i]; - if (Fonts.lookupById(id).loading) { + for (var i = 0; i < objs.length; i++) { + var fontObj = objs[i]; + if (fontObj.loading) { return false; } } @@ -102,18 +73,17 @@ var FontLoader = { return true; } - var allIds = []; - var rules = [], names = [], ids = []; + var rules = [], names = [], objs = []; for (var i = 0; i < fonts.length; i++) { var font = fonts[i]; var obj = new Font(font.name, font.file, font.properties); - font.fontDict.fontObj = obj; - allIds.push(obj.id); + obj.loading = true; + objs.push(obj); var str = ''; - var data = Fonts.lookupById(obj.id).data; + var data = obj.data; var length = data.length; for (var j = 0; j < length; j++) str += String.fromCharCode(data[j]); @@ -122,13 +92,12 @@ var FontLoader = { if (rule) { rules.push(rule); names.push(obj.loadedName); - ids.push(obj.id); } } this.listeningForFontLoad = false; if (!isWorker && rules.length) { - FontLoader.prepareFontLoadEvent(rules, names, ids); + FontLoader.prepareFontLoadEvent(rules, names, objs); } if (!checkFontsLoaded()) { @@ -136,14 +105,14 @@ var FontLoader = { 'pdfjsFontLoad', checkFontsLoaded, false); } - return; + return objs; }, // 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, ids) { + prepareFontLoadEvent: function(rules, names, objs) { /** 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 @@ -184,8 +153,8 @@ var FontLoader = { 'message', function(e) { var fontNames = JSON.parse(e.data); - for (var i = 0; i < fontNames.length; ++i) { - var font = Fonts.lookupById(fontNames[i].substring(7) | 0); + for (var i = 0; i < objs.length; ++i) { + var font = objs[i]; font.loading = false; } var evt = document.createEvent('Events'); @@ -377,12 +346,12 @@ var Font = (function() { this.name = name; this.textMatrix = properties.textMatrix || IDENTITY_MATRIX; this.encoding = properties.encoding; + this.sizes = []; // If the font is to be ignored, register it like an already loaded font // to avoid the cost of waiting for it be be loaded by the platform. if (properties.ignore) { - this.id = Fonts.blacklistFont(name); - this.loadedName = 'pdfFont' + this.id; + this.loadedName = 'Arial'; return; } @@ -412,11 +381,15 @@ var Font = (function() { } this.data = data; this.type = properties.type; - this.id = Fonts.registerFont(name, data, properties); - this.loadedName = 'pdfFont' + this.id; + this.loadedName = getUniqueName(); this.compositeFont = properties.compositeFont; }; + var numFonts = 0; + function getUniqueName() { + return 'pdfFont' + numFonts++; + } + function stringToArray(str) { var array = []; for (var i = 0; i < str.length; ++i) diff --git a/pdf.js b/pdf.js index df8e3f3cc..1f02d7d6d 100644 --- a/pdf.js +++ b/pdf.js @@ -19,6 +19,7 @@ function warn(msg) { } function error(msg) { + log(backtrace()); throw new Error(msg); } @@ -43,6 +44,16 @@ function assertWellFormed(cond, msg) { malformed(msg); } +function backtrace() { + var stackStr; + try { + throw new Error(); + } catch(e) { + stackStr = e.stack; + }; + return stackStr.split('\n').slice(1).join('\n'); +} + function shadow(obj, prop, value) { Object.defineProperty(obj, prop, { value: value, enumerable: true, configurable: true, writable: false }); return value; @@ -2959,7 +2970,7 @@ var Page = (function() { return shadow(this, 'mediaBox', ((IsArray(obj) && obj.length == 4) ? obj : null)); }, - startRendering: function(canvasCtx, continuation) { + startRendering: function(canvasCtx, continuation, onerror) { var self = this; var stats = self.stats; stats.compile = stats.fonts = stats.render = 0; @@ -2970,18 +2981,26 @@ var Page = (function() { this.compile(gfx, fonts); stats.compile = Date.now(); - FontLoader.bind( + var fontObjs = FontLoader.bind( fonts, function() { stats.fonts = Date.now(); // Always defer call to display() to work around bug in // Firefox error reporting from XHR callbacks. setTimeout(function () { - self.display(gfx); - stats.render = Date.now(); - continuation(); + var exc = null; + try { + self.display(gfx); + stats.render = Date.now(); + } catch (e) { + exc = e.toString(); + } + continuation(exc); }); }); + + for (var i = 0, ii = fonts.length; i < ii; ++i) + fonts[i].fontDict.fontObj = fontObjs[i]; }, @@ -3544,23 +3563,9 @@ var PartialEvaluator = (function() { eval: function(stream, xref, resources, fonts) { resources = xref.fetchIfRef(resources) || new Dict(); var xobjs = xref.fetchIfRef(resources.get('XObject')) || new Dict(); - var parser = new Parser(new Lexer(stream), false); - var objpool = []; - - function emitArg(arg) { - if (typeof arg == 'object' || typeof arg == 'string') { - var index = objpool.length; - objpool[index] = arg; - return 'objpool[' + index + ']'; - } - return arg; - } - - var src = ''; - - var args = []; - var obj; + var args = [], argsArray = [], fnArray = [], obj; + while (!IsEOF(obj = parser.getObj())) { if (IsCmd(obj)) { var cmd = obj.cmd; @@ -3582,10 +3587,7 @@ var PartialEvaluator = (function() { ); if ('Form' == type.name) { - args[0].code = this.eval(xobj, - xref, - xobj.dict.get('Resources'), - fonts); + args[0].code = this.eval(xobj, xref, xobj.dict.get('Resources'), fonts); } } } else if (cmd == 'Tf') { // eagerly collect all fonts @@ -3605,21 +3607,19 @@ var PartialEvaluator = (function() { } } - src += 'this.'; - src += fn; - src += '('; - src += args.map(emitArg).join(','); - src += ');\n'; - - args.length = 0; + fnArray.push(fn); + argsArray.push(args); + args = []; } else { assertWellFormed(args.length <= 33, 'Too many arguments'); args.push(obj); } } - var fn = Function('objpool', src); - return function(gfx) { fn.call(gfx, objpool); }; + return function(gfx) { + for(var i = 0, length = argsArray.length; i < length; i++) + gfx[fnArray[i]].apply(gfx, argsArray[i]); + } }, translateFont: function(fontDict, xref, resources) { @@ -3638,7 +3638,12 @@ var PartialEvaluator = (function() { if (!df) return null; compositeFont = true; - descendant = xref.fetch(df[0]); + + if (IsRef(df)) { + df = xref.fetch(df); + } + + descendant = xref.fetch(IsRef(df) ? df : df[0]); subType = descendant.get('Subtype'); fd = descendant.get('FontDescriptor'); } else { @@ -4090,7 +4095,7 @@ var CanvasGraphics = (function() { this.ctx.$setFont(fontName, size); } else { this.ctx.font = size + 'px "' + fontName + '"'; - Fonts.setActive(fontName, fontObj, size); + FontMeasure.setActive(fontObj, size); } }, setTextRenderingMode: function(mode) { @@ -4142,7 +4147,7 @@ var CanvasGraphics = (function() { text = font.charsToUnicode(text); } ctx.fillText(text, 0, 0); - current.x += Fonts.measureText(text); + current.x += FontMeasure.measureText(text); } this.ctx.restore(); @@ -4853,10 +4858,10 @@ var ColorSpace = (function() { case 'Lab': case 'DeviceN': default: - error("unrecognized color space object '" + mode + "'"); + error("unimplemented color space object '" + mode + "'"); } } else { - error('unrecognized color space object'); + error('unrecognized color space object: "'+ cs +"'"); } }; @@ -5130,6 +5135,10 @@ var PDFImage = (function() { this.bpc = bitsPerComponent; var colorSpace = dict.get('ColorSpace', 'CS'); + if (!colorSpace) { + TODO('JPX images (which don"t require color spaces'); + colorSpace = new Name('DeviceRGB'); + } this.colorSpace = ColorSpace.parse(colorSpace, xref, res); this.numComps = this.colorSpace.numComps; diff --git a/test/driver.js b/test/driver.js index e397f108b..a6b0b1dc2 100644 --- a/test/driver.js +++ b/test/driver.js @@ -111,7 +111,10 @@ function nextPage(task, loadError) { page.startRendering( ctx, - function() { snapshotCurrentPage(page, task, failure); }); + function(e) { + snapshotCurrentPage(page, task, + (!failure && e) ? ('render: '+ e) : failure); + }); } catch(e) { failure = 'page setup: '+ e.toString(); }