0

I need to use custom fonts and render them on off-screen canvas, I've done this already, but I cannot set the size of the font rendering -it results to a tiny rendering.

All sources I've seen including w3schools.com, developer.mozilla.org etc. naively assume that we all use system fonts and define the size in a sloppy way mixed with the font name as: ctx.font = "30px Arial";

T̶h̶e̶ ̶f̶a̶c̶t̶ ̶i̶s̶,̶ ̶t̶h̶a̶t̶ ̶i̶f̶ ̶I̶ ̶u̶s̶e̶ ̶t̶h̶e̶ ̶n̶a̶m̶e̶ ̶o̶f̶ ̶m̶y̶ ̶c̶u̶s̶t̶o̶m̶ ̶f̶o̶n̶t̶ ̶f̶i̶l̶e̶ ̶p̶l̶a̶c̶e̶d̶ ̶o̶n̶ ̶t̶h̶e̶ ̶s̶a̶m̶e̶ ̶f̶o̶l̶d̶e̶r̶ ̶(̶"̶c̶u̶s̶t̶o̶m̶.̶w̶o̶f̶f̶"̶)̶ ̶(̶o̶r̶ ̶.̶o̶t̶f̶)̶ ̶i̶t̶ ̶w̶o̶r̶k̶s̶,̶ ̶b̶u̶t̶ if I add the size in pixels or in points ("30px custom.woff") that does NOT work!

Frustratingly enough, other parameters such as font weight seem to be defined separately (talk about unbelievable HTML5 inconsistency that reminds us the past). So, is there a way to change the rendering size of a custom font for an off-screen canvas created in JS?

I create the off-screen canvas like this:

var offScreenCanvas = document.createElement('canvas');
offScreenCanvas.width = 256;
offScreenCanvas.height = 256;
var ctx = offScreenCanvas.getContext("2d");

later, I render the font like this:

ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.font = 'custom.woff'; 
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, offScreenCanvas.width, 256);

ctx.fillStyle = 'black';
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(txt, offScreenCanvas.width / 2, offScreenCanvas.height / 2); 

Then I make a texture of that rendering and use it on a three.js surface.

three.js r96.0

Font substitution on jsfiddle (see comments):

user5515
  • 301
  • 2
  • 18

2 Answers2

3

You can create a CSS font face rule and name it as you like. Then you can use your own name.

<style>
@font-face {
    font-family: 'myfontname';
    src: url('custom.woff');
}
</style>

and then in your script

ctx.font = '30px myfontname';

If as you say it work with passing the font file, then try

ctx.font = "fontfile.woff";
// most likely you need to wait/check that the font has loaded first and then
ctx.font = ctx.font.replace('(\d+)px', "20px");
Gabriele Petrioli
  • 191,379
  • 34
  • 261
  • 317
  • That works, thanks! So it needs to be defined in CSS. I presume that JS lacks that capability. Still, that is enough for me. I'll wait a couple of days in case someone posts a simple JS solution, if not, I will select this as the answer. – user5515 Sep 18 '18 at 14:59
  • CORRECTION: NOPE, it does NOT work! I saw the text size increasing in Chrome and I thought that the problem was solved, but the font was SUBSTITUTED with a system font! Firefox doesn't do that, and doesn't render the font at all. So this method doesn't work either (and I cannot take my vote back due to ...not enough reputation -go figure.). – user5515 Sep 18 '18 at 15:18
  • @user5515 it should work. Most likely an issue with the font file not having been loaded yet. See https://stackoverflow.com/questions/5680013/how-to-be-notified-once-a-web-font-has-loaded – Gabriele Petrioli Sep 18 '18 at 15:23
  • The custom font does load and appears in a tiny rendering. Only when I add "40px" in front of the font name it doesn't load at all in both FF and Chrome and Chrome substitutes it with a system font like I wrote. Unless the addition of "40px" causes it to delay... – user5515 Sep 18 '18 at 15:43
  • It is enough to add an alert("!") after the ctx.font = '30px myfontname'; as per your suggestion to verify that even given plenty of time the font won't load if 30px is added. So that's NOT the problem. – user5515 Sep 18 '18 at 17:24
  • Nope, it doesn't work. That is, after I deleted the Lobster font from my system (I saved it first). Try this with a font that you don't have in your system. – user5515 Sep 19 '18 at 13:31
  • @user5515 you say that when you pass "custom.woff" alone it works. Can you log the `ctx.font` after you set it like that and post what it outputs ? – Gabriele Petrioli Sep 19 '18 at 13:44
  • @ Gabriele Petrioli <> Here is a screenshot of Chrome, you can see that the font on the jsfiddle has been substituted with another one. On FF the text doesn't render at all -the rectangle remains white. -NOT ALLOWED, I'll add it to the question. – user5515 Sep 19 '18 at 14:26
  • @ While I was writing that, it worked on my code with my custom font plus the addition of "40px" AFTER I added a line similar to your jsfiddle: . BUT that created a white bar on the top, so I added that font style to the body: Obviously that is good only if the app has ONLY ONE font, which is highly unlikely. So given that, what do you think it should be done to support many custom and non-system fonts? – user5515 Sep 19 '18 at 14:26
  • BUT, that only works on Firefox, Chrome still substitutes that font! In other words, we are back to the old damn HTLM / CSS / browser schizophrenia of the previous decades. I'm starting to see that messing with the HTML-standard morons once again is not very wise. The less dependence to them the better, to keep our sanity. I don't know whether there is a choice though in three.js – user5515 Sep 19 '18 at 14:49
0

Since I'm creating off-screen canvas in JS without predefining it first in HTML because I'm creating a number of textures from various font renderings on canvas and since I'm using custom fonts placed on a folder instead of loading Google fonts, no suggested solution worked as it is, but both Gabriele Petrioli and Mugen87 from discourse.threejs.org have helped a lot.

So it finally worked properly on Firefox, Chrome and Opera. IE11 is out of the question as it doesn't support all three.js functionality anyway, so I don't have a reason to support it at all (and I don't have any idea about the Edge browser as I'm still using Windows 7 but I suspect similar issues...).

1) Mugen87's suggestion to wait for the fonts to load was key:

document.fonts.onloadingdone = function (fontFaceSetEvent) {

   // create canvas and render the font here (see above) 

}

2) For custom fonts on a folder this worked in my case:

placed inside style or in CSS ( format is optional but it is recommended):

  @font-face {
    font-family: 'custom bold';
    src: url('fonts/custom bold.woff') format('woff');
  }
  @font-face {
    font-family: 'custom reg';
    src: url('fonts/custom regular.woff') format('woff');;
  }

3) To work in Chrome, I found that it was key to also include the font-family inside body style, but since I wanted to use more fonts than one (obviously), I did that in JS each time I needed a font, like this:

var fontreg;

fontreg = "custom reg";
document.body.style.fontFamily = fontreg;

ctx.font = "40px " + fontreg;

Hope that helps somebody else too.

user5515
  • 301
  • 2
  • 18