From 0866ad5bfffe724f6029b400d8dc02e4bfb90d4a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 13 Mar 2014 05:56:12 -0700 Subject: [PATCH] Add a cache for glyphs. This reduces memory consumption for text heavy documents. I tested five documents and saw hit rates ranging from 97.4% to 99.8% (most of the misses are due to |width| varying even when |fontChar| matches). On two of those documents I saw improvements of 40 and 50 MiB. The patch also introduces the Glyph constructor, and renames the |unicodeChars| local variable as |unicode| for consistency with the corresponding Glyph property. --- src/core/fonts.js | 50 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/src/core/fonts.js b/src/core/fonts.js index ba4bba527..794701e06 100644 --- a/src/core/fonts.js +++ b/src/core/fonts.js @@ -2127,6 +2127,29 @@ function adjustWidths(properties) { properties.defaultWidth *= scale; } +var Glyph = (function GlyphClosure() { + function Glyph(fontChar, unicode, accent, width, vmetric, operatorList) { + this.fontChar = fontChar; + this.unicode = unicode; + this.accent = accent; + this.width = width; + this.vmetric = vmetric; + this.operatorList = operatorList; + } + + Glyph.prototype.matchesForCache = + function(fontChar, unicode, accent, width, vmetric, operatorList) { + return this.fontChar === fontChar && + this.unicode === unicode && + this.accent === accent && + this.width === width && + this.vmetric === vmetric && + this.operatorList === operatorList; + }; + + return Glyph; +})(); + /** * '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). @@ -2144,6 +2167,8 @@ var Font = (function FontClosure() { this.loadCharProcs = properties.coded; this.sizes = []; + this.glyphCache = {}; + var names = name.split('+'); names = names.length > 1 ? names[1] : names[0]; names = names.split(/[-,_]/g)[0]; @@ -4305,9 +4330,9 @@ var Font = (function FontClosure() { width = isNum(width) ? width : this.defaultWidth; var vmetric = this.vmetrics && this.vmetrics[widthCode]; - var unicodeChars = this.toUnicode[charcode] || charcode; - if (typeof unicodeChars === 'number') { - unicodeChars = String.fromCharCode(unicodeChars); + var unicode = this.toUnicode[charcode] || charcode; + if (typeof unicode === 'number') { + unicode = String.fromCharCode(unicode); } // First try the toFontChar map, if it's not there then try falling @@ -4332,14 +4357,17 @@ var Font = (function FontClosure() { }; } - return { - fontChar: String.fromCharCode(fontCharCode), - unicode: unicodeChars, - accent: accent, - width: width, - vmetric: vmetric, - operatorList: operatorList - }; + var fontChar = String.fromCharCode(fontCharCode); + + var glyph = this.glyphCache[charcode]; + if (!glyph || + !glyph.matchesForCache(fontChar, unicode, accent, width, vmetric, + operatorList)) { + glyph = new Glyph(fontChar, unicode, accent, width, vmetric, + operatorList); + this.glyphCache[charcode] = glyph; + } + return glyph; }, charsToGlyphs: function Font_charsToGlyphs(chars) {