blob: 80b0dcab1b476c3c40b6adbda9f4ddccca95b610 [file] [log] [blame]
var FontManager = (function(){
var maxWaitingTime = 5000;
var emptyChar = {
w: 0,
size:0,
shapes:[]
};
var combinedCharacters = [];
//Hindi characters
combinedCharacters = combinedCharacters.concat([2304, 2305, 2306, 2307, 2362, 2363, 2364, 2364, 2366
, 2367, 2368, 2369, 2370, 2371, 2372, 2373, 2374, 2375, 2376, 2377, 2378, 2379
, 2380, 2381, 2382, 2383, 2387, 2388, 2389, 2390, 2391, 2402, 2403]);
function setUpNode(font, family){
var parentNode = createTag('span');
parentNode.style.fontFamily = family;
var node = createTag('span');
// Characters that vary significantly among different fonts
node.innerHTML = 'giItT1WQy@!-/#';
// Visible - so we can measure it - but not on the screen
parentNode.style.position = 'absolute';
parentNode.style.left = '-10000px';
parentNode.style.top = '-10000px';
// Large font size makes even subtle changes obvious
parentNode.style.fontSize = '300px';
// Reset any font properties
parentNode.style.fontVariant = 'normal';
parentNode.style.fontStyle = 'normal';
parentNode.style.fontWeight = 'normal';
parentNode.style.letterSpacing = '0';
parentNode.appendChild(node);
document.body.appendChild(parentNode);
// Remember width with no applied web font
var width = node.offsetWidth;
node.style.fontFamily = font + ', '+family;
return {node:node, w:width, parent:parentNode};
}
function checkLoadedFonts() {
var i, len = this.fonts.length;
var node, w;
var loadedCount = len;
for(i=0;i<len; i+= 1){
if(this.fonts[i].loaded){
loadedCount -= 1;
continue;
}
if(this.fonts[i].fOrigin === 'n' || this.fonts[i].origin === 0){
this.fonts[i].loaded = true;
} else{
node = this.fonts[i].monoCase.node;
w = this.fonts[i].monoCase.w;
if(node.offsetWidth !== w){
loadedCount -= 1;
this.fonts[i].loaded = true;
}else{
node = this.fonts[i].sansCase.node;
w = this.fonts[i].sansCase.w;
if(node.offsetWidth !== w){
loadedCount -= 1;
this.fonts[i].loaded = true;
}
}
if(this.fonts[i].loaded){
this.fonts[i].sansCase.parent.parentNode.removeChild(this.fonts[i].sansCase.parent);
this.fonts[i].monoCase.parent.parentNode.removeChild(this.fonts[i].monoCase.parent);
}
}
}
if(loadedCount !== 0 && Date.now() - this.initTime < maxWaitingTime){
setTimeout(this.checkLoadedFonts.bind(this),20);
}else{
setTimeout(function(){this.isLoaded = true;}.bind(this),0);
}
}
function createHelper(def, fontData){
var tHelper = createNS('text');
tHelper.style.fontSize = '100px';
//tHelper.style.fontFamily = fontData.fFamily;
tHelper.setAttribute('font-family', fontData.fFamily);
tHelper.setAttribute('font-style', fontData.fStyle);
tHelper.setAttribute('font-weight', fontData.fWeight);
tHelper.textContent = '1';
if(fontData.fClass){
tHelper.style.fontFamily = 'inherit';
tHelper.setAttribute('class', fontData.fClass);
} else {
tHelper.style.fontFamily = fontData.fFamily;
}
def.appendChild(tHelper);
var tCanvasHelper = createTag('canvas').getContext('2d');
tCanvasHelper.font = fontData.fWeight + ' ' + fontData.fStyle + ' 100px '+ fontData.fFamily;
//tCanvasHelper.font = ' 100px '+ fontData.fFamily;
return tHelper;
}
function addFonts(fontData, defs){
if(!fontData){
this.isLoaded = true;
return;
}
if(this.chars){
this.isLoaded = true;
this.fonts = fontData.list;
return;
}
var fontArr = fontData.list;
var i, len = fontArr.length;
var _pendingFonts = len;
for(i=0; i<len; i+= 1){
var shouldLoadFont = true;
var loadedSelector;
var j;
fontArr[i].loaded = false;
fontArr[i].monoCase = setUpNode(fontArr[i].fFamily,'monospace');
fontArr[i].sansCase = setUpNode(fontArr[i].fFamily,'sans-serif');
if(!fontArr[i].fPath) {
fontArr[i].loaded = true;
_pendingFonts -= 1;
}else if(fontArr[i].fOrigin === 'p' || fontArr[i].origin === 3){
loadedSelector = document.querySelectorAll('style[f-forigin="p"][f-family="'+ fontArr[i].fFamily +'"], style[f-origin="3"][f-family="'+ fontArr[i].fFamily +'"]');
if (loadedSelector.length > 0) {
shouldLoadFont = false;
}
if (shouldLoadFont) {
var s = createTag('style');
s.setAttribute('f-forigin', fontArr[i].fOrigin);
s.setAttribute('f-origin', fontArr[i].origin);
s.setAttribute('f-family', fontArr[i].fFamily);
s.type = "text/css";
s.innerHTML = "@font-face {" + "font-family: "+fontArr[i].fFamily+"; font-style: normal; src: url('"+fontArr[i].fPath+"');}";
defs.appendChild(s);
}
} else if(fontArr[i].fOrigin === 'g' || fontArr[i].origin === 1){
loadedSelector = document.querySelectorAll('link[f-forigin="g"], link[f-origin="1"]');
for (j = 0; j < loadedSelector.length; j++) {
if (loadedSelector[j].href.indexOf(fontArr[i].fPath) !== -1) {
// Font is already loaded
shouldLoadFont = false;
}
}
if (shouldLoadFont) {
var l = createTag('link');
l.setAttribute('f-forigin', fontArr[i].fOrigin);
l.setAttribute('f-origin', fontArr[i].origin);
l.type = "text/css";
l.rel = "stylesheet";
l.href = fontArr[i].fPath;
document.body.appendChild(l);
}
} else if(fontArr[i].fOrigin === 't' || fontArr[i].origin === 2){
loadedSelector = document.querySelectorAll('script[f-forigin="t"], script[f-origin="2"]');
for (j = 0; j < loadedSelector.length; j++) {
if (fontArr[i].fPath === loadedSelector[j].src) {
// Font is already loaded
shouldLoadFont = false;
}
}
if (shouldLoadFont) {
var sc = createTag('link');
sc.setAttribute('f-forigin', fontArr[i].fOrigin);
sc.setAttribute('f-origin', fontArr[i].origin);
sc.setAttribute('rel','stylesheet');
sc.setAttribute('href',fontArr[i].fPath);
defs.appendChild(sc);
}
}
fontArr[i].helper = createHelper(defs,fontArr[i]);
fontArr[i].cache = {};
this.fonts.push(fontArr[i]);
}
if (_pendingFonts === 0) {
this.isLoaded = true;
} else {
//On some cases even if the font is loaded, it won't load correctly when measuring text on canvas.
//Adding this timeout seems to fix it
setTimeout(this.checkLoadedFonts.bind(this), 100);
}
}
function addChars(chars){
if(!chars){
return;
}
if(!this.chars){
this.chars = [];
}
var i, len = chars.length;
var j, jLen = this.chars.length, found;
for(i=0;i<len;i+=1){
j = 0;
found = false;
while(j<jLen){
if(this.chars[j].style === chars[i].style && this.chars[j].fFamily === chars[i].fFamily && this.chars[j].ch === chars[i].ch){
found = true;
}
j += 1;
}
if(!found){
this.chars.push(chars[i]);
jLen += 1;
}
}
}
function getCharData(char, style, font){
var i = 0, len = this.chars.length;
while( i < len) {
if(this.chars[i].ch === char && this.chars[i].style === style && this.chars[i].fFamily === font){
return this.chars[i];
}
i+= 1;
}
if((typeof char === 'string' && char.charCodeAt(0) !== 13 || !char) && console && console.warn) {
console.warn('Missing character from exported characters list: ', char, style, font);
}
return emptyChar;
}
function measureText(char, fontName, size) {
var fontData = this.getFontByName(fontName);
var index = char.charCodeAt(0);
if(!fontData.cache[index + 1]) {
var tHelper = fontData.helper;
//Canvas version
//fontData.cache[index] = tHelper.measureText(char).width / 100;
//SVG version
//console.log(tHelper.getBBox().width)
if (char === ' ') {
tHelper.textContent = '|' + char + '|';
var doubleSize = tHelper.getComputedTextLength();
tHelper.textContent = '||';
var singleSize = tHelper.getComputedTextLength();
fontData.cache[index + 1] = (doubleSize - singleSize)/100;
} else {
tHelper.textContent = char;
fontData.cache[index + 1] = (tHelper.getComputedTextLength())/100;
}
}
return fontData.cache[index + 1] * size;
}
function getFontByName(name){
var i = 0, len = this.fonts.length;
while(i<len){
if(this.fonts[i].fName === name) {
return this.fonts[i];
}
i += 1;
}
return this.fonts[0];
}
function getCombinedCharacterCodes() {
return combinedCharacters;
}
function loaded() {
return this.isLoaded;
}
var Font = function(){
this.fonts = [];
this.chars = null;
this.typekitLoaded = 0;
this.isLoaded = false;
this.initTime = Date.now();
};
//TODO: for now I'm adding these methods to the Class and not the prototype. Think of a better way to implement it.
Font.getCombinedCharacterCodes = getCombinedCharacterCodes;
Font.prototype.addChars = addChars;
Font.prototype.addFonts = addFonts;
Font.prototype.getCharData = getCharData;
Font.prototype.getFontByName = getFontByName;
Font.prototype.measureText = measureText;
Font.prototype.checkLoadedFonts = checkLoadedFonts;
Font.prototype.loaded = loaded;
return Font;
}());