three

This is a complicated one. I've created a Chrome Extension that will receive JSON-RPC requests over a WebSocket from a client, render text using HTML decoration, and respond back to the client with a PNG image. I'm using a Canvas and an SVG element to render the text using the foreignObject tag that contains the HTML I'm rendering. This all works fantastic except for when I need to use an external font. There are three levels of indirection happening here, so maybe I'm asking too much of the browser; but I hope not.

You might think that rendering text directly to an SVG would be an option, but that means I would lose some of the rich features provided by HTML like word-wrap. There are hacks for word-wrap for SVGs, but I don't want to go down that road. That likely would be just the first of many problems I would encounter.

What I need is for the HTML inside of the SVG tag to recognize the external font I want it to use. This only needs to work in Chromium, so a solution specific to Chromium is a viable option for me.

 <link rel='preload' href=' https://fonts.gstatic.com/s/roboto/v27/KFOkCnqEu92Fr1MmgVxIIzI.woff2 ' as='font' crossorigin='anonymous' /> <style>@font-face { font-family: Roboto; src: url(' https://fonts.gstatic.com/s/roboto/v27/KFOkCnqEu92Fr1MmgVxIIzI.woff2 '); }  body { font-family: Roboto; font-size: 28px; }</style> <canvas style='position:absolute;  top:90px;  left:394px;'  id='canvas'></canvas> <div><span style="font-family:courier; font-size:20px">External straight-HTML (works): </span>Hello World!</ div><br/> <span style='font-family:courier;  font-size:20px'>HTML within SVG (doesn't work): </span><br/><br/> <span style='font-family:courier;  font-size:20px'>&nbsp;& nbsp; Text directly to SVG (works): </span><br/><br/> <script> // Get the canvas element from the above HTML and an associated context. var canvas = document.querySelector('#canvas'); var ctx = canvas.getContext('2d'); // Create an empty SVG image. var svg = new Image(); // Before we set the svg.src value, we need to define what to do  // immediately after the svg element has completed loading. svg.onload = function() { // DOES NOT RENDER USING CORRECT FONT HERE. // Draw the image into the canvas context.   This is the HTML // source containing the foreign object that I wish to render // using the external font. ctx.drawImage(svg, 0, 0); // Prove that the SVG element itself can use the external font. // THIS WORKS, but is not what I want. ctx.font = "28px Roboto"; ctx.fillText("Hello World!", 0, 80); } // Build HTML to create a SVG image generated from HTML. var source = "<svg xmlns=' http://www.w3.org/2000/svg '>" + "<foreignObject width='2000' height='800' overflow='visible'>" + "<div xmlns=' http://www.w3.org/1999/xhtml '>"  + "<link rel='preload' href=' https://fonts.gstatic.com/s/roboto/v27/KFOkCnqEu92Fr1MmgVxIIzI.woff2 ' as='font' type='font/woff2' crossorigin='anonymous' />" + "<style>@font-face { font-family: Roboto; src: url(' https://fonts.gstatic.com/s/roboto/v27/KFOkCnqEu92Fr1MmgVxIIzI.woff2 ); }  body { font-family: Roboto; font-size: 28px; }</style>" + "<div>Hello World!</div>" + "</div>" + "</foreignObject>" + "</svg>"; // This doesn't always work because sometimes the font isn't yet available even though we've theoretically preloaded the font. svg.src = 'data:image/svg+xml; charset=utf-8,' + encodeURIComponent(source); </script>

two
  • two
    SVG used as an image must be self contained so you'd need to convert the font to a data URL and embed it into the SVG source. Apr 5, 2021 at 19:53
  • Thanks for the advice! I do remember seeing something about that, but I think the dots didn't connect in my head because a canvas lets you live-build an SVG without using a data URL. I see that the key here is by my using svg.src I'm effectively specifying a static SVG image (not building an SVG using canvas tools), and therefore that src image needs to contain everything needed to draw the SVG. I'll get right on that and hopefully post the answer here upon success. Thanks again!
    –  Jerry
    Apr 6, 2021 at 10:25

1 Answer one

Reset to default
five

Special thanks to Robert Longson for setting me on the right track. The primary issue was that when you're setting the SVG's src data value, your SVG data must contain EVERYTHING it needs to draw the image within that source data. This is also true when using a foreign object. This is very different than drawing an SVG image on an HTML canvas. So (along with some other minor changes) the external font itself must be part of that src data, and therefore you must provide a data URL containing that font data. The other thing I had to do was explicitly specify which font to use within the div's style.

 <style id="myStyle">@font-face { font-family: Roboto; src: url('data:application/octet-stream; base64, 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 PNApxyvmAw=='); }  body { font-family: Roboto; font-size: 28px; }</style> <canvas style='position:absolute;  top:75px;  left:394px;'  id='canvas'></canvas> <div><span style="font-family:courier; font-size:20px">External straight-HTML (works): </span>Hello World!</ div><br/> <span style='font-family:courier;  font-size:20px'>&nbsp;& nbsp;& nbsp; HTML within SVG (now works): </span><br/><br/> <span style='font-family:courier;  font-size:20px'>&nbsp;& nbsp; Text directly to SVG (works): </span><br/><br/> <script> // Get the canvas element from the above HTML and an associated context. var canvas = document.querySelector('#canvas'); var ctx = canvas.getContext('2d'); // Create an empty SVG image. var svg = new Image(); // Before we set the svg.src value, we need to define what to do  // immediately after the svg element has completed loading. svg.onload = function() { // Draw the image into the canvas context.   This is the HTML // source containing the foreign object that I wish to render // using the external font.   THIS NOW WORKS. ctx.drawImage(svg, 0, 0); // Prove that the SVG element itself can use the external font. // THIS WORKS, but is not what I want. ctx.font = "28px Roboto"; ctx.fillText("Hello World!", 0, 95); } // Hacky way of getting the data URI from the above HTML. // I would have just duplicated the data URI below, but Stack  // Overflow limits the size of this source, and that would make // it too big, so I have to programmatically extract it. var dataUri = document.getElementById("myStyle").innerText.split("url('")[1].split("');")[0]; // Build HTML to create a SVG image generated from HTML. var source = "<svg xmlns=' http://www.w3.org/2000/svg '>" // Beginning of foreign object. + "<foreignObject width='2000' height='800' overflow='visible'><div xmlns=' http://www.w3.org/1999/xhtml '>"  // This NOW WORKS inside of the foreign object. + "<style>@font-face { font-family: Roboto; src: url('" + dataUri + "'); }</style>" + "<div style='font-family:Roboto; font-size:28px'>Hello World!</div>" // End of foreign object. + "</div></foreignObject>" + "</svg>"; // Set the SVG source data now. svg.src = 'data:image/svg+xml; charset=utf-8,' + encodeURIComponent(source); </script>

six
  • But the texts "Hello World!" in the 2nd and 3rd lines are not the same as the text in the first line. The first line shows thinner, cleaner font. How can be this addressed to have precise, the same rendering?
    –  Ωmega
    May 21, 2022 at 16:40
  • Ωmega, what browser are you using? I'm getting consistent results using a Chromium-based browser. All three "Hello World!" texts look the same to me when running the code snippet directly above.
    –  Jerry
    May 22, 2022 at 20:04
  • My Chrome 101.0.4951.67 (Official Build) (64-bit) renders it like this: pasteboard.co/grouCaopeVCv.png
    –  Ωmega
    May 22, 2022 at 20:26
  • Ωmega, I think the answer is that the top line is scalable HTML, but the bottom two are actually images that won't scale when you zoom in. I can get the same results as the image you posted by zooming my browser to 200%. I'm guessing your browser is zoomed in when you're viewing this post. Try resetting your zoom to 100%. If you need a higher resolution SVG image, then when you generate the image I think you'd need to up the SVG resolution and also increase the size of the font being rendered. Does that make sense?
    –  Jerry
    May 23, 2022 at 16:45
  • SVG = Scalable Vector Graphics --- it should be scalable and clean during zooming.
    –  Ωmega
    May 23, 2022 at 18:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy .

Not the answer you're looking for? Browse other questions tagged or ask your own question .