|
| 1 | +#+title: Fonts Q1 |
| 2 | +#+date: <2026-04-11 Sat> |
| 3 | +#+author: Will S. Medrano |
| 4 | + |
| 5 | +* Introduction |
| 6 | +:PROPERTIES: |
| 7 | +:ID: 8441fc9d-1865-439c-95de-5de5833e41f0 |
| 8 | +:END: |
| 9 | + |
| 10 | +I just completed my first full quarter on my new team[fn:startdate], the Google |
| 11 | +Fonts team. It went about as I expected; I learned a lot of stuff, but wish I |
| 12 | +had learned it all faster. |
| 13 | + |
| 14 | +So what does the Google Fonts team do? Quite a bit of random stuff here and |
| 15 | +there, some of it internal to Google, and some of it open source. It's easy to |
| 16 | +talk about the more open stuff than the internal. |
| 17 | + |
| 18 | +** Google Fonts |
| 19 | +:PROPERTIES: |
| 20 | +:ID: eae09021-7a19-4c3f-acd7-c48c0c58f804 |
| 21 | +:END: |
| 22 | + |
| 23 | +The easiest thing to explain under my team's purview is [[https://fonts.google.com/][fonts.google.com]]. You |
| 24 | +have probably used this! Many websites load their fonts from the Google Fonts |
| 25 | +catalog. Google Fonts lets you browse a catalog of fonts. Once the fonts have |
| 26 | +been selected, the site gives you a link to embed the font files into your |
| 27 | +website. |
| 28 | + |
| 29 | +#+BEGIN_SRC html |
| 30 | +<link rel="preconnect" href="https://fonts.googleapis.com"> |
| 31 | +<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
| 32 | +<link href="https://fonts.googleapis.com/css2?family=Baskervville:wght@700&family=Sen:wght@400..800&display=swap" rel="stylesheet"> |
| 33 | +#+END_SRC |
| 34 | + |
| 35 | +The neat part about the Google Fonts API is that: |
| 36 | + |
| 37 | +1. Google Fonts provides the font hosting. No need to download and serve the fonts. |
| 38 | +2. Efficient (enough) formatting. If you look at the generated CSS, you can see |
| 39 | + a few different technologies to better serve fonts. |
| 40 | + |
| 41 | +#+CAPTION: Note the use of =woff2= and =unicode-range= to serve only the required glyphs. This page actually only needs the /latin/ range. |
| 42 | +#+BEGIN_SRC css |
| 43 | +/* latin-ext */ |
| 44 | +@font-face { |
| 45 | + font-family: 'Baskervville'; |
| 46 | + font-style: normal; |
| 47 | + font-weight: 700; |
| 48 | + src: url(https://fonts.gstatic.com/s/baskervville/v20/YA9Br0yU4l_XOrogbkun3kQ6vLFYXmpq8sRsYgfsigq4dC1F.woff2) format('woff2'); |
| 49 | + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; |
| 50 | +} |
| 51 | +/* latin */ |
| 52 | +@font-face { |
| 53 | + font-family: 'Baskervville'; |
| 54 | + font-style: normal; |
| 55 | + font-weight: 700; |
| 56 | + src: url(https://fonts.gstatic.com/s/baskervville/v20/YA9Br0yU4l_XOrogbkun3kQ6vLFYXmpq8sRsYgfsigS4dA.woff2) format('woff2'); |
| 57 | + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; |
| 58 | +} |
| 59 | +/* latin-ext */ |
| 60 | +@font-face { |
| 61 | + font-family: 'Sen'; |
| 62 | + font-style: normal; |
| 63 | + font-weight: 400 800; |
| 64 | + src: url(https://fonts.gstatic.com/s/sen/v12/6xKjdSxYI9_3kvWNEmo.woff2) format('woff2'); |
| 65 | + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; |
| 66 | +} |
| 67 | +/* latin */ |
| 68 | +@font-face { |
| 69 | + font-family: 'Sen'; |
| 70 | + font-style: normal; |
| 71 | + font-weight: 400 800; |
| 72 | + src: url(https://fonts.gstatic.com/s/sen/v12/6xKjdSxYI9_3nPWN.woff2) format('woff2'); |
| 73 | + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; |
| 74 | +} |
| 75 | +#+END_SRC |
| 76 | + |
| 77 | +** Font Compiler |
| 78 | +:PROPERTIES: |
| 79 | +:ID: 346f6172-0f28-4b18-b41f-a27789e7c580 |
| 80 | +:END: |
| 81 | + |
| 82 | +Google maintains and develops [[https://github.com/googlefonts/fontc][fontc]], a Rust-based compiler that takes font sources |
| 83 | +and converts them into ttf/otf files. It's a lot quicker than the previous Python |
| 84 | +font compiler, [[https://github.com/googlefonts/fontmake][fontmake]], which was also developed and maintained by Google. |
| 85 | + |
| 86 | +** Fontations |
| 87 | +:PROPERTIES: |
| 88 | +:ID: 412ffb16-9652-4652-bb99-d3abac6383b3 |
| 89 | +:END: |
| 90 | + |
| 91 | +The Rust [[https://github.com/googlefonts/fontations][Fontations]] libraries provide a foundation to read and write font |
| 92 | +data. This library is used in =fontc= as well as Skia, the 2D graphics engine |
| 93 | +used by Chrome, Chromium, Firefox, and Android. |
| 94 | + |
| 95 | +* Accomplishments |
| 96 | +:PROPERTIES: |
| 97 | +:ID: b806dbeb-ba06-497c-8f8d-3d733cff1d16 |
| 98 | +:END: |
| 99 | + |
| 100 | +I did a few things that are internal to Google, but most of my work has been |
| 101 | +open source. Since it's open source, I can actually present some of the stuff |
| 102 | +I've done. |
| 103 | + |
| 104 | +** colrv1/RadialGradients |
| 105 | + |
| 106 | +*** Task |
| 107 | + |
| 108 | +My starter project was to fix [[https://github.com/googlefonts/sleipnir][Sleipnir]] rendering of color fonts like |
| 109 | +[[https://fonts.google.com/specimen/Nabla?query=nabla&preview.script=Latn][Nabla]]. Sleipnir is a Rust library that can render text and icons into png or |
| 110 | +svg. |
| 111 | + |
| 112 | +This one was quite a rabbit hole. The most important skill I picked up was |
| 113 | +understanding the font format. Fonts (ttf/otf files) are composed of tables. The |
| 114 | +tables define things such as contours, supported unicodes, ligatures, kerning, |
| 115 | +and lots of other stuff; it's a fairly extensible format. |
| 116 | + |
| 117 | +To inspect the contents of a font, you can open it with the =ttx= command from |
| 118 | +[[https://github.com/fonttools/fonttools][fonttools]]. This produces an =xml= representation of the tables. To help me out, |
| 119 | +I even made an Emacs package that adds the ability to open ttf, otf, and |
| 120 | +woff2[fn:woff2] font files. Check out [[https://github.com/wmedrano/ttx-mode][=ttx-mode=]] if interested. |
| 121 | + |
| 122 | +*** Detour |
| 123 | + |
| 124 | +It turns out that getting Nabla to work was not too complicated. However, the |
| 125 | +underlying 2D graphics library did not have the full expressiveness of Radial |
| 126 | +Gradients and was missing Sweep Gradients entirely. I originally was going to |
| 127 | +leave it as is, but my manager encouraged me to continue. So I ended up learning |
| 128 | +some math and implementing [[https://github.com/linebender/tiny-skia/pull/164][radial gradient]] and [[https://github.com/linebender/tiny-skia/pull/166][sweep gradients]]. |
| 129 | + |
| 130 | +What's rewarding about this is that it affects a few other users. Once /typst/ |
| 131 | +updates their dependencies, they should have better gradients ([[https://github.com/typst/typst/issues/7760][issues]]). I also |
| 132 | +[[https://github.com/linebender/resvg/pull/1014][completed]] /resvg/ integration to better render radial gradients. Both of these |
| 133 | +are pretty popular open source tools. |
| 134 | + |
| 135 | +** IFT Support for TTX |
| 136 | +:PROPERTIES: |
| 137 | +:ID: 3642f9d0-72a4-417b-b4b1-8e39420c65f4 |
| 138 | +:END: |
| 139 | + |
| 140 | +*** Background |
| 141 | +:PROPERTIES: |
| 142 | +:ID: 5bb31103-5b81-476f-a9d3-079015baac71 |
| 143 | +:END: |
| 144 | + |
| 145 | +The main project I should be working on at the moment is landing IFT on the |
| 146 | +client. I said should since I've spent most of this quarter procrastinating and |
| 147 | +failing to comprehend specs. At least I've made a bit of progress. |
| 148 | + |
| 149 | +IFT stands for Incremental Font Transfer. It is a font technology that |
| 150 | +theoretically enables more efficient transfer of fonts. One of my coworkers has |
| 151 | +been working on the spec and prototype for a while. He's done a great job at |
| 152 | +authoring the [[https://www.w3.org/TR/IFT/][W3 Spec]] for it. |
| 153 | + |
| 154 | +IFT requires 2 components: |
| 155 | + |
| 156 | +1. A font must be IFT encoded. This splits the font into a base font and a |
| 157 | + collection of patches. |
| 158 | +2. Client support. Clients (web browsers) must be able to request and apply the |
| 159 | + patches as needed. |
| 160 | + |
| 161 | +#+BEGIN_SRC dot :file ift-encoder.svg :exports results |
| 162 | +digraph IFT { |
| 163 | + rankdir=LR; |
| 164 | + node [fontname="sans-serif"; fontsize=12; shape=record;]; |
| 165 | + font [label="Font File"]; |
| 166 | + base [label="Base Font"]; |
| 167 | + patches [label="Patch 0|Patch 1|⋮|Patch N"]; |
| 168 | + encoder [label="IFT\nEncoder"]; |
| 169 | + font -> encoder; |
| 170 | + encoder -> base; |
| 171 | + encoder -> patches; |
| 172 | +} |
| 173 | +#+END_SRC |
| 174 | + |
| 175 | +#+CAPTION: The encoder converts a font file into a font and its patches. |
| 176 | +#+ATTR_HTML: :style width:50%; |
| 177 | +#+RESULTS: |
| 178 | +[[file:ift-encoder.svg]] |
| 179 | + |
| 180 | +#+BEGIN_SRC dot :file ift-client.svg :exports results |
| 181 | +digraph IFTClient { |
| 182 | + rankdir=LR; |
| 183 | + node [fontname="sans-serif"; fontsize=12; shape=record;]; |
| 184 | + |
| 185 | + webpage [label="Web Page"]; |
| 186 | + browser [label="Browser"]; |
| 187 | + |
| 188 | + subgraph cluster_cdn { |
| 189 | + label="CDN"; |
| 190 | + style="rounded"; |
| 191 | + color=gray; |
| 192 | + fontname="sans-serif"; |
| 193 | + fontsize=12; |
| 194 | + |
| 195 | + basefont [label="Base Font"]; |
| 196 | + patches [label="Patches"]; |
| 197 | + } |
| 198 | + |
| 199 | + webpage -> browser; |
| 200 | + basefont -> browser; |
| 201 | + patches -> browser; |
| 202 | +} |
| 203 | +#+END_SRC |
| 204 | + |
| 205 | +#+CAPTION: The client must download and apply the relevant patches at runtime. |
| 206 | +#+ATTR_HTML: :style width:33%; |
| 207 | +#+RESULTS: |
| 208 | +[[file:ift-client.svg]] |
| 209 | + |
| 210 | +So why is it useful to split up a font? This is all to reduce download sizes and |
| 211 | +make things load quickly and more consistently. When using a font, a web page |
| 212 | +may not need all the glyphs (characters) or styles (italic/bold/underline). |
| 213 | + |
| 214 | +*** Actual Deliverables |
| 215 | +:PROPERTIES: |
| 216 | +:ID: 0894041b-86cb-4551-818e-95c382fee073 |
| 217 | +:END: |
| 218 | + |
| 219 | +Well, I've been really procrastinating on this since it's rather |
| 220 | +intimidating. I've read some spec, IFT code, and Chromium code, but that's not |
| 221 | +really a deliverable[fn:ift-progress]. |
| 222 | + |
| 223 | +However, at the end of the quarter I managed to squeeze in =fonttools= support |
| 224 | +([[https://github.com/fonttools/fonttools/pull/4072][PR]]). This allows us to inspect the =IFT= table with the very useful =ttx= |
| 225 | +tool[fn:ttx]. This is useful for debugging the feature as =IFT= contains all the |
| 226 | +metadata for defining the available patches. |
| 227 | + |
| 228 | +*** Next |
| 229 | +:PROPERTIES: |
| 230 | +:ID: ac6005e1-8823-4c19-be1e-b92839a10764 |
| 231 | +:END: |
| 232 | + |
| 233 | +My Q2 goal is to have working IFT support in the browser. This means understanding |
| 234 | +the Chromium code, implementing the feature, and shipping it behind a flag. |
| 235 | + |
| 236 | +* An Appreciation for Typeface |
| 237 | + |
| 238 | +#+CAPTION: It's not all "fonts", Steve. |
| 239 | +#+ATTR_HTML: :style width:50%;margin:auto; |
| 240 | +[[./pooh.jpg]] |
| 241 | + |
| 242 | +I've also gained an appreciation for typeface[fn:typeface]. I've liked tweaking |
| 243 | +around the monospace font I use after I started programming. |
| 244 | + |
| 245 | +| Purpose | Typeface | Support Link | |
| 246 | +|--------------+-----------+----------------------| |
| 247 | +| OS Interface | [[https://indestructibletype.com/Jost.html][Jost*]] | [[https://indestructibletype.com/BuyJost.html][indestructible type*]] | |
| 248 | +| Body Text | [[https://rsms.me/inter/][Inter]] | [[https://rsms.me/inter/download/][rsms]] | |
| 249 | +| Code | Fira Code | 😵💫 | |
| 250 | + |
| 251 | + |
| 252 | +* Footnotes |
| 253 | +:PROPERTIES: |
| 254 | +:ID: 94bb74e4-b31a-4b91-bc53-55343586c83d |
| 255 | +:END: |
| 256 | + |
| 257 | + |
| 258 | +[fn:startdate] I actually started on the team in late November, but it's a short |
| 259 | +quarter between all the holidays, nobody being present, and some unexpected sick |
| 260 | +days💩. |
| 261 | + |
| 262 | +[fn:woff2] A woff2 font is just a compressed version of a font. |
| 263 | + |
| 264 | +[fn:ift-progress] At the time this was written, my grasp is a lot better. That's |
| 265 | +a relief, but it would be a 2026Q2 update! |
| 266 | + |
| 267 | +[fn:ttx] You know, the thing I talked about earlier. The font file to xml |
| 268 | +converter. |
| 269 | + |
| 270 | +[fn:typeface] Typeface is the design, for example, the Helvetica typeface. The |
| 271 | +font is the digital representation manifested into bytes. |
0 commit comments