An Xft Tutorial Keith Packard XFree86 Core Team, SuSE Inc. Introduction This is a quick tutorial about the basics of Xft; a comparison to core X routines is given along with some brief examples 1. Opening Fonts Xft uses a more sophisticated font matching scheme than the core protocol, patterns are built out of a list of elements, each with a name and a list of values. Once built, the pattern is matched against all available fonts and the one most closely matching is selected. This contrasts to the core mechanism which uses simple shell-style globbing where the first matching font is selected. 1.1 Xft Font Names Xft font names can be represented in string form for storage in configuration files or internally within applications, presenting these names to the user is not quite as bad as using XLFD, but it's still less polite than using a reasonable UI. The format for Xft font names is: -:=... An arbitrary set of additional elements can be appended to the font name, the complete list of possible properties is: CPP constant Name Type ---------------------------------------------- XFT_FAMILY family String XFT_STYLE style String XFT_SLANT slant Int XFT_WEIGHT weight Int XFT_SIZE size Double XFT_PIXEL_SIZE pixelsize Double XFT_ENCODING encoding String XFT_SPACING spacing Int XFT_FOUNDRY foundry String XFT_CORE core Bool XFT_ANTIALIAS antialias Bool XFT_XLFD xlfd String XFT_FILE file String XFT_INDEX index Int XFT_RASTERIZER rasterizer String XFT_OUTLINE outline Bool XFT_SCALABLE scalable Bool XFT_RGBA rgba Int (Defaults from resources) XFT_SCALE scale Double XFT_RENDER render Bool XFT_MINSPACE minspace Bool (Specific to FreeType rasterizer) XFT_CHAR_WIDTH charwidth Int XFT_CHAR_HEIGHT charheight Int XFT_MATRIX matrix XftMatrix As family and size are both nearly always needed to access a font, they're given a privileged place, but really they're no different than the remaining values. For elements that use an enumerated list of possible values, the values are given names which can be used in place of an integer, or can actually replace the whole name=value part. They're all unique, so this actually works. Here's a list of all of the enumerated values and the associated name: Value Element CPP value constant -------------------------------------------------- light weight XFT_WEIGHT_LIGHT medium weight XFT_WEIGHT_MEDIUM demibold weight XFT_WEIGHT_DEMIBOLD bold weight XFT_WEIGHT_BOLD black weight XFT_WEIGHT_BLACK roman slant XFT_SLANT_ROMAN italic slant XFT_SLANT_ITALIC oblique slant XFT_SLANT_OBLIQUE proportional spacing XFT_PROPORTIONAL mono spacing XFT_MONO charcell spacing XFT_CHARCELL rgb rgba XFT_RGBA_RGB bgr rgba XFT_RGBA_BGR vrgb rgba XFT_RGBA_VRGB vbgr rgba XFT_RGBA_VBGR Some Example Xft Font Patterns: times-12 12 point times times,charter-12:bold 12 point, preferring 'times', but accepting 'charter', bold. times-12:bold:slant=italic,oblique 12 point times bold, either italic or oblique times-12:rgba=vbgr 12 point times, optimized for display on an LCD screen with sub-pixel elements arranged vertically with blue on the top and red on the bottom. times:pixelsize=100 100 pixel times -- pixel size overrides any point size. 1.2. Programatic Font Names Internally, font patterns are stored in an XftPattern structure. This contains an array of pattern elements and can be created, edited and used in several different ways. 1.2.1 Xft string name functions XftPattern * XftNameParse (const char *name); This takes a name in the above form and parses it into an Xft pattern. The inverse function: Bool XftNameUnparse (XftPattern *pat, char *dest, int len); takes a pattern and generates the resulting name, returning False if there's not enough space in dest. 1.2.2 Xft pattern functions At a lower level, Xft patterns can be constructed directly from lists of elements XftPattern * XftPatternCreate (void); This creates a pattern with no elements. XftPattern * XftPatternDuplicate (XftPattern *p); Duplicates a pattern returning the copy. void XftPatternDestroy (XftPattern *p); Destroys a pattern. XftPatternElt * XftPatternFind (XftPattern *p, const char *object, Bool insert); Locates a pattern element with the given name. If 'insert' is True, it will create an empty pattern element if none exists. Bool XftPatternAdd (XftPattern *p, const char *object, XftValue value, Bool append); Add a value to the specified pattern element. If 'append' is true, the value is added after existing values, otherwise it is added before them. XftResult XftPatternGet (XftPattern *p, const char *object, int id, XftValue *v); Return a value from the specified element -- multiple values can be indexed with 'id' starting at zero. Bool XftPatternDel (XftPattern *p, const char *object); Delete the named pattern element. There are short forms of the Add and Get function for each type: Bool XftPatternAddInteger (XftPattern *p, const char *object, int i); Bool XftPatternAddDouble (XftPattern *p, const char *object, double d); Bool XftPatternAddString (XftPattern *p, const char *object, const char *s); Bool XftPatternAddMatrix (XftPattern *p, const char *object, const XftMatrix *s); Bool XftPatternAddBool (XftPattern *p, const char *object, Bool b); XftResult XftPatternGetInteger (XftPattern *p, const char *object, int n, int *i); XftResult XftPatternGetDouble (XftPattern *p, const char *object, int n, double *d); XftResult XftPatternGetString (XftPattern *p, const char *object, int n, char **s); XftResult XftPatternGetMatrix (XftPattern *p, const char *object, int n, XftMatrix **s); XftResult XftPatternGetBool (XftPattern *p, const char *object, int n, Bool *b); There are also two functions to build names more quickly using a list of name/value pairs: XftPattern * XftPatternVaBuild (XftPattern *orig, va_list va); XftPattern * XftPatternBuild (XftPattern *orig, ...); The list of arguments is a set of triplets: name, type, value where 'name' is the element name, 'type' is one of: XftTypeVoid XftTypeInteger XftTypeDouble XftTypeString XftTypeBool XftTypeMatrix and value is the value to set. The list is terminated with a name of 0. The type is included in the API to allow multiple representations for numeric values and also to provide some visual typechecking otherwise unavailable in a varargs function. 1.3 Matching Fonts A constructed pattern is passed to XftPattern * XftFontMatch (Display *dpy, int screen, XftPattern *pattern, XftResult *result); This returns a pattern which represents the font most closely matching the requested pattern. Included in this pattern are the matched font name, the file the font comes from and a bunch of other stuff. This pattern can then be handed off to: XftFont * XftFontOpenPattern (Display *dpy, XftPattern *pattern); which opens the specified font and returns the font object. 1.3.1 XftConfig pattern editing To allow for family aliasing and a myriad of other configuration possibilities, Xft pattern used to match fonts are first edited using the instructions found in the XftConfig file. This file matches incoming patterns and makes substitutions within them before the pattern is compared with the available fonts. The operation of this file might eventually be described in another document. 1.3.2 Convenience functions As the sequence of primitive calls to open a font can be tiresome to type, there are convenience functions as well: XftFont * XftFontOpenPattern (Display *dpy, XftPattern *pattern); Matches and opens a font XftFont * XftFontOpen (Display *dpy, int screen, ...); Takes arguments like XftPatternBuild does and opens a font XftFont * XftFontOpenName (Display *dpy, int screen, const char *name); Parses an Xft-syle font name and opens a font XftFont * XftFontOpenXlfd (Display *dpy, int screen, const char *xlfd); Parses an XLFD name and opens a font. Note that this will not return the same font that XOpenFont would have; the XLFD name is not passed to the X server, rather it is parsed into an Xft pattern and then matched locally. Usually this works better than core X. 2. Listing fonts While the matching functions provide sophisticated heuristics which generally locate a suitable font, it's still nice to present a list of available font families and styles to the user. Xft separates the notion of matching fonts for listing from the list of which values are desired by the application. Xft takes a pattern and a list of element names and returns an array of patterns formed by extracting the desired pattern elements from available fonts which match the specified pattern. Further, Xft returns only those patterns which are unique in the requested elements. This permits applications to list 'available families' by matching all fonts but only returning the family element of each font. 2.1 The XftObjectSet structure The list of desired elements is held in an XftObjectSet structure, it can be built using the varargs functions XftObjectSet * XftObjectSetVaBuild (const char *first, va_list va); XftObjectSet * XftObjectSetBuild (const char *first, ...); The arguments are just a list of element names terminated with null char * pointer: XftObjectSetBuild (XFT_FAMILY, XFT_STYLE, (char *) 0); Alternatively, XftObjectSets may be built incrementally using: XftObjectSet * XftObjectSetCreate (void); and Bool XftObjectSetAdd (XftObjectSet *os, const char *object); 2.3 Generating the list of matching fonts With a pattern to match against, and a list of desired element names, the set of matching fonts is generated with: XftFontSet * XftListFontsPatternObjects (Display *dpy, int screen, XftPattern *pattern, XftObjectSet *os); This returns an XftFontSet which holds the array of patterns constructed by extracting the desired pattern elements from all of the available fonts which match the specified pattern. As a convenience, this whole mess can be done in one call: XftFontSet * XftListFonts (Display *dpy, int screen, ...); This takes first an XftPattern description as used by XftPatternBuild followed by a null char pointer and then a list of element names as used by XftObjectSetBuild followed by another null char pointer: XftListFonts (dpy, screen, XFT_FAMILY, XftTypeString, "times", 0, XFT_STYLE, XFT_WEIGHT, 0); This returns patterns holding all of the unique style and weight combinations for the 'times' family of fonts. 3. Metrics functions Once a font has been opened, metrics for sequences of characters can be computed. Xft metrics use the X Rendering extension data structure which is a bit different than either core X or other metrics data structures: The metrics for each glyph are described by an XGlyphInfo structure: typedef struct _XGlyphInfo { unsigned short width; unsigned short height; short x; short y; short xOff; short yOff; } XGlyphInfo; Width/height are the size of the glyph image in pixels. x,y is the offset from the origin of the glyph image to the rendering origin. xOff, yOff is the normal spacing to the next glyph. Note that x,y is backwards, the location of the bitmap is computed by subtracting them from the rendering origin. So, to compute the rectangle covered by a single glyph rendered at x,y: top = y - glyphInfo.y; left = x - glyphInfo.x; bottom = top + glyphInfo.height; right = left + glyphInfo.width; And to compute the normal location for the next glyph: x = x + glyphInfo.xOff; y = y + glyphInfo.yOff; The "width" of a glyph is thus found in the xOff member of this structure. Xft has four functions that return the metrics of text, depending on their encoding: void XftTextExtents8 (Display *dpy, XftFont *font, XftChar8 *string, int len, XGlyphInfo *extents); void XftTextExtents16 (Display *dpy, XftFont *font, XftChar16 *string, int len, XGlyphInfo *extents); void XftTextExtents32 (Display *dpy, XftFont *font, XftChar32 *string, int len, XGlyphInfo *extents); void XftTextExtentsUtf8 (Display *dpy, XftFont *font, XftChar8 *string, int len, XGlyphInfo *extents); Note that the 'Utf8' function doesn't actually recode the glyphs using the encoding of the font, rather it simply uses the Utf8 encoding to express 32 bit values as a variable-length sequence of bytes. 4. Drawing functions Xft requires additional state related to each drawable surface used as a target, so it wraps X drawables in another data structure called an XftDraw. XftDraw * XftDrawCreate (Display *dpy, Drawable drawable, Visual *visual, Colormap colormap); Creates an XftDraw for the associated drawable using the pixel format found in 'visual' and colors from 'colormap'. To create an XftDraw for a mask bitmap (i.e. a 1-bit alpha channel), use: XftDraw * XftDrawCreateBitmap (Display *dpy, Pixmap bitmap); When the application is finished with an XftDraw, destroy it with: void XftDrawDestroy (XftDraw *draw); 4.1 Modifying an XftDraw The target drawable for an XftDraw can be switched with void XftDrawChange (XftDraw *draw, Drawable drawable); This function assumes that the new drawable uses the same visual and colormap as the old. The clip list for an XftDraw is set with: Bool XftDrawSetClip (XftDraw *d, Region r); Construct the region using regular Xlib functions. 4.2 Color Allocation As Xft hides the difference between core X text and Render extension text, it requires both pixel values and ARGB values to draw with. An XftColor structure holds both of these: typedef struct { unsigned short red; unsigned short green; unsigned short blue; unsigned short alpha; } XRenderColor; typedef struct _XftColor { unsigned long pixel; XRenderColor color; } XftColor; Two functions can be used to allocate these colors: Bool XftColorAllocName (Display *dpy, Visual *visual, Colormap cmap, char *name, XftColor *result) XftColorAllocName asks the X server to allocate a pixel and return the associated RGB values which are used to construct an XRenderColor; this requires a round trip (bad). XftColorAllocValue (Display *dpy, Visual *visual, Colormap cmap, XRenderColor *color, XftColor *result) For TrueColor visuals, XftColorAllocValue computes the nearest supported ARGB value and the associated pixel value and stores them in the result. No round trip is required. For non-TrueColor visuals, XftColorAllocValue asks the X server to allocate the specified color and stores the resulting pixel and ARGB values into result. When the color is no longer in use, it can be freed with: void XftColorFree (Display *dpy, Visual *visual, Colormap cmap, XftColor *color); 4.3 Text Rendering As with the extents functions above, Xft has four different text rendering functions differing only in the format for the string: void XftDrawString8 (XftDraw *d, XftColor *color, XftFont *font, int x, int y, XftChar8 *string, int len); void XftDrawString16 (XftDraw *draw, XftColor *color, XftFont *font, int x, int y, XftChar16 *string, int len); void XftDrawString32 (XftDraw *draw, XftColor *color, XftFont *font, int x, int y, XftChar32 *string, int len); void XftDrawStringUtf8 (XftDraw *d, XftColor *color, XftFont *font, int x, int y, XftChar8 *string, int len); 4.4 Rectangle rendering As a convenience, Xft also contains a function to draw a rectangle; while this can usually be done with core X calls, the Xft variant allows for translucent colors: void XftDrawRect (XftDraw *d, XftColor *color, int x, int y, unsigned int width, unsigned int height); 5.0 Switching Core X rendering to Xft rendering Taking an existing X application and switching text rendering from core X to Xft is fairly easy as there are rough equivalents for most functions. 5.1 Font Opening The quick fix is to use Xft XLFD font name handling: font = XLoadQueryFont -> font = XftFontOpenXlfd This limits the pattern matching to fields supported in XLFD names, which is rather restrictive. It's better to switch font naming the Xft names if possible: font = XftFontOpenName 5.2 Font Listing This is the hardest change, usually it involves ripping large chunks of XLFD parsing code out of applications and leaving that up to Xft. A simple example really isn't possible in this case as the font listing of Xft is significantly different from core X. 5.2 Text Metrics Note that the values within the metrics structures have significantly different interpretation; code using the metrics will need modification XTextWidth -> XftTextExtents8 (...); width = extents.xOff; XTextExtents -> XftTextExtents8 5.3 Text Drawing Xft doesn't support "image text" at all; rather the application must explicitly fill the background rectangle before drawing the string. With AA text, there's no other way to make it work. XDrawString -> XftDrawString8 XDrawImageString -> XftDrawRect + XftDrawString8 Note that drawing the same string multiple times in the same place will generate the wrong result with AA text. This can be especially hard to fix in applications that avoid setting clipping when redrawing parts of windows. An easy fix here is to simply clear the entire window before redrawing it. This does cause extensive flashing on the screen, the alternative is to use clipping and make sure that any areas within the clip region get cleared to the background before text is drawn on top. 6. Conclusions Converting applications from core X text to Xft is tricky in places, but relatively straightforward in most ways. Attention must be paid to text metric usages and erasing areas to background before drawing text.