Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add monospace tabular fonts (numbers) #31691

Closed
maryx opened this issue Apr 26, 2019 · 7 comments · Fixed by flutter/engine#8823
Closed

Add monospace tabular fonts (numbers) #31691

maryx opened this issue Apr 26, 2019 · 7 comments · Fixed by flutter/engine#8823

Comments

@maryx
Copy link
Contributor

maryx commented Apr 26, 2019

In Android, one can add tabular numbers that are monospaced, for a font that isn't monospaced.
E.g. https://stackoverflow.com/questions/41143336/monospace-tabular-numbers-in-android-textviews

How to do this in Flutter?
@GaryQian

@jason-simmons
Copy link
Member

Which font is the app using, and what is its current number spacing behavior?

I tried laying out some strings of digits in Flutter using the Roboto font on Android. It looks like the layout used tabular numbers by default.

The number spacing is controlled by the features selected in the HarfBuzz shaper. You can lay out proportional numbers in libtxt by patching GetFontAndMinikinPaint to set:
paint->fontFeatureSettings = "pnum"

TextStyle could expose an API to control this feature flag if needed.

@maryx
Copy link
Contributor Author

maryx commented Apr 29, 2019

Google Sans, and currently the spacing has the 1 take up less space than the 0.

@jason-simmons
Copy link
Member

Confirmed that by default Google Sans is rendering with proportional numbers. Setting paint->fontFeatureSettings = "tnum" will select tabular numbers.

@Hixie @GaryQian any thoughts on what an API for this should look like?

@Hixie
Copy link
Contributor

Hixie commented Apr 29, 2019

Is there documentation on fontFeatureSettings I could read?

@jason-simmons
Copy link
Member

Minikin is passing the fontFeatureSettings values to the HarfBuzz hb_feature_from_string API.

Here is some info on how HarfBuzz interprets features:
https://github.com/ufyTeX/luaharfbuzz/wiki/Feature-Strings

Looks like it's based on the CSS font-feature-settings property and OpenType feature tags

@Hixie
Copy link
Contributor

Hixie commented Apr 30, 2019

Straw-man proposal:

  @immutable
  class FontFeature {
    const FontFeature(this.feature, [ this.value = 1 ]) : assert(feature != null), assert(feature.length == 4), assert(value != null), assert(value >= 0);
    const FontFeature.enable(this.feature) : value = 1;
    const FontFeature.disable(this.feature) : value = 0;

    // Randomize the alternate forms used in text.
    //
    // For example, this can be used with suitable-prepared handwriting fonts to vary the forms used
    // for each character, so that, for instance, the word "cross-section" would be rendered with two
    // different "c"s, two different "o"s, and three different "s"s.
    // 
    // See also:
    //
    //  * <https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#rand>
    const FontFeature.randomize() : feature = 'rand', value = 1;

    // Select a stylistic set.
    //
    // Fonts may have up to 20 stylistic sets, numbered 1 through 20.
    // 
    // See also:
    //
    //  * <https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#ssxx>
    factory FontFeature.stylisticSet(int value) : assert(value >= 1), assert(value <= 20) {
      return FontFeature('ss${value.padLeft(2, "0")}');
    }

    // Use the Slashed Zero.
    //
    // Some fonts contain both a circular zero and a zero with a slash. This enables the use
    // of the latter form.
    //
    // This is overridden by [FontFeature.oldstyleFigures].
    // 
    // See also:
    //
    //  * <https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#zero>
    const FontFeature.slashedZero() : feature = 'zero', value = 1;

    // Use oldstyle figures.
    //
    // Some fonts have variants of the figures (e.g. the digit 9), that, when this feature is enabled,
    // render with descenders under the baseline instead of being entirely above the baseline.
    //
    // This overrides [FontFeature.slashedZero].
    // 
    // See also:
    //
    //  * <https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#onum>
    const FontFeature.oldstyleFigures() : feature = 'onum', value = 1;

    // Use proportional (varying width) figures.
    //
    // For fonts that have both proportional and tabular (monospace) figures,
    // this enables the proportional figures.
    //
    // This overrides an earlier use of [FontFeature.tabularFigures].
    //
    // The default behavior varies from font to font.
    // 
    // See also:
    //
    //  * <https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#pnum>
    const FontFeature.proportionalFigures() : feature = 'pnum', value = 1;

    // Use tabular (monospace) figures.
    //
    // For fonts that have both proportional (varying width) and tabular figures, this
    // enables the proportional figures.
    //
    // This overrides an earlier use of [FontFeature.proportionalFigures].
    //
    // The default behavior varies from font to font.
    // 
    // See also:
    //
    //  * <https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#tnum>
    const FontFeature.tabularFigures() : feature = 'tnum', value = 1;

    // ...provide constructors for each of the registered features, see https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist ...

    final String feature;

    final int value;

    // operator == and hashCode to compare feature and value

    // pretty toString returns '$feature $value' or similar (maybe only '$feature' if value is 1).
  }

// in dart:ui TextStyle and painting.TextStyle:
  final Set<FontFeature> fontFeatures;

jason-simmons added a commit to jason-simmons/flutter_engine that referenced this issue May 2, 2019
jason-simmons added a commit to jason-simmons/flutter_engine that referenced this issue May 2, 2019
jason-simmons added a commit to jason-simmons/flutter_engine that referenced this issue May 3, 2019
jason-simmons added a commit to jason-simmons/flutter_engine that referenced this issue May 6, 2019
jason-simmons added a commit to jason-simmons/flutter_engine that referenced this issue May 13, 2019
jason-simmons added a commit to jason-simmons/flutter_engine that referenced this issue May 13, 2019
jason-simmons added a commit to jason-simmons/flutter_engine that referenced this issue May 13, 2019
jason-simmons added a commit to jason-simmons/flutter_engine that referenced this issue May 21, 2019
jason-simmons added a commit to jason-simmons/flutter that referenced this issue May 23, 2019
jason-simmons added a commit to jason-simmons/flutter that referenced this issue Jun 4, 2019
@github-actions
Copy link

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 30, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants