How to only PRELOAD a font if it doesn't exist localy? - html

On my website, I want to strive for reducing the page size as much as possible. As such, on Apple devices - I want to display my site using the native San Francisco font. On all other devices, I want to display (the extremely similar) Roboto font.
What's great about Roboto is that it's locally installed with Android ... and as such, I don't want to install the font if it already exist.
What I want to do is, is what's describen in the pseudo code below
if ("San Francisco" or "Roboto" not installed locally) {
// download Robot (and preload it for super fast performance)
<link rel="preload" as="font" href="/assets/fonts/roboto.woff2" type="font/woff2" crossorigin/>
}
I totally realize in my CSS, it already has the ability to fallback to another font like so
body { font:normal 1em -apple-system,"Roboto",sans-serif;}
The problem with the CSS code above, it slow and doesn't preload.
Now yes, I guess I could just PRELOAD the Roboto font for ALL page views, but that's seems completely wasteful.
Thoughts on how I can do this?

One way to do this is through JS and the CSS Font Loading API.
You can start by trying to load the local "San Francisco" font from a FontFace instance, if it fails, load the Roboto fallbacks.
But doing so, we loose some of the advantages of link preload and we may face a Flash Of Unstyled Content.
(async () => {
const sanfrancisco = new FontFace( "San Francisco", "local('San Francisco')" );
try {
await sanfrancisco.load();
console.log( "'San Francisco' font is available on this system" );
document.fonts.add( sanfrancisco );
}
catch( err ) {
console.log( "'San Francisco' font is not available on this system" );
// use the local version
// or fallback to the online one if not available
const roboto = new FontFace( "Roboto", `local(Roboto),
url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2)
` );
await roboto.load();
console.log( "Roboto font loaded" );
document.fonts.add( roboto );
}
document.body.classList.add( "font-loaded" ); // avoid FOUC
})();
body:not(.font-loaded) {
opacity: 0;
}
.my-font {
font-family: 'San Francisco','Roboto';
}
The following paragraph
<p class="my-font">uses either San Francisco or Roboto.</p>

Related

Customize font-size depending on locale in Vue3/Laravel8?

I am working on a Vue3/Laravel8 app, that has to support english and arabic languages. Problem is that there is a huge font-size difference between the en and ar locales:
I've been looking for a while now for a way to change the arabic font-size but it doesn't seem simple.
First I tried finding a way in vue-i18n without success. It would be most convenient if that is possible.
Then I tried going the CSS route, using the :lang(ar) selector but it depends on html or in my case laravel.blade and controlling project language seems to be a hassle, creating middleware and controllers.
Is there a convenient way to do that?
Vue 3.2 introduced some new features for single file components (SFC):
https://v3.vuejs.org/api/sfc-style.html#state-driven-dynamic-css
So in your case you could try something like this (untested):
<template>
<div class="text">hello</div>
</template>
<script>
export default {
data() {
return {
lang: 'en',
},
},
computed() {
fontSize() {
if (this.lang === 'ar') {
return '1.5em';
}
return '1em';
},
},
}
</script>
<style>
.text {
font-size: v-bind(fontSize);
}
</style>

Font Awesome in eKoopmans / html2pdf.js did not show

I putted html2pdf.js in my project,
https://github.com/eKoopmans/html2pdf.js .
And too I putted Font Awesome in my project,
Before pdf
After pdf
Why did not show the font awesome after output the pdf?
this is my codes
<script src="{{ asset('js/html2pdf.bundle.min.js') }}"></script>
<script>
function generatePDF() {
const element = document.getElementById("invoice");
let opt = {
filename: 'resume.pdf',
image: { type: 'jpeg', quality: 0.98 },
jsPDF: { unit: 'in', format: 'letter', orientation: 'portrait' }
};
html2pdf()
.set(opt)
.from(element)
.save();
{{--location.replace("{{route('index')}}");--}}
}
</script>
A font can be embedded only if it contains a setting by the font vendor that permits it to be embedded. Embedding prevents font substitution when readers view or print the file, and ensures that readers see the text in its original font. Embedding increases file size only slightly, unless the document uses CID fonts. a font format commonly used for Asian languages. You can embed or substitute fonts in Acrobat or when you export an InDesign document to PDF.
Please read the adobe documentation.
It is possible that the version you are using to do not be supported.

