Snelheid van fonts op je site verbeteren met Glyphhanger

Auteur: Wiljan Slofstra

Recent kregen we te maken met een project waar een lettertype werd gebruikt die behoorlijk groot was. Alhoewel het lettertype alleen gebruikt moest worden voor een paar knoppen, kwam het na beperking tot de latin karakters alsnog uit op ~140KB. Daarnaast waren er nog twee andere lettertypen nodig voor de pagina waarmee de totale gewicht uitkwam op ruim 200KB.

Voor een snelle internetverbinding is dit niet heel schokkend en zul je het allicht niet merken. Maar zodra je deze fonts gaat inladen via een 3G verbinding, ga je merken dat tekst lang onzichtbaar blijft (FOIT, Flash of Invisible Text) of pas later in het juiste font in beeld springt (FOUT, Flash of Unstyled Text), afhankelijk van hoe we het geconfigureerd hebben op de pagina.

Fonts zijn een essentieel deel van de eerste render van een pagina. Deze wil je zoveel mogelijk optimaliseren. Eén van de mogelijkheden is subsetten.

Lettertypen bevatten veel karakters, afhankelijk van het lettertype kan dit lopen van tientallen tot honderden karakters. Het aantal karakters ligt onder andere aan het aantal talen dat ondersteund wordt door een lettertype. Voor de meeste websites hebben we lang niet alles nodig en kunnen we een subset maken van de karakters die we nodig hebben.

Om een subset te maken kunnen we gebruik maken van een commandline tool--Glyphhanger ontwikkeld door de Filament Group.

Installeren van Glyphhanger

Glyphhanger is een NPM package en kan geïnstalleerd worden met:

npm install -g glyphhanger

Dit maakt glyphhanger beschikbaar in je Commandline. Dit is het begin van het verhaal, want je hebt nog een aantal onderdelen nodig om alle features van Glyphhanger te kunnen gebruiken.

Ten eerste heb je fonttools nodig die beschikbaar zijn in Python's package manager:

pip install fonttools

Vervolgens zijn er twee extensies die je nodig hebt om WOFF en WOFF2 te genereren. WOFF is gebaseerd op de Zopfli compressie bibliotheek, en WOFF2 op Brotli compressie. De installatie gaat als volgt:

# Brotli
git clone https://github.com/google/brotli
cd brotli
python setup.py install

# Zopfli
git clone https://github.com/anthrotype/py-zopfli
cd py-zopfli
git submodule update --init --recursive
python setup.py install

Voor de laatste documentatie over de te installeren extensies verwijs ik je door naar de documentatie van Glyphhanger.

Glyphhanger gebruiken

Glyphhanger kun je op verschillende manieren gebruiken. Afhankelijk van hoe je werkt kun je een afweging maken.

Ten eerste kun je een bestand maken met alle karakters die je in de subset wil hebben. Dit kan bijvoorbeeld een .txt of .html bestand zijn. Je kunt ook een deel van je website in het bestand plakken, dan maakt Glyphhanger een font van alle karakters uit de teksten.

Het onderstaande commando subset alle .ttf bestanden in de huidige map op basis van de karakters in test.html.

glyphhanger ./test.html --subset=*.ttf

Een tweede optie is dat Glyphhanger een website scant en de karakters daaruit pakt. Bijvoorbeeld met het volgende commando:

glyphhanger https://www.orangetalent.nl --subset=*.ttf

Het is ook mogelijk dat je Glyphhanger je site laat crawlen, zodat je een completer beeld krijgt:

glyphhanger https://www.orangetalent.nl --spider --subset=*.ttf

Voor de complete documentatie en nog meer opties kan je kijken in de documentatie van Glyphhanger.

Glyphhanger in het build proces

De installatie van Glyphhanger is niet heel complex, maar het vereist wel de installatie van een aantal bibliotheken. Meestal werkt dit prima, maar vooral Puppeteer/Chromium kan nog wel eens voor problemen zorgen bij de installatie. Daarnaast willen we niet altijd handmatig bovenstaande commando's uitvoeren.

Daarom hebben we het gedeeltelijk in ons continuous integratie proces ingebouwd. Binnen OrangeTalent maken we gebruik van GitLab CI om builds, tests en deployments uit te voeren. Het subsetten van lettertypen kunnen we daar deel van laten uitmaken, zodat het altijd automatisch gaat.

De configuratie van GitLab CI gaat doormiddel van een .gitlab-ci.yml bestand. We hebben een aparte image gemaakt voor Glyphhanger om te voorkomen dat de build tools voor Glyphhanger in ons algemene PHP image komen. Dit ziet er bij ons ongeveer zo uit:

subset:
  image: orangetalent/gitlab-ci:glyphhanger
  script:
    - glyphhanger ./characters.html --subset=public/fonts/*.ttf --formats=ttf,woff2,woff --jsdom --css
  artifacts:
    paths:
      - public/fonts
    expire_in: 4 hrs

We definiëren eerst onze Glyphhanger image (orangetalent/gitlab-ci:glyphhanger), deze image bevat alles wat nodig is om het glyphhanger commando uit te voeren.

We specificeren --jsdom, dit is een alternatief op de standaard Puppeteer/Chromium implementatie in Glyphhanger, maar omdat onze image gebaseerd is op Alpine Linux vergt het nog wat werk om Chromium werkend te krijgen.

--css zorgt ervoor dat er in de terminal de @font-face regels worden getoond en dat Glyphhanger .css bestanden output.

In artifacts laten we GitLab CI weten welke bestanden, die we hebben aangemaakt, (tijdelijk) willen bewaren, zodat we ze kunnen deployen of downloaden.

Als alles goed staat ingesteld komt het er als volgt uit te zien in GitLab:

Glyphhanger output in GitLab CI