From e5a0d89da9d221af47ba81687462a7b64e30bc49 Mon Sep 17 00:00:00 2001 From: Yury Delendik Date: Mon, 19 May 2014 16:27:54 -0500 Subject: [PATCH] Refactors loadFont for translateFont be async; fixes type3 dup data --- src/core/evaluator.js | 181 +++++++++++++++++++++++++----------------- src/core/fonts.js | 23 +++--- src/core/obj.js | 1 - src/display/canvas.js | 7 +- 4 files changed, 124 insertions(+), 88 deletions(-) diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 5752f6445..2498d979a 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -292,28 +292,26 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { fontArgs = fontArgs.slice(); fontName = fontArgs[0].name; } + var self = this; - return this.loadFont(fontName, fontRef, this.xref, resources, - operatorList).then(function (font) { - state.font = font; - var loadedName = font.loadedName; - if (!font.sent) { - var fontData = font.translated.exportData(); - - self.handler.send('commonobj', [ - loadedName, - 'Font', - fontData - ]); - font.sent = true; - } - - return loadedName; + return this.loadFont(fontName, fontRef, this.xref, resources).then( + function (translated) { + if (!translated.font.isType3Font) { + return translated; + } + return translated.loadType3Data(self, resources, operatorList).then( + function () { + return translated; }); + }).then(function (translated) { + state.font = translated.font; + translated.send(self.handler); + return translated.loadedName; + }); }, handleText: function PartialEvaluator_handleText(chars, state) { - var font = state.font.translated; + var font = state.font; var glyphs = font.charsToGlyphs(chars); var isAddToPathSet = !!(state.textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG); @@ -439,16 +437,12 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { }, loadFont: function PartialEvaluator_loadFont(fontName, font, xref, - resources, - parentOperatorList) { + resources) { function errorFont() { - return Promise.resolve({ - translated: new ErrorFont('Font ' + fontName + ' is not available'), - loadedName: 'g_font_error' - }); + return Promise.resolve(new TranslatedFont('g_font_error', + new ErrorFont('Font ' + fontName + ' is not available'), font)); } - var fontRef; if (font) { // Loading by ref. assert(isRef(font)); @@ -471,6 +465,12 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { return errorFont(); } + // We are holding font.translated references just for fontRef that are not + // dictionaries (Dict). See explanation below. + if (font.translated) { + return font.translated; + } + var fontCapability = createPromiseCapability(); var preEvaluatedFont = this.preEvaluateFont(font, xref); @@ -487,8 +487,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var aliasFontRef = fontAliases[hash].aliasRef; if (aliasFontRef && this.fontCache.has(aliasFontRef)) { this.fontCache.putAlias(fontRef, aliasFontRef); - var cachedFont = this.fontCache.get(fontRef); - return cachedFont; + return this.fontCache.get(fontRef); } } @@ -517,49 +516,28 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { font.loadedName = 'g_font_' + (fontRefIsDict ? fontName.replace(/\W/g, '') : fontID); - if (!font.translated) { - var translated; - try { - translated = this.translateFont(preEvaluatedFont, xref); - } catch (e) { - UnsupportedManager.notify(UNSUPPORTED_FEATURES.font); - translated = new ErrorFont(e instanceof Error ? e.message : e); - } - font.translated = translated; + font.translated = fontCapability.promise; + + // TODO move promises into translate font + var translatedPromise; + try { + translatedPromise = Promise.resolve( + this.translateFont(preEvaluatedFont, xref)); + } catch (e) { + translatedPromise = Promise.reject(e); } - var loadCharProcsPromise = Promise.resolve(); - if (font.translated.loadCharProcs) { - var charProcs = font.get('CharProcs').getAll(); - var fontResources = (font.get('Resources') || resources); - var charProcKeys = Object.keys(charProcs); - var charProcOperatorList = {}; - for (var i = 0, n = charProcKeys.length; i < n; ++i) { - loadCharProcsPromise = loadCharProcsPromise.then(function (key) { - var glyphStream = charProcs[key]; - var operatorList = new OperatorList(); - return this.getOperatorList(glyphStream, fontResources, - operatorList). - then(function () { - charProcOperatorList[key] = operatorList.getIR(); - - // Add the dependencies to the parent operator list so they are - // resolved before sub operator list is executed synchronously. - if (parentOperatorList) { - parentOperatorList.addDependencies(operatorList.dependencies); - } - }); - }.bind(this, charProcKeys[i])); - } - loadCharProcsPromise = loadCharProcsPromise.then(function () { - font.translated.charProcOperatorList = charProcOperatorList; - }); - } - return loadCharProcsPromise.then(function () { - font.loaded = true; - fontCapability.resolve(font); - return fontCapability.promise; - }, fontCapability.reject); + translatedPromise.then(function (translatedFont) { + fontCapability.resolve(new TranslatedFont(font.loadedName, + translatedFont, font)); + }, function (reason) { + // TODO fontCapability.reject? + UnsupportedManager.notify(UNSUPPORTED_FEATURES.font); + fontCapability.resolve(new TranslatedFont(font.loadedName, + new ErrorFont(reason instanceof Error ? reason.message : reason), + font)); + }); + return fontCapability.promise; }, buildPath: function PartialEvaluator_buildPath(operatorList, fn, args) { @@ -830,11 +808,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } function handleSetFont(fontName, fontRef) { - return self.loadFont(fontName, fontRef, xref, resources, null). - then(function (font) { - var fontTranslated = textState.font = font.translated; - textState.fontMatrix = fontTranslated.fontMatrix ? - fontTranslated.fontMatrix : + return self.loadFont(fontName, fontRef, xref, resources). + then(function (translated) { + textState.font = translated.font; + textState.fontMatrix = translated.font.fontMatrix || FONT_IDENTITY_MATRIX; }); } @@ -1609,7 +1586,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { this.extractWidths(dict, xref, descriptor, properties); if (type.name === 'Type3') { - properties.coded = true; + properties.isType3Font = true; } return new Font(fontName.name, fontFile, properties); @@ -1619,6 +1596,64 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { return PartialEvaluator; })(); +var TranslatedFont = (function TranslatedFontClosure() { + function TranslatedFont(loadedName, font, dict) { + this.loadedName = loadedName; + this.font = font; + this.dict = dict; + this.type3Loaded = null; + this.sent = false; + } + TranslatedFont.prototype = { + send: function (handler) { + if (this.sent) { + return; + } + var fontData = this.font.exportData(); + handler.send('commonobj', [ + this.loadedName, + 'Font', + fontData + ]); + this.sent = true; + }, + loadType3Data: function (evaluator, resources, parentOperatorList) { + assert(this.font.isType3Font); + + if (this.type3Loaded) { + return this.type3Loaded; + } + + var translatedFont = this.font; + var loadCharProcsPromise = Promise.resolve(); + var charProcs = this.dict.get('CharProcs').getAll(); + var fontResources = this.dict.get('Resources') || resources; + var charProcKeys = Object.keys(charProcs); + var charProcOperatorList = {}; + for (var i = 0, n = charProcKeys.length; i < n; ++i) { + loadCharProcsPromise = loadCharProcsPromise.then(function (key) { + var glyphStream = charProcs[key]; + var operatorList = new OperatorList(); + return evaluator.getOperatorList(glyphStream, fontResources, + operatorList). + then(function () { + charProcOperatorList[key] = operatorList.getIR(); + + // Add the dependencies to the parent operator list so they are + // resolved before sub operator list is executed synchronously. + parentOperatorList.addDependencies(operatorList.dependencies); + }); + }.bind(this, charProcKeys[i])); + } + this.type3Loaded = loadCharProcsPromise.then(function () { + translatedFont.charProcOperatorList = charProcOperatorList; + }); + return this.type3Loaded; + } + }; + return TranslatedFont; +})(); + var OperatorList = (function OperatorListClosure() { var CHUNK_SIZE = 1000; var CHUNK_SIZE_ABOUT = CHUNK_SIZE - 5; // close to chunk size diff --git a/src/core/fonts.js b/src/core/fonts.js index 5620cbcb3..205fc95c4 100644 --- a/src/core/fonts.js +++ b/src/core/fonts.js @@ -2118,23 +2118,23 @@ function adjustWidths(properties) { } var Glyph = (function GlyphClosure() { - function Glyph(fontChar, unicode, accent, width, vmetric, operatorList) { + function Glyph(fontChar, unicode, accent, width, vmetric, operatorListId) { this.fontChar = fontChar; this.unicode = unicode; this.accent = accent; this.width = width; this.vmetric = vmetric; - this.operatorList = operatorList; + this.operatorListId = operatorListId; } Glyph.prototype.matchesForCache = - function(fontChar, unicode, accent, width, vmetric, operatorList) { + function(fontChar, unicode, accent, width, vmetric, operatorListId) { return this.fontChar === fontChar && this.unicode === unicode && this.accent === accent && this.width === width && this.vmetric === vmetric && - this.operatorList === operatorList; + this.operatorListId === operatorListId; }; return Glyph; @@ -2154,8 +2154,7 @@ var Font = (function FontClosure() { this.name = name; this.loadedName = properties.loadedName; - this.coded = properties.coded; - this.loadCharProcs = properties.coded; + this.isType3Font = properties.isType3Font; this.sizes = []; this.glyphCache = {}; @@ -4376,7 +4375,7 @@ var Font = (function FontClosure() { }, charToGlyph: function Font_charToGlyph(charcode) { - var fontCharCode, width, operatorList; + var fontCharCode, width, operatorListId; var widthCode = charcode; if (this.cMap && charcode in this.cMap.map) { @@ -4398,9 +4397,9 @@ var Font = (function FontClosure() { fontCharCode = mapSpecialUnicodeValues(fontCharCode); } - if (this.type === 'Type3') { + if (this.isType3Font) { // Font char code in this case is actually a glyph name. - operatorList = this.charProcOperatorList[fontCharCode]; + operatorListId = fontCharCode; } var accent = null; @@ -4418,9 +4417,9 @@ var Font = (function FontClosure() { var glyph = this.glyphCache[charcode]; if (!glyph || !glyph.matchesForCache(fontChar, unicode, accent, width, vmetric, - operatorList)) { + operatorListId)) { glyph = new Glyph(fontChar, unicode, accent, width, vmetric, - operatorList); + operatorListId); this.glyphCache[charcode] = glyph; } return glyph; @@ -4485,6 +4484,8 @@ var Font = (function FontClosure() { var ErrorFont = (function ErrorFontClosure() { function ErrorFont(error) { this.error = error; + this.loadedName = 'g_font_error'; + this.loading = false; } ErrorFont.prototype = { diff --git a/src/core/obj.js b/src/core/obj.js index 67a191d5b..f2c3586c3 100644 --- a/src/core/obj.js +++ b/src/core/obj.js @@ -513,7 +513,6 @@ var Catalog = (function CatalogClosure() { }); return Promise.all(promises).then(function (fonts) { for (var i = 0, ii = fonts.length; i < ii; i++) { - delete fonts[i].sent; delete fonts[i].translated; } this.fontCache.clear(); diff --git a/src/display/canvas.js b/src/display/canvas.js index 646f8a343..ee5a4e716 100644 --- a/src/display/canvas.js +++ b/src/display/canvas.js @@ -1196,7 +1196,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { this.current.font = fontObj; this.current.fontSize = size; - if (fontObj.coded) { + if (fontObj.isType3Font) { return; // we don't need ctx.font for Type3 fonts } @@ -1343,7 +1343,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { } // Type3 fonts - each glyph is a "mini-PDF" - if (font.coded) { + if (font.isType3Font) { ctx.save(); ctx.transform.apply(ctx, current.textMatrix); ctx.translate(current.x, current.y); @@ -1363,7 +1363,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { this.save(); ctx.scale(fontSize, fontSize); ctx.transform.apply(ctx, fontMatrix); - this.executeOperatorList(glyph.operatorList); + var operatorList = font.charProcOperatorList[glyph.operatorListId]; + this.executeOperatorList(operatorList); this.restore(); var transformed = Util.applyTransform([glyph.width, 0], fontMatrix);