In the HTML version of my PlayN game, how can I reliably load fonts so text displays on startup? - playn

I'm using v1.3.1 of PlayN. This issue is discussed in the following google groups thread but I'm not sure how to implement the suggestions proposed:
https://groups.google.com/forum/?fromgroups#!topic/playn/kiE2iEYJqM0
Perhaps someone can offer some sample code. Currently I'm following the technique referenced in the HTML link in this answer:
https://stackoverflow.com/a/9116829/1093087
My problem: on the home screen of my game, I display some text using loaded fonts. Works fine in Java version. However, in HTML version, the text doesn't display initially. On the next screen, or if I later return to the home screen, text is properly displayed. So I concluded that it was due to the asynchronous loading of fonts as discussed in the google groups thread.
My remedy was to add a splash screen that displays an image for a few seconds, giving the fonts a chance to load, before redirecting to screen with the text on it. But no matter how long I set the delay, the text is still not displayed.
Here's my HTML file which loads my game and the fonts:
<!DOCTYPE html>
<html>
<head>
<title>mygamePlayn</title>
<!-- fonts -->
<style>
#font-face {
font-family: "DroidSans-Bold";
src: url(mygame/fonts/DroidSans-Bold.ttf);
}
#font-face {
font-family: "UbuntuMono";
src: url(mygame/fonts/UbuntuMono-Bold.ttf);
}
</style>
</head>
<body bgcolor="black">
<script src="mygame/mygame.nocache.js"></script>
</body>
</html>
Here's my core Java code that generates the text that's not initially displaying (but works otherwise):
public static CanvasImage generateTextImage(String text, String fontName,
Integer fontSize, Integer fontColor, Style fontStyle, Integer padding) {
Font font = graphics().createFont(fontName, fontStyle, fontSize);
TextFormat fontFormat = new TextFormat().withFont(font).withTextColor(fontColor);
TextLayout layout = graphics().layoutText(text, fontFormat);
Integer width = (int) layout.width() + padding * 2;
Integer height = (int) layout.height() + padding * 2;
CanvasImage textImage = graphics().createImage(width, height);
textImage.canvas().drawText(layout, padding, padding);
return textImage;
}

I think I've finally figured out a solution to my problem. It required using Google WebFont Loader in the following somewhat roundabout fashion:
1) I saved the fonts -- in this case, DroidSans-Bold, Inconsolata, and UbuntuMono-Bold -- in my PlayN project's resources/fonts directory.
2) In resources/css, I add a fonts.css stylesheet where I add the #font-face definitions for my locally saved fonts. My fonts.css file:
#font-face {
font-family: DroidSans;
src: url('../fonts/DroidSans-Bold.ttf');
}
#font-face {
font-family: Inconsolata;
src: url('../fonts/Inconsolata.ttf');
}
#font-face {
font-family: UbuntuMono;
src: url('../fonts/UbuntuMono-Bold.ttf');
}
#font-face {
font-family: UbuntuMono;
font-weight: bold;
src: url('../fonts/UbuntuMono-Bold.ttf');
}
Note: I use the same value for my font-family name as that which I used for the font names in my PlayN code. For example, I load the DroidSans font in my PlayN code like this:
Font font = graphics().createFont("DroidSans", fontStyle, fontSize);
3) I then use Google WebFont Loader in my game's html file (MyGame.html) to load the fonts before the game loads. My MyGame.html file:
<!DOCTYPE html>
<html>
<head>
<title>MyGame</title>
<style>
body {
background-color:black;
color:white;
}
</style>
<!-- Google AJAX Libraries API -->
<script src="http://www.google.com/jsapi"></script>
<script>
google.load("jquery", "1.4.2");
google.load("webfont", "1");
WebFontConfig = {
custom: { families: ['DroidSans', 'UbuntuMono'],
urls: [ 'mygame/css/fonts.css' ]
},
loading: function() {
console.log("loading fonts");
},
fontloading: function(fontFamily, fontDescription) {
console.log("loading font: " + fontFamily + "-" + fontDescription);
},
fontactive: function(fontFamily, fontDescription) {
console.log(fontFamily + "-" + fontDescription + " is active");
},
fontinactive: function(fontFamily, fontDescription) {
console.log(fontFamily + "-" + fontDescription + " is INACTIVE");
},
active: function() {
console.log("font-loading complete");
},
};
google.setOnLoadCallback(function() {
console.log("Google onLoad callback");
WebFont.load(WebFontConfig);
});
</script>
</head>
<body>
<div id="playn-root">
<script src="mygame/mygame.nocache.js"></script>
</div>
</body>
</html>
The console logging in the WebFont.load callbacks helped verify that the fonts were successfully loaded before the PlayN game code.
I would have preferred to use WebFont Loader with the fonts served through googleapis.com, but I couldn't figure out how to sync up the references between my PlayN code and the stylesheet. (Now that I look at it, if I didn't want to host the fonts myself, I suppose I could have just used the same url listed in the googleapi.com stylesheets.) Whatever the case, this pattern seems to solve the problem.*
*For Google Chrome. I haven't tested any other browsers.

