Hi, my name is Niklas Borson and I’m a developer on the DirectWrite team at Microsoft. DirectWrite has multiple layers of functionality, including text layout, script processing, glyph rendering, and the font system. In this post, I’d like to talk about some of the thinking behind the design of the font API in DirectWrite, and how it differs from GDI.
It will be helpful in the discussion that follows to distinguish between logical and physical fonts. Of course you might argue that fonts were really only “physical” back in the days of metal type. However, what I mean by a physical font in the context of computer typography is the actual data that defines the appearance of text in a particular font. A physical font can be represented as an OpenType file on your hard disk or as a virtual file embedded in a document.
A logical font, on the other hand, is something closer to a typical user’s conception of a font. Users rarely deal directly with font files. If you’re using a word processor, you select the name of a font family from a list. If you want to make the text bold you click a button or check a box. Doing so might mean the text is rendered using timesbd.ttf instead of times.ttf, but you neither know nor care about that. Thus, a logical font is not really the font itself, but a set of abstract properties that describe the font you want.
The reason for making this distinction is that different application scenarios naturally fit at one level or another. If you just want to draw some simple text on the screen, it makes sense to work at the logical font level – for example, just specify “Times New Roman” and let the text formatting engine handle the details. Among those details are what to do if times.ttf can’t render some of the characters you specify. Since you’re working at the logical font level, the text formatting engine has the flexibility to fall back to a different physical font.
On the other hand, some applications need to work with fonts at a lower level. For example, if an application implements its own text formatting engine, it ultimately needs a physical font so it can map Unicode characters to glyphs (character shapes), position the glyphs, and render them.
The level of abstraction affects a developer’s expectations of an API. At a high level, it can be helpful if an API just “does the right thing,” even if that means making certain decisions for you like falling back to a different font. However, developers using low-level APIs tend to value control and predictability more than convenience. For example, glyph indices are font-specific – they don’t have a standard meaning like Unicode characters – so if you’re rendering glyph indices you really want to know exactly what physical font will be used.
In GDI, one typically creates a font by filling in a LOGFONT structure and then calling CreateFontIndirect, which returns a font handle (HFONT). As you would expect from the name, a LOGFONT structure is a logical font – i.e., merely a set of properties that describe the font you want. What might surprise you is that an HFONT is also a logical font. For example, you can fill in a LOGFONT with a face name of “Fred” and successfully create an HFONT from it, whether or not a font named “Fred” actually exists. If you draw some text using that HFONT, GDI will look for a matching physical font to draw the text with. If it doesn’t find a “Fred” it will use some other font. This font mapping process is entirely internal to GDI and there’s no way for an application to determine the physical font it used.
As described above, there are a lot of application scenarios where working at the logical font level makes sense, and HFONT works well for this. For applications that need to work with fonts at a lower level, GDI does have some functions that can really be thought of as physical font APIs. Examples are GetFontData, GetGlyphIndices, and even ExtTextOut when used with the ETO_GLYPH_INDEX flag. Yet these “physical” font functions still use an HFONT to specify the font. This means font mapping still occurs under the hood, though since no text is specified there is no font fallback and the same HFONT always maps to the same physical font internally. While this model works, the lack of a true physical font object in GDI can make it harder for application developers that use these low-level functions to be sure of what is going on.
DirectWrite has multiple layers of functionality, in which the lowest layers are intended to provide a solid foundation on which higher-level services can be built. The lowest layer of the font API is the IDWriteFontFace interface, which represents a physical font. This interface exposes low-level methods for getting glyph metrics, glyph indices, and so on. It is also used with other low-level APIs for complex script analysis and glyph rendering. Because a physical font encapsulates font data, you create an IDWriteFontFace object by specifying the source of the data: an IDWriteFontFile object.
We thought about introducing another interface to represent a logical font. However, after a lot of thought and discussion, we eventually concluded that a so-called “logical font” isn’t really a font at all, but merely a set of properties used to select a font. Also, since these logical font properties are inputs to the text formatting process, the appropriate place to specify them is in the text formatting API. Thus, DirectWrite’s nearest equivalent to GDI’s LOGFONT structure is the IDWriteTextFormat interface, which has properties for font family name, font weight, font style, font stretch, and font size. You can also set each of these properties individually on ranges of text after creating an IDWriteTextLayout object.
In between those two layers is the font collection API. A font collection is simply a set of fonts, which can be either the system font collection (i.e., the set of installed fonts) or a custom font collection (i.e., a set of fonts enumerated by an application-defined callback). Font collections are organized hierarchically. The collection itself contains a list of font families each of which contains a list of fonts. An application can use the font collection API to implement a font selection UI such as a font dialog box.
The reason I say the font collection API is “in between” the logical and physical layers is that it can serve as a bridge between the two. Suppose you were implementing your own text formatting engine. As inputs you would take logical properties like family name, weight, etc., from which you would somehow need to obtain a physical font. The font collection API provides lookup methods that enable you to find the physical font that best matches to a given set of properties. By way of analogy, you could think of a font collection as a database, a physical font as a record in the database, and a logical font as a query.
Both the physical font API and the font collection API have extensibility mechanisms. You create a physical font object from an IDWriteFontFile interface, but this interface doesn’t have to be a conventional Win32 file. It can be a virtual file implemented in terms of callbacks you define. Similarly, you can also create a custom font collection by implementing a callback interface that enumerates font files. This allows you to format and render text using application-defined fonts without having to install them first. Because the custom font collection is completely separate from the system font collection, this approach also avoids any naming conflicts that might otherwise arise between your custom fonts and fonts already installed on the system.
One final difference I should mention between DirectWrite and GDI concerns the way fonts are grouped into font families. DirectWrite uses the Weight/Width/Slope font model, which means you can have any number of fonts in the same font family as long as each can be uniquely differentiated by weight, stretch (aka. width), and/or style (aka. slope). This is the same model used by WPF, as described in an earlier blog post about the WPF font selection model.
To give a specific example, if you enumerate font families using GDI’s EnumFontFamiliesEx function, you’ll find that the following are all enumerated as separate family names by GDI:
In DirectWrite, “Franklin Gothic Book” is a separate family, but all of the other fonts are grouped into a single font family called “Franklin Gothic.” In GDI, you’d specify the demi-bold condensed font using the name “Franklin Gothic Demi Cond”. If you did the same thing with DirectWrite, you’d find that the family name wasn’t recognized and DirectWrite would fall back to another font entirely like Arial. Instead, you would specify “Franklin Gothic” as the family name, DWRITE_FONT_WEIGHT_DEMI_BOLD as the weight, DWRITE_FONT_STRETCH_CONDENSED as the stretch, and DWRITE_FONT_STYLE_NORMAL as the style.
If your application only uses DirectWrite then none of the above is likely to cause you any problems. After all, DirectWrite is internally consistent and won’t enumerate family names that it doesn’t recognize! However, you may need to take special care if you use DirectWrite in combination with another API. More on that below.
The DirectWrite font system has two levels of interoperability with GDI. First, at the logical font level, we provide round-trip conversions to and from GDI’s LOGFONT. Second, at the physical font level, there is a one-way conversion from an HFONT (selected into an HDC) to an IDWriteFontFace. All of these are exposed through the IDWriteGdiInterop interface.
The LOGFONT conversions exist to compensate for the fact that DirectWrite uses a different font model than GDI. As described in the previous section, you sometimes use a different name to specify a font in DirectWrite than you’d use to specify the same font in GDI. If you use both DirectWrite and GDI in the same application, you may need to convert the family name and other logical font properties back and forth between the two systems. The ConvertFontToLOGFONT and CreateFontFromLOGFONT methods enable you to do this. There is also a ConvertFontFaceToLOGFONT method, which takes a font face object as input but is otherwise functionally equivalent to the ConvertFontToLOGFONT method.
For example, suppose your application uses DirectWrite internally but also uses the Win32 common font dialog box. In this case, you would call CreateFontFromLOGFONT to convert the LOGFONT returned by the dialog box to an IDWriteFont object. From this you could get the logical font properties compatible with DirectWrite’s font system. A subtler example would be an application that uses DirectWrite internally but uses a file format that stores GDI-style font names. In this case, you might use CreateFontFromLOGFONT when reading from the file and ConvertFontToLOGFONT when writing to the file.
Warning: All of the LOGFONT conversions described above operate at the logical font level, which means they don’t offer the kind of strong guarantees you would expect from a physical font API. They are useful for the kind of high-level scenarios described in the previous paragraph but should not be used if you require a specific physical font. For example, you might be tempted to render a DWRITE_GLYPH_RUN using GDI by converting the fontFace member to a LOGFONT, creating an HFONT and selecting it into an HDC, and then using ExtTextOut with ETO_GLYPH_INDEX. Don’t do this! It is not guaranteed that GDI will select the same physical font as the fontFace member you started with, in which cases (since glyph indices are font-specific) you’ll get garbage output.
We do provide one conversion that works at the physical font level, but it works on only one direction: from an HDC to an IDWriteFontFace. It is intended for a very specific scenario. Suppose your application already has a complicated text formatting engine which uses GDI (and perhaps Uniscribe) and renders glyphs using ExtTextOut with ETO_GLYPH_INDEX. You don’t want to rewrite your whole text engine, but you’d like to use DirectWrite (or maybe Direct2D) for just the final step of rendering the glyphs. To do so, you would remove the ExtTextOut call, and instead call CreateFontFaceFromHdc to get an IDWriteFontFace object, which you could then use to render the glyph indices. Unlike the LOGFONT conversions described above, this method is guaranteed to yield the same physical font that would have been used by GDI to render the glyphs.
If you’ve made it this far, thank you for your attention! The DirectWrite font system does differ from the familiar GDI way of doing things, and I hope the above will help people understand how it differs and why. We all look forward to hearing what you think!