https://venam.net/blog/unix/2020/09/14/playing_with_fonts.html * Blog * About * Projects * Others Venam's Blog // Patrick Louis Did You Know Fonts Could Do All This? Sep 14, 2020 Confusing Mexican Calendar, at least for those not in the know Freetype, included in the font stack on Unix, is quite complex. There are so many layers to get it to do what it does that it's easy to get lost. From finding the font, to actually rendering it, and everything in between. Like most of the world, I use a rather low screens definition (1366x768 with 96 dpi) and rather old-ish laptop, unlike some font designers that live in a filter bubble where everyone has the latest macbook. Thus, good and legible font rendering is important. Let's play with lesser known toggles available to us when it comes to font rendering and see what they do, let's have fun and explore possibilities. A General Picture Generally, to make a font look better on screens, which are arrays of pixels, we use a combination of these three: * Antialiasing: Applying a light shade around the glyph. It is useful at small scale, when you don't have enough pixels, but it makes most glyphs look bolder. Font anti-alias example * Subpixel rendering: A technique similar to antialias but using subpixels, the color components inside the pixels. By applying a small amount of colors on the sides you can reach more granular precision. However, if applied clumsily, or if you simply move the window containing the text, these colored subpixels will become apparent, what we call fringe. Font sub-pixel rendering example * Hinting: Pixels are blocks but text is made of curves, that means these curves will never match exactly with screen pixels. Hinting is about repositioning or selecting the closest pixels while trying as much as possible to keep the shape of the glyph intact. There are multiple levels of hinting, hinting information provided by the font itself (bytecode interpreter hinting), and hinting provided by the rendering library (auto-hinting). Font hinting example NB: "It's just text"... This article is yet another that shows how fonts aren't as easy as they look. For more info about the font stack, please visit my previous article on the topic, and if you want an idea of what it means to draw them on the screen take a look a this article. What is applied, when, how to control all of this, can we see what they do, and should we even care? Freetype and fontconfig default rendering these days is pretty good, so there shouldn't be anything to worry about; Until there's something to worry about, like a font not looking the way you want. Our first stop will be something that intrigued me because I haven't heard many talk about it: the Freetype driver's properties. The Freetype driver is used whenever hinting is needed, so this is the part it actually changes -- how hinting is applied. Getting The Right Tools For The Task Let's start with arming ourselves with ways to easily test all this. Freetype2 demos utilities are a must, you can clone them here or fetch them from your package repositiory, for example Debian and Arch Linux. These will give you a bunch of useful tools such as ftdiff, ftview, ftstring ftgrid, fttimer, ftbench, and others. The most important ones for us are ftdiff and ftgrid. Example usage: ftdiff -r 96 -s 10 ~/.local/share/fonts/times.ttf ftgrid -r 96 -f 20 10 ~/.local/share/fonts/times.ttf ftstring -r 96 -m 'Hello World!' 10 ~/.local/share/fonts/times.ttf Additionally, you can install pango-view from pango-tools to later test if fontconfig applies your configurations properly. It can be used by preparing a file written in pango markup and displaying it using pango-view --markup file.pangpang. You can set the fontconfig debug level higher to see which font is actually loaded by setting the FC_DEBUG to something like 4096, FC_DEBUG=4096. More values can be found here, we'll use them later to see if our fontconfig settings are applied properly: Name Value Meaning --------------------------------------------------------- MATCH 1 Brief information about font matching MATCHV 2 Extensive font matching information EDIT 4 Monitor match/test/edit execution FONTSET 8 Track loading of font information at startup CACHE 16 Watch cache files being written CACHEV 32 Extensive cache file writing information PARSE 64 (no longer in use) SCAN 128 Watch font files being scanned to build caches SCANV 256 Verbose font file scanning information MEMORY 512 Monitor fontconfig memory usage CONFIG 1024 Monitor which config files are loaded LANGSET 2048 Dump char sets used to construct lang values MATCH2 4096 Display font-matching transformation in patterns Yet another way is to test directly in your browser URL bar: data:text/html,
Hello World
The Freetype2 Drivers Properties So let's get back to our testing of Freetype2 drivers. On this documentation page, ft (freetype) properties are listed and are said to affect the behavior of the drivers, each touching a different one. They are set by modifying the FREETYPE_PROPERTIES environment variable, normally loaded from /etc/profile.d/ freetype2.sh. However, most of the ones listed are targeted at the CFF, Type 1, and CID fonts driver and not at TrueType fonts, so they do nothing if you don't have these font types. The only toggle available for TrueType is the interpreter-version which controls the bytecode interpreter, the rasterizer, and thus how the outline gets hinted. The options available to us are the following: * 35 -- For classic mode GDI (Win 98/2000) * 38 -- GDI+ old (Vista, Win 7), Infinality, considered slow * 40 -- For minimal mode (stripped down Infinality, this is the default) (After Win 7) Kind of weird that we jump from 35 to 38, where did 36 and the rest go? The answer is that it's a choice from the Freetype devs to only include those and not the ones in between. And the differences look as follows, notice the native hinter in the left column: * v35 FREETYPE_PROPERTIES="truetype:interpreter-version=35" ftdiff -r 96 -s 10 ~/.local/share/fonts/times.ttf ftdiff interpreter v35 FREETYPE_PROPERTIES="truetype:interpreter-version=35" ftgrid -r 96 -f 36 10 ~/.local/share/fonts/times.ttf ftgrid interpreter v35 * v38 FREETYPE_PROPERTIES="truetype:interpreter-version=38" ftdiff -r 96 -s 10 ~/.local/share/fonts/times.ttf ftdiff interpreter v38 FREETYPE_PROPERTIES="truetype:interpreter-version=38" ftgrid -r 96 -f 36 10 ~/.local/share/fonts/times.ttf ftgrid interpreter v38 * v40 FREETYPE_PROPERTIES="truetype:interpreter-version=40" ftdiff -r 96 -s 10 ~/.local/share/fonts/times.ttf ftdiff interpreter v40 FREETYPE_PROPERTIES="truetype:interpreter-version=40" ftgrid -r 96 -f 36 10 ~/.local/share/fonts/times.ttf ftgrid interpreter v40 We can also test using pango-view (remember again that this should be a font that has native hinting enabled but not the auto-hinter): Lorem ipsum dolor sit amet, c onsectetur adipiscing elit, s ed do eiusmod tempor incididu nt ut labore et dolore magna aliqua. Ut enim ad minim venia m, quis nostrud exercitation u llamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehende rit in voluptate velit esse ci llum dolore eu fugiat nulla pa riatur. Excepteur sint occaeca t cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. You can also change the font via the --font= argument of pango-view. FREETYPE_PROPERTIES="truetype:interpreter-version=35" pango-view --markup text.bangarang * v35 pango interpreter v35 * v38 pango interpreter v38 * v40 pango interpreter v40 So definitely, older interpreter versions were rougher with hinting, much bolder, and could deform the glyphs. The newer ones are more minimal with it. We also notice that the auto-hinter isn't that bad and that avoiding hinting can help. I took the specific case of the Windows font 'Times New Roman' because it has the reputation of rendering badly with Freetype, mostly because of the job the interpreter does. Applying very light or no hinting at all helps tremendously, even at very small point size as you can see in the next comparison. The hinting does indeed help legibility at this scale but the font shape and personality is completely destroyed. From left to right: v35, v38, v40. pang interpreter small point comparison How Fontconfig Works We're not done with hinting yet, there can be many levels of hinting that can be applied, but let's first take a detour and learn a bit about fontconfig and how to use it. Fontconfig is the layer in the font stack responsible for loading the font along with the configurations that tell the next layer how to find the font file and what changes to apply when rendering it. It is usually composed of a library, a preset of configuration files, and a bunch of helpful tools all starting with the prefix fc- such as: fc-cache, fc-query, fc-match, and fc-conflist, to name a few. The configuration files are usually found in /etc/fonts/ and split into the presets available /etc/fonts/conf.avail, and the chosen presets in /etc/fonts/conf.d, which are symbolic links to the former. The precedence of the rules is alphanumerical, a first-come first-served principle, thus 01-custom-rule.conf will be loaded before 99-not-important-rule.conf. Local user configurations, in the user's $XDG_CONFIG_HOME/fontconfig directory, are loaded from one of these configurations that contains an include statement. On my machine it is the 50-user.conf, so it's precedence is lower than anything loaded before it. This isn't practical when testing rules so rename this file to something like 01-user.conf. Now anything you put in $XDG_CONFIG_HOME/fontconfig/conf.d or $XDG_CONFIG_HOME/fontconfig/ fonts.conf should have priority. You can make sure the order and configurations are loaded properly by using the fc-conflist command. It lists in order of precedence the configurations found, the ones starting with a + are loaded, the ones with - are not. These files are composed of mainly 4 components: * Match rules: If something matches, then edit the properties mentioned. There are ton of matching and editing rules, even including stuff like the program name that is currently trying to load the fonts and custom ones. You can also match at different times: when looking for a pattern/font, after finding the font, when scanning the font. * Aliases creation: An alias is a font name shorthand, it's useful when querying generic family names such as "monospace". * Inclusion of other configurations: There can be so many configuration files that it's good practice to split them. * Where to look for settings and fonts, and if some fonts should be skipped entirely (like if they aren't scalable -- bitmap): You may think that the location of fonts is a constant value, but it's not. For example, on my machine it's set in /etc/fonts/fonts.conf as: