1

I have a CSS file that uses @font-face to refer to a font resource on a networked server. Can I write client-side JavaScript that puts an image in place of styled text if a CSS font-family request fails because the server is unavailable?

Borodin
  • 126,100
  • 9
  • 70
  • 144
  • Check this SO answer: http://stackoverflow.com/a/4282218/910868 If the "ping" fails, serve image. – mreq Feb 28 '12 at 16:26
  • Thanks Petr. That solution uses Ajax, which is a lot to include just to solve this problem. But I will dig around the code and see if I can untie the dependency. – Borodin Feb 28 '12 at 16:46

3 Answers3

1

I wrote this as an answer to a question that I didn't read properly so I thought I would post it here instead:

As @James_Hill has said, you could try using this javascript font detector library. The snippet below has the full code of the library (because I couldn't load it externally) but take a look at the end of the snippet and you will see a very simple loop to check which fonts are available on the computer.

var detective = new Detector();
var fonts_to_test = ["Non-existent Font", "Quaver Serif", "Times New Roman", "Serif"]
fonts_to_test.forEach((font_name) => {
  console.log(font_name, ":", detective.detect(font_name))
})

If fonts_to_test is the same as your font-family setting (with the same order). You could reduce the array and find the used font:

var font_used = fonts_to_test.reduce((result, font_name) => !result && detective.detect(font_name) ? font_name : result, false)

/**
 * JavaScript code to detect available availability of a
 * particular font in a browser using JavaScript and CSS.
 *
 * Author : Lalit Patel
 * Website: http://www.lalit.org/lab/javascript-css-font-detect/
 * License: Apache Software License 2.0
 *          http://www.apache.org/licenses/LICENSE-2.0
 * Version: 0.15 (21 Sep 2009)
 *          Changed comparision font to default from sans-default-default,
 *          as in FF3.0 font of child element didn't fallback
 *          to parent element if the font is missing.
 * Version: 0.2 (04 Mar 2012)
 *          Comparing font against all the 3 generic font families ie,
 *          'monospace', 'sans-serif' and 'sans'. If it doesn't match all 3
 *          then that font is 100% not available in the system
 * Version: 0.3 (24 Mar 2012)
 *          Replaced sans with serif in the list of baseFonts
 */

/**
 * Usage: d = new Detector();
 *        d.detect('font name');
 */
var Detector = function() {
    // a font will be compared against all the three default fonts.
    // and if it doesn't match all 3 then that font is not available.
    var baseFonts = ['monospace', 'sans-serif', 'serif'];

    //we use m or w because these two characters take up the maximum width.
    // And we use a LLi so that the same matching fonts can get separated
    var testString = "mmmmmmmmmmlli";

    //we test using 72px font size, we may use any size. I guess larger the better.
    var testSize = '72px';

    var h = document.getElementsByTagName("body")[0];

    // create a SPAN in the document to get the width of the text we use to test
    var s = document.createElement("span");
    s.style.fontSize = testSize;
    s.innerHTML = testString;
    var defaultWidth = {};
    var defaultHeight = {};
    for (var index in baseFonts) {
        //get the default width for the three base fonts
        s.style.fontFamily = baseFonts[index];
        h.appendChild(s);
        defaultWidth[baseFonts[index]] = s.offsetWidth; //width for the default font
        defaultHeight[baseFonts[index]] = s.offsetHeight; //height for the defualt font
        h.removeChild(s);
    }

    function detect(font) {
        var detected = false;
        for (var index in baseFonts) {
            s.style.fontFamily = font + ',' + baseFonts[index]; // name of the font along with the base font for fallback.
            h.appendChild(s);
            var matched = (s.offsetWidth != defaultWidth[baseFonts[index]] || s.offsetHeight != defaultHeight[baseFonts[index]]);
            h.removeChild(s);
            detected = detected || matched;
        }
        return detected;
    }

    this.detect = detect;
};

var detective = new Detector();
var fonts_to_test = ["Non-existent Font", "Quaver Serif", "Times New Roman", "Serif"]
//test each font for availability
fonts_to_test.forEach((font_name) => {
  console.log(font_name, ":", detective.detect(font_name))
})

//reduce example
var font_used = fonts_to_test.reduce((result, font_name) => !result && detective.detect(font_name) ? font_name : result, false)
console.log("Font Used:", font_used)
jcuenod
  • 55,835
  • 14
  • 65
  • 102
1

I'm partial to this font detection helper: http://www.lalit.org/lab/javascript-css-font-detect/

After including the .js file, here is the example usage:

// Usage
var detective = new Detector();
alert(detective.test('font name'));
James Hill
  • 60,353
  • 20
  • 145
  • 161
1

I would recommend against this approach altogether. How absolutely mission critical is it that a piece of text appears in a given font? I'm betting your users would much rather have the regular text.

There are a couple issues - number one, accessibility. You'll have to be super careful to make sure proper alt tags get inserted with these images. What happens if the JS chokes, removing the text, but not loading the image properly?

Two - maintainability - how are these substitute images going to be generated? If you're making a three page site that will never, ever, ever, ever change, this could be feasible. Other than that, it'll be a maintenance nightmare.

The use of @font-face is a welcome addition to the toolkit of web designers, but it's still a 'nice to have' visual flourish - it's only slightly more important than border-radius.

I love having it, I love to use it, but I think if it doesn't happen to fire, you're far better off using a similar 'fallback' web-safe font. Will your visitor see Georgia instead of Adobe Caslon Pro? Sure. But I think they'll be far better off than having an image shoved in place.

chipcullen
  • 6,840
  • 1
  • 21
  • 17
  • This is for the *Cooper* font, which I think is irreplaceable, and is a 60pt header that used to be an image. In the past I had to render a new image every time the text changed, and I hoped I could make things a lot more simple while allowing for fallback as long as I had to. – Borodin Feb 28 '12 at 16:40
  • Ahhh - Cooper - yup, that is a very particular font. I think Georgia Bold is as close as you'd get with "web safe fonts". I don't know if this is for a client or what, but to me, the advantages of using real HTML text far outweigh the drawbacks of how the text would appear if the web font doesn't load. I mean, we're talking about a relatively small edge case, or are you experiencing issues getting @font-face to work? – chipcullen Feb 28 '12 at 16:52
  • @chipcullen Georgia bold is a good call I guess but it's scarce outside Windows platforms. Clarendon would have been an option but is no more likely to be a native font than Cooper. I think I'm stuck with automating a text image. – Borodin Feb 28 '12 at 17:25
  • Georgia has good penetration on the Mac side as well, and iOS. I'm assuming Android is good with it. Are there other platforms you're worried about? This is a really great resource, a list of fonts and how certain you can be about their presence on Macs or PC's: http://www.speaking-in-styles.com/web-typography/Web-Safe-Fonts/. – chipcullen Feb 28 '12 at 17:39