tldr: using specific save settings in illustrator and a script, we can generate an SVG file that uses embedded CSS web fonts and can have its text content edited dynamically (this just solves the font display problem, not the text alignment discrepancies introduced by the way text is layed out in the SVG, that will have to wait). Check it out - all the fonts in this processed SVG image show up just fine in Firefox and are being loaded from embedded data:
URIs rather than system fonts.
This is how I'm saving my illustrator file:
- Subsetting: All Glyphs
- CSS Properties: Style elements (the value of this setting actually doesn't matter to the process I think? but the default is stupid so I changed it for development, need to revisit this)
- Output fewer <tspan> elements: enabled
- Responsive: disabled (this may also not matter but I disabled it because we're always going to be displaying overlays at a consistent size for streaming, so we don't need to make the output more complicated to support resizing the
<svg>
object)
Then, after saving, I used the following script to generate the browser compatible file. Yes it's jank as hell but this is a proof of concept don't taze me bro
import * as fs from 'fs';
import * as childProcess from 'child_process';
let overlaySVG = fs.readFileSync('Untitled-1.svg', 'utf-8');
let preamble = overlaySVG.match(/^[\s\S]*?<svg[^>]*?>/)[0];
// Separate out <font>s and process them individually
let fonts = overlaySVG.match(/<font[\s\S]*?<\/font>/g);
for (let fontSource of fonts) {
// Get the name of the font family so we know what to call it
let fontFamily = fontSource.match(/font-family="([^"]*)"/)[1];
// Add the parts of the glyph outlines that Illustrator didn't generate, and
// wrap the whole thing in <svg> so FontForge recognizes it
const fixedFontSource = `${preamble}${fontSource.replace(/M(-?\d+,-?\d+)([^z]*?)(?=M|")/g, 'M$1$2L$1')}</svg>`;
// Write the output to a temporary file because fontforge only opens files
fs.writeFileSync(`out-${fontFamily}.svg`, fixedFontSource, {
encoding: 'utf-8'
});
// Do some FontForge magic to convert the font from SVG to TTF
// this is incredibly unsafe lmao
// tried this at first with WOFF2 but ended up getting browser errors, TTF seems more stable
childProcess.execSync(`fontforge -c "open('out-${fontFamily}.svg').generate('out-${fontFamily}.ttf')"`);
// Read the TTF file, shove it in a data: URI, wrap it in a @font-face
const woffSourceBuffer = fs.readFileSync(`out-${fontFamily}.ttf`);
let fontDataURI = `data:font/ttf;base64,${woffSourceBuffer.toString('base64')}`;
let fontFaceCSS = `
@font-face {
font-family: "${fontFamily}";
src: url(${fontDataURI});
}
`;
// Replace the original font data with a <style> that implements the font
overlaySVG = overlaySVG.replace(fontSource, `<style>${fontFaceCSS}</style>`);
}
console.log(overlaySVG);
I ran it with node script.mjs > out.svg
.