Skip to content

Instantly share code, notes, and snippets.

@fangel
Last active February 3, 2017 10:15
Show Gist options
  • Save fangel/59681d2685c7d3bdcc7c9adddb1f9332 to your computer and use it in GitHub Desktop.
Save fangel/59681d2685c7d3bdcc7c9adddb1f9332 to your computer and use it in GitHub Desktop.
A Makefile for automatically subsetting and optimising TTF-fonts to WOFF and WOFF2 web fonts

TTF to WOFF + WOFF2 subset and optimizations

Dependencies

You must install Fonttools and SFNT2WOFF-Zopfli to use the functionality in this Gist. You will also need to ensure that the brotli library is available for Fonttools.

Fonttools can be easily installed using Homebrew, by doing brew install fonttools. If you installed via Brew, you can install the brotli extension using

/usr/local/Cellar/fonttools/3.5.0/libexec/bin/pip install brotli

SFNT2WOFF-Zopfli is a somewhat more complicated to install. The simples way is to just have it in a folder somewhere, which you can do it by downloading the source off of GitHub and then running make inside the downloaded directory.

Placement of files

You should make two folders, called input and output. Place your TTF-files in the input-folder. Then create a text-file called subset.txt which contains the unicode-characters you want the subset to contain.

Running the optimize scripts

Simply run make in this directory.

If Fonttools isn't in your path, use FONTTOOLS_DIR to change where to search for the tools. Similarly for SFNT2WOFF-Zopfli where you can use SFNT2WOFF_DIR to modify the search-path.

If you wish to change the number of iterations that Zopfli runs, then you can use the variable ITERATIONS

If you want to utilize more than one CPU, you can use the -j-flag, which enables parallel processing in Make.

Example

If you have Fonttools installed via Brews (and thus in your path), but have SFNT2WOFF-Zopfli in your user-directory (Users/xyz/sfnt2woff-zopfli), and you want to run 50 Zopfli-iterations and utilize 4 CPU-cores, you can use

SFNT2WOFF_DIR=/Users/xyz/sfnt2woff-zopfli ITERATIONS=50 make -j4

Modifying the subset of the font

The characters of the font that is included in the subset is determined by the contents of the file subset.txt. When you modify it, you can simply rerun make to optimize based on the new subset.

# The following variables determine which directories to look for input fonts,
# and which directory to place the output fonts in.
INDIR = input
OUTDIR = output
SUBSET_FILE = subset.txt
ZOPFLI_ITERATIONS = 15
ifneq ($(shell echo $$ITERATIONS),)
ZOPFLI_ITERATIONS = $(shell echo $$ITERATIONS)
endif
# SUBSET will point to pyftsubset, but we allow changing the search-dir using
# the variable `FONTTOOLS_DIR`
SUBSET := pyftsubset
ifneq ($(shell echo $$FONTTOOLS_DIR),)
SUBSET = $(shell echo $$FONTTOOLS_DIR)/bin/pyftsubset
endif
# WOFF2SFNT & SFNT2WOFF will point to sfnt2woff-zopfli, but again, we allow
# chaing the search-dir. This time using the variable SFNT2WOFF_DIR
WOFF2SFNT = woff2sfnt-zopfli
SFNT2WOFF = sfnt2woff-zopfli
ifneq ($(shell echo $$SFNT2WOFF_DIR),)
WOFF2SFNT = $(shell echo $$SFNT2WOFF_DIR)/woff2sfnt-zopfli
SFNT2WOFF = $(shell echo $$SFNT2WOFF_DIR)/sfnt2woff-zopfli
endif
# Fonts will contain a list of all of our input files. Not used, besides to
# build the next two lists
FONTS := $(shell find $(INDIR) -name '*.ttf')
# A list of all of our woff and woff2 output files. They are based on the
# available input-fonts, but placed in the ourdir.
WOFFS := $(patsubst $(INDIR)/%.ttf,$(OUTDIR)/%.woff,$(FONTS))
WOFF2S := $(patsubst $(INDIR)/%.ttf,$(OUTDIR)/%.woff2,$(FONTS))
# A build-rule for all of our woff-output
$(OUTDIR)/%.woff : $(SUBSET_FILE) $(INDIR)/%.ttf
@# First we subset the font to a normal zlib compressed woff-file
$(SUBSET) --text-file=$(SUBSET_FILE) --layout-features='' --obfuscate-names --flavor=woff $(patsubst $(OUTDIR)/%.woff,$(INDIR)/%.ttf,$@) --output-file=$(patsubst %.woff,%.tmp.woff,$@)
@# And then we want to run it through Zopfli - but to do that we must first
@# convert it to OTF, and then back to WOFF with Zopfli compression
$(WOFF2SFNT) $(patsubst %.woff,%.tmp.woff,$@) > $(patsubst %.woff,%.otf,$@)
$(SFNT2WOFF) -n $(ZOPFLI_ITERATIONS) $(patsubst %.woff,%.otf,$@)
@# We want to output a little debug-info about how much we saved with Zopfli
@echo \# $(patsubst ${OUTDIR}/%.woff,%,$@): Pre zopfli: `wc -c $(patsubst %.woff,%.tmp.woff,$@) | sed -e 's/^[[:space:]]*//' | cut -d' ' -f 1`, With Zopfli: `wc -c $@ | sed -e 's/^[[:space:]]*//' | cut -d' ' -f 1`
@# And then we can clean up our tmp files
$(RM) $(patsubst %.woff,%.otf,$@) $(patsubst %.woff,%.tmp.woff,$@)
# A build-rule for all of our woff-output
$(OUTDIR)/%.woff2 : $(SUBSET_FILE) $(INDIR)/%.ttf
$(SUBSET) --text-file=$(SUBSET_FILE) --layout-features='' --obfuscate-names --flavor=woff2 $(patsubst $(OUTDIR)/%.woff2,$(INDIR)/%.ttf,$@) --output-file=$@
# The all rule, which has all of our woff and woff2 fonts as dependencies.
all : $(WOFFS) $(WOFF2S)
# And a clean-task to remove the built files again
clean :
$(RM) $(OUTDIR)/*.woff $(OUTDIR)/*.woff2 $(OUTDIR)/*.otf
@fangel
Copy link
Author

fangel commented Feb 3, 2017

I admit that I'm pretty bad at doing anything in Make, so it's been a lot of trial-and-error to get it working. If anyone has suggestions for doing something differently, let me know.

If there is enough interest, I guess this should be made into a proper repo.

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