Detecting individual Unicode character support with JavaScript

Viewed 5658

Is it possible to detect if the client supports a particular Unicode character or if it will be rendered as a missing glyph box?

Important: Support in as many browsers as possible

Not important: Efficiency, speed, or elegance

The only method I can think of trying is using a canvas, so I figured I'd ask before I start going down that road.

This is not intended for use on a public web site; I am just trying to compile a list of characters supported by each browser.

5 Answers

This is more of a wild idea than a real answer:

If you could find a character which you knew would always render as a missing glyph box, you could use the same technique as this javascript font detector--render the character and the missing glyph box offscreen and compare their widths. If they're different, then you know the character is not rendering as a missing glyph box. Of course, this won't work at all for fixed-width fonts, and it could have a lot of fixed negatives for other fonts where a lot of the characters are the same width.

You can use a canvas to check whether or not the character is rendered identically to a character you know is not supported. U+FFFF is a good choice for a character to compare to, since it's guaranteed not to be a valid unicode character.

So you create one canvas where you render a U+FFFF character, and another canvas where you render the character you want to test. You then compare the two canvases by comparing their data URLs by using the toDataURL method. If the canvases are identical, the test character was rendered identically to the unsupported U+FFFF character, meaning it's not supported, and if the canvases are not identical, the test character was not rendered in the same way as unsupported characters so it is supported.

The following code does that:

//The first argument is the character you want to test, and the second argument is the font you want to test it in.
//If the second argument is left out, it defaults to the font of the <body> element.
//The third argument isn't used under normal circumstances, it's just used internally to avoid infinite recursion.
function characterIsSupported(character, font = getComputedStyle(document.body).fontFamily, recursion = false){
    //Create the canvases
    let testCanvas = document.createElement("canvas");
    let referenceCanvas = document.createElement("canvas");
    testCanvas.width = referenceCanvas.width = testCanvas.height = referenceCanvas.height = 150;

    //Render the characters
    let testContext = testCanvas.getContext("2d");
    let referenceContext = referenceCanvas.getContext("2d");
    testContext.font = referenceContext.font = "100px " + font;
    testContext.fillStyle = referenceContext.fillStyle = "black";
    testContext.fillText(character, 0, 100);
    referenceContext.fillText('\uffff', 0, 100);
    
    //Firefox renders unsupported characters by placing their character code inside the rectangle making each unsupported character look different.
    //As a workaround, in Firefox, we hide the inside of the character by placing a black rectangle on top of it.
    //The rectangle we use to hide the inside has an offset of 10px so it can still see part of the character, reducing the risk of false positives.
    //We check for Firefox and browers that behave similarly by checking if U+FFFE is supported, since U+FFFE is, just like U+FFFF, guaranteed not to be supported.
    if(!recursion && characterIsSupported('\ufffe', font, true)){
        testContext.fillStyle = referenceContext.fillStyle = "black";
        testContext.fillRect(10, 10, 80, 80);
        referenceContext.fillRect(10, 10, 80, 80);
    }

    //Check if the canvases are identical
    return testCanvas.toDataURL() != referenceCanvas.toDataURL();
}

//Examples
console.log("a is supported: " + characterIsSupported('a'));    //Returns true, 'a' should be supported in all browsers
console.log("\ufffe is supported: " + characterIsSupported('\ufffe'));    //Returns false, U+FFFE is guaranteed to be unsupported just like U+FFFF
console.log("\u2b61 is supported: " + characterIsSupported('\u2b61'));    //Results vary depending on the browser. At the time of writing this, this returns true in Chrome on Windows and false in Safari on iOS.
console.log("\uf8ff is supported: " + characterIsSupported('\uf8ff'));    //The unicode Apple logo is only supported on Apple devices, so this should return true on Apple devices and false on non-Apple devices.

You can always evaluate each character using the charCodeAt() method. This will return the unicode character value. Depending on what you are doing, you can restrict the range of which you want to accept as "Valid" characters... If you copy the character that's in the "box", you can use a character translator on the web to see what the corresponding unicode value is.

Here's one that I googled and found: enter link description here

If you want to maximize browser support, you probably don't want to rely on javascript for anything. Many mobile browsers don't even support it.

If the browser does not support a char set, what is the fall back? Displaying the content in another language? Perhaps links one the site that switch languages on demand would be more robust.

Related