Some local fonts doesn't render on the browser

I've created a small app to view all local fonts on a browser. (Demo).
I'm using Flash to get font names:
import flash.external.ExternalInterface;
ExternalInterface.call("getFontList", getDeviceFonts());
function getDeviceFonts(): Array {
var embeddedAndDeviceFonts: Array = Font.enumerateFonts(true);
var deviceFontNames: Array = [];
for each(var font: Font in embeddedAndDeviceFonts) {
deviceFontNames.push(font.fontName);
}
deviceFontNames.sort();
return deviceFontNames;
}
and later process the names and create a 4 column layout to view them:
function getFontList(fontList) {
$("#flash").remove();
fontList.forEach(function (fontName) {
var cell = elem("div", {"class":"tile"}, [
elem("div", {"class":"fontName"}, fontName),
elem("div", {"class":"demo", "style":"font-family: \"" + fontName.replace(/"/g, '\\"') + "\", Consolas, Arial;"}),
]);
output.append(cell);
});
updateCells($("#input").val());
}
This is working fine, but there's slight problem. Some fonts doesn't render on the browser, and the fallback font is applied.
For example, this font: !PaulMaul. I can view this on MS Word and Photoshop:
and this is how it looks (Consolas applied) on the browser:
I haven't encountered something like this before, so I don't know what the problem is. I thought any local font is usable on the browser, especially if MS Word and PS can render them fine. I guess, that isn't true.
What exactly is the problem here, and is there a way to solve this?
App is available on Github, if anyone wants to tinker.

iTextSharp HTML to PDF conversion - unable to change font

I'm creating some PDF documents with iTextSharp (5.5.7.0) from HTML in ASP.NET MVC5 application, but I'm unable to change the font. I've tried almost everything that I was able to find on SO or from some other resources.
Code for PDF generation is as follows:
public Byte[] GetRecordsPdf(RecordsViewModel model)
{
var viewPath = "~/Template/RecordTemplate.cshtml";
var renderedReport = RenderViewToString(viewPath, model);
FontFactory.RegisterDirectory(Environment.GetFolderPath(Environment.SpecialFolder.Fonts));
using (var ms = new MemoryStream())
{
using (var doc = new Document())
{
doc.SetPageSize(PageSize.A4.Rotate());
using (var writer = PdfWriter.GetInstance(doc, ms))
{
doc.Open();
using (var html = new MemoryStream(Encoding.Default.GetBytes(renderedReport)))
{
XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, html, Encoding.Default);
}
doc.Close();
}
}
var bytes = ms.ToArray();
return bytes;
}
}
Actual HTML is contained in renderedReport string variable (I have strongly typed .cshtml file which I render using MVC Razor engine and then return HTML in string).
I've tried to register some specific fonts, but that didn't help. I've also tried to register all fonts on my machine (as shown in example above), but that also didn't help. The fonts were loaded I've checked that in debug mode.
CSS is embedded in HTML file (in heading, style tag) like this:
body {
font-size: 7px;
font-family: Comic Sans MS;
}
(for test, I've decided to use Comic Sans, because I can recognize it with ease, I'm more interested in Arial Unicode MS actually).
And I'm actually able to change the font with that font-family attribute from CSS, but only from fonts that are preloaded by iTextSharp by default - Times New Roman, Arial, Courier, and some other (Helvetica i think). When I change it to - Comic Sans, or some other that is not preloaded iTextSharp renders with default font (Arial I would say).
The reason why I need to change the font is because I have some Croatian characters in my rendered HTML (ČĆŠĐŽčćšđž) which are missing from PDF, and currently I think the main reason is - font.
What am I missing?
A couple of things to make this work.
First, XMLWorkerHelper doesn't use FontFactory by default, you need to use one of the overloads to ParseXHtml() that takes an IFontProvider. Both of those overloads require that you specify a Stream for a CSS file but you can just pass null if your CSS lives inside your HTML file. Luckily FontFactory has a static property that implements this that you can use called FontFactory.FontImp
// **This guy**
XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, msHTML, null, Encoding.UTF8, FontFactory.FontImp);
Second, I know that you said that you tried registering your entire font directory out of desperation but that can be a rather expensive call. If you can, always try to just register the fonts you need. Although optional, I also strongly recommend that you explicitly define the font's alias because fonts can have several names and they're not always what we think.
FontFactory.Register(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts), "comic.ttf"), "Comic Sans MS");
Third, and this might not affect you, but any tags not present in the HTML, even if they're logically implied, won't get styling applied to them from CSS. That sounds weird so to say it differently, if your HTML is just <p>Hello</p> and your CSS is body{font-size: 7px;}, the font size won't get applied because your HTML is missing the <body> tag.
Fourth, and this is optional, but usually its easier to specify your HTML and CSS separately from each other which I'll do in the example below.
Your code was 95% there so with just a couple of tweaks it should work. Instead of a view I'm just parsing raw HTML and CSS but you can modify as needed. Please do remember (and I think you know this) that iTextSharp cannot process ASP.Net, only HTML, so you need to make sure that your ASP.Net to HTML conversion process is sane.
//Sample HTML and CSS
var html = #"<body><p>Sva ljudska bića rađaju se slobodna i jednaka u dostojanstvu i pravima. Ona su obdarena razumom i sviješću i trebaju jedna prema drugima postupati u duhu bratstva.</p></body>";
var css = "body{font-size: 7px; font-family: Comic Sans MS;}";
//Register a single font
FontFactory.Register(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts), "comic.ttf"), "Comic Sans MS");
//Placeholder variable for later
Byte[] bytes;
using (var ms = new MemoryStream()) {
using (var doc = new Document()) {
doc.SetPageSize(PageSize.A4.Rotate());
using (var writer = PdfWriter.GetInstance(doc, ms)) {
doc.Open();
//Get a stream of our HTML
using (var msHTML = new MemoryStream(Encoding.UTF8.GetBytes(html))) {
//Get a stream of our CSS
using (var msCSS = new MemoryStream(Encoding.UTF8.GetBytes(css))) {
XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, msHTML, msCSS, Encoding.UTF8, FontFactory.FontImp);
}
}
doc.Close();
}
}
bytes = ms.ToArray();
}

