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
Regular Expression For Extracting Tag Attributes
Unwanted Margin in Inline-Block List Items
How to Turn Off Word Wrapping in Html
Text-Overflow Is Not Working When Using Display:Flex
How to Use Text-Overflow:Ellipsis on Multiline Text
CSS Background Image on Top of <Img>
How to Make Bootstrap 3 Fluid Layout Without Horizontal Scrollbar
Valid to Use <A> (Anchor Tag) Without Href Attribute
Form Inside a Form, Is That Alright
Nesting <A> Inside <Button> Doesn't Work in Firefox
Embed Image in a ≪Button≫ Element
Using "Margin: 0 Auto;" in Internet Explorer 8
Text Overflow Ellipsis on Two Lines
How to Display the Checkbox Over the Images For Selection
How to Position Two Elements Side by Side Using Css
Setting the Cursor in the Element's Default Styles, or in Element:Hover