0

I have a problem loading .otf fonts in the latest Safari 15.2. This same code used to work just fine in previous versions of the same browser and still does work fine in other browsers.

One of the problematic fonts I experience the issue with is Adobe Gilbert.

I've tried loading the font file in two different ways:

@font-face {
  font-family: gcb;
  src: url(/assets/fonts/Gilbert-Color-Bold-Preview5.otf);
}

and

var selectedFont = new FontFace('gcb', 'url(/assets/fonts/Gilbert-Color-Bold-Preview5.otf)');
selectedFont.load().then(function(font) {
  document.fonts.add(font);
});

(async () => {
  const fontURL = "https://cdn.jsdelivr.net/gh/Fontself/TypeWithPride@master/fonts/Gilbert-Color%20Bold%20Preview_1005.otf";
  const selectedFont = new FontFace('gcb', 'url(' + fontURL + ')');
  await selectedFont.load();
  document.fonts.add(selectedFont);
  const canvas = document.querySelector("canvas");
  ctx = canvas.getContext("2d", { alpha: false });
  ctx.fillStyle = '#fff';
  ctx.fillRect(0, 0, 300, 30);
  ctx.font = "15pt gcb";
  ctx.fillStyle = '#000';
  ctx.fillText("I'm a canvas", 0, 20);
  ctx.font = "15pt sans-serif";
  ctx.fillText(", right?", 105, 20);
})().catch(console.error)
span, text {
  font: 15pt gcb;
}
<canvas height=30></canvas><br>
<span>I'm a span</span><br>
<svg height=30><text y="15">I'm an SVG</text></svg>

In Safari I get nothing drawn in the canvas:

Screenshot of Safari's result showing a blank, then the text ", right?" in black on one line, and two lines of colored text reading "I'm a span" and "I'm an SVG"

While on Chrome I get the text rendered in black, and in Firefox the text rendered in color as expected.

How can I get Safari to at least render the text like Chrome?

Kaiido
  • 123,334
  • 13
  • 219
  • 285
Howard Zoopaloopa
  • 3,798
  • 14
  • 48
  • 87
  • URLs should be quoted in CSS (e.g., `url("/assets/fonts/Gilbert-Color-Bold-Preview5.otf")`). You need to be sure the font is loaded before trying to use it on the `canvas`. The easiest way would be to run your canvas text drawing routine in the `then` after the `load()` of `FontFace`. – Heretic Monkey Jan 19 '22 at 17:35
  • Does this answer your question? [Drawing text to with @font-face does not work at the first time](https://stackoverflow.com/questions/2756575/drawing-text-to-canvas-with-font-face-does-not-work-at-the-first-time) – Heretic Monkey Jan 19 '22 at 17:36
  • @HereticMonkey I have seen that answer, unfortunately does not seem to be a solution to my issue. Also the quotes didn't seem to make a difference. The fonts are definitely available to the document (I can load them in standard divs, added an image above)... – Howard Zoopaloopa Jan 19 '22 at 17:42
  • @HowardZoopaloopa Tough you're saying that it's not a problem with the font not being preloaded yet - maybe Safari **is** the only browser having a problem with that. Did you try making sure that the font is loaded before trying to draw to the canvas? – obscure Jan 19 '22 at 19:32
  • @obscure the image above is divs that render before the canvas is drawn. So I know the fonts are loaded into the document object. Also, I don't have this issue with .ttf fonts, purely .otf fonts :/ – Howard Zoopaloopa Jan 19 '22 at 19:53
  • @HowardZoopaloopa Yeah, but maybe the browser itself updates the DIVs as soon as a font is loaded - surely not the case with a canvas element. – obscure Jan 19 '22 at 19:57
  • @obscure why would it handle .ttf fonts differently? – Howard Zoopaloopa Jan 19 '22 at 20:00
  • @obscure also the canvas elements are not drawn until after those divs have rendered. – Howard Zoopaloopa Jan 19 '22 at 20:01
  • @obscure btw thank you for your responses. I hope I'm not sounding defensive, just trying to figure this out. – Howard Zoopaloopa Jan 19 '22 at 20:03

1 Answers1

1

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>
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • I think you are correct that it is a Safari issue. I'm giving you the checkmark even if this is a hack because at least you've created a method where it is possible. Thank you! – Howard Zoopaloopa Jan 20 '22 at 17:03