{"id":4691,"date":"2018-04-21T18:01:30","date_gmt":"2018-04-21T16:01:30","guid":{"rendered":"https:\/\/florianbrinkmann.com\/en\/?p=4691"},"modified":"2020-02-09T11:23:36","modified_gmt":"2020-02-09T10:23:36","slug":"glyphhanger","status":"publish","type":"post","link":"https:\/\/florianbrinkmann.com\/en\/glyphhanger-4691\/","title":{"rendered":"Conversion of fonts to WOFF\/WOFF2 and font subsetting with Glyphhanger"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">To use web fonts with wide browser support, we need the font files in <code class=\"lang-markup\">.woff<\/code> and <code class=\"lang-markup\">.woff2<\/code> format. Who does not care about Internet Explorer, Safari on Mac OS before Sierra, and a few mobile browsers can choose only <code class=\"lang-markup\">.woff2<\/code> (there are <a href=\"https:\/\/caniuse.com\/#search=woff\">tables with the browser support of the two formats<\/a> on Can I use).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this post, I show how to use the command line tool <a href=\"https:\/\/github.com\/filamentgroup\/glyphhanger\">Glyphhanger<\/a> to convert <code class=\"lang-markup\">.ttf<\/code> files to <code class=\"lang-markup\">.woff<\/code> and <code class=\"lang-markup\">.woff2<\/code> and subset fonts to remove unused characters.<\/p>\n\n\n\n<!--more-->\n\n\n\n<div class=\"update-box\">\n<p><strong>Update from July 22, 2018:<\/strong> Today I tried to install Glyphhanger and got an error that Chromium could not be downloaded. <a href=\"https:\/\/github.com\/GoogleChrome\/puppeteer\/issues\/2173#issuecomment-399745457\">I found a fix for that in a GitHub issue<\/a> and updated the post accordingly.<\/p>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Why all the effort? There are services like Google Fonts or Typekit...<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">That is right. But if you use fonts via third-party services, user information (at least the IP address) are sent to this service, and that could be problematic with the upcoming GDPR.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Loading fonts locally from the web server is the safer variant.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What is Glyphhanger?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The <a href=\"https:\/\/github.com\/filamentgroup\/glyphhanger\">project\u2019s GitHub repo<\/a> describes it as \u00bbweb font utility belt\u00ab: it comes with various features that make working with web fonts easier. In the blog post \u00bb<a href=\"https:\/\/www.filamentgroup.com\/lab\/glyphhanger\/\">It\u2019s Dangerous to Go Stallone. Take Glyphhanger<\/a>\u00ab, Zach Leatherman introduces the tool that he created for the Filament Group.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Glyphhanger\u2019s features in short:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>creating WOFF\/WOFF2 files from <code class=\"lang-markup\">.ttf<\/code>.<\/li><li>subsetting fonts\n<ul>\n<li>by specifying certain Unicode characters(ranges).<\/li>\n<li>by specifying one or more URL(s) from which the characters of the content are used as subsetting value. For that, there is also a crawler feature, so that linked pages of the website are also analyzed. Besides that, it is possible to provide a <code class=\"lang-markup\">font-family<\/code> value, to only get the characters that are inside of elements with that rule. Via a whitelist, you can specify characters that should be part of the subset in any case.<\/li>\n<\/ul>\n<\/li><li>convert a string to Unicode ranges.<\/li><li>convert Unicode ranges to a string.<\/li><li>CSS rules for using the subset.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Installation of Glyphhanger<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Preliminary note: I installed it on Windows 10 with the <em>Windows Subsystem for Linux<\/em>. For the installation of <a href=\"https:\/\/github.com\/fonttools\/fonttools\">fontTools<\/a> (if I remember correctly) it was necessary to run <em>Bash on Ubuntu on Windows<\/em> as administrator.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To install Glyphhanger, run the following command (using <code class=\"lang-bash\">--unsafe-perm=true --allow-root<\/code> was necessary for me with Ubuntu on Windows 10. Without it, I got an error that Chromium cannot be downloaded):<\/p>\n\n\n<pre class=\"wp-block-code lang-bash\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">npm install -g glyphhanger\u00a0--unsafe-perm=<span class=\"hljs-literal\">true<\/span> --allow-root<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">To use the subsetting feature, you need the Python library fontTools, which can be installed with the following <a href=\"https:\/\/github.com\/filamentgroup\/glyphhanger#installing-pyftsubset\">commands from the readme of the Glyphhanger project<\/a>:<\/p>\n\n\n<pre class=\"wp-block-code lang-bash\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">pip install fonttools\n\n<span class=\"hljs-comment\"># Additional installation for --flavor=woff2<\/span>\ngit <span class=\"hljs-built_in\">clone<\/span> https:\/\/github.com\/google\/brotli\n<span class=\"hljs-built_in\">cd<\/span> brotli\npython setup.py install\n\n<span class=\"hljs-comment\"># Additional installation for --flavor=woff --with-zopfli<\/span>\ngit <span class=\"hljs-built_in\">clone<\/span> https:\/\/github.com\/anthrotype\/py-zopfli\n<span class=\"hljs-built_in\">cd<\/span> py-zopfli\ngit submodule update --init --recursive\npython setup.py install<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">After that, Glyphhanger is ready to use.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Converting <code class=\"lang-markup\">.ttf<\/code> files to <code class=\"lang-markup\">.woff<\/code> and <code class=\"lang-markup\">.woff2<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>First a note:<\/strong> the font license must, of course, allow the font to be used as a web font, be converted and modified (subsetting). The font license should present in the downloaded folder.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you downloaded such a font, for example, from Google Fonts, switch to the command line and navigate to the folder with the font files. To convert them to WOFF and WOFF2 files without subsetting, the following command is enough:<\/p>\n\n\n<pre class=\"wp-block-code lang-bash\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">glyphhanger --formats=woff2,woff --subset=*.ttf<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">You should get back messages from the command line while the tool is working, like the following:<\/p>\n\n\n<pre class=\"wp-block-code lang-bash\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">Subsetting NotoSans-Black.ttf to NotoSans-Black-subset.woff2 (was 458.75 KB, now 162.26 KB)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Depending on the number of font files (and probably the size of the individual files), this may take some time. Afterward, you have files, which you can integrate via the <code class=\"lang-css\">@font-face<\/code> rule.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Useful for that: besides the new font files, you will notice CSS files, that contain examples of the rules to load the fonts:<\/p>\n\n\n<pre class=\"wp-block-code lang-css\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@font-face<\/span> {\n  <span class=\"hljs-attribute\">src<\/span>: <span class=\"hljs-built_in\">url<\/span>(NotoSans-Black-subset.woff2) <span class=\"hljs-built_in\">format<\/span>(<span class=\"hljs-string\">\"woff2\"<\/span>), <span class=\"hljs-built_in\">url<\/span>(NotoSans-Black-subset.woff) <span class=\"hljs-built_in\">format<\/span>(<span class=\"hljs-string\">\"woff\"<\/span>);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Font subsetting<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Now the subsetting of fonts, the removal of unused characters from the font, to make the files smaller. Glyphhanger brings with <code class=\"lang-bash\">--US_ASCII<\/code> and <code class=\"lang-bash\">--LATIN<\/code> two presets for the subsetting. For my conversion from Google Fonts to self-hosted fonts, I used another solution.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The Google Fonts API URL for Noto Sans, for example, looks like this, when only loading the regular style: <a href=\"https:\/\/fonts.googleapis.com\/css?family=Noto+Sans:400\">fonts.googleapis.com\/css?family=Noto+Sans:400<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Accessing this URL will return several <code class=\"lang-css\">@font-face<\/code> rules for various character sets, including the specific <code class=\"lang-css\">unicode-range<\/code> details (with that, supporting browsers will only download the font file if needed). I used the values of <code class=\"lang-css\">unicode-range<\/code> for the <code class=\"lang-bash\">--whitelist<\/code> parameter of Glyphhanger and created one subset after the other for the different character sets.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The command for creating the <em>cyrillic-ext<\/em> subset of Noto Sans, for example, looks like that:<\/p>\n\n\n<pre class=\"wp-block-code lang-bash\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">glyphhanger --formats=woff2,woff --subset=*.ttf --whitelist=<span class=\"hljs-string\">\"U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F\"<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">I used the <code class=\"lang-css\">@font-face<\/code> rules from the Google API, modified the <code class=\"lang-css\">url()<\/code> values, added the <code class=\"lang-css\">url()<\/code> of the WOFF file, and used the <code class=\"lang-css\">unicode-range<\/code> values from the Glyphhanger CSS files (those are shortened by (hopefully) unnecessary characters, for example, <code class=\"lang-css\">U+0460-052F<\/code> becomes <code class=\"lang-css\">U+460-52F<\/code>).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And with that, we now have optimized font files for loading from the website\u2019s server. Like said, the tool comes with various other options, like using the characters from a Website for the subsetting \u2013 that could be handy, for example, if it is a static website, that will not have content changes in the foreseeable future.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>To use web fonts with wide browser support, we need the font files in .woff and .woff2 format. Who does not care about Internet Explorer, Safari on Mac OS before Sierra, and a few mobile browsers can choose only .woff2 (there are tables with the browser support of the two formats on Can I use). [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"wpf_show_in_dewp_planet_feed":false,"flobn_post_versions":"","webmentions_disabled_pings":false,"webmentions_disabled":false,"lazy_load_responsive_images_disabled":false,"footnotes":""},"categories":[6],"tags":[],"class_list":["post-4691","post","type-post","status-publish","format-standard","hentry","category-web-development"],"wp-worthy-pixel":{"ignored":false,"public":"74844d4b833a41c08dc328e2cead9775","server":"vg07.met.vgwort.de","url":"https:\/\/vg07.met.vgwort.de\/na\/74844d4b833a41c08dc328e2cead9775"},"wp-worthy-type":"normal","_links":{"self":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts\/4691","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/comments?post=4691"}],"version-history":[{"count":5,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts\/4691\/revisions"}],"predecessor-version":[{"id":5965,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts\/4691\/revisions\/5965"}],"wp:attachment":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/media?parent=4691"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/categories?post=4691"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/tags?post=4691"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}