Drawing text to <canvas> with #font-face does not work at the first time

When I draw a text in a canvas with a typeface that is loaded via #font-face, the text doesn't show correctly. It doesn't show at all (in Chrome 13 and Firefox 5), or the typeface is wrong (Opera 11). This type of unexpected behavior occurs only at the first drawing with the typeface. After then everything works fine.
Is it the standard behavior or something?
Thank you.
PS: Following is the source code of the test case
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>#font-face and <canvas></title>
<style id="css">
#font-face {
font-family: 'Press Start 2P';
src: url('fonts/PressStart2P.ttf');
}
</style>
<style>
canvas, pre {
border: 1px solid black;
padding: 0 1em;
}
</style>
</head>
<body>
<h1>#font-face and <canvas></h1>
<p>
Description: click the button several times, and you will see the problem.
The first line won't show at all, or with a wrong typeface even if it does.
<strong>If you have visited this page before, you may have to refresh (or reload) it.</strong>
</p>
<p>
<button id="draw">#draw</button>
</p>
<p>
<canvas width="250" height="250">
Your browser does not support the CANVAS element.
Try the latest Firefox, Google Chrome, Safari or Opera.
</canvas>
</p>
<h2>#font-face</h2>
<pre id="view-css"></pre>
<h2>Script</h2>
<pre id="view-script"></pre>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script id="script">
var x = 30,
y = 10;
$('#draw').click(function () {
var canvas = $('canvas')[0],
ctx = canvas.getContext('2d');
ctx.font = '12px "Press Start 2P"';
ctx.fillStyle = '#000';
ctx.fillText('Hello, world!', x, y += 20);
ctx.fillRect(x - 20, y - 10, 10, 10);
});
</script>
<script>
$('#view-css').text($('#css').text());
$('#view-script').text($('#script').text());
</script>
</body>
</html>
Drawing on canvas has to happen and return immediately when you call the fillText method. However, the browser has not yet loaded the font from the network, which is a background task. So it has to fall back to the font it does have available.
If you want to make sure the font is available, have some other element on the page preload it, eg.:
<div style="font-family: PressStart;">.</div>
Use this trick and bind an onerror event to an Image element.
Demo here: works on the latest Chrome.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = 'http://fonts.googleapis.com/css?family=Vast+Shadow';
document.getElementsByTagName('head')[0].appendChild(link);
// Trick from https://stackoverflow.com/questions/2635814/
var image = new Image();
image.src = link.href;
image.onerror = function() {
ctx.font = '50px "Vast Shadow"';
ctx.textBaseline = 'top';
ctx.fillText('Hello!', 20, 10);
};
You can load fonts with the FontFace API before using it in the canvas:
const myFont = new FontFace('My Font', 'url(https://myfont.woff2)');
myFont.load().then((font) => {
document.fonts.add(font);
console.log('Font loaded');
});
The font resource myfont.woff2 is first downloaded. Once the download completes, the font is added to the document's FontFaceSet.
The specification of the FontFace API is a working draft at the time of this writing. See browser compatibility table here.
The nub of the problem is that you are trying to use the font but the browser has not loaded it yet and possibly has not even requested it. What you need is something that will load the font and give you a callback once it is loaded; once you get the callback, you know it is okay to use the font.
Look at Google's WebFont Loader; it seems like a "custom" provider and an active callback after the load would make it work.
I've never used it before, but from a quick scan of the docs you need to make a css file fonts/pressstart2p.css, like this:
#font-face {
font-family: 'Press Start 2P';
font-style: normal;
font-weight: normal;
src: local('Press Start 2P'), url('http://lemon-factory.net/reproduce/fonts/Press Start 2P.ttf') format('ttf');
}
Then add the following JS:
WebFontConfig = {
custom: { families: ['Press Start 2P'],
urls: [ 'http://lemon-factory.net/reproduce/fonts/pressstart2p.css']},
active: function() {
/* code to execute once all font families are loaded */
console.log(" I sure hope my font is loaded now. ");
}
};
(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);
})();
What about using simple CSS to hide a div using the font like this:
CSS:
#preloadfont {
font-family: YourFont;
opacity:0;
height:0;
width:0;
display:inline-block;
}
HTML:
<body>
<div id="preloadfont">.</div>
<canvas id="yourcanvas"></canvas>
...
</body>
https://drafts.csswg.org/css-font-loading/
var myFont = new FontFace('My Font', 'url(https://myfont.woff2)');
myFont.load().then(function(font){
// with canvas, if this is ommited won't work
document.fonts.add(font);
console.log('Font loaded');
});
i've bumped into the issue when playing with it recently http://people.opera.com/patrickl/experiments/canvas/scroller/
worked around it by adding the font-family to canvas directly in the CSS, so you can just add
canvas { font-family: PressStart; }
This article sorted out my issues with lazy loaded fonts not being displayed.
How to load web fonts to avoid performance issues and speed up page loading
This helped me ...
<link rel="preload" as="font" href="assets/fonts/Maki2/fontmaki2.css" rel="stylesheet" crossorigin="anonymous">
Since I've not found any example of the actual FontFace usage, here's #Fred
Fred Bergman's answer, slightly modified
const fontUrl = 'https://fonts.gstatic.com/s/robotomono/v22/L0xuDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_Of2_ROW-AJi8SJQt.woff'
const robotoFont = new FontFace('Roboto Mono', `url(${fontUrl})`);
// Canvas variables
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
robotoFont.load().then((font: FontFace) => {
// This is required
document.fonts.add(font);
// Usage example
ctx.font = `30px '${font.family}'`;
ctx.fillText("Hello World!", 10, 50);
});
Here's the compatibility table
If you want to redraw everytime a new font is loaded (and probably change the rendering) the font loading api has a nice event for that, too. I had problems with the Promise in a complete dynamic environment.
var fontFaceSet = document.fonts;
if (fontFaceSet && fontFaceSet.addEventListener) {
fontFaceSet.addEventListener('loadingdone', function () {
// Redraw something
});
} else {
// no fallback is possible without this API as a font files download can be triggered
// at any time when a new glyph is rendered on screen
}
I am not sure if this will help you, but to solve the problem with my code I simply created a for loop at the top of my Javascript which ran through all the fonts that I wanted to load. I then ran a function to clear the canvas and preload the items I wanted on the canvas. So far it has worked perfectly. That was my logic I have posted my code below:
var fontLibrary = ["Acme","Aladin","Amarante","Belgrano","CantoraOne","Capriola","CevicheOne","Chango","ChelaOne","CherryCreamSoda",
"ConcertOne","Condiment","Damion","Devonshire","FugazOne","GermaniaOne","GorditasBold","GorditasRegular",
"KaushanScript","LeckerliOne","Lemon","LilitaOne","LuckiestGuy","Molle","MrDafoe","MrsSheppards",
"Norican","OriginalSurfer","OswaldBold","OswaldLight","OswaldRegular","Pacifico","Paprika","Playball",
"Quando","Ranchers","SansitaOne","SpicyRice","TitanOne","Yellowtail","Yesteryear"];
for (var i=0; i < fontLibrary.length; i++) {
context.fillText("Sample",250,50);
context.font="34px " + fontLibrary[i];
}
changefontType();
function changefontType() {
selfonttype = $("#selfontype").val();
inputtextgo1();
}
function inputtextgo1() {
var y = 50;
var lineHeight = 36;
area1text = document.getElementById("bag1areatext").value;
context.clearRect(0, 0, 500, 95)
context.drawImage(section1backgroundimage, 0, 0);
context.font="34px " + selfonttype;
context.fillStyle = seltextcolor;
context.fillText(area1text, 250, y);
}
I wrote a jsfiddle incorporating most of the suggested fixes here but none resolved the issue. However, I am a novice programmer so perhaps did not code the suggested fixes correctly:
http://jsfiddle.net/HatHead/GcxQ9/23/
HTML:
<!-- you need to empty your browser cache and do a hard reload EVERYTIME to test this otherwise it will appear to working when, in fact, it isn't -->
<h1>Title Font</h1>
<p>Paragraph font...</p>
<canvas id="myCanvas" width="740" height="400"></canvas>
CSS:
#import url(http://fonts.googleapis.com/css?family=Architects+Daughter);
#import url(http://fonts.googleapis.com/css?family=Rock+Salt);
canvas {
font-family:'Rock Salt', 'Architects Daughter'
}
.wf-loading p {
font-family: serif
}
.wf-inactive p {
font-family: serif
}
.wf-active p {
font-family:'Architects Daughter', serif;
font-size: 24px;
font-weight: bold;
}
.wf-loading h1 {
font-family: serif;
font-weight: 400;
font-size: 42px
}
.wf-inactive h1 {
font-family: serif;
font-weight: 400;
font-size: 42px
}
.wf-active h1 {
font-family:'Rock Salt', serif;
font-weight: 400;
font-size: 42px;
}
JS:
// do the Google Font Loader stuff....
WebFontConfig = {
google: {
families: ['Architects Daughter', 'Rock Salt']
}
};
(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);
})();
//play with the milliseconds delay to find the threshold - don't forget to empty your browser cache and do a hard reload!
setTimeout(WriteCanvasText, 0);
function WriteCanvasText() {
// write some text to the canvas
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
context.font = "normal" + " " + "normal" + " " + "bold" + " " + "42px" + " " + "Rock Salt";
context.fillStyle = "#d50";
context.fillText("Canvas Title", 5, 100);
context.font = "normal" + " " + "normal" + " " + "bold" + " " + "24px" + " " + "Architects Daughter";
context.fillText("Here is some text on the canvas...", 5, 180);
}
Workaround
I eventually gave in and, on the first load, used an image of the text while also positioning the text with the font-faces outside of the canvas display area. All subsequent displays of the font-faces within the canvas display area worked no problem. This is not an elegant workaround by any means.
The solution is baked into my website but if anyone needs I will try to create a jsfiddle to demonstrate.
Some browsers support the CSS Font Loading specification. It allows you to register register a callback for when all the fonts have been loaded. You can delay drawing your canvas (or at least drawing text into your canvas) until then, and trigger a redraw once the font is available.
The canvas is drawn independently of the DOM loading. The preload technique will only work if the canvas is drawn after the preload.
My solution, even if it is not the best:
CSS:
.preloadFont {
font-family: 'Audiowide', Impact, Charcoal, sans-serif, cursive;
font-size: 0;
position: absolute;
visibility: hidden;
}
HTML:
<body onload="init()">
<div class="preloadFont">.</div>
<canvas id="yourCanvas"></canvas>
</body>
JavaScript:
function init() {
myCanvas.draw();
}
I try to use FontFaceSet.load to fix the problem:
https://jsfiddle.net/wengshenshun/gr1zkvtq/30
const prepareFontLoad = (fontList) => Promise.all(fontList.map(font => document.fonts.load(font)))
You can find the browser compatibility from https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/load
First of all use Google's Web Font loader as was advised in the other answer and add your drawing code to the callback it provides to indicate the fonts have been loaded. However this is not the end of the story. From this point it is very browser dependent. Most of the time it will work fine, but sometimes it may be required to wait for couple hundreds of milliseconds or use the fonts somewhere else on the page. I tried different options and the one method that afaik always works is to quickly draw some test messages in the canvas with the font family and font size combinations that you are going to use. You can do it with the same color as background, so they won't even be visible and it will happen very fast. After that fonts always worked for me and in all browsers.
My answer addresses Google Web fonts rather than #font-face. I searched everywhere for a solution to the problem of the font not showing up in the canvas. I tried timers, setInterval, font-delay libraries, and all kinds of tricks. Nothing worked. (Including putting font-family in the CSS for canvas or the ID of the canvas element.)
However, I found that animating text rendered in a Google font worked easily. What's the difference? In canvas animation, we re-draw the animated items again and again. So I got the idea to render the text twice.
That did not work either -- until I also added a short (100ms) timer delay. I've only tested on a Mac so far. Chrome worked fine at 100ms. Safari required a page reload, so I increased the timer to 1000, and then it was fine. Firefox 18.0.2 and 20.0 wouldn't load anything on the canvas if I was using Google fonts (including the animation version).
Full code: http://www.macloo.com/examples/canvas/canvas10.html
http://www.macloo.com/examples/canvas/scripts/canvas10.js
Faces same problem. After reading "bobince" and others comments, I uses following javascript to workaround it :
$('body').append("<div id='loadfont' style='font-family: myfont;'>.</div>");
$('#loadfont').remove();
Add a delay as below
<script>
var c = document.getElementById('myCanvas');
var ctx = c.getContext('2d');
setTimeout(function() {
ctx.font = "24px 'Proxy6'"; // uninstalled #fontface font style
ctx.textBaseline = 'top';
ctx.fillText('What!', 20, 10);
}, 100);
</script>