|
| 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 the efficient /woff2/ format and splitting the font based on the =unicode-range= to split the font into pieces. On the day this was written, only the /latin/ unicode ranges are needed. |
| 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'll go over my accomplishments. Most of what I did was open source, though I |
| 101 | +did do some uneventful internal-only changes as well. |
| 102 | + |
| 103 | +** colrv1/RadialGradients |
| 104 | + |
| 105 | +*** Task |
| 106 | + |
| 107 | +My starter project was to fix [[https://github.com/googlefonts/sleipnir][Sleipnir]] rendering of color fonts like |
| 108 | +[[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 |
| 109 | +svg. |
| 110 | + |
| 111 | +This one was quite a rabbit hole. The most important skill I picked up was |
| 112 | +understanding the font format. Fonts (ttf/otf files) are composed of tables. The |
| 113 | +tables define things such as contours, supported unicodes, ligatures, kerning, |
| 114 | +and lots of other stuff; it's a fairly extensible format. |
| 115 | + |
| 116 | +To inspect the contents of a font, you can open it with the =ttx= command from |
| 117 | +[[https://github.com/fonttools/fonttools][fonttools]]. This produces an =xml= representation of the tables. To help me out, |
| 118 | +I even made an Emacs package that adds the ability to open ttf, otf, and |
| 119 | +woff2[fn:woff2] font files. Check out [[https://github.com/wmedrano/ttx-mode][=ttx-mode=]] if interested. |
| 120 | + |
| 121 | +*** Detour |
| 122 | + |
| 123 | +It turns out that getting Nabla to work was not too complicated. However, the |
| 124 | +underlying 2D graphics library did not have the full expressiveness of Radial |
| 125 | +Gradients and was missing Sweep Gradients entirely. I originally was going to |
| 126 | +leave it as is, but my manager encouraged me to continue. So I ended up learning |
| 127 | +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]]. |
| 128 | + |
| 129 | +What's rewarding about this is that it affects a few other users. Once /typst/ |
| 130 | +updates their dependencies, they should have better gradients ([[https://github.com/typst/typst/issues/7760][issues]]). I also |
| 131 | +[[https://github.com/linebender/resvg/pull/1014][completed]] /resvg/ integration to better render radial gradients. Both of these |
| 132 | +are pretty popular open source tools. |
| 133 | + |
| 134 | +** IFT Support for TTX |
| 135 | +:PROPERTIES: |
| 136 | +:ID: 3642f9d0-72a4-417b-b4b1-8e39420c65f4 |
| 137 | +:END: |
| 138 | + |
| 139 | +*** Background |
| 140 | +:PROPERTIES: |
| 141 | +:ID: 5bb31103-5b81-476f-a9d3-079015baac71 |
| 142 | +:END: |
| 143 | + |
| 144 | +The main project I should be working on at the moment is landing IFT on the |
| 145 | +client. I said should since I've spent most of this quarter procrastinating and |
| 146 | +failing to comprehend specs. At least I've made a bit of progress. |
| 147 | + |
| 148 | +IFT stands for Incremental Font Transfer. It is a font technology that |
| 149 | +theoretically enables more efficient transfer of fonts. One of my coworkers has |
| 150 | +been working on the spec and prototype for a while. He's done a great job at |
| 151 | +authoring the [[https://www.w3.org/TR/IFT/][W3 Spec]] for it. |
| 152 | + |
| 153 | +IFT requires 2 components: |
| 154 | + |
| 155 | +1. A font must be IFT encoded. This splits the font into a base font and a |
| 156 | + collection of patches. |
| 157 | +2. Client support. Clients (web browsers) must be able to request and apply the |
| 158 | + patches as needed. |
| 159 | + |
| 160 | +#+BEGIN_SRC dot :file ift-encoder.svg :exports results |
| 161 | +digraph IFT { |
| 162 | + rankdir=LR; |
| 163 | + node [fontname="sans-serif"; fontsize=12; shape=record;]; |
| 164 | + font [label="Font File"]; |
| 165 | + base [label="Base Font"]; |
| 166 | + patches [label="Patch 0|Patch 1|⋮|Patch N"]; |
| 167 | + encoder [label="IFT\nEncoder"]; |
| 168 | + font -> encoder; |
| 169 | + encoder -> base; |
| 170 | + encoder -> patches; |
| 171 | +} |
| 172 | +#+END_SRC |
| 173 | + |
| 174 | +#+CAPTION: The encoder converts a font file into a font and its patches. |
| 175 | +#+RESULTS: |
| 176 | +[[file:ift-encoder.svg]] |
| 177 | + |
| 178 | +#+BEGIN_SRC dot :file ift-client.svg :exports results |
| 179 | +digraph IFTClient { |
| 180 | + rankdir=LR; |
| 181 | + node [fontname="sans-serif"; fontsize=12; shape=record;]; |
| 182 | + |
| 183 | + webpage [label="Web Page"]; |
| 184 | + browser [label="Browser"]; |
| 185 | + |
| 186 | + subgraph cluster_cdn { |
| 187 | + label="CDN"; |
| 188 | + style="rounded"; |
| 189 | + color=gray; |
| 190 | + fontname="sans-serif"; |
| 191 | + fontsize=12; |
| 192 | + |
| 193 | + basefont [label="Base Font"]; |
| 194 | + patches [label="Patches"]; |
| 195 | + } |
| 196 | + |
| 197 | + webpage -> browser; |
| 198 | + basefont -> browser; |
| 199 | + patches -> browser; |
| 200 | +} |
| 201 | +#+END_SRC |
| 202 | + |
| 203 | +#+CAPTION: The client must download and apply the relevant patches at runtime. |
| 204 | +#+RESULTS: |
| 205 | +[[file:ift-client.svg]] |
| 206 | + |
| 207 | +So why is it useful to split up a font? This is all to reduce download sizes and |
| 208 | +make things load quickly and more consistently. When using a font, a web page |
| 209 | +may not need all the glyphs (characters) or styles (italic/bold/underline). |
| 210 | + |
| 211 | +*** Actual Deliverables |
| 212 | +:PROPERTIES: |
| 213 | +:ID: 0894041b-86cb-4551-818e-95c382fee073 |
| 214 | +:END: |
| 215 | + |
| 216 | +Well, I've been really procrastinating on this since it's rather |
| 217 | +intimidating. I've read some spec, IFT code, and Chromium code, but that's not |
| 218 | +really a deliverable[fn:ift-progress]. |
| 219 | + |
| 220 | +However, at the end of the quarter I managed to squeeze in =fonttools= support |
| 221 | +([[https://github.com/fonttools/fonttools/pull/4072][PR]]). This allows us to inspect the =IFT= table with the very useful =ttx= |
| 222 | +tool[fn:ttx]. This is useful for debugging the feature as =IFT= contains all the |
| 223 | +metadata for defining the available patches. |
| 224 | + |
| 225 | +*** Next |
| 226 | +:PROPERTIES: |
| 227 | +:ID: ac6005e1-8823-4c19-be1e-b92839a10764 |
| 228 | +:END: |
| 229 | + |
| 230 | +I will continue to read the Chromium code and implement IFT support. My main |
| 231 | +goal is to have a deep understanding of this problem by the end of Q2. |
| 232 | + |
| 233 | +* Footnotes |
| 234 | +:PROPERTIES: |
| 235 | +:ID: 94bb74e4-b31a-4b91-bc53-55343586c83d |
| 236 | +:END: |
| 237 | + |
| 238 | +[fn:startdate] I actually started on the team in late November, but it's a short |
| 239 | +quarter between all the holidays, nobody being present, and some unexpected sick |
| 240 | +days💩. |
| 241 | + |
| 242 | +[fn:woff2] A woff2 font is just a compressed version of a font. |
| 243 | + |
| 244 | +[fn:ift-progress] At the time this was written, my grasp is a lot better. That's |
| 245 | +a relief, but it would be a 2026Q2 update! |
| 246 | + |
| 247 | +[fn:ttx] You know, the thing I talked about earlier. The font file to xml |
| 248 | +converter. |
0 commit comments