comdirectwrite

Are 2 IDWriteFontFace instances equals?


I need to determine whether two IDWriteFontFace instances represent the same font file (and index for .ttc font). Would using this solution be a reliable approach?

From my testing, even if two IDWriteFontFace instances reference the same font file, their IUnknown values can differ depending on how they were created.

For example:

Given this inconsistency, is this solution only reliable when both IDWriteFontFace instances are created using the same method? Or even, is it reliable?


Solution

  • The IDWriteFontFace5::Equals method compares the following things:

    Comparing axis values is necessary because two instances of a variable font may not be the same even if they were created from the same file and face index and with the same simulations.

    Note that even static (non-variable) fonts can have axis values declared in the OpenType STAT table. Also, DWrite always treats every font as having at least the WGHT, WDTH, ITAL, and SLNT axes. If the font file does not actually specify these axes, DWrite derives them from the weight, stretch, and style.

    DWrite does have internal logic in the CreateFontFace method to try to return an existing object if a font face with the specified parameters already exists. For this reason, simply comparing IUnknown pointers may be good enough, depending on your requirements. However, as the OP observed, there are cases where you can end up with two different IDWriteFontFace objects (different addresses) that are actually equivalent (Equals returns true). This occurs if the parameters passed in to create the font face were different but the resulting font faces actually ended up being equivalent after the axis values were normalized (put in canonical order and clamped to their respective ranges). Also, this logic did not exist at all in Windows 7; the Windows 7 implementation of CreateFontFace always returned a new object.

    So my recommendations would be:

    1. If you don't care about the occasional false negative or always create font face objects in the same way (and don't need to support Windows 7), you might be able to just compare IUnknown pointers. Otherwise...
    2. If you can successfully QueryInterface for IDWriteFontFace5, use its Equals method. This takes into account axis values and is future-proof.
    3. Otherwise, compare the loaders, reference keys, face indices, and simulations. You won't need to compare axis values in this case because there were no axis values before IDWriteFontFace5.