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.
This commit is contained in:
Nicholas Nethercote 2014-03-13 05:56:12 -07:00
parent e5cd75083f
commit 0866ad5bff

View File

@ -2127,6 +2127,29 @@ function adjustWidths(properties) {
properties.defaultWidth *= scale; 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 * '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).
@ -2144,6 +2167,8 @@ var Font = (function FontClosure() {
this.loadCharProcs = properties.coded; this.loadCharProcs = properties.coded;
this.sizes = []; this.sizes = [];
this.glyphCache = {};
var names = name.split('+'); var names = name.split('+');
names = names.length > 1 ? names[1] : names[0]; names = names.length > 1 ? names[1] : names[0];
names = names.split(/[-,_]/g)[0]; names = names.split(/[-,_]/g)[0];
@ -4305,9 +4330,9 @@ var Font = (function FontClosure() {
width = isNum(width) ? width : this.defaultWidth; width = isNum(width) ? width : this.defaultWidth;
var vmetric = this.vmetrics && this.vmetrics[widthCode]; var vmetric = this.vmetrics && this.vmetrics[widthCode];
var unicodeChars = this.toUnicode[charcode] || charcode; var unicode = this.toUnicode[charcode] || charcode;
if (typeof unicodeChars === 'number') { if (typeof unicode === 'number') {
unicodeChars = String.fromCharCode(unicodeChars); unicode = String.fromCharCode(unicode);
} }
// First try the toFontChar map, if it's not there then try falling // First try the toFontChar map, if it's not there then try falling
@ -4332,14 +4357,17 @@ var Font = (function FontClosure() {
}; };
} }
return { var fontChar = String.fromCharCode(fontCharCode);
fontChar: String.fromCharCode(fontCharCode),
unicode: unicodeChars, var glyph = this.glyphCache[charcode];
accent: accent, if (!glyph ||
width: width, !glyph.matchesForCache(fontChar, unicode, accent, width, vmetric,
vmetric: vmetric, operatorList)) {
operatorList: operatorList glyph = new Glyph(fontChar, unicode, accent, width, vmetric,
}; operatorList);
this.glyphCache[charcode] = glyph;
}
return glyph;
}, },
charsToGlyphs: function Font_charsToGlyphs(chars) { charsToGlyphs: function Font_charsToGlyphs(chars) {