I'm trying to write some javascript that detects if a unicode character has been rendered in the browser or not.
Note — this question or similar questions have been asked before, but none of them have had an answer that actually answers the question. Either, the chosen answer doesn't work for every unicode character, or the chosen answer solves the questioner's problem in a different way. e.g. install a font that has the unicode character you want to use.
There are a couple of approaches to this problem I have come accross. Neither of them i think cover every possible situation, or are particularly elegant solutions.
HTMLCanvas
Render the unicode character in a canvas element and compare it to all existing fallback glyphs e.g. � or ▯ etc...
if it matches any of those characters then you know that it hasn't rendered correctly.
The problem with this solution is it requires knowing what all those fallback characters are, which is basically not possible. It also would show a false negative result if you test a character like "▯"
Use a fallback font with blank, zero-width characters for every code point
Render the unicode character with the following css, if the character has no width, then you know it has failed to render correctly.
#font-face {
font-family: 'Blank';
src:url('~assets/fonts/blankfont.ttf') format('truetype');
}
.testfont {
font-family:
-apple-system,
system-ui,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
"Helvetica Neue",
Arial,
sans-serif,
serif...
... every possible font that it could render in by default...
"Blank"
}
The main problem with this approach is knowing all the possible fonts the character could render in. I've given this approach a try. I'd render the same unicode character twice, once with the above css, and again without the css.
I found that the unicode character would sometimes render with correctly without the css and render as blank with the css. This suggests that there are more fallback fonts on my system that aren't covered by serif, sans-serif and other generic css font families.
So, you would have to add all the possible fonts before falling back to the "blank" font.
Both these approaches might work most of the time with a lot of tinkering. But I'm looking for an approach that will work all of the time.
Any help would be much appreciated. If I find a solution I'll make sure to post it here.
The only way to do this is to have all the fonts in the font stack available for inspection, and then testing each font in that stack to see whether it supports the character(s) you care about.
The canvas won't work because (a) there is no universal "not supported" glyph, each font gets to define its own, so a list of "not supported" glyphs is something you'd have to build for each set of fonts yo use. But then also (b) the canvas is an imaging bitmap, and so trying to match pixels to a known picture becomes horrendously complex due to point sizes, subpixel offsets, and text rendering optimizations like ClearType.
Also, wanting "missing glyphs" to take up zero space is generally a really bad idea: instead, use a font that very clearly shows that you have missing glyphs, and fix those before pushing updates with missing characters live. This is one of the things you write integration tests for.
The only reliable way would be to only use webfonts, not built-ins (so no "Times New Roman" or "Hiragino") and no "serif" or "sans" as final generic category keyword, but something like Adobe Blank, and then for each of those fonts check whether at the OpenType cmap level those fonts support the character(s) you're trying to render.
(and you could do that server-side, or you could do that in-browser with something like fontkit or font.js)
Related
I'm trying to get an id of a font-awesome icon. It is located in ::before style. When i use
window.getComputedStyle(document.querySelector("[id='5']"), '::before').getPropertyValue('content')
to get it, instead of "\f458", "\"\"" is returned.
I assume that JavaScript is trying to convert the code into a char but fails. Is there any way to prevent this?
The decoding works, the problem is the font. If there's no match for this character in a font you use, it'll be mangled or in the form of the unicode value in a box.
Since it's in the private area, depending on the font it might be resolved into a glyph or be mangled or be just empty/space.
Checking in the Font Awesome Cheatsheet it looks like a an icon for quidditch.
Perhaps there's a text to image/svg map somewhere on the internet (and if not, then just copy-paste localy and create one) which you might use if the font itself isn't good or you are decoding in a problematic environment (can't install fonts, etc).
Google Chrome displays the rendered font in the DevTools.
For example, given:
font-family: Montserrat, Helvetica, sans-serif;
and the Montserrat font is missing/disabled, Chrome tells us that Helvetica is being rendered:
Is there a way to get the rendered font in JavaScript? (even if it just works in Chrome)
Notes:
This solution suggests getComputedStyle(...).fontFamily, but it returns the CSS declaration "Montserrat, Helvetica, sans-serif", not the actual rendered font.
This solution uses puppeteer, but I couldn't figure out how to achieve the same purely in DevTools (without puppeteer).
It is still not possible to access this information from Web-APIs.
There is an ongoing discussion in the Houdini group about including a font-metrics API, that is supposed to include something like that, but it's still not even proposed as a spec draft and there will be a lot of burden on the road.
What font(s) are being used? This is complicated because multiple fonts can be used per paragraph, per line, per word, and even per glyph. The fonts should be exposed in the form of handles with complete font information, and (for web fonts) a handle to the raw font data. dbaron & eae are going to own this area and propose an API.
Indeed, one could have one font for the glyph ̂ (U+0302), and another one for the glyph a (U+0061) which would make the combined glyph â would actually use two different fonts.
Current discussions seem to point to a Font interface available from document.measureElement and document.measureText methods. This interface would expose two properties: a DOMString name, and a number glyphsRendered.
However, once again these are still discussion and still not yet proposed as drafts, a lot of discussion is still to be made and I wouldn't hold my breath waiting for it to be implemented anywhere any time soon.
Now, there are hacks, like numerous other Q/A already told don't stick to the accepted answer there, implying looking at the size of the rendering for the simplest, and looking at the rendered pixels for the more advanced ones, but being hacks, they won't work in every cases.
For instance, I could have a custom font on my system that would render only some characters borrowed from a well-known font, no such hack would be able to tell if the browser did fallback to that font or the actual well-known one.
The only way to know for sure is to keep the control and use web-fonts.
Since no one had suggested it yet, there would be one more way to find out which font is rendered.
The snippet below gets the font-family CSS definition in use for the element (or of course you can hardcode the font-family names if you want) and checks in order, whether the font family is loaded and returns the name of the first loaded font family.
Since the font-family CSS property specifies a prioritized list of one or more font family names, the first available font is very likely also the rendered font.
Snippet uses the CSS Font Loading API, which is well supported (but not in IE of course)
https://developer.mozilla.org/en-US/docs/Web/API/CSS_Font_Loading_API
For example, let's imagine that the CSS would be:
.body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI Adjusted", "Segoe UI", "Liberation Sans", sans-serif;
}
Snippet
const getRenderedFontFamilyName = ( element ) => {
// Font families set in CSS for the element
const fontFamilies = window.getComputedStyle( element, null ).getPropertyValue( "font-family" );
// const hardcodedFamilies = '-apple-system, BlinkMacSystemFont, "Segoe UI Adjusted", "Segoe UI", "Liberation Sans", sans-serif';
// Remove the " sign from names (font families with spaces in their names) and split names to the array
const fontFamiliesArr = fontFamilies.replaceAll('"', "").split(", ");
// Find the first loaded font from the array
return fontFamiliesArr.find( e => document.fonts.check( `12px ${e}`) );
}
Example how to get rendered font-family of StackOverflow body:
getRenderedFontFamilyName(document.querySelector('body'));
I'm working on a font detection library that needs to be very, very small, suitable for including inline on every page of a website. I've already gotten it pretty small (417 bytes gzipped). You can check it out at the Github repo.
Upon further thinking, I can reduce the size of the library significantly again (around another 10%) if I can make a few basic assumptions:
It is impossible to define a working font file (for browsers, anyway) without a space character/definition.
OR, if such a space-omitting font could be created, that all modern browsers and IE9+ will choose the fallback font's space character when a space character is needed. That is, there is otherwise nothing special about the space character in terms of font-fallback.
And, tangentially, all modern browsers and IE9+ will have a different width for the space character in serif and monospace generic fonts provided by the browser (probable, but I will need to test further).
I just attempted to explicitly define a font without a space character using FontSquirrel's font generator. This was done by explicitly omitting the space from the selected Subset and disabling the 'Fix Missing Glyphs' for space. FontSquirrel still generated a font with a space character with a width differing from both serif and monospace.
I understand that some languages do not have a space character in the traditional sense, but due to the nature of font file formats and definitions, I do not think that fonts tailored to such a language could or would omit a space character.
If these assumptions all hold, the library could remove the need to support custom text checking and reduce the number of tests from 3 to 2, also speeding up the library and reducing its memory usage. The new size would be around 380 bytes or less when gzipped.
So how about it, font experts? Is it possible to define a valid font without a space character definition? If there is such a font, can you provide an example?
It appears that while it is possible, it is virtually unheard-of to create a font without a space character. Even icon font services appear to include it. Other than a special case for a font detection library, there are no known fonts that omit the space character.
This research led to an improvement to my onfontready library. Details here. Thanks to Mike 'Pomax' Kamermans for his contributions.
How can I check if a special character is available in the user's computer?
For example: ♥ ♦ ♣ ♠ ♪ ♫ ¶
If the user's browser doesn't support one of them, then a rectangle () will appear instead of the symbol.
I’m afraid there’s no way to test it, and there’s the added complexity that even if a character is available, browsers (especially IE) may fail to render it.
On the other hand, the information would not be particularly useful, except perhaps in the sense that you could dynamically change the character to an image if it can’t be rendered as a character.
A better approach to having your characters rendered properly is to write your style sheets so that they select suitable fonts. This also addresses the problem that a character might be displayed using a font that does not suit the overall design, such as the basic copytext font.
For example, if you need the characters ♥ ♦ ♣ ♠ ♪ ♫ ¶, select a font that contains them and all the other characters you need. This would probably boil down just to
body { font-family: Arial, sans-serif; }
a different solution can be using google's webfonts
http://www.google.com/webfonts
if you see a character, every other computer [with a recent browser] will see it
Suppose I have the following CSS rule in my page:
body {
font-family: Calibri, Trebuchet MS, Helvetica, sans-serif;
}
How could I detect which one of the defined fonts were used in the user's browser?
For people wondering why I want to do this is because the font I'm detecting contains glyphs that are not available in other fonts. If the user does not have the font, then I want it to display a link asking the user to download that font (so they can use my web application with the correct font).
Currently, I am displaying the download font link for all users. I want to only display this for people who do not have the correct font installed.
I've seen it done in a kind of iffy, but pretty reliable way. Basically, an element is set to use a specific font and a string is set to that element. If the font set for the element does not exist, it takes the font of the parent element. So, what they do is measure the width of the rendered string. If it matches what they expected for the desired font as opposed to the derived font, it's present. This won't work for monospaced fonts.
Here's where it came from:
Javascript/CSS Font Detector (ajaxian.com; 12 Mar 2007)
I wrote a simple JavaScript tool that you can use it to check if a font is installed or not.
It uses simple technique and should be correct most of the time.
jFont Checker on github
#pat Actually, Safari does not give the font used, Safari instead always returns the first font in the stack regardless of whether it is installed, at least in my experience.
font-family: "my fake font", helvetica, san-serif;
Assuming Helvetica is the one installed/used, you'll get:
"my fake font" in Safari (and I believe other webkit browsers).
"my fake font, helvetica, san-serif" in Gecko browsers and IE.
"helvetica" in Opera 9, though I read that they are changing this in Opera 10 to match
Gecko.
I took a pass at this problem and created Font Unstack, which tests each font in a stack and returns the first installed one only. It uses the trick that #MojoFilter mentions, but only returns the first one if multiple are installed. Though it does suffer from the weakness that #tlrobinson mentions (Windows will substitute Arial for Helvetica silently and report that Helvetica is installed), it otherwise works well.
A technique that works is to look at the computed style of the element. This is supported in Opera and Firefox (and I recon in safari, but haven't tested). IE (7 at least), provides a method to get a style, but it seems to be whatever was in the stylesheet, not the computed style. More details on quirksmode: Get Styles
Here's a simple function to grab the font used in an element:
/**
* Get the font used for a given element
* #argument {HTMLElement} the element to check font for
* #returns {string} The name of the used font or null if font could not be detected
*/
function getFontForElement(ele) {
if (ele.currentStyle) { // sort of, but not really, works in IE
return ele.currentStyle["fontFamily"];
} else if (document.defaultView) { // works in Opera and FF
return document.defaultView.getComputedStyle(ele,null).getPropertyValue("font-family");
} else {
return null;
}
}
If the CSS rule for this was:
#fonttester {
font-family: sans-serif, arial, helvetica;
}
Then it should return helvetica if that is installed, if not, arial, and lastly, the name of the system default sans-serif font. Note that the ordering of fonts in your CSS declaration is significant.
An interesting hack you could also try is to create lots of hidden elements with lots of different fonts to try to detect which fonts are installed on a machine. I'm sure someone could make a nifty font statistics gathering page with this technique.
A simplified form is:
function getFont() {
return document.getElementById('header').style.font;
}
If you need something more complete, check this out.
There is a simple solution - just use element.style.font:
function getUserBrowsersFont() {
var browserHeader = document.getElementById('header');
return browserHeader.style.font;
}
This function will exactly do what you want. On execution It will return the font type of the user/browser. Hope this will help.
Another solution would be to install the font automatically via #font-face which might negate the need for detection.
#font-face {
font-family: "Calibri";
src: url("http://www.yourwebsite.com/fonts/Calibri.eot");
src: local("Calibri"), url("http://www.yourwebsite.com/fonts/Calibri.ttf") format("truetype");
}
Of course it wouldn't solve any copyright issues, however you could always use a freeware font or even make your own font. You will need both .eot & .ttf files to work best.
Calibri is a font owned by Microsoft, and shouldn't be distributed for free. Also, requiring a user to download a specific font isn't very user-friendly.
I would suggest purchasing a license for the font and embedding it into your application.
I am using Fount. You just have to drag the Fount button to your bookmarks bar, click on it and then click on a specific text on the website. It will then show the font of that text.
https://fount.artequalswork.com/
You can use this website :
http://website-font-analyzer.com/
It does exactly what you want...
You can put Adobe Blank in the font-family after the font you want to see, and then any glyphs not in that font won't be rendered.
e.g.:
font-family: Arial, 'Adobe Blank';
As far as I'm aware there is no JS method to tell which glyphs in an element are being rendered by which font in the font stack for that element.
This is complicated by the fact that browsers have user settings for serif/sans-serif/monospace fonts and they also have their own hard-coded fall-back fonts that they will use if a glyph is not found in any of the fonts in a font stack. So browser may render some glyphs in a font that is not in the font stack or the user's browser font setting. Chrome Dev Tools will show you each rendered font for the glyphs in the selected element. So on your machine you can see what it's doing, but there's no way to tell what's happening on a user's machine.
It's also possible the user's system may play a part in this as e.g. Window does Font Substitution at the glyph level.
so...
For the glyphs you are interested in, you have no way of knowing whether they will be rendered by the user's browser/system fallback, even if they don't have the font you specify.
If you want to test it in JS you could render individual glyphs with a font-family including Adobe Blank and measure their width to see if it is zero, BUT you'd have to iterate thorough each glyph and each font you wanted to test, but although you can know the fonts in an elements font stack there is no way of knowing what fonts the user's browser is configured to use so for at least some of your users the list of fonts you iterate through will be incomplete. (It is also not future proof if new fonts come out and start getting used.)