Consider the following example (note that db.onlinewebfonts.com is very slow for me, but if I wait out a half a minute to a minute or so, eventually font file gets downloaded, and text gets styled):
<!DOCTYPE html>
<html>
<head>
<title>test</title>
<style>
@font-face {
font-family: 'Impact';
src: url('https://db.onlinewebfonts.com/t/4cbf858ccd9ffd3923bed6843a474080.ttf') format('woff'); /*https://www.onlinewebfonts.com/download/4cbf858ccd9ffd3923bed6843a474080/*/
font-weight: bold;
font-style: normal;
}
.testfont {
font-family: 'Impact', sans-serif;
font-weight: bold;
}
</style>
</head>
<body>
<div class="testfont">Testing the font</div>
</body>
<script>
for(let idss=0; idss<document.styleSheets.length; idss++) {
let dss = document.styleSheets[idss];
for(let idcss=0; idcss<dss.cssRules.length; idcss++) {
let cssrule = dss.cssRules[idcss];
let csstypename = cssrule.constructor.name; // https://developer.mozilla.org/en-US/docs/Web/API/CSSRule/type -> .type is deprecated, use .constructor.name
if (csstypename == "CSSFontFaceRule") {
console.log("idss", idss, "idcss", idcss, "src", cssrule.style.getPropertyValue("src") );
}
}
}
console.log("fonts.size", document.fonts.size );
console.log("fonts.keys", document.fonts.keys());
console.log("fonts.values", document.fonts.values());
console.log("fonts.keys().next().value.family", document.fonts.keys().next().value.family); // .value is FontFace
console.log("fonts.check Impact", document.fonts.check('bold 12px Impact'));
console.log("fonts.check whatever", document.fonts.check('bold 12px Whatever'));
</script>
</html>
Ultimately, I would like to obtain in JavaScript, the base64 string of the font file that was loaded with the @font-face CSS at-rule. So, the example simply tries to style an example text div with Impact web font; and then the JavaScript code tries to iterate some document properties.
When I run the above example in Firefox, I get an output in JavaScript Console:
idss 0 idcss 0 src url("https://db.onlinewebfonts.com/t/4cbf858ccd9ffd3923bed6843a474080.ttf") format("woff") test.html:30:15
fonts.size 1 test.html:35:9
fonts.keys FontFaceSetIterator { } test.html:36:9
fonts.values FontFaceSetIterator { } test.html:37:9
fonts.keys().next().value "Impact" test.html:37:9
fonts.check Impact false test.html:38:9
fonts.check whatever true test.html:39:9
So, I can get to the CSS declaration that imports the font; unfortunately I cannot get to the actual font data - rather, I can get to the FontFace via document.fonts.keys().next().value - but I do not see actual font file data there.
(btw, how do you iterate document.fonts.keys()? When I tried for(let key of document.fonts.keys()) {console.log("key", key)}, I got "TypeError: document.fonts.keys() is not iterable"??)
One problem is that, in Firefox https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/check :
Nonexistent fonts
If we specify a font that is not in the FontFaceSet and is not a system font, check() returns true, because in this situation we will not rely on any fonts from the set: ...
Note: In this situation Chrome incorrectly returns false. This can make fingerprinting easier, because an attacker can easily test which system fonts the browser has.
So, I guess I was lucky that db.onlinewebfonts.com is slow, so I got "fonts.check Impact false" at start - otherwise, if it is loaded, it returns true, and I cannot tell the difference from accessible to non-existing font.
Anyways, let's say I detect document.fonts.check('bold 12px Impact') to be false at start, and wait it out for it to become true. Can I then somehow retrieve the font file contents from document.fonts (at least in new Firefox and Chrome)?
(If it is impossible, then I guess I have to drop the @font-face CSS statements, and go instead with direct JavaScript loading of .ttf etc files, maybe using an external JS library - but I'd much rather keep the @font-face, then read from wherever the requested font data ended up in the browser using JavaScript, if possible).