From 111378633062024411efc2cacd712b98092142aa Mon Sep 17 00:00:00 2001 From: Santhosh Thottingal Date: Fri, 17 Jan 2014 16:36:25 +0530 Subject: [PATCH] Detect tofu before applying any default fonts To detect whether the client can render a given string, use a tofu detection algorithm. If the user has an explicit font preference, do not override it. If the user has no preference and there is a default font for a language, see if that language can be rendered at the user's browser using tofu detection. If tofu is detected, apply the font. If the element has 'autonym' class, apply Autonym font only when that language has tofu. Change-Id: Ib9ee9497e6bcfa7eb86f7d264e0980d8880d6b70 --- Resources.php | 2 +- UniversalLanguageSelector.php | 2 +- lib/jquery.webfonts.js | 12 ++-- resources/css/ext.uls.webfonts.css | 4 -- resources/js/ext.uls.webfonts.js | 111 ++++++++++++++++++++++++++--- 5 files changed, 110 insertions(+), 21 deletions(-) delete mode 100644 resources/css/ext.uls.webfonts.css diff --git a/Resources.php b/Resources.php index 46427214..2b5b70bc 100644 --- a/Resources.php +++ b/Resources.php @@ -143,10 +143,10 @@ $wgResourceModules['ext.uls.preferences'] = array( $wgResourceModules['ext.uls.webfonts'] = array( 'scripts' => 'resources/js/ext.uls.webfonts.js', - 'styles' => 'resources/css/ext.uls.webfonts.css', 'dependencies' => array( 'jquery.webfonts', 'ext.uls.init', + 'jquery.uls.data', 'ext.uls.webfonts.repository', 'ext.uls.preferences', ), diff --git a/UniversalLanguageSelector.php b/UniversalLanguageSelector.php index b98d9208..94ce4e27 100644 --- a/UniversalLanguageSelector.php +++ b/UniversalLanguageSelector.php @@ -155,7 +155,7 @@ $GLOBALS['wgULSNoImeSelectors'] = array( '#wpCaptchaWord', '.ve-ce-documentNode' * Autonym * @since 2013.09 */ -$GLOBALS['wgULSNoWebfontsSelectors'] = array( '.autonym' ); +$GLOBALS['wgULSNoWebfontsSelectors'] = array( '#p-lang li.interlanguage-link > a' ); /** * Base path of ULS font repository. diff --git a/lib/jquery.webfonts.js b/lib/jquery.webfonts.js index 63c34f61..e7ce234b 100644 --- a/lib/jquery.webfonts.js +++ b/lib/jquery.webfonts.js @@ -58,13 +58,14 @@ /** * Get the default font family for given language. * @param {String} language Language code. + * @param {array} classes * @return {String} Font family name */ - getFont: function( language ) { + getFont: function( language, classes ) { language = ( language || this.language ).toLowerCase(); if ( this.options.fontSelector ) { - return this.options.fontSelector( this.repository, language ); + return this.options.fontSelector( this.repository, language, classes ); } else { return this.repository.defaultFont( language ); } @@ -199,7 +200,6 @@ // Note: it depends on the browser whether this returns font names // which don't exist. In Chrome it does, while in Opera it doesn't. fontFamilyStyle = $element.css( 'fontFamily' ); - // Note: It is unclear whether this can ever be falsy. Maybe also // browser specific. if ( fontFamilyStyle ) { @@ -220,7 +220,7 @@ // browser settings. return; } else { - fontFamily = webfonts.getFont( element.lang ); + fontFamily = webfonts.getFont( element.lang, element.className.split(/\s+/) ); } if ( !fontFamily ) { @@ -269,8 +269,8 @@ // whether the font is inherited from top element to which plugin applied return this.$element.css( 'fontFamily' ) !== elementFontFamily - // whether the element has generic font family - && ( $.inArray( elementFontFamily, + // whether the element has generic font family + && ( $.inArray( elementFontFamily, ['monospace', 'serif', 'cursive','fantasy', 'sans-serif'] ) < 0 ); }, diff --git a/resources/css/ext.uls.webfonts.css b/resources/css/ext.uls.webfonts.css deleted file mode 100644 index eb348f9a..00000000 --- a/resources/css/ext.uls.webfonts.css +++ /dev/null @@ -1,4 +0,0 @@ -#p-lang li.interlanguage-link, -.autonym { /* provide autonym class for simplifying the Autonym font usage */ - font-family: 'Autonym', sans-serif; -} diff --git a/resources/js/ext.uls.webfonts.js b/resources/js/ext.uls.webfonts.js index f315c173..30a41ec0 100644 --- a/resources/js/ext.uls.webfonts.js +++ b/resources/js/ext.uls.webfonts.js @@ -18,7 +18,11 @@ */ ( function ( $, mw, undefined ) { 'use strict'; - var mediawikiFontRepository, ulsPreferences; + var mediawikiFontRepository, ulsPreferences, + // Text to prepend the sample text. 0D00 is an unassigned unicode point. + tofuSalt = '\u0D00', + // cache languages with tofu. + tofuLanguages = {}; mw.webfonts = mw.webfonts || {}; ulsPreferences = mw.uls.preferences(); @@ -48,19 +52,107 @@ } }; + + /** + * Detect tofu + * + * Create a temporary span in the page with fontsize 72px and font-family + * sans-serif for each letter of the text. + * For each of these spans, calculate the width and height. If they are same + * for all spans, we can understand that each of the letter is rendered using + * same glyph - it must be a tofu. + * + * @param {string} text + * @return {boolean} + */ + function detectTofu( text ) { + var index, + $fixture, + width = {}, + height = {}, + length = Math.min( 4, text.length ), + detected = false; + + text = tofuSalt + text; + $fixture = $( '' ) + .css( { + fontSize: '72px', + fontFamily: 'sans-serif' + } ) + .appendTo( 'body' ); + + for ( index = 0; index < length; index++ ) { + $fixture.text( text[index] ); + width[index] = $fixture.width() || width[index-1]; + height[index] = $fixture.height(); + + if( index > 0 && + ( width[index] !== width[index - 1] || + height[index] !== height[index - 1] ) + ) { + detected = false; + break; + } + } + + $fixture.remove(); + + if ( index === length ) { + detected = true; + } + + return detected; + } + mediawikiFontRepository = $.webfonts.repository; mediawikiFontRepository.base = mw.config.get( 'wgULSFontRepositoryBasePath' ); mw.webfonts.setup = function () { // Initialize webfonts $.fn.webfonts.defaults = $.extend( $.fn.webfonts.defaults, { - fontSelector: function ( repository, language ) { - var font; + /** + * Font selector - depending the language and optionally + * based on the class given choose a font. + * + * @param {Object} repository + * @param {string} language + * @param {array} classes + */ + fontSelector: function ( repository, language, classes ) { + var font, tofu, autonym, defaultFont; + if ( !language ) { + return null; + } + + defaultFont = repository.defaultFont( language ); + + if ( classes && $.inArray( 'autonym', classes ) >= 0 ) { + autonym = true; + } + + // If the user has a font preference, apply it always. font = mw.webfonts.preferences.getFont( language ); + if ( !font || autonym ) { + // Is there any default font for this language? + if ( ( !defaultFont || defaultFont === 'system' ) && !autonym ) { + return font; + } - if ( !font ) { - font = repository.defaultFont( language ); + // There is a default font for this language, + // but check whether the user sees tofu for it. + tofu = tofuLanguages[language] || + detectTofu( $.uls.data.getAutonym( language ) ); + + if ( tofu ) { + mw.log( 'tofu detected for ' + language ); + // Cache the languages with tofu + tofuLanguages[language] = true; + font = autonym ? 'Autonym' : defaultFont; + } else { + // No tofu and no font preference. Use system font. + font = 'system'; + } } if ( font === 'system' ) { @@ -70,15 +162,16 @@ return font; }, + exclude: ( function () { var excludes = $.fn.webfonts.defaults.exclude; if ( mw.user.options.get( 'editfont' ) !== 'default' ) { - // Exclude textboxes from webfonts if user has edit area font option + // Exclude textboxes from webfonts if the user has edit area font option // set using 'Preferences' page - excludes = ( excludes ) - ? excludes + ',textarea' - : 'textarea'; + excludes = ( excludes ) ? + excludes + ',textarea' : + 'textarea'; } return excludes;