* http://cgit.freedesktop.org/poppler/poppler/tree/poppler/GfxFont.cc#n65
*/
- var kScalePrecision = 40;
- var Fonts = {
- _active: null,
-
- get active() {
- return this._active;
- },
-
- setActive: function fonts_setActive(name, size) {
- this._active = this[name];
- this.ctx.font = (size * kScalePrecision) + 'px "' + name + '"';
- },
-
- charsToUnicode: function fonts_chars2Unicode(chars) {
- var active = this._active;
- if (!active)
- return chars;
-
- // if we translated this string before, just grab it from the cache
- var str = active.cache[chars];
- if (str)
- return str;
-
- // translate the string using the font's encoding
- var encoding = active.properties.encoding;
- if (!encoding)
- return chars;
-
- str = "";
- for (var i = 0; i < chars.length; ++i) {
- var charcode = chars.charCodeAt(i);
- var unicode = encoding[charcode];
-
- // Check if the glyph has already been converted
- if (!IsNum(unicode))
- unicode = encoding[unicode] = GlyphsUnicode[unicode.name];
+ var Fonts = (function () {
+ var kScalePrecision = 40;
+ var fonts = Object.create(null);
+ var ctx = document.createElement("canvas").getContext("2d");
+ ctx.scale(1 / kScalePrecision, 1);
- // Handle surrogate pairs
- if (unicode > 0xFFFF) {
- str += String.fromCharCode(unicode & 0xFFFF);
- unicode >>= 16;
- }
- str += String.fromCharCode(unicode);
- }
+ function Font(name, data, properties) {
+ this.name = name;
+ this.data = data;
+ this.properties = properties;
+ this.loading = true;
+ this.charsCache = Object.create(null);
+ this.sizes = [];
+ }
- // Enter the translated string into the cache
- return active.cache[chars] = str;
- },
+ var current;
+ var charsCache;
+ var measureCache;
- get ctx() {
- var ctx = document.createElement("canvas").getContext("2d");
- ctx.scale(1 / kScalePrecision, 1);
- return shadow(this, "ctx", ctx);
- },
+ return {
+ registerFont: function fonts_registerFont(fontName, data, properties) {
+ fonts[fontName] = new Font(fontName, data, properties);
+ },
+ blacklistFont: function fonts_blacklistFont(fontName) {
+ registerFont(fontName, null, {});
+ markLoaded(fontName);
+ },
+ lookup: function fonts_lookup(fontName) {
+ return fonts[fontName];
+ },
+ setActive: function fonts_setActive(fontName, size) {
+ current = fonts[fontName];
+ charsCache = current.charsCache;
+ var sizes = current.sizes;
+ if (!(measureCache = sizes[size]))
+ measureCache = sizes[size] = Object.create(null);
+ ctx.font = (size * kScalePrecision) + 'px "' + fontName + '"';
+ },
+ charsToUnicode: function fonts_chars2Unicode(chars) {
+ if (!charsCache)
+ return chars;
+
+ // if we translated this string before, just grab it from the cache
+ var str = charsCache[chars];
+ if (str)
+ return str;
+
+ // translate the string using the font's encoding
+ var encoding = current.properties.encoding;
+ if (!encoding)
+ return chars;
+
+ str = "";
+ for (var i = 0; i < chars.length; ++i) {
+ var charcode = chars.charCodeAt(i);
+ var unicode = encoding[charcode];
+
+ // Check if the glyph has already been converted
+ if (!IsNum(unicode))
+ unicode = encoding[unicode] = GlyphsUnicode[unicode.name];
- measureText: function fonts_measureText(text) {
- return this.ctx.measureText(text).width / kScalePrecision;
+ // 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;
+ },
+ measureText: function fonts_measureText(text) {
+ var width;
+ if (measureCache && (width = measureCache[text]))
+ return width;
+ width = ctx.measureText(text).width / kScalePrecision;
+ if (measureCache)
+ measureCache[text] = width;
+ return width;
+ }
}
- };
+ })();
var FontLoader = {
- bind: function(fonts) {
+ bind: function(fonts, callback) {
var worker = (typeof window == "undefined");
- var ready = true;
- for (var i = 0; i < fonts.length; i++) {
- var font = fonts[i];
- if (Fonts.lookup(font.name)) {
- ready = ready && !Fonts.lookup(font.name).loading;
- continue;
+ function checkFontsLoaded() {
+ for (var i = 0; i < fonts.length; i++) {
+ var font = fonts[i];
- if (Fonts[font.name].loading) {
++ if (Fonts.lookup(font.name).loading) {
+ return false;
+ }
}
- ready = false;
+ document.documentElement.removeEventListener(
+ "pdfjsFontLoad", checkFontsLoaded, false);
- var obj = new Font(font.name, font.file, font.properties);
+ callback();
+ return true;
+ }
- var str = "";
- var data = Fonts.lookup(font.name).data;
- var length = data.length;
- for (var j = 0; j < length; j++)
- str += String.fromCharCode(data[j]);
+ for (var i = 0; i < fonts.length; i++) {
+ var font = fonts[i];
- if (!Fonts[font.name]) {
++ if (!Fonts.lookup(font.name)) {
+ var obj = new Font(font.name, font.file, font.properties);
- worker ? obj.bindWorker(str) : obj.bindDOM(str);
+ var str = "";
- var data = Fonts[font.name].data;
++ var data = Fonts.lookup(font.name).data;
+ var length = data.length;
+ for (var j = 0; j < length; j++)
+ str += String.fromCharCode(data[j]);
+
+ worker ? obj.bindWorker(str) : obj.bindDOM(str);
+ }
}
- return ready;
+ if (!checkFontsLoaded()) {
+ document.documentElement.addEventListener(
+ "pdfjsFontLoad", checkFontsLoaded, false);
+ }
+
+ return;
}
};
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + "}";
var styleSheet = document.styleSheets[0];
styleSheet.insertRule(rule, styleSheet.length);
- src += ' var font = Fonts[fontName];\n';
+
+ /** 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
+ // ready. This code will be obsoleted by Mozilla bug 471915.
+ //
+ // The only reliable way to know if a font is loaded in Gecko
+ // (at the moment) is document.onload in a document with
+ // a @font-face rule defined in a "static" stylesheet. We use a
+ // subdocument in an <iframe>, set up properly, to know when
+ // our @font-face rule was loaded. However, the subdocument and
+ // outer document can't share CSS rules, so the inner document
+ // is only part of the puzzle. The second piece is an invisible
+ // paragraph created in order to force loading of the @font-face
+ // in the *outer* document. (The font still needs to be loaded
+ // for its metrics, for reflow). We create the <p> first, in
+ // the outer document, then create the iframe. Unless something
+ // goes really wonkily, we expect the @font-face for the outer
+ // document to processed before the inner. That's still
+ // fragile, but seems to work in practice.
+ var p = document.createElement("p");
+ p.setAttribute("style",
+ 'font-family: "'+ fontName +'";'+
+ 'visibility: hidden;'+
+ 'width: 10px; height: 10px;'+
+ 'position: absolute; top: 0px; left: 0px;');
+ p.innerHTML = 'Hello';
+ document.body.appendChild(p);
+
+ // XXX we should have a time-out here too, and maybe fire
+ // pdfjsFontLoadFailed?
+ var src = '<!DOCTYPE HTML><html><head>'
+ src += '<style type="text/css">';
+ src += rule;
+ src += '</style>'
+ src += '<script type="application/javascript">'
+ src += ' var fontName="'+ fontName +'";\n';
+ src += ' window.onload = function () {\n'
+ src += ' var Fonts = top.document.defaultView.Fonts;\n';
++ src += ' var font = Fonts.lookup(fontName);\n';
+ src += ' font.loading = false;\n';
+ src += ' var doc = top.document;\n';
+ src += ' var evt = doc.createEvent("Events");\n';
+ src += ' evt.initEvent("pdfjsFontLoad", true, false);\n'
+ src += ' doc.documentElement.dispatchEvent(evt);\n';
+ src += ' }';
+ src += '</script>';
+ src += '</head>';
+ src += '<body style="font-family:\''+ fontName +'\'">';
+ src += 'Hello</body></html>';
+ var frame = document.createElement("iframe");
+ frame.src = 'data:text/html,'+ src;
+ frame.setAttribute("style",
+ 'visibility: hidden;'+
+ 'width: 10px; height: 10px;'+
+ 'position: absolute; top: 0px; left: 0px;');
+ document.body.appendChild(frame);
+
+ /** Hack end */
}
};