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'> 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>