Skip to content

Instantly share code, notes, and snippets.

@eritbh
Last active September 4, 2021 07:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eritbh/0ac7394c5888e25b6ecd869ed0952183 to your computer and use it in GitHub Desktop.
Save eritbh/0ac7394c5888e25b6ecd869ed0952183 to your computer and use it in GitHub Desktop.

Dealing with Illustrator SVG Fonts

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.

image

This is how I'm saving my illustrator file:

image image image

  • 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment