In this post I’m going to go over “fonts for xcb” a mini-project I’ve been working on recently and I’ll document the parts that are not usually found online.
XCB, the X C Binding, is a client library that offers a set of C functions to interact with the X graphical server in a lightweight minimal way. You can refer to this older post to understand more about what this library is about. XCB is getting popular in the niche world of small graphical utilities and window manager however it doesn’t offer everything. One thing in particular it doesn’t offer is a facility for pretty font handling.
There are many reasons for this, first not many are interested in XCB in itself, Xlib (The older easier synchronous X client side library) is much easier to handle. Second of all, XCB is hardly documented and thus has a big barrier of entry. Third of all, it’s missing wrapper functions that were present in Xlib. And fourth of all, Wayland is on its way (though long way) to make X deprecated.
That is why I’ve made the font-for-xcb project, to document my process and offer a base for others that are interested in learning about fonts, rendering, and XCB.
Enough talk, let’s got through the process needed to implement good font rendering with xcb.
You probably need to review what was discussed in this post Fonts on Unix as it’s a direct application of the knowledge from there.
The people that truly understand the full font stack can be counted on one’s hand, I’d like to salute those true heroes.
The steps can be divided into the following:
- Fontconfig for font selection and configuration
- Freetype for font rasterization
- Xrender for rendering
- Extra - Font fallback
- Wrappers for merging things together in an easy library
Fontconfig for font selection and configuration
To begin you have to select a font and how that font is going to be
displayed. The best way to do that is to rely on the fontconfig library,
which is probably already present on your machine. You know this is the
case when you issue the command fc-match "some font name"
.
The reference for developers is found here however it isn’t explaining but rather simply laying down the function names and data structures within the library.
The crux of the functionality lies within building a “search pattern” and matching using this pattern. A “search pattern” is composed of whatever font properties you are looking for, those are not limited to font names but can be anything from font weight to font family name, to what characters should be present in the font character set. You can find the list of possible font properties here.
The possibilities with fontconfig are not limited to search but everything related to fonts and fonts sets listing, getting the properties in the font matched, manipulating the structures related to fonts like characters in different encoding such as utf8, utf16, and Ucs4, and much more.
The easiest way to do the search is to match the font from a string that will be interpreted automatically by fontconfig as all the parameters that need to be found in the result of the search. If you’ve ever used Xft this is the same search string in a form similar to:
<families>-<point sizes>:<name1>=<values1>:<name2>=<values2>...
This is documented in the user page for fontconfig here. To do that you have to convert the font query from a string to an actual usable pattern.
As you can see
FcNameParse
isn’t documented in the reference. fc_finding_pattern
will contain the
pattern that we can use to do the matching later on. So the next step is
to use that pattern to match a font, however it’s a good idea, sometimes,
to let fontconfig fill some default configuration is has for things we
haven’t set yet in the search. We do that using FcDefaultSubstitute
and passing it the pattern. There’s also a substitution to fill system
configuration values set for the kind of pattern, a FcMatchPattern.
So how do we know what it’s actually filling in the pattern? There’s a function offered by fontconfig to do exactly that.
The last step after creating the pattern is the actual matching.
The result will also be stored in a pattern structure and whatever is not
needed anymore should be cleaned using fontconfig built-in functions such
as FcPatternDestroy
. You may ask what is the difference if the result
is stored in a pattern but the search is also a pattern. What differs
is that the real values of the font properties will be filled and usable
now. Properties such as file
which is “The filename holding the font”
for example.
Let’s be aware that some values returned or filled from a fontconfig
function may not need to be cleaned as they are pointers to other values
found inside living structures and not newly allocated values. For
instance, when issuing FcPatternGet for the fc_file
in the example
above we don’t need to free the variable.
And so we’ve found our font, now how to load and use the glyphs in it?
Freetype for font rasterization
We have a match and its properties, we should now load the font content itself so that we can use the glyphs in it. That is what we call rasterizing, converting from a mathematical definition (here the font file) to actual bitmap/pixel representation. This is done with the Freetype library. To start with Freetype it’s good to read their Glyph Conventions page. It explains things such as the difference between a face and a font family, what are the metrics used in fonts, kerning, and much more.
Loading a face happens in 3 steps. (1) The freetype library needs to be
loaded if not already. (2) The value for the font file location and the
face index within that font file needs to be extracted from the fontconfig
pattern match of earlier. (3) Finally the loading of the face happens with
FT_New_Face
.
Much more options can be set on the face later on after it has been loaded. Glyph modifications such as slants for italic, pixel or point size change, etc.
Let’s say we have some text we want to write, we have loaded a face, and we want to get the glyph for every character in that text. Every glyph in a face has an index, and we use that index to fetch the glyph.
The FT_Load_Glyph
function renders/rasterizes the glyph and
make the bitmap object of type FT_Bitmap
point to it. In fact
face->glyph->bitmap
will always point to the latest rasterized glyph.
We have the bits/pixels form of the character we want to display on the screen but what if there are many of them, how would we know the space to add between every character. There are fonts that are horizontal, others that are vertical, some with ligature, and other much fancier features. Unfortunately this isn’t something that is provided by Freetype, those last features are part of what we refer to as a font shaping engine such as HarfBuzz. However, for simple cases like ours, the distance between glyphs can be queried from the “advance”, which is a glyph metric that you get when loading the font via Freetype representing how much you need to move forward in x or y position.
The division by 64 is out of scope of this article, it is related to typographic metrics. You can check this page for more information.
Xrender for rendering
Alright, thus far we haven’t mentioned xcb at all but in this section
we will. We have loaded our glyph from its glyph index within the font
file into an FT_Bitmap
structure, that means we have its representation
rasterized, its pixel form, now how do we get this unto the screen.
To do that we have to yet again tackle another topic called “digital image composition” which is the base for rendering within the X window system. The documenttion on this one is also scarce, all theoretical. Those two pages are the base of the concept: “The X Rendering Extension” and “Design and Implementation of the X Rendering Extension”. Rendering was also discussed in one of the previous posts called Drawables, Regions, Shapes, Types of WM, Reparenting, Compositing, Redirecting, Unredirecting, Rendering. It’s a good idea to review that last post to get a better general knowledge of rendering.
Digital image composition, or what we call here “rendering” is about taking the pixels we’ve got from somewhere (glyph) and mix them up with other pixels from somewhere else (on a pixmap). There are many rendering engines as was said in the previous post, so we’ll limit ourselves to discussing the XRender engine.
All rendering engine, Xrender included, offer basic pixel operations that come from a paper by Porter and Duff in 1984 called the “Porter-Duff compositing model”, also known as alpha compositing. The cool and maybe intimidating name asides those operations are quite simple, they are bitwise operation done on the two pixels given as input. Things such as binary Xor and Add are certainly there, plus operations such as Src or Dst which will only take one side of the inputs (the source or the destination). There are plenty of others like reversing the pixels, or merging them, or taking the minimum or the maximum values.
To use that rendering concept within XCB you can rely on the XRender library or use an external one like OpenGL. Let’s limit ourselves to XRender.
The world of xcb introduces the concept of “picture” to join together the rendering operation with the canvas/pixmap it’s drawing to.
Like other things within the X windowing system, pictures are objects with unique identifiers.
To create a picture, attaching both rendering/composition to a drawable such as a pixmap, you need at least two things: A mode of rendering (how precise does the merging happen) and a picture format (the depth and pixel format of the picture).
The picture could be used to do any composition operations and the
result will appear on the drawable. Specifically in our case to draw
glyphs xcb provides a helper data structure to hold a set of glyphs and
draw text that has characters within that glyph set, this is called the
xcb_render_glyphset_t
.
As with the picture the glyphset being a composition object needs to have
a format. To add a glyph of the FT_Bitmap
type to the glyphset we have
to fill an xcb_render_glyphinfo_t
, which is a translation between the
glyph type inside FreeType and the ones within XCB.
Until now, we’ve discovered these things in this section:
- What composition operations are
- What a “picture” structure is and how it links drawables to the operations
- How a glyphset structure can be used to store glyphs converted from Freetype format to something xcb understands in its composition library
The next step is to take those glyphs and use them to draw some text. To do that we are also going to require the aid of some helper functions.
That helper function is the xcb_render_util_composite_text
which
executes a composition operation OVER
(draws over without any merging)
from a picture containing some simple color (fg_pen
is a 1x1 pixel
colored pictured) to the final picture destination we want (the one that
has the drawable on it). This function requires a text stream object,
which can be created from the glyphset we’ve built beforehand and from
some text we want to write.
(NB: As stated here this only supports drawing ~252 glyphs at a time)
This is what happens in the xcb_render_util_glyphs_32
call, the
glyphset that was already loaded in the text stream is asked to load
the characters from a string (text.str
in this case). The _32
prepending the function indicates that this text is in Ucs4 format,
there are also _16
postfix for UTF-16 and _8
for UTF-8 format. The
text conversion functions can be found in the fontconfig library, as
mentioned before, so we don’t have to worry about those. (String
utilities)
And that’s it, at this point we’ll have our characters drawn on the drawable using whatever font we wanted!
… Also don’t forget to clean up afterwards.
Extra - Font fallback
There’s the edge case of font fallback we haven’t mentioned. What if you load a font with FreeType and when the time comes to get the glyph index of that character you can’t find it (glyph_index == 0). That character won’t be drawn on the screen or it’ll be drawn with the default box that shows non-support.
The way to handle that is to let the user of the function pass a list of search patterns and switch to the next font in the list when fontconfig says that the font doesn’t have the character needed to be drawn.
Wrappers for merging things together in an easy library
All that’s left is to wrap everything nicely in an easy-to-use wrapper tying everything together.
Conclusion
The process can be summarized by this list:
- Fontconfig
- Build a search query for fontconfig
- Let fontconfig substitute within that pattern to fill the default query parameters
- Match the font using fontconfig
- Freetype
- Load the face
- Fetch the glyph_index for the character needed
- Get the bitmap related
- Get the advance in x and y position
- Rendering
- Create a picture linking composition to drawable
- Create a glyphset to contain the glyph information
- Convert the glyph information from the bitmap Freetype format to XCB format
- Create a text stream using the glyphset to facilitate drawing
- Tell the text stream to draw some specific text
- Composite the stream on the picture
Again the implementation can be found at the font-for-xcb project, it’s not perfect but it can be used as an example for later work and reference.
Let’s hope this is going to help understand how the fonts are drawn on the X window system and Unix. Cheers!
Attribution
Gearóid Molloy, Phillipp HeiseThis file was made by User:Sven TranslationIf this image contains text, it can be translated easily into yourlanguage. If you need help, contact me Flexible licensesIf you want to use this picture with another license than stated below,contact me Contact the authorIf you need a really fast answer, mail me. If youneed only a fast answer, write me here. / GPL (http://www.gnu.org/licenses/gpl.html)
If you want to have a more in depth discussion I'm always available by email or irc.
We can discuss and argue about what you like and dislike, about new ideas to consider, opinions, etc..
If you don't feel like "having a discussion" or are intimidated by emails
then you can simply say something small in the comment sections below
and/or share it with your friends.