Related

How to find css classes (in e.g. chrome dev tools) that use a specific font-family?

I'm currently in the process of updating all my websites from using webfonts to hosting the fonts locally by myself. This process is a little bit frustrating, because I often can't find the css classes of the webfonts. At the moment, it's more a "try and error" kind of thing, where I'm just klicking trough the google chrome dev tools and looking for the corresponding css classes. So I was wondering if there is a simple way to look in a published website via browser for the css classes of a specific font family? (I cannot search for the classes in the IDE, because in this use case the websites where developed with webflow)
EDIT: The websites in question were created with a "building block" system called "Webflow". There, the fonts are selected via graphical interfaces. Now the problem is that somewhere in these old and huge web pages there are CSS classes that use the "Lato webfont". I want to replace this font, but I can't search for used fonts in this graphical interface. What I can search for are the CSS classes. So my idea was to use the Chrome Dev Tools to find out which CSS classes used the Lato font to ultimately replace it.
Find css rules by properties
If you can't edit you site's css files globally you might at least get some sort of "cheat sheet" containing all selectors matching certain property values.
let cssRules = getCssRules();
let filterLato400 = findRulesByProperties(cssRules, {
"font-family": "Lato",
});
console.log(filterLato400);
let filterLato400Italic = findRulesByProperties(cssRules, {
"font-family": "Lato",
"font-weight": 400,
"font-style": "italic"
});
console.log(filterLato400Italic);
//get all css rules in document
function getCssRules() {
let cssText = "";
let rules = [
...(document.styleSheets[0].rules || document.styleSheets[0].cssRules)
];
let cssArr = [];
rules.forEach(function(rule) {
let selector = rule.selectorText;
let cssText = rule.cssText;
if (selector && cssText) {
let properties = cssText
.replace(selector, "")
.replace(/[{}]/g, "")
.split(";")
.map((val) => {
return val.trim();
})
.filter(Boolean)
.map((vals) => {
return vals.split(":");
});
cssArr.push({
selector: selector,
properties: properties
});
}
});
return cssArr;
}
//filter css rules by properties
function findRulesByProperties(css, filters) {
let classList = [];
css.forEach(function(rule) {
let selector = rule.selector;
let props = rule.properties;
let vals = [];
let valsFilter = [];
for (let key in filters) {
let filterName = key;
let filterValue = filters[key];
valsFilter.push(filterValue.toString());
}
for (let i = 0; i < props.length; i++) {
let prop = props[i];
let propName = prop[0];
let propValue = prop[1].trim();
if (valsFilter.indexOf(propValue) != -1) {
vals.push(propValue);
}
}
if (vals.length == valsFilter.length) {
classList.push(selector)
}
});
return `results ${classList.length}: ${classList.join(", ")} || match: ${JSON.stringify(filters)}`;
}
body {
font-family: Georgia;
}
h1 {
font-family: "Lato";
font-weight: 700;
}
h2 {
font-family: "Lato";
font-weight: 400;
}
.classLato400 {
font-family: "Lato";
font-weight: 400;
}
.classLato400italic {
font-family: "Lato";
font-weight: 400;
font-style: italic;
}
.classLato700 {
font-family: "Lato";
font-weight: 700;
}
.classRoboto400 {
font-family: "Roboto";
font-weight: 400;
}
In the above example we're searching for all rules containing font-family:Lato (and other filters like font-weight or font-style).
You could paste your main css file in the snippet to get a list of selectors matching all criteria.
Replace external #font-face rules
If I got you right and your ultimate goal is to replace externally hosted font files with local ones (e.g. to improve GDPR compliance), you don't need to get every css font-family class reference.
The most important part are the #font-face rules that are actually responsive for downloading font files.
OK that's not perfectly correct since a font file won't be downloaded unless some DOM element uses this particular font-family.
In other words, your css might actually contain a plethora of unused font-families – on the other hand if they aren't used anywhere they won't be downloaded (so browsers have a default lazyloading method for fonts).
Example: you need to replace google webfonts with locally hosted fonts
Open your devTools and switch to the "Font" tab
Now you can see a list of all downloaded font files as well as their origin (URL) and their "Initiator" – the source file, that initiated the file download. Usually this would be a <link> stylesheet reference or an #import rule within your css, but it can also be a javaScript font loader method.
By inspecting the "URL" column, we can clearly see if a font is loaded from an external host.
Clicking the "Initiator" row/entry will open the file triggering the download – this will either be a file (like a .css) you want to completely remove or just a portion of a css file (take a closer look at #font-face rules, especially the src properties).
Following the google webfonts use case/example
(actually pretty similar to other font delivery services)
obviously we need to get local copies of my previously externally hosted font files –
google web font helper might be helpful to get a ready-to-go #font-face css and a download package including all needed font files.
we need to delete all css files or #font-face or #import rules that are still referring to external file sources and replace them with custom local font file urls.
Possible shortcuts to remove external font file references:
Check your HTML/template head for undesired elements like these
(so containing an external URL like "fonts.googleapis.com"):
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght#400;700" rel="stylesheet">
or within inline <style> tags for #import rules like #import url('https://fonts.googleapis.com/css2?family=Roboto:wght#400;700')
or similar #import rules within your main css file – they should usually be found at the top of your css code.
Delete these references and replace with custom #font-face rules like so (example is based on google web font helper output using "Roboto" font-family and font-weights 400+700 ... regular and bold).
/* roboto-regular - latin */
#font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url('fonts/roboto-v30-latin-regular.woff2') format('woff2');
}
/* roboto-700 - latin */
#font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: url('fonts/roboto-v30-latin-700.woff2') format('woff2');
}
Inspect the network tab once again
If everthing is working fine we should see the locally retrieved font files for each style (e.g. regular, bold, italic, bold italic etc.)
If not: double check your file paths!
Seriously, this is probably the most common source of errors. (e.g "../fonts/" or "./fonts/" or just "fonts/").

How do you save a pdf with Avenir font using CSS?

I am trying to save a pdf of html text in Google Apps Script. The full script sends a stylized email with the html text but I would like to back up the body of the email as a pdf. Using the below code I am able to save a pdf, but it does not use the Avenir font as I'd expect.
function printToPDF() {
var htmlMessage = '<html style="font-family: `Avenir`;">Avenir test</html>';
var folder = DriveApp.getFolderById(folderId).createFolder('test')
var blob = Utilities.newBlob(htmlMessage, MimeType.HTML, "text.html");
var pdf = blob.getAs(MimeType.PDF);
var document = DriveApp.getFolderById(folderId).createFile(pdf);
}
General CSS styling works (followed this post Google Apps Script - Convert HTML with styling to PDF and attach to an email - Create a PDF blob from HTML).
I tried to replicate your code, test few font family and found out that not all fonts are accepted in PDF.
Example:
Here I tried using Comic Sans MS.
function printToPDF() {
var folderId = "id";
var html = HtmlService.createTemplateFromFile("ABCD");
var output = html.evaluate();
var blob = Utilities.newBlob(output.getContent(), MimeType.HTML, "text.html");
var pdf = blob.getAs(MimeType.PDF);
DriveApp.getFolderById(folderId).createFile(pdf);
}
ABCD.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<style>
body {
font-family: 'Comic Sans MS';
font-size: 48px;
}
</style>
</head>
<body>
Comic Sans MS
</body>
</html>
Output:
Since there is no proper documentation on what are the accepted fonts in PDF, what you can do for now is to search for fonts similar to Avenir and do a trial and error.

PhantomJS PDF Capture Doesn't Pick Up Font Server Fonts

I'm using PhantomJS to turn an html page into PDF. This page uses fonts from cloud.typography.com. Now the html page itself is fine, and if I manually print it to a PDF, that too is fine. However, when I use PhantomJS to turn it to a PDF, the server fonts do not show up.
I researched this issue, and while I could find general issues involving fonts, I could find nothing on this specific case. The closest thing I could find was a bug in which PhantomJS would use the fallback font even if the primary one was available, but removing the fallback font didn't fix the issue for me.
Is this a known bug? Did I do something wrong? Do you know of any work-arounds?
Here's the (generic) html to PDF rendering script I'm using. I'm parametizing the page on the command line:
var page = require('webpage').create(),
args = require('system').args,
if ( args[2].split('.').pop() == 'pdf' ) {
page.viewportSize = { width:1920, height:1080 };
page.paperSize = {
width: '8.5in',
height: '11in',
};
}
page.open(args[1], render);
function render() {
page.render(args[2]);
phantom.exit();
}
Here's a relevant html snippet that points to the font server and the style sheet:
<link rel="stylesheet" type="text/css" href="print.css">
<link rel="stylesheet" type="text/css" href="//cloud.typography.com/7787052/625786/css/fonts.css" />
And here's a relevant section from print.css that references the fonts:
body {
font-family: "Gotham SSm A", "Gotham SSm B", sans-serif;
font-weight: 400;
font-style: normal;
margin: 0;
padding: 0;
}

How to use Google Webfont loader only for specific paragraph classes?

I have been using the Google Webfont loader to display parts of my website with nice fonts. I have this part work fine, all of my <p> show with the font I want. However, I would like only some classes of <p> (i.e. <p class="someclass">) to show with the webfonts, and then the rest with regular font (i.e. not Google Webfonts).
What is the best way for me to achieve this? At the moment, I am using this
<script type="text/javascript">
WebFontConfig = {
google: { families: [ 'McLaren' ] }
};
(function() {
var wf = document.createElement('script');
wf.src = ('https:' == document.location.protocol ? 'https' : 'http') +
'://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
wf.type = 'text/javascript';
wf.async = 'true';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(wf, s);
})();
</script>
<style type="text/css">
.wf-loading p.someclass {
font-family: serif
}
.wf-inactive p.someclass {
font-family: cursive
}
.wf-active p.someclass {
font-family: 'McLaren', cursive
}
</style>
and all <p> throughout the page display with the 'McLaren' font. I'm sure I am doing something dumb with the CSS selector but I'm not quite sure what.
Any help would be greatly appreciated. Thanks
First off you don’t necessarily need the js version of Google fonts I have just linked them through css and I have had no loading issues. Secondly, someone would need to see your body of your actual html BUT you don’t need the p prefix just add the fonts to your class .someclass{font-family:cursizve;} also like I said using css pseudo classes work better and a lot lighter on the client

Wait for fonts to load before rendering web page

I'm using #font-face to embed fonts in my website. First the text renders as the system default, and then (once the font file has loaded presumably) the correct font renders a fraction of a second later. Is there a way to minimise/get rid of this delay, by delaying the page rendering until after fonts have loaded or similar.
Since nobody mentioned that, I believe this question needs an update. The way I managed to solve the problem was using the "preload" option supported by modern browsers.
In case someone does not need to support old browsers.
<link rel="preload" href="assets/fonts/xxx.woff" as="font" type="font/woff" crossorigin>
some useful links with more details:
https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content
http://www.bramstein.com/writing/preload-hints-for-web-fonts.html
Edit: The best approach is probably to base64 encode your fonts. This means your font will have to be loaded fully by the time your HTML is parsed and displayed. You can do this with font squirrel's webfont generator https://www.fontsquirrel.com/tools/webfont-generator by clicking "Expert" and then "base64 encode". This is how services like TypeKit work.
Original answer:
Another way to detect if fonts are loaded would be using FontLoader https://github.com/smnh/FontLoader or by copying their strategy.
They bind to the scroll event in the browser, because when the font loads it will resize the text. It uses two containing divs (that will scroll when the height changes) and a separate fallback for IE.
An alternative is to check the DOM periodically with setInterval, but using javascript events is far faster and superior.
Obviously, you might do something like set the opacity of body to 0 and then display it in once the font loads.
This is down to how the browser behaves.
First off where is your #font declared? Is it inline to your HTML, declared in a CSS sheet on the page, or (hopefully) declared in an external CSS sheet?
If it is not in an external sheet, try moving it to one (this is better practice anyway usually).
If this doesn't help, you need to ask yourself is the fraction of a second difference really significantly detrimental to the user experience? If it is, then consider JavaScript, there are a few things you might be able to do, redirects, pauses etc, but these might actually be worse than the original problem. Worse for users, and worse to maintain.
This link might help:
http://paulirish.com/2009/fighting-the-font-face-fout/
Joni Korpi has a nice article on loading fonts before the rest of the page.
http://jonikorpi.com/a-smoother-page-load/
He also uses a loading.gif to alleviate the delay so users won't get frustrated.
This code works very well for me. It uses the Font Loading API which has good support among modern browsers.
<style>
#font-face {
font-family: 'DemoFont';
font-style: normal;
font-weight: 400;
src: url("./fonts/DemoFont.eot");
src: url("./fonts/DemoFont.woff2") format("woff2"),
url("./fonts/DemoFont.woff") format("woff"),
url("./fonts/DemoFont.ttf") format("truetype");
}
.font {
font-family: 'DemoFont';
color: transparent;
}
html.font-loaded .font {
color: inherit; // Override `transparent` from .font
}
</style>
<script>
// Check if API exists
if (document && document.fonts) {
// Do not block page loading
setTimeout(function () {
document.fonts.load('16px "DemoFont"').then(() => {
// Make font using elements visible
document.documentElement.classList.add('font-loaded')
})
}, 0)
} else {
// Fallback if API does not exist
document.documentElement.classList.add('font-loaded')
}
</script>
The trick is to set the CSS color to transparent for elements using the font. Once loaded this is reset by adding font-loaded class to <html> element.
Please replace DemoFont with something meaningful for your project to get it work.
I had a similar problem while rendering to an HTML canvas, and this was my solution. It's based on the FontFace API, and similar to Holtwicks approach. The key differences are that this is a generic approach and that it will work out-of-the-box for external fonts/stylesheets (e.g. google fonts).
A couple of notes;
fonts.load( ... ) will happily resolve with an empty set of fonts if the font isn't known yet. Presumably, this happens if this code is called before the stylesheet declaring the font was added. I added a fonts.check(...) to overcome that.
This will let you await javascript execution until a font is available, so it won't work out of the box for 'normal' HTML content. You can combine this with Holtwicks answer above.
export async function waitForFontLoad(
font: string,
timeout = 1000,
interval = 10
) {
return new Promise((resolve, reject) => {
// repeatedly poll check
const poller = setInterval(async () => {
try {
await document.fonts.load(font);
} catch (err) {
reject(err);
}
if (document.fonts.check(font)) {
clearInterval(poller);
resolve(true);
}
}, interval);
setTimeout(() => clearInterval(poller), timeout);
});
}
Only IE loads first the font and then the rest of the page.
The other browsers load things concurrently for a reason. Imagine that there's a problem with the server hosting the font or with the font downloading.
You will hang your entire site until the font is loaded. On my opinion a flash of unstyled text is better than not seeing the site at all
You can use CSS font-display inside your #font-face.
The keywords for all the available values are:
auto
block
swap
fallback
optional
Giulio Mainardi has written a nice article about all of them, and which you should use where on sitepoint.
You can read it here: https://www.sitepoint.com/css-font-display-future-font-rendering-web/?utm_source=frontendfocus&utm_medium=email
Use https://github.com/typekit/webfontloader
and check the events in the configuration
https://github.com/typekit/webfontloader#configuration
<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js"></script>
<script>
WebFont.load({
custom: {
families: [ "CustomFont1", "CustomFont2" ]
},
active: function() {
//Render your page
}
});
</script>
while the answer posted by #fluffy works. But the interval function runs after every interval and doesn't wait for fonts.load promise to resolve, better solution would be to use recursive function
function waitForFontLoad(font: string, timeout = 1000, interval = 10) {
const startTime = Date.now();
return new Promise((resolve, reject) => {
const recursiveFn = () => {
const currTime = Date.now();
if (currTime - startTime >= timeout) {
reject("font listener timeout " + font);
} else {
document.fonts
.load(font)
.then((fonts) => {
if (fonts.length >= 1) {
resolve(true);
} else {
setTimeout(recursiveFn, interval);
}
})
.catch((err) => {
reject(err);
});
}
};
recursiveFn();
});
}
reference - webfontloader
Simplest solution
// Single font
FontLoad( ['Font name one'])
// Multiple fonts
FontLoad( ['Font name one','Another font name'])
you can use without CallBack funtion too, callback function is optional incase you want invoked inside when FontLoad function completed.
const FontLoad = async ( fonts=[] , callback=()=>{} ) => {
await fonts;
for (const font of fonts) {
document.fonts.check(`80px ${font}`)
? document.fonts.load(`80px ${font}`).then( () => { console.log( `Font: ${font} loaded ✔️` ) } )
: console.log( `Font: ${font} not founded ❌` )
}
document.fonts.ready.then(() => { console.log("Ready"); callback() })
}
FontLoad( ['Arial','FONT_NOT_FOUNDED'], ()=> console.log("External function") )
(function() {
document.getElementsByTagName("html")[0].setAttribute("class","wf-loading")
document.getElementsByTagName("html")[0].setAttribute("className","wf-loading")
})();
use this method.. use with Webfont.js
Maybe something like this:
$("body").html("<img src='ajax-loader.gif' />");
Then when the page loads, replace body's content with the actual content and hopefully, fully rendered fonts, you may have to play around with this though...

Resources