/* ** fontface -- Font Preparation for Web usage via CSS @font-face ** Copyright (c) 2010-2015 Ralf S. Engelschall ** Licensed under GPL ** fontface.js: browser workarounds (language: JavaScript/1.5) ** ** Background: ** Opera (even until 11.60, appVersion 9.80) is known ** to be buggy when it comes to the @font-face rules. ** The bug is that is does only use the font-family ** as the unique name and not the combination of ** font-family+font-style+font-weight+font-stretch+font-variant. ** Additionally, the CSS3 font-stretch property is also not ** understood at all. ** ** Problem: ** The last @font-face rule for a particular font-family overrides ** all previous ones and as a result the wrong fonts are used. ** ** Workaround: ** We walk through all @font-face rules and make copies of ** them with a font-family property value including the ** font-weight+font-style+font-stretch+font-variant information. ** Additionally, we activate these generated @font-face rules ** and change the style on all DOM elements to those generated ** @font-face rules. ** ** See Also: ** http://dev.opera.com/articles/view/seven-web-fonts-showcases/ */ (function () { var fixme = function () { /* 1. determine all @font-face CSS rules, remember their font-family attributes (for later substitution) and assemble the substitution CSS rules */ /* iterate over all stylesheets */ var css_rules = ""; var font_families = {}; for (var i = 0; i < document.styleSheets.length; i++) { /* iterate over all stylesheet rules */ var len = document.styleSheets[i].cssRules.length; for (var j = 0; j < len; j++) { /* act on @font-face rules only */ var m; var cssText = document.styleSheets[i].cssRules[j].cssText; if (m = cssText.match(/^@font-face\s+\{(.+)\}/)) { var rules = m[1]; /* extract font related rules */ var font_family = "sans-serif"; var font_style = "normal"; var font_weight = "normal"; var font_stretch = "normal"; var font_variant = "normal"; if (m = rules.match(/font-family:\s*("([^""]+)"|'([^'']+)'|([^\s;}]+))/)) font_family = m[2] || m[3] || m[4]; if (m = rules.match(/font-style:\s*("([^""]+)"|'([^'']+)'|([^\s;}]+))/)) font_style = m[2] || m[3] || m[4]; if (m = rules.match(/font-weight:\s*("([^""]+)"|'([^'']+)'|([^\s;}]+))/)) font_weight = m[2] || m[3] || m[4]; if (m = rules.match(/font-stretch:\s*("([^""]+)"|'([^'']+)'|([^\s;}]+))/)) font_stretch = m[2] || m[3] || m[4]; if (m = rules.match(/font-variant:\s*("([^""]+)"|'([^'']+)'|([^\s;}]+))/)) font_variant = m[2] || m[3] || m[4]; /* special treatment for oblique style */ if (font_style == "oblique") { font_style = "italic"; cssText = cssText.replace(/(font-style:\s*)[^\s;}]+/, "$1" + font_style); } /* extend the font-family with the style-weight-stretch tag and remember the original font-family */ var tag = font_style + "-" + font_weight + "-" + font_stretch + "-" + font_variant; cssText = cssText.replace(/src:\s+url\('.+?\.eot'\);/, ""); cssText = cssText.replace(/src:\s+url\(.+?\.eot\);/, ""); cssText = cssText.replace(/url\('\S+?\.(eot|woff)'\) format\(.+?\),/g, ""); cssText = cssText.replace(/url\(\S+?\.(eot|woff)\) format\(.+?\),/g, ""); cssText = cssText.replace(/url\('\S+?\.svg#.+?'\) format\(.+?\);/, ""); cssText = cssText.replace(/url\(\S+?\.svg#.+?\) format\(.+?\);/, ""); cssText = cssText.replace(/,(\s+font-style:)/, ";$1"); cssText = cssText.replace(/local\('.+?'\),/g, ""); cssText = cssText.replace(/local\(.+?\),/g, ""); cssText = cssText.replace(/(font-family:\s*"[^""]+)(")/, "$1 "+tag+"$2"); cssText = cssText.replace(/(font-family:\s*'[^'']+)(')/, "$1 "+tag+"$2"); css_rules += cssText + "\n"; font_families[font_family] = true; } } } /* 2. activate all generated @font-face CSS rules */ var sheet = document.createElement('style') sheet.innerHTML = css_rules; document.body.appendChild(sheet); /* 3. walk over all elements in the whole DOM tree and substitute font-family rules */ var walker = document.createTreeWalker( document, NodeFilter.SHOW_ELEMENT, { acceptNode: function(node) { return NodeFilter.FILTER_ACCEPT; } }, false ); while (walker.nextNode()) { var node = walker.currentNode; /* determine the computed style of an element */ var style = window.getComputedStyle(node, null); var font_family = style.getPropertyValue("font-family") || "sans-serif"; var font_style = style.getPropertyValue("font-style") || "normal"; var font_weight = style.getPropertyValue("font-weight") || "normal"; var font_stretch = style.getPropertyValue("font-stretch") || "normal"; var font_variant = style.getPropertyValue("font-variant") || "normal"; /* font-weight alias canonicalization */ if (font_weight == "normal") { font_weight = "400"; } else if (font_weight == "bold") { font_weight = "700"; } /* act on font-family styled elements for which a @font-face was seen */ font_family = font_family.replace(/^"(.+)"$/, "$1").replace(/^'(.+)'$/, "$1"); if (typeof font_families[font_family] !== "undefined" && font_families[font_family]) { /* extend the font-family with the style-weight-stretch tag */ font_family += " " + font_style + "-" + font_weight + "-" + font_stretch + "-" + font_variant; /* apply the font-family (but delay it a little bit to ensure that Opera does not stumple over its feet because we are currently walking through the elements) */ (function (el, font_family) { setTimeout(function () { el.style.fontFamily = font_family; }, 20); })(node, font_family); } } }; /* act for Opera only */ if (window.opera && navigator.userAgent.match(/Opera/)) { var m = navigator.userAgent.match(/Version\/(\d+\.\d+)/) var version = (m && m[1] ? parseFloat("" + m[1]) : 0.0); if (version < 12.00) { window.onload = function () { setTimeout(function () { fixme(); }, 1*1000); } } } })();