What are the default typography settings used by IDWriteTextLayout?
Asked Answered
F

2

2

I would like to disable ligatures, which appear to be enabled by default, at least in the fonts that I'm using (i.e. Calibri). It appears that the way to do this is to use IDWriteTextLayout::SetTypography to set an IDWriteTypography object to the layout which contains the various ligature-related font features with a value of zero to disable them. That works to disable the ligatures, but it also affects (disables) other typography settings that I don't want to mess with, such as kerning.

After some experimentation, it turns out that I could disable ligatures just by setting an empty IDWriteTypography object (one that was simply created by the IDWriteFactory and then applied to the layout without alteration). An empty typography seems to have the effect of setting all possible font features to zero/disable. So what I actually want to do is retrieve the default typography settings, override the ones related to ligatures, and then set that to the layout.

Unfortunately I can't find anywhere to retrieve the default settings. Using IDWriteTextLayout::GetTypography just returns null if none has been set, yet it clearly has various typography settings like ligatures enabled in that case. I also can't find any other methods on IDWriteFactory (or any of its newer versions) or any of the font-related interfaces for creating an IDWriteTypography instance. How does the IDWriteTextLayout decide which typography settings to use when no IDWriteTypography has been set? Are the default settings font-specific? Can I retrieve those settings somehow so that I can tweak a few of them and inherit the default values for the rest?

Fielder answered 13/9, 2015 at 2:16 Comment(0)
F
3

There's no way to get default set of features out of IDWriteTextLayout, or IDWriteTextAnalyzer to be more precise, and no, it's not font-specific, it's script-specific.

If you're interested in how it works in general, you can use opensource implementations like HarfBuzz, you can find arrays of script-specific features that are applied during shaping.

Microsoft approach to that is documented in OpenType documentation, an example for Arabic - https://www.microsoft.com/typography/OpenTypeDev/arabic/intro.htm.

Ferebee answered 7/1, 2016 at 17:6 Comment(6)
Oh good, thanks for this answer; I was about to write a super long essay question about the same thing =P So would I have to drop the use of IDWriteTextLayout entirely in my code if I want to be able to add typographical features on top of the defaults? Or is there a way I can hijack the text analyzer used by IDWriteTextLayout to figure out which script is in use for a given text run before it gets to my IDWriteTextRenderer (at which point it's too late)? If I could do that I could definitely just copy the lists Harfbuzz uses, since it would give me the script (cont'd.)Irwinirwinn
(but what list is "zero-based index representation of writing system script" from? the font file's?)… Unless even that's too complicated to apply directly, since IDWriteTextAnalyzer doesn't seem to tell you whether it's time for a mandatory ligature? Would I have to just drop DirectWrite and write a layout engine myself? And how is XAML <Paragraph> able to combine features additively if we can't? (XAML seems to me like it uses D2D+DWrite but does all the stuff that's missing from these two…) I'm starting to not be sure what the point of explicitly specifying OpenType features is anymore.Irwinirwinn
*XAML <Run> under <Paragraph>, as shown at learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/…Irwinirwinn
Oh, I should also mention I need to know this because all the other layout engines I use do add the user-specified list of features to the defaults, rather than replacing the default list entirely, and I can't switch to DirectWrite behavior without explicitly turning every feature off (and Harfbuzz still has forced overrides anyway).Irwinirwinn
Meh, I asked this as its own question anyway. Thanks!Irwinirwinn
That's correct. As of 2017-09-07, there is no DWrite method to get a list of the default features. Specifying your own will replace the defaults (but required ones are still applied {ccmp, locl, rlig, rclt, rvrn, pres ...}).Restoration
J
1

It appears that the way to do this is to use IDWriteTextLayout::SetTypography

As early as of Sep 13, 2015 (the date of your asking this question) you WERE ABLE to disable ligatures, in particular, for a Calibri font, WITHOUT messing with a IDWriteTypography object and its default settings.

You may be were unable to disable ligatures "globally" in the way you liked, but you always do have an option to disable ligatures when you actually extract glyphs for a piece of text in your code. The 9th, 10th, and 11th ('features', 'featureLengths', and 'featureCount') parameters of an IDWriteTextAnalyzer::GetGlyph(...) method are to your help.

For example, to disable ligatures, you write in your code (I borrowed this piece from a FLowLayout::ShapeGlyphRun method of the Windows7 SDK CustomLayout sample and added some feature params initializations; initially, the 9th to 11th parameter values were NULL, NULL, 0):

    DWRITE_FONT_FEATURE fontFeature = { DWRITE_FONT_FEATURE_TAG_STANDARD_LIGATURES, 0 };

    const DWRITE_TYPOGRAPHIC_FEATURES* typoFeatures = 
        new DWRITE_TYPOGRAPHIC_FEATURES{ { &fontFeature } };

    UINT32 featureLengths[1];

    featureLengths[0] = textLength;

    hr = textAnalyzer->GetGlyphs(
            &text_[textStart],
            textLength,
            fontFace_,
            run.isSideways,         // isSideways,
            (run.bidiLevel & 1),    // isRightToLeft
            &run.script,
            localeName_,
            (run.isNumberSubstituted) ? numberSubstitution_ : NULL,
            &typoFeatures,                   // features
            featureLengths,                   // featureLengths
            1,                      // featureCount
            maxGlyphCount,          // maxGlyphCount
            &glyphClusters_[textStart],
            &textProps[0],
            &glyphIndices_[glyphStart],
            &glyphProps[0],
            &actualGlyphCount
            );

    delete typoFeatures;

A second parameter (0) in the fontFeature initializer DISABLES use of ligatures for a text range {textStart, textStart + textLength}, whatever ligature-enabled font you use in this text range. A non-zero parameter value would enable this feature, but this feature, as you know, is enabled by default.

For the next piece of text (starting at textStart + textLength and further on) you can re-use your initialized feature values in order to continue disable ligatures; but, if you return to NULL, NULL, 0 values of GetGlyph's 9th to 11th feature params, ligatures for this latter text range are enabled without use of explicit feature settings in GetGlyph.

See reference: https://msdn.microsoft.com/en-us/library/windows/desktop/dd316625(v=vs.85).aspx .

As of now, a new DirectWrite text engine might be offering more convenient ways to control use of typographic/font features -- I cannot tell you for sure, but the above info might be helpful for those writing code compatible with Windows 7.

Jargonize answered 15/2, 2018 at 6:11 Comment(2)
Thanks for your answer, that's a helpful addition. Unfortunately it doesn't help in my particular case because I'm not directly using IDWriteTextAnalyzer. That's lower-level work that I'm effectively delegating to the higher-level IDWriteTextLayout interface that I am using. While I could in theory stop using IDWriteTextLayout and instead directly call the lower-level APIs, in practice that's just not feasible for me; IDWriteTextLayout does too much other useful work that I need.Fielder
I actually had a very similar issue with number substitution: IDWriteTextAnalyzer supports it (your example shows that it's one of the other parameters to GetGlyphs), but IDWriteTextLayout doesn't expose any way for you to control which number substitution value it passes down to its own internal IDWriteTextAnalyzer when it's laying out your text. In the case of font features, IDWriteTextLayout does at least expose a mechanism to control them -- IDWriteTypography -- but that mechanism appears to be flawed in that it forces you to set them in an all or nothing manner.Fielder

© 2022 - 2024 — McMap. All rights reserved.