Drawing Text to <Canvas> with @Font-Face Does Not Work at the First Time

HTML5 Canvas doesn't display any text using CSS3's @font-face

It looks like embedded fonts should work in <canvas>, but you have to ensure the font is completely loaded before drawing. This means using a <div> to force the browser to load the font, then waiting to draw until after it has downloaded.

Have you tried calling drawTitle after document.load or even a setTimeout(drawTitle, 1000)?

Unable to load font to canvas

If you are seeing the font file within the debugger, your code for the canvas is probably running before the font has been loaded.

You can read more in this answer to the possible duplicate question Daniel White mentioned: https://stackoverflow.com/a/7289880/864799

Instead of the WebFontLoader, you might want to use the more recent FontFaceObserver, by the same maintainer.

For example:

<style>
@font-face {
font-family: 'Example';
src: url('Example-Regular.woff2') format('woff2');
}
</style>

<canvas id="c" width="1000" height="1400"></canvas>

<!-- You could also include this locally, but this is the latest version at the time of writing. -->
<script src="https://unpkg.com/fontfaceobserver@2.1.0/fontfaceobserver.standalone.js"></script>
<script>

var c = document.getElementById("c").getContext('2d');

var font = new FontFaceObserver('Example', {
weight: 400
});

font.load().then(function () {
console.log('Font is available');

c.font = '100px Example';
c.fillText('Loading...', 110, 110);

}, function () {
console.log('Font is not available');
});
</script>

You can find more detailed examples in the FontFaceObserver README.

Safari 15.2 Canvas and .otf Fonts

So first, this seems to affect only the colored fonts, I was able to draw the non-colored version of the same font (also in .otf) in Safari just fine.

Then, that's a bug, feel free to let Safari folks know about it on their issue tracker.

Unfortunately, there isn't much you can do.

The only hack that comes to mind would be to render an SVG image on your canvas where the text would be drawn.

To do so you'd have to first fetch the font and generate a data URL version of it so that it can be embedded in a standalone SVG document that you'd load in an HTMLImageElement to be drawn on the canvas.

Really, that's a hack, but since they are still able to render that font through their SVG renderer, that works:

(async () => {
const fontURL = "https://cdn.jsdelivr.net/gh/Fontself/TypeWithPride@master/fonts/Gilbert-Color%20Bold%20Preview_1005.otf";
const fontRes = await fetch(fontURL);
if (!fontRes.ok) {
throw new Error("failed to load");
}
const fontBlob = await fontRes.blob();
const dataURL = await new Promise((res) => {
const reader = new FileReader();
reader.onload = () => res(reader.result);
reader.readAsDataURL(fontBlob);
});
const svgNode = document.querySelector("svg").cloneNode(true);
const style = document.createElementNS("http://www.w3.org/2000/svg", "style");
style.textContent = `
@font-face {
font-family: gcb;
src: url("${ dataURL }") format("opentype");
}
text {
font: 15pt gcb;
}
`;
svgNode.prepend(style);
svgNode.querySelector("text").textContent = "I'm a svg drawn in a canvas";
const markup = new XMLSerializer().serializeToString(svgNode);
const svgBlob = new Blob([markup], { type: "image/svg+xml" });
const img = new Image;
await new Promise((res, rej) => {
img.onload = e => setTimeout(res, 100); // webkit fires load before inner fonts are loaded...
img.onerror = rej;
img.src = URL.createObjectURL(svgBlob);
});
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d", { alpha: false });
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, 300, 30);
ctx.font = "15pt gcb";
ctx.drawImage(img, 0, 0);
})().catch(console.error)
span, text {
font: 15pt gcb;
}
@font-face {
font-family: gcb;
src: url(https://cdn.jsdelivr.net/gh/Fontself/TypeWithPride@master/fonts/Gilbert-Color%20Bold%20Preview_1005.otf) format("opentype");
}
<canvas height=30></canvas><br>
<span>I'm a span</span><br>
<svg height=30 width="300"><text y="15">I'm an SVG</text></svg>

How do I wait for first canvas-repaint until @font-face-font is loaded?

I think I found a solution with the help of Reigel's answer:

$.get('font/url.ttf', function() {
// do canvas codes.... cause font is loaded...
});

Additionally use the font via font-family: 'fontfacename'; for the canvas' parent-element.

Could be that the font is loaded twice, don't know. But without the second load it won't be displayed right.



Related Topics



Leave a reply



Submit