Skip to content

Commit

Permalink
Add font features (such as tabular numbers) as an option in text styl…
Browse files Browse the repository at this point in the history
  • Loading branch information
jason-simmons authored May 21, 2019
1 parent 45664a6 commit 135a140
Show file tree
Hide file tree
Showing 14 changed files with 490 additions and 15 deletions.
2 changes: 2 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,8 @@ FILE: ../../../flutter/third_party/txt/src/txt/font_asset_provider.cc
FILE: ../../../flutter/third_party/txt/src/txt/font_asset_provider.h
FILE: ../../../flutter/third_party/txt/src/txt/font_collection.cc
FILE: ../../../flutter/third_party/txt/src/txt/font_collection.h
FILE: ../../../flutter/third_party/txt/src/txt/font_features.cc
FILE: ../../../flutter/third_party/txt/src/txt/font_features.h
FILE: ../../../flutter/third_party/txt/src/txt/font_skia.cc
FILE: ../../../flutter/third_party/txt/src/txt/font_skia.h
FILE: ../../../flutter/third_party/txt/src/txt/font_style.h
Expand Down
139 changes: 139 additions & 0 deletions lib/stub_ui/lib/src/ui/text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,145 @@ class FontWeight {
}
}

/// A feature tag and value that affect the selection of glyphs in a font.
class FontFeature {
/// Creates a [FontFeature] object, which can be added to a [TextStyle] to
/// change how the engine selects glyphs when rendering text.
///
/// `feature` is the four-character tag that identifies the feature.
/// These tags are specified by font formats such as OpenType.
///
/// `value` is the value that the feature will be set to. The behavior
/// of the value depends on the specific feature. Many features are
/// flags whose value can be 1 (when enabled) or 0 (when disabled).
///
/// See <https://docs.microsoft.com/en-us/typography/opentype/spec/featuretags>
const FontFeature(this.feature, [ this.value = 1 ]) : assert(feature != null), assert(feature.length == 4), assert(value != null), assert(value >= 0);

/// Create a [FontFeature] object that enables the feature with the given tag.
const FontFeature.enable(String feature) : this(feature, 1);

/// Create a [FontFeature] object that disables the feature with the given tag.
const FontFeature.disable(String feature) : this(feature, 0);

/// Randomize the alternate forms used in text.
///
/// For example, this can be used with suitably-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.toString().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_uz#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_ko#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 is mutually exclusive with [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 tabular figures.
///
/// This is mutually exclusive with [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;

/// The tag that identifies the effect of this feature. Must consist of 4
/// ASCII characters (typically lowercase letters).
///
/// See <https://docs.microsoft.com/en-us/typography/opentype/spec/featuretags>
final String feature;

/// The value assigned to this feature.
///
/// Must be a positive integer. Many features are Boolean values that accept
/// values of either 0 (feature is disabled) or 1 (feature is enabled).
final int value;

static const int _kEncodedSize = 8;

void _encode(ByteData byteData) {
assert(feature.codeUnits.every((int c) => c >= 0x20 && c <= 0x7F));
for (int i = 0; i < 4; i++) {
byteData.setUint8(i, feature.codeUnitAt(i));
}
byteData.setInt32(4, value, _kFakeHostEndian);
}

@override
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
if (other.runtimeType != runtimeType)
return false;
final FontFeature typedOther = other;
return feature == typedOther.feature
&& value == typedOther.value;
}

@override
int get hashCode => hashValues(feature, value);

@override
String toString() => 'FontFeature($feature, $value)';
}

/// Whether and how to align text horizontally.
// The order of this enum must match the order of the values in RenderStyleConstants.h's ETextAlign.
enum TextAlign {
Expand Down
5 changes: 5 additions & 0 deletions lib/ui/natives.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ class _Logger {
static void _printString(String s) native 'Logger_PrintString';
}

// If we actually run on big endian machines, we'll need to do something smarter
// here. We don't use [Endian.Host] because it's not a compile-time
// constant and can't propagate into the set/get calls.
const Endian _kFakeHostEndian = Endian.little;

// A service protocol extension to schedule a frame to be rendered into the
// window.
Future<developer.ServiceExtensionResponse> _scheduleFrame(
Expand Down
5 changes: 0 additions & 5 deletions lib/ui/painting.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1019,11 +1019,6 @@ enum Clip {
antiAliasWithSaveLayer,
}

// If we actually run on big endian machines, we'll need to do something smarter
// here. We don't use [Endian.Host] because it's not a compile-time
// constant and can't propagate into the set/get calls.
const Endian _kFakeHostEndian = Endian.little;

// Indicates that the image should not be resized in this dimension.
//
// Used by [instantiateImageCodec] as a magical value to disable resizing
Expand Down
Loading

0 comments on commit 135a140

Please sign in to comment.