Extract SVG paths from font glyphs on macOS.
Print the version with glyphsvg --version.
./build.shProduces universal (arm64 + x86_64) binaries in build/bin/. The deployment
target defaults to macOS 11.0 regardless of the build machine's OS version;
override it with MIN_MACOS=12.0 ./build.sh if you need a higher floor. Going
lower has no effect on the arm64 slice, since macOS 11.0 is where arm64 begins.
Extract SF Symbols by name from SF Pro Text:
./build/bin/glyphsvg heart bold 768 --output=heart.svgArguments: <name> <weight> <size>
Weight selects the SF Pro Text font variant. Valid weights: black, bold, heavy, light, medium, regular, semibold, thin, ultralight.
A glyph set is a directory holding one or more icon-font faces plus the name-to-codepoint tables that map symbol names onto glyphs. Both variable fonts (Google Material Symbols) and static fonts (Material Design Icons, Fluent System Icons) are rendered by the same code path: the font is loaded from its file, asked which variation axes it declares, and only those axes are applied.
./build/bin/glyphsvg --set=./sets/mdi home 256 --output=home.svg
GLYPHSVG_SET_PATH=./sets ./build/bin/glyphsvg --set=mdi home 256 --output=home.svg
GLYPHSVG_SET_PATH=./sets ./build/bin/glyphsvg --set=nunito Q 256 --weight=900 --output=Q.svgEight sets ship in sets/, in two kinds.
Icon fonts, whose symbols are named pictograms and whose name tables come
from the vendor: mdi (Pictogrammers Material Design Icons, 7188 icons),
fluent (Microsoft Fluent System Icons, 2819 outlined plus 2859 filled) and
phosphor (Phosphor Icons, 1512 icons in five weights). All three are static,
so --face rather than --weight is the control; Phosphor is the one with a
real weight ramp.
Text fonts, whose symbols are characters - for putting a letter or two on an
app icon: nunito (rounded, wght 200..1000), alexandria (geometric sans,
wght 100..900), bungee (signage display, static), monaspace (Monaspace
Krypton, monospace, wght 200..800) and notoemoji (monochrome emoji, wght
300..700). Their name tables are generated from the font's own cmap, and each
entry is named for its character, so a symbol name is simply Q - or 🚀. Four
of the five are variable, so --weight here takes a number anywhere in the
declared range.
Every set's manifest and download script is committed; the fonts and the
codepoint maps are not, so run each set's download.py once - see
sets/README.md.
Arguments: --set=<name|dir> [--face=<face>] <name> [<weight>] <size>
A set given with a / in it is a directory path, used as given. A bare name is
looked up as <root>/<name>/ under each colon-separated entry of
$GLYPHSVG_SET_PATH, then under the current directory - and nowhere else. The tool
does not walk up from its own executable or search a user library directory:
naming the data is the caller's job, and a tool that guesses at other locations is
one that can quietly render from a set nobody asked for.
So an app embedding glyphsvg should pass --set=<absolute dir>, or set
$GLYPHSVG_SET_PATH to the directory holding its sets. This also covers the case
that motivated a wider search - a font downloaded at runtime cannot be written into
a code-signed app bundle, so it goes wherever the app keeps its support files and
the app names that path.
Each set directory carries a glyphset.conf, one key = value per line, #
starts a comment:
| Key | Meaning |
|---|---|
title |
human readable set name, reported by --info |
license |
one-line license name, for a caller to show beside the set |
kind |
icon (pictograms) or text (characters), matched case-blind and reported lowercase; default icon |
font |
font filename, for a set with a single face |
codepoints |
default codepoints filename, used by faces that omit their own |
metadata |
optional search-metadata filename, for callers to read |
default |
face to use when none is requested (default: the first face) |
face |
<name> <fontfile> [codepointsfile], repeatable |
A single-face set needs no face line; the font key gives it a face named
regular. Filenames may not contain spaces. Faces whose font file is missing are
dropped, so a partial install still resolves - and because a partial install must
not shadow a complete one, the search prefers the first root whose set actually
has the requested face (by --face, or by a weight that names a face), falling
back to any usable set only if none does.
A static family, one file per weight:
title = Material Design Icons
license = Pictogrammers Free License (Apache 2.0 terms)
kind = icon
codepoints = mdi.codepoints
face = light mdi-light.ttf
face = regular mdi-regular.ttf
face = bold mdi-bold.ttf
license and kind are for the caller, not for rendering. A picker offering
eight fonts has to say what each one's terms are and has to keep pictogram sets
apart from character sets, and reading that out of the manifest is what stops a
second copy of the same table living in the UI and drifting from this one. An
unrecognized kind is a warning and falls back to icon; a set that names
neither key still resolves, since every set predating them is an icon font.
kind answers what a set's symbols ARE, not how its name table was built. Those
usually agree - a vendor table means pictograms, a cmap means characters - but
notoemoji is the case where they part: its table comes from the font's own
cmap like any text font's, and its symbols are pictographs, so it is kind = icon. The provisioning detail is not what a person choosing artwork is sorting
by.
A single-face set:
title = Material Design Icons
font = materialdesignicons-webfont.ttf
codepoints = mdi.codepoints
Plain text, one <name> <hexcodepoint> per line, e.g. home f02dc. This is the
format Google publishes for Material Symbols, and the common format every set uses.
Material Symbols is a built-in set: a directory holding Google's published
MaterialSymbols<Style>.ttf and .codepoints files needs no glyphset.conf, and
--material[=<style>] is an alias for --set=material [--face=<style>].
First fetch the fonts and codepoint maps (one-time, not committed to git):
./material/download.shThen extract by name:
./build/bin/glyphsvg --material home 256 --output=home.svg
./build/bin/glyphsvg --material=rounded settings bold 256 --output=settings.svg
./build/bin/glyphsvg --material=sharp star 256 --weight=600 --output=./icons/
./build/bin/glyphsvg --material favorite 256 --fill --output=favorite.svgArguments: --material[=<style>] <name> [<weight>] <size>
Styles (faces): outlined (default), rounded, sharp. The data is found the
same way as any other set - material/ under $GLYPHSVG_SET_PATH or the current
directory - and GLYPHSVG_MATERIAL_DIR names the directory holding the files
outright, which is what a caller running from an arbitrary working directory
should use.
Render straight from a font file, with no set directory or manifest:
./build/bin/glyphsvg --font-file=./mdi.ttf --codepoints=./mdi.codepoints home 256 --output=home.svg
./build/bin/glyphsvg --font-file=./mdi.ttf U+F02DC 256 --output=home.svg
./build/bin/glyphsvg --font-file=./Inter.ttf "Hello" 100 --output=./out/Arguments:
--font-file=<path> --codepoints=<path> <name> [<weight>] <size>
--font-file=<path> <characters|codepoint> <size>
With --codepoints=<path> the first argument is a symbol name looked up in that
file, and a weight may precede the size; without it, the first argument is
characters or a codepoint. A single font file has a single face, so --face= does
not apply here - use --set for a family. A --font= argument that names
a font file (it contains a /, or ends in .ttf/.otf/.ttc/.otc and exists)
takes the same by-path route.
These apply to the modes that load a font by path:
| Option | Effect | Applies to |
|---|---|---|
--face=<name> |
face to render with (--style= is an alias) |
--set, --material |
--weight=<N|name> |
the wght axis, or the nearest face of a static family |
all three |
--fill[=<0..1>] |
the FILL axis; default is outline (0) |
all three |
--axis=<TAG>=<N> |
any other variation axis, e.g. --axis=GRAD=200 |
all three |
--info |
print the resolved paths, faces and axes, then exit | all three |
A weight may also be given positionally, before the size. Named weights are the SF
Symbols names (ultralight 100 through black 900) and map onto the wght axis,
clamped to that font's declared range.
An axis a font does not declare is ignored, which is what lets one code path serve variable and static fonts:
- On a variable font the axes do the work.
--weight/--fill/--axissetwght/FILL/anything else, each clamped to the font's own range for that axis. An axis you did not ask about is set to the font's own declared default, never to this tool's idea of "regular" - a font whosewghtaxis runs 0.48 to 3.2 is rendered at its default instance, not pinned to an endpoint. - On a static font there are no axes, so the face does the work. A weight
matching a face name selects that face, and any other weight selects the nearest
face the set actually offers, measured by what each face name means as a weight
(
--weight=800on aregular/bold/blackfamily picksbold, ties going to the lighter face). Nearest-face matching applies only when every face is named for a weight - a set mixing styles and weights (Outlined,bold) is not weight-indexed, so the weight goes to the axis and the default face is used. A weight or fill that nothing can honor is reported as a warning, not an error. - The two are not exclusive. A set may pick a face by weight and be variable, in which case the face selects the file and the axis still moves within it.
--axis=wins over--weight/--fillwhen both name the same axis.
--info is the way to find out which of the two you are dealing with. It prints
key: value lines meant to be parsed:
$ glyphsvg --material=rounded --info
set: material
title: Google Material Symbols
license: Apache License 2.0
kind: icon
dir: /path/to/material
faces: Outlined Rounded Sharp
face: Rounded
font: /path/to/material/MaterialSymbolsRounded.ttf
codepoints: /path/to/material/MaterialSymbolsRounded.codepoints
metadata: /path/to/material/material_symbols_metadata.json
postscript-name: MaterialSymbolsRounded-Regular_Thin
glyphs: 6587
variable: yes
axis: FILL 0 0 1 Fill
axis: GRAD -50 0 200 Grade
axis: opsz 20 24 48 Optical Size
axis: wght 100 400 700 Weight
Each axis: line is <tag> <min> <default> <max> <name>. A static font reports
variable: no and no axis lines, so a UI can offer a face list instead of a weight
control.
Extract glyphs from any installed font using character input or codepoint:
./build/bin/glyphsvg --font=Helvetica "Hello" 100 --output=./output/
./build/bin/glyphsvg --font=Helvetica U+0041 100 --output=A.svgArguments: --font=<name|path> <characters|codepoint> <size>
Codepoint format: U+XXXX or 0xXXXX
--output=<path>specifies output file or directory (a trailing/or an existing directory is treated as a directory)- If output is a directory, each glyph is saved as a separate file. The name is
the symbol name when the glyph was looked up by name (e.g.
home.svg), the codepoint and index for character input (e.g.U+48_0.svg), and the codepoint alone otherwise (e.g.U+0041.svg) - With no
--output, a single glyph is written to stdout - When extracting multiple characters, you must specify an output directory
0- every requested glyph was written.1- anything else: bad arguments, a font that is not installed, a symbol name that is not in the map, a codepoint the font has no glyph for, or a path too large to serialize.
Nothing is written when extraction fails, so a 0 exit means the output files
exist. Glyphs that exist but have no outline (spaces and other blanks inside a
multi-character string) are skipped with a warning and do not fail the run,
unless they leave nothing to write at all.
The tool also refuses to fall back silently: an unknown --font= name is an
error rather than a substituted system font, and Material Symbols extraction
fails if CoreText hands back a different font family than the one loaded from
disk.