blob: 18aec485487ffb65eaf3b47b45ccd459ef277cd1 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Synced 2019-05-30T14:20:57.833907.
// @dart = 2.6
part of ui;
/// Whether to slant the glyphs in the font
enum FontStyle {
/// Use the upright glyphs
normal,
/// Use glyphs designed for slanting
italic,
}
/// Where to vertically align the placeholder relative to the surrounding text.
///
/// Used by [ParagraphBuilder.addPlaceholder].
enum PlaceholderAlignment {
/// Match the baseline of the placeholder with the baseline.
///
/// The [TextBaseline] to use must be specified and non-null when using this
/// alignment mode.
baseline,
/// Align the bottom edge of the placeholder with the baseline such that the
/// placeholder sits on top of the baseline.
///
/// The [TextBaseline] to use must be specified and non-null when using this
/// alignment mode.
aboveBaseline,
/// Align the top edge of the placeholder with the baseline specified
/// such that the placeholder hangs below the baseline.
///
/// The [TextBaseline] to use must be specified and non-null when using this
/// alignment mode.
belowBaseline,
/// Align the top edge of the placeholder with the top edge of the font.
///
/// When the placeholder is very tall, the extra space will hang from
/// the top and extend through the bottom of the line.
top,
/// Align the bottom edge of the placeholder with the top edge of the font.
///
/// When the placeholder is very tall, the extra space will rise from the
/// bottom and extend through the top of the line.
bottom,
/// Align the middle of the placeholder with the middle of the text.
///
/// When the placeholder is very tall, the extra space will grow equally
/// from the top and bottom of the line.
middle,
}
/// The thickness of the glyphs used to draw the text
class FontWeight {
const FontWeight._(this.index);
/// The encoded integer value of this font weight.
final int index;
/// Thin, the least thick
static const FontWeight w100 = FontWeight._(0);
/// Extra-light
static const FontWeight w200 = FontWeight._(1);
/// Light
static const FontWeight w300 = FontWeight._(2);
/// Normal / regular / plain
static const FontWeight w400 = FontWeight._(3);
/// Medium
static const FontWeight w500 = FontWeight._(4);
/// Semi-bold
static const FontWeight w600 = FontWeight._(5);
/// Bold
static const FontWeight w700 = FontWeight._(6);
/// Extra-bold
static const FontWeight w800 = FontWeight._(7);
/// Black, the most thick
static const FontWeight w900 = FontWeight._(8);
/// The default font weight.
static const FontWeight normal = w400;
/// A commonly used font weight that is heavier than normal.
static const FontWeight bold = w700;
/// A list of all the font weights.
static const List<FontWeight> values = <FontWeight>[
w100,
w200,
w300,
w400,
w500,
w600,
w700,
w800,
w900
];
/// Linearly interpolates between two font weights.
///
/// Rather than using fractional weights, the interpolation rounds to the
/// nearest weight.
///
/// Any null values for `a` or `b` are interpreted as equivalent to [normal]
/// (also known as [w400]).
///
/// The `t` argument represents position on the timeline, with 0.0 meaning
/// that the interpolation has not started, returning `a` (or something
/// equivalent to `a`), 1.0 meaning that the interpolation has finished,
/// returning `b` (or something equivalent to `b`), and values in between
/// meaning that the interpolation is at the relevant point on the timeline
/// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
/// 1.0, so negative values and values greater than 1.0 are valid (and can
/// easily be generated by curves such as [Curves.elasticInOut]). The result
/// is clamped to the range [w100]–[w900].
///
/// Values for `t` are usually obtained from an [Animation<double>], such as
/// an [AnimationController].
static FontWeight lerp(FontWeight a, FontWeight b, double t) {
assert(t != null);
return values[
lerpDouble(a?.index ?? normal.index, b?.index ?? normal.index, t)
.round()
.clamp(0, 8)];
}
@override
String toString() {
return const <int, String>{
0: 'FontWeight.w100',
1: 'FontWeight.w200',
2: 'FontWeight.w300',
3: 'FontWeight.w400',
4: 'FontWeight.w500',
5: 'FontWeight.w600',
6: 'FontWeight.w700',
7: 'FontWeight.w800',
8: 'FontWeight.w900',
}[index];
}
}
/// 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;
@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 {
/// Align the text on the left edge of the container.
left,
/// Align the text on the right edge of the container.
right,
/// Align the text in the center of the container.
center,
/// Stretch lines of text that end with a soft line break to fill the width of
/// the container.
///
/// Lines that end with hard line breaks are aligned towards the [start] edge.
justify,
/// Align the text on the leading edge of the container.
///
/// For left-to-right text ([TextDirection.ltr]), this is the left edge.
///
/// For right-to-left text ([TextDirection.rtl]), this is the right edge.
start,
/// Align the text on the trailing edge of the container.
///
/// For left-to-right text ([TextDirection.ltr]), this is the right edge.
///
/// For right-to-left text ([TextDirection.rtl]), this is the left edge.
end,
}
/// A horizontal line used for aligning text.
enum TextBaseline {
/// The horizontal line used to align the bottom of glyphs for alphabetic characters.
alphabetic,
/// The horizontal line used to align ideographic characters.
ideographic,
}
/// A linear decoration to draw near the text.
class TextDecoration {
const TextDecoration._(this._mask);
/// Creates a decoration that paints the union of all the given decorations.
factory TextDecoration.combine(List<TextDecoration> decorations) {
int mask = 0;
for (TextDecoration decoration in decorations) {
mask |= decoration._mask;
}
return TextDecoration._(mask);
}
final int _mask;
/// Whether this decoration will paint at least as much decoration as the given decoration.
bool contains(TextDecoration other) {
return (_mask | other._mask) == _mask;
}
/// Do not draw a decoration
static const TextDecoration none = TextDecoration._(0x0);
/// Draw a line underneath each line of text
static const TextDecoration underline = TextDecoration._(0x1);
/// Draw a line above each line of text
static const TextDecoration overline = TextDecoration._(0x2);
/// Draw a line through each line of text
static const TextDecoration lineThrough = TextDecoration._(0x4);
@override
bool operator ==(dynamic other) {
if (other is! TextDecoration) {
return false;
}
final TextDecoration typedOther = other;
return _mask == typedOther._mask;
}
@override
int get hashCode => _mask.hashCode;
@override
String toString() {
if (_mask == 0) {
return 'TextDecoration.none';
}
final List<String> values = <String>[];
if (_mask & underline._mask != 0) {
values.add('underline');
}
if (_mask & overline._mask != 0) {
values.add('overline');
}
if (_mask & lineThrough._mask != 0) {
values.add('lineThrough');
}
if (values.length == 1) {
return 'TextDecoration.${values[0]}';
}
return 'TextDecoration.combine([${values.join(", ")}])';
}
}
/// The style in which to draw a text decoration
enum TextDecorationStyle {
/// Draw a solid line
solid,
/// Draw two lines
double,
/// Draw a dotted line
dotted,
/// Draw a dashed line
dashed,
/// Draw a sinusoidal line
wavy
}
/// Defines how the paragraph will apply [TextStyle.height] the ascent of the
/// first line and descent of the last line.
///
/// The boolean value represents whether the [TextStyle.height] modifier will
/// be applied to the corresponding metric. By default, all properties are true,
/// and [TextStyle.height] is applied as normal. When set to false, the font's
/// default ascent will be used.
class TextHeightBehavior {
/// Creates a new TextHeightBehavior object.
///
/// * applyHeightToFirstAscent: When true, the [TextStyle.height] modifier
/// will be applied to the ascent of the first line. When false, the font's
/// default ascent will be used.
/// * applyHeightToLastDescent: When true, the [TextStyle.height] modifier
/// will be applied to the descent of the last line. When false, the font's
/// default descent will be used.
///
/// All properties default to true (height modifications applied as normal).
const TextHeightBehavior({
this.applyHeightToFirstAscent = true,
this.applyHeightToLastDescent = true,
});
/// Creates a new TextHeightBehavior object from an encoded form.
///
/// See [encode] for the creation of the encoded form.
const TextHeightBehavior.fromEncoded(int encoded) : applyHeightToFirstAscent = (encoded & 0x1) == 0,
applyHeightToLastDescent = (encoded & 0x2) == 0;
/// Whether to apply the [TextStyle.height] modifier to the ascent of the first
/// line in the paragraph.
///
/// When true, the [TextStyle.height] modifier will be applied to to the ascent
/// of the first line. When false, the font's default ascent will be used and
/// the [TextStyle.height] will have no effect on the ascent of the first line.
///
/// This property only has effect if a non-null [TextStyle.height] is specified.
///
/// Defaults to true (height modifications applied as normal).
final bool applyHeightToFirstAscent;
/// Whether to apply the [TextStyle.height] modifier to the descent of the last
/// line in the paragraph.
///
/// When true, the [TextStyle.height] modifier will be applied to to the descent
/// of the last line. When false, the font's default descent will be used and
/// the [TextStyle.height] will have no effect on the descent of the last line.
///
/// This property only has effect if a non-null [TextStyle.height] is specified.
///
/// Defaults to true (height modifications applied as normal).
final bool applyHeightToLastDescent;
/// Returns an encoded int representation of this object.
int encode() {
return (applyHeightToFirstAscent ? 0 : 1 << 0) | (applyHeightToLastDescent ? 0 : 1 << 1);
}
@override
bool operator ==(dynamic other) {
if (other.runtimeType != runtimeType)
return false;
return other is TextHeightBehavior
&& other.applyHeightToFirstAscent == applyHeightToFirstAscent
&& other.applyHeightToLastDescent == applyHeightToLastDescent;
}
@override
int get hashCode {
return hashValues(
applyHeightToFirstAscent,
applyHeightToLastDescent,
);
}
@override
String toString() {
return 'TextHeightBehavior('
'applyHeightToFirstAscent: $applyHeightToFirstAscent, '
'applyHeightToLastDescent: $applyHeightToLastDescent'
')';
}
}
/// An opaque object that determines the size, position, and rendering of text.
abstract class TextStyle {
/// Creates a new TextStyle object.
///
/// * `color`: The color to use when painting the text. If this is specified, `foreground` must be null.
/// * `decoration`: The decorations to paint near the text (e.g., an underline).
/// * `decorationColor`: The color in which to paint the text decorations.
/// * `decorationStyle`: The style in which to paint the text decorations (e.g., dashed).
/// * `fontWeight`: The typeface thickness to use when painting the text (e.g., bold).
/// * `fontStyle`: The typeface variant to use when drawing the letters (e.g., italics).
/// * `fontFamily`: The name of the font to use when painting the text (e.g., Roboto). If a `fontFamilyFallback` is
/// provided and `fontFamily` is not, then the first font family in `fontFamilyFallback` will take the postion of
/// the preferred font family. When a higher priority font cannot be found or does not contain a glyph, a lower
/// priority font will be used.
/// * `fontFamilyFallback`: An ordered list of the names of the fonts to fallback on when a glyph cannot
/// be found in a higher priority font. When the `fontFamily` is null, the first font family in this list
/// is used as the preferred font. Internally, the 'fontFamily` is concatenated to the front of this list.
/// When no font family is provided through 'fontFamilyFallback' (null or empty) or `fontFamily`, then the
/// platform default font will be used.
/// * `fontSize`: The size of glyphs (in logical pixels) to use when painting the text.
/// * `letterSpacing`: The amount of space (in logical pixels) to add between each letter.
/// * `wordSpacing`: The amount of space (in logical pixels) to add at each sequence of white-space (i.e. between each word).
/// * `textBaseline`: The common baseline that should be aligned between this text span and its parent text span, or, for the root text spans, with the line box.
/// * `height`: The height of this text span, as a multiple of the font size.
/// * `locale`: The locale used to select region-specific glyphs.
/// * `background`: The paint drawn as a background for the text.
/// * `foreground`: The paint used to draw the text. If this is specified, `color` must be null.
factory TextStyle({
Color color,
TextDecoration decoration,
Color decorationColor,
TextDecorationStyle decorationStyle,
double decorationThickness,
FontWeight fontWeight,
FontStyle fontStyle,
TextBaseline textBaseline,
String fontFamily,
List<String> fontFamilyFallback,
double fontSize,
double letterSpacing,
double wordSpacing,
double height,
Locale locale,
Paint background,
Paint foreground,
List<Shadow> shadows,
List<FontFeature> fontFeatures,
}) {
if (engine.experimentalUseSkia) {
return engine.SkTextStyle(
color: color,
decoration: decoration,
decorationColor: decorationColor,
decorationStyle: decorationStyle,
decorationThickness: decorationThickness,
fontWeight: fontWeight,
fontStyle: fontStyle,
textBaseline: textBaseline,
fontFamily: fontFamily,
fontFamilyFallback: fontFamilyFallback,
fontSize: fontSize,
letterSpacing: letterSpacing,
wordSpacing: wordSpacing,
height: height,
locale: locale,
background: background,
foreground: foreground,
shadows: shadows,
fontFeatures: fontFeatures,
);
} else {
return engine.EngineTextStyle(
color: color,
decoration: decoration,
decorationColor: decorationColor,
decorationStyle: decorationStyle,
decorationThickness: decorationThickness,
fontWeight: fontWeight,
fontStyle: fontStyle,
textBaseline: textBaseline,
fontFamily: fontFamily,
fontFamilyFallback: fontFamilyFallback,
fontSize: fontSize,
letterSpacing: letterSpacing,
wordSpacing: wordSpacing,
height: height,
locale: locale,
background: background,
foreground: foreground,
shadows: shadows,
fontFeatures: fontFeatures,
);
}
}
}
/// An opaque object that determines the configuration used by
/// [ParagraphBuilder] to position lines within a [Paragraph] of text.
abstract class ParagraphStyle {
/// Creates a new ParagraphStyle object.
///
/// * `textAlign`: The alignment of the text within the lines of the
/// paragraph. If the last line is ellipsized (see `ellipsis` below), the
/// alignment is applied to that line after it has been truncated but before
/// the ellipsis has been added.
// See: https://github.com/flutter/flutter/issues/9819
///
/// * `textDirection`: The directionality of the text, left-to-right (e.g.
/// Norwegian) or right-to-left (e.g. Hebrew). This controls the overall
/// directionality of the paragraph, as well as the meaning of
/// [TextAlign.start] and [TextAlign.end] in the `textAlign` field.
///
/// * `maxLines`: The maximum number of lines painted. Lines beyond this
/// number are silently dropped. For example, if `maxLines` is 1, then only
/// one line is rendered. If `maxLines` is null, but `ellipsis` is not null,
/// then lines after the first one that overflows the width constraints are
/// dropped. The width constraints are those set in the
/// [ParagraphConstraints] object passed to the [Paragraph.layout] method.
///
/// * `fontFamily`: The name of the font to use when painting the text (e.g.,
/// Roboto).
///
/// * `fontSize`: The size of glyphs (in logical pixels) to use when painting
/// the text.
///
/// * `height`: The minimum height of the line boxes, as a multiple of the
/// font size. The lines of the paragraph will be at least
/// `(height + leading) * fontSize` tall when fontSize
/// is not null. When fontSize is null, there is no minimum line height. Tall
/// glyphs due to baseline alignment or large [TextStyle.fontSize] may cause
/// the actual line height after layout to be taller than specified here.
/// [fontSize] must be provided for this property to take effect.
///
/// * `fontWeight`: The typeface thickness to use when painting the text
/// (e.g., bold).
///
/// * `fontStyle`: The typeface variant to use when drawing the letters (e.g.,
/// italics).
///
/// * `strutStyle`: The properties of the strut. Strut defines a set of minimum
/// vertical line height related metrics and can be used to obtain more
/// advanced line spacing behavior.
///
/// * `ellipsis`: String used to ellipsize overflowing text. If `maxLines` is
/// not null, then the `ellipsis`, if any, is applied to the last rendered
/// line, if that line overflows the width constraints. If `maxLines` is
/// null, then the `ellipsis` is applied to the first line that overflows
/// the width constraints, and subsequent lines are dropped. The width
/// constraints are those set in the [ParagraphConstraints] object passed to
/// the [Paragraph.layout] method. The empty string and the null value are
/// considered equivalent and turn off this behavior.
///
/// * `locale`: The locale used to select region-specific glyphs.
factory ParagraphStyle({
TextAlign textAlign,
TextDirection textDirection,
int maxLines,
String fontFamily,
double fontSize,
double height,
TextHeightBehavior textHeightBehavior,
FontWeight fontWeight,
FontStyle fontStyle,
StrutStyle strutStyle,
String ellipsis,
Locale locale,
}) {
if (engine.experimentalUseSkia) {
return engine.SkParagraphStyle(
textAlign: textAlign,
textDirection: textDirection,
maxLines: maxLines,
fontFamily: fontFamily,
fontSize: fontSize,
height: height,
textHeightBehavior: textHeightBehavior,
fontWeight: fontWeight,
fontStyle: fontStyle,
strutStyle: strutStyle,
ellipsis: ellipsis,
locale: locale,
);
} else {
return engine.EngineParagraphStyle(
textAlign: textAlign,
textDirection: textDirection,
maxLines: maxLines,
fontFamily: fontFamily,
fontSize: fontSize,
height: height,
textHeightBehavior: textHeightBehavior,
fontWeight: fontWeight,
fontStyle: fontStyle,
strutStyle: strutStyle,
ellipsis: ellipsis,
locale: locale,
);
}
}
}
abstract class StrutStyle {
/// Creates a new StrutStyle object.
///
/// * `fontFamily`: The name of the font to use when painting the text (e.g.,
/// Roboto).
///
/// * `fontFamilyFallback`: An ordered list of font family names that will be searched for when
/// the font in `fontFamily` cannot be found.
///
/// * `fontSize`: The size of glyphs (in logical pixels) to use when painting
/// the text.
///
/// * `lineHeight`: The minimum height of the line boxes, as a multiple of the
/// font size. The lines of the paragraph will be at least
/// `(lineHeight + leading) * fontSize` tall when fontSize
/// is not null. When fontSize is null, there is no minimum line height. Tall
/// glyphs due to baseline alignment or large [TextStyle.fontSize] may cause
/// the actual line height after layout to be taller than specified here.
/// [fontSize] must be provided for this property to take effect.
///
/// * `leading`: The minimum amount of leading between lines as a multiple of
/// the font size. [fontSize] must be provided for this property to take effect.
///
/// * `fontWeight`: The typeface thickness to use when painting the text
/// (e.g., bold).
///
/// * `fontStyle`: The typeface variant to use when drawing the letters (e.g.,
/// italics).
///
/// * `forceStrutHeight`: When true, the paragraph will force all lines to be exactly
/// `(lineHeight + leading) * fontSize` tall from baseline to baseline.
/// [TextStyle] is no longer able to influence the line height, and any tall
/// glyphs may overlap with lines above. If a [fontFamily] is specified, the
/// total ascent of the first line will be the min of the `Ascent + half-leading`
/// of the [fontFamily] and `(lineHeight + leading) * fontSize`. Otherwise, it
/// will be determined by the Ascent + half-leading of the first text.
factory StrutStyle({
String fontFamily,
List<String> fontFamilyFallback,
double fontSize,
double height,
double leading,
FontWeight fontWeight,
FontStyle fontStyle,
bool forceStrutHeight,
}) = engine.EngineStrutStyle;
}
/// A direction in which text flows.
///
/// Some languages are written from the left to the right (for example, English,
/// Tamil, or Chinese), while others are written from the right to the left (for
/// example Aramaic, Hebrew, or Urdu). Some are also written in a mixture, for
/// example Arabic is mostly written right-to-left, with numerals written
/// left-to-right.
///
/// The text direction must be provided to APIs that render text or lay out
/// boxes horizontally, so that they can determine which direction to start in:
/// either right-to-left, [TextDirection.rtl]; or left-to-right,
/// [TextDirection.ltr].
///
/// ## Design discussion
///
/// Flutter is designed to address the needs of applications written in any of
/// the world's currently-used languages, whether they use a right-to-left or
/// left-to-right writing direction. Flutter does not support other writing
/// modes, such as vertical text or boustrophedon text, as these are rarely used
/// in computer programs.
///
/// It is common when developing user interface frameworks to pick a default
/// text direction — typically left-to-right, the direction most familiar to the
/// engineers working on the framework — because this simplifies the development
/// of applications on the platform. Unfortunately, this frequently results in
/// the platform having unexpected left-to-right biases or assumptions, as
/// engineers will typically miss places where they need to support
/// right-to-left text. This then results in bugs that only manifest in
/// right-to-left environments.
///
/// In an effort to minimize the extent to which Flutter experiences this
/// category of issues, the lowest levels of the Flutter framework do not have a
/// default text reading direction. Any time a reading direction is necessary,
/// for example when text is to be displayed, or when a
/// writing-direction-dependent value is to be interpreted, the reading
/// direction must be explicitly specified. Where possible, such as in `switch`
/// statements, the right-to-left case is listed first, to avoid the impression
/// that it is an afterthought.
///
/// At the higher levels (specifically starting at the widgets library), an
/// ambient [Directionality] is introduced, which provides a default. Thus, for
/// instance, a [Text] widget in the scope of a [MaterialApp] widget does not
/// need to be given an explicit writing direction. The [Directionality.of]
/// static method can be used to obtain the ambient text direction for a
/// particular [BuildContext].
///
/// ### Known left-to-right biases in Flutter
///
/// Despite the design intent described above, certain left-to-right biases have
/// nonetheless crept into Flutter's design. These include:
///
/// * The [Canvas] origin is at the top left, and the x-axis increases in a
/// left-to-right direction.
///
/// * The default localization in the widgets and material libraries is
/// American English, which is left-to-right.
///
/// ### Visual properties vs directional properties
///
/// Many classes in the Flutter framework are offered in two versions, a
/// visually-oriented variant, and a text-direction-dependent variant. For
/// example, [EdgeInsets] is described in terms of top, left, right, and bottom,
/// while [EdgeInsetsDirectional] is described in terms of top, start, end, and
/// bottom, where start and end correspond to right and left in right-to-left
/// text and left and right in left-to-right text.
///
/// There are distinct use cases for each of these variants.
///
/// Text-direction-dependent variants are useful when developing user interfaces
/// that should "flip" with the text direction. For example, a paragraph of text
/// in English will typically be left-aligned and a quote will be indented from
/// the left, while in Arabic it will be right-aligned and indented from the
/// right. Both of these cases are described by the direction-dependent
/// [TextAlign.start] and [EdgeInsetsDirectional.start].
///
/// In contrast, the visual variants are useful when the text direction is known
/// and not affected by the reading direction. For example, an application
/// giving driving directions might show a "turn left" arrow on the left and a
/// "turn right" arrow on the right — and would do so whether the application
/// was localized to French (left-to-right) or Hebrew (right-to-left).
///
/// In practice, it is also expected that many developers will only be
/// targeting one language, and in that case it may be simpler to think in
/// visual terms.
// The order of this enum must match the order of the values in TextDirection.h's TextDirection.
enum TextDirection {
/// The text flows from right to left (e.g. Arabic, Hebrew).
rtl,
/// The text flows from left to right (e.g., English, French).
ltr,
}
/// A rectangle enclosing a run of text.
///
/// This is similar to [Rect] but includes an inherent [TextDirection].
class TextBox {
/// Creates an object that describes a box containing text.
const TextBox.fromLTRBD(
this.left,
this.top,
this.right,
this.bottom,
this.direction,
);
/// The left edge of the text box, irrespective of direction.
///
/// To get the leading edge (which may depend on the [direction]), consider [start].
final double left;
/// The top edge of the text box.
final double top;
/// The right edge of the text box, irrespective of direction.
///
/// To get the trailing edge (which may depend on the [direction]), consider [end].
final double right;
/// The bottom edge of the text box.
final double bottom;
/// The direction in which text inside this box flows.
final TextDirection direction;
/// Returns a rect of the same size as this box.
Rect toRect() => Rect.fromLTRB(left, top, right, bottom);
/// The [left] edge of the box for left-to-right text; the [right] edge of the box for right-to-left text.
///
/// See also:
///
/// * [direction], which specifies the text direction.
double get start {
return (direction == TextDirection.ltr) ? left : right;
}
/// The [right] edge of the box for left-to-right text; the [left] edge of the box for right-to-left text.
///
/// See also:
///
/// * [direction], which specifies the text direction.
double get end {
return (direction == TextDirection.ltr) ? right : left;
}
@override
bool operator ==(dynamic other) {
if (identical(this, other)) {
return true;
}
if (other.runtimeType != runtimeType) {
return false;
}
final TextBox typedOther = other;
return typedOther.left == left &&
typedOther.top == top &&
typedOther.right == right &&
typedOther.bottom == bottom &&
typedOther.direction == direction;
}
@override
int get hashCode => hashValues(left, top, right, bottom, direction);
@override
String toString() {
return 'TextBox.fromLTRBD(${left.toStringAsFixed(1)}, ${top.toStringAsFixed(1)}, ${right.toStringAsFixed(1)}, ${bottom.toStringAsFixed(1)}, $direction)';
}
}
/// A way to disambiguate a [TextPosition] when its offset could match two
/// different locations in the rendered string.
///
/// For example, at an offset where the rendered text wraps, there are two
/// visual positions that the offset could represent: one prior to the line
/// break (at the end of the first line) and one after the line break (at the
/// start of the second line). A text affinity disambiguates between these two
/// cases.
///
/// This affects only line breaks caused by wrapping, not explicit newline
/// characters. For newline characters, the position is fully specified by the
/// offset alone, and there is no ambiguity.
///
/// [TextAffinity] also affects bidirectional text at the interface between LTR
/// and RTL text. Consider the following string, where the lowercase letters
/// will be displayed as LTR and the uppercase letters RTL: "helloHELLO". When
/// rendered, the string would appear visually as "helloOLLEH". An offset of 5
/// would be ambiguous without a corresponding [TextAffinity]. Looking at the
/// string in code, the offset represents the position just after the "o" and
/// just before the "H". When rendered, this offset could be either in the
/// middle of the string to the right of the "o" or at the end of the string to
/// the right of the "H".
enum TextAffinity {
/// The position has affinity for the upstream side of the text position, i.e.
/// in the direction of the beginning of the string.
///
/// In the example of an offset at the place where text is wrapping, upstream
/// indicates the end of the first line.
///
/// In the bidirectional text example "helloHELLO", an offset of 5 with
/// [TextAffinity] upstream would appear in the middle of the rendered text,
/// just to the right of the "o". See the definition of [TextAffinity] for the
/// full example.
upstream,
/// The position has affinity for the downstream side of the text position,
/// i.e. in the direction of the end of the string.
///
/// In the example of an offset at the place where text is wrapping,
/// downstream indicates the beginning of the second line.
///
/// In the bidirectional text example "helloHELLO", an offset of 5 with
/// [TextAffinity] downstream would appear at the end of the rendered text,
/// just to the right of the "H". See the definition of [TextAffinity] for the
/// full example.
downstream,
}
/// A position in a string of text.
///
/// A TextPosition can be used to locate a position in a string in code (using
/// the [offset] property), and it can also be used to locate the same position
/// visually in a rendered string of text (using [offset] and, when needed to
/// resolve ambiguity, [affinity]).
///
/// The location of an offset in a rendered string is ambiguous in two cases.
/// One happens when rendered text is forced to wrap. In this case, the offset
/// where the wrap occurs could visually appear either at the end of the first
/// line or the beginning of the second line. The second way is with
/// bidirectional text. An offset at the interface between two different text
/// directions could have one of two locations in the rendered text.
///
/// See the documentation for [TextAffinity] for more information on how
/// TextAffinity disambiguates situations like these.
class TextPosition {
/// Creates an object representing a particular position in a string.
///
/// The arguments must not be null (so the [offset] argument is required).
const TextPosition({
this.offset,
this.affinity = TextAffinity.downstream,
}) : assert(offset != null),
assert(affinity != null);
/// The index of the character that immediately follows the position in the
/// string representation of the text.
///
/// For example, given the string `'Hello'`, offset 0 represents the cursor
/// being before the `H`, while offset 5 represents the cursor being just
/// after the `o`.
final int offset;
/// Disambiguates cases where the position in the string given by [offset]
/// could represent two different visual positions in the rendered text. For
/// example, this can happen when text is forced to wrap, or when one string
/// of text is rendered with multiple text directions.
///
/// See the documentation for [TextAffinity] for more information on how
/// TextAffinity disambiguates situations like these.
final TextAffinity affinity;
@override
bool operator ==(dynamic other) {
if (other.runtimeType != runtimeType) {
return false;
}
final TextPosition typedOther = other;
return typedOther.offset == offset && typedOther.affinity == affinity;
}
@override
int get hashCode => hashValues(offset, affinity);
@override
String toString() {
return '$runtimeType(offset: $offset, affinity: $affinity)';
}
}
/// A range of characters in a string of text.
class TextRange {
/// Creates a text range.
///
/// The [start] and [end] arguments must not be null. Both the [start] and
/// [end] must either be greater than or equal to zero or both exactly -1.
///
/// Instead of creating an empty text range, consider using the [empty]
/// constant.
const TextRange({
this.start,
this.end,
}) : assert(start != null && start >= -1),
assert(end != null && end >= -1);
/// A text range that starts and ends at offset.
///
/// The [offset] argument must be non-null and greater than or equal to -1.
const TextRange.collapsed(int offset)
: assert(offset != null && offset >= -1),
start = offset,
end = offset;
/// A text range that contains nothing and is not in the text.
static const TextRange empty = TextRange(start: -1, end: -1);
/// The index of the first character in the range.
///
/// If [start] and [end] are both -1, the text range is empty.
final int start;
/// The next index after the characters in this range.
///
/// If [start] and [end] are both -1, the text range is empty.
final int end;
/// Whether this range represents a valid position in the text.
bool get isValid => start >= 0 && end >= 0;
/// Whether this range is empty (but still potentially placed inside the text).
bool get isCollapsed => start == end;
/// Whether the start of this range precedes the end.
bool get isNormalized => end >= start;
/// The text before this range.
String textBefore(String text) {
assert(isNormalized);
return text.substring(0, start);
}
/// The text after this range.
String textAfter(String text) {
assert(isNormalized);
return text.substring(end);
}
/// The text inside this range.
String textInside(String text) {
assert(isNormalized);
return text.substring(start, end);
}
@override
bool operator ==(dynamic other) {
if (identical(this, other)) {
return true;
}
if (other is! TextRange) {
return false;
}
final TextRange typedOther = other;
return typedOther.start == start && typedOther.end == end;
}
@override
int get hashCode => hashValues(
start.hashCode,
end.hashCode,
);
@override
String toString() => 'TextRange(start: $start, end: $end)';
}
/// Layout constraints for [Paragraph] objects.
///
/// Instances of this class are typically used with [Paragraph.layout].
///
/// The only constraint that can be specified is the [width]. See the discussion
/// at [width] for more details.
class ParagraphConstraints {
/// Creates constraints for laying out a pargraph.
///
/// The [width] argument must not be null.
const ParagraphConstraints({
this.width,
}) : assert(width != null);
/// The width the paragraph should use whey computing the positions of glyphs.
///
/// If possible, the paragraph will select a soft line break prior to reaching
/// this width. If no soft line break is available, the paragraph will select
/// a hard line break prior to reaching this width. If that would force a line
/// break without any characters having been placed (i.e. if the next
/// character to be laid out does not fit within the given width constraint)
/// then the next character is allowed to overflow the width constraint and a
/// forced line break is placed after it (even if an explicit line break
/// follows).
///
/// The width influences how ellipses are applied. See the discussion at [new
/// ParagraphStyle] for more details.
///
/// This width is also used to position glyphs according to the [TextAlign]
/// alignment described in the [ParagraphStyle] used when building the
/// [Paragraph] with a [ParagraphBuilder].
final double width;
@override
bool operator ==(dynamic other) {
if (other.runtimeType != runtimeType) {
return false;
}
final ParagraphConstraints typedOther = other;
return typedOther.width == width;
}
@override
int get hashCode => width.hashCode;
@override
String toString() => '$runtimeType(width: $width)';
}
/// Defines various ways to vertically bound the boxes returned by
/// [Paragraph.getBoxesForRange].
enum BoxHeightStyle {
/// Provide tight bounding boxes that fit heights per run. This style may result
/// in uneven bounding boxes that do not nicely connect with adjacent boxes.
tight,
/// The height of the boxes will be the maximum height of all runs in the
/// line. All boxes in the same line will be the same height. This does not
/// guarantee that the boxes will cover the entire vertical height of the line
/// when there is additional line spacing.
///
/// See [BoxHeightStyle.includeLineSpacingTop], [BoxHeightStyle.includeLineSpacingMiddle],
/// and [BoxHeightStyle.includeLineSpacingBottom] for styles that will cover
/// the entire line.
max,
/// Extends the top and bottom edge of the bounds to fully cover any line
/// spacing.
///
/// The top and bottom of each box will cover half of the
/// space above and half of the space below the line.
///
/// {@template flutter.dart:ui.boxHeightStyle.includeLineSpacing}
/// The top edge of each line should be the same as the bottom edge
/// of the line above. There should be no gaps in vertical coverage given any
/// amount of line spacing. Line spacing is not included above the first line
/// and below the last line due to no additional space present there.
/// {@endtemplate}
includeLineSpacingMiddle,
/// Extends the top edge of the bounds to fully cover any line spacing.
///
/// The line spacing will be added to the top of the box.
///
/// {@macro flutter.dart:ui.boxHeightStyle.includeLineSpacing}
includeLineSpacingTop,
/// Extends the bottom edge of the bounds to fully cover any line spacing.
///
/// The line spacing will be added to the bottom of the box.
///
/// {@macro flutter.dart:ui.boxHeightStyle.includeLineSpacing}
includeLineSpacingBottom,
/// Calculate box heights based on the metrics of this paragraph's [StrutStyle].
///
/// Boxes based on the strut will have consistent heights throughout the
/// entire paragraph. The top edge of each line will align with the bottom
/// edge of the previous line. It is possible for glyphs to extend outside
/// these boxes.
strut,
}
/// Defines various ways to horizontally bound the boxes returned by
/// [Paragraph.getBoxesForRange].
enum BoxWidthStyle {
// Provide tight bounding boxes that fit widths to the runs of each line
// independently.
tight,
/// Adds up to two additional boxes as needed at the beginning and/or end
/// of each line so that the widths of the boxes in line are the same width
/// as the widest line in the paragraph. The additional boxes on each line
/// are only added when the relevant box at the relevant edge of that line
/// does not span the maximum width of the paragraph.
max,
}
abstract class LineMetrics {
factory LineMetrics({
bool hardBreak,
double ascent,
double descent,
double unscaledAscent,
double height,
double width,
double left,
double baseline,
int lineNumber,
}) = engine.EngineLineMetrics;
/// {@template dart.ui.LineMetrics.hardBreak}
/// True if this line ends with an explicit line break (e.g. '\n') or is the end
/// of the paragraph. False otherwise.
/// {@endtemplate}
final bool hardBreak;
/// {@template dart.ui.LineMetrics.ascent}
/// The rise from the [baseline] as calculated from the font and style for this line.
///
/// This is the final computed ascent and can be impacted by the strut, height, scaling,
/// as well as outlying runs that are very tall.
///
/// The [ascent] is provided as a positive value, even though it is typically defined
/// in fonts as negative. This is to ensure the signage of operations with these
/// metrics directly reflects the intended signage of the value. For example,
/// the y coordinate of the top edge of the line is `baseline - ascent`.
/// {@endtemplate}
final double ascent;
/// {@template dart.ui.LineMetrics.descent}
/// The drop from the [baseline] as calculated from the font and style for this line.
///
/// This is the final computed ascent and can be impacted by the strut, height, scaling,
/// as well as outlying runs that are very tall.
///
/// The y coordinate of the bottom edge of the line is `baseline + descent`.
/// {@endtemplate}
final double descent;
/// {@template dart.ui.LineMetrics.unscaledAscent}
/// The rise from the [baseline] as calculated from the font and style for this line
/// ignoring the [TextStyle.height].
///
/// The [unscaledAscent] is provided as a positive value, even though it is typically
/// defined in fonts as negative. This is to ensure the signage of operations with
/// these metrics directly reflects the intended signage of the value.
/// {@endtemplate}
final double unscaledAscent;
/// {@template dart.ui.LineMetrics.height}
/// Total height of the line from the top edge to the bottom edge.
///
/// This is equivalent to `round(ascent + descent)`. This value is provided
/// separately due to rounding causing sub-pixel differences from the unrounded
/// values.
/// {@endtemplate}
final double height;
/// {@template dart.ui.LineMetrics.width}
/// Width of the line from the left edge of the leftmost glyph to the right
/// edge of the rightmost glyph.
///
/// This is not the same as the width of the pargraph.
///
/// See also:
///
/// * [Paragraph.width], the max width passed in during layout.
/// * [Paragraph.longestLine], the width of the longest line in the paragraph.
/// {@endtemplate}
final double width;
/// {@template dart.ui.LineMetrics.left}
/// The x coordinate of left edge of the line.
///
/// The right edge can be obtained with `left + width`.
/// {@endtemplate}
final double left;
/// {@template dart.ui.LineMetrics.baseline}
/// The y coordinate of the baseline for this line from the top of the paragraph.
///
/// The bottom edge of the paragraph up to and including this line may be obtained
/// through `baseline + descent`.
/// {@endtemplate}
final double baseline;
/// {@template dart.ui.LineMetrics.lineNumber}
/// The number of this line in the overall paragraph, with the first line being
/// index zero.
///
/// For example, the first line is line 0, second line is line 1.
/// {@endtemplate}
final int lineNumber;
}
/// A paragraph of text.
///
/// A paragraph retains the size and position of each glyph in the text and can
/// be efficiently resized and painted.
///
/// To create a [Paragraph] object, use a [ParagraphBuilder].
///
/// Paragraphs can be displayed on a [Canvas] using the [Canvas.drawParagraph]
/// method.
abstract class Paragraph {
/// The amount of horizontal space this paragraph occupies.
///
/// Valid only after [layout] has been called.
double get width;
/// The amount of vertical space this paragraph occupies.
///
/// Valid only after [layout] has been called.
double get height;
/// The distance from the left edge of the leftmost glyph to the right edge of
/// the rightmost glyph in the paragraph.
///
/// Valid only after [layout] has been called.
double get longestLine;
/// {@template dart.ui.paragraph.minIntrinsicWidth}
/// The minimum width that this paragraph could be without failing to paint
/// its contents within itself.
/// {@endtemplate}
///
/// Valid only after [layout] has been called.
double get minIntrinsicWidth;
/// {@template dart.ui.paragraph.maxIntrinsicWidth}
/// Returns the smallest width beyond which increasing the width never
/// decreases the height.
/// {@endtemplate}
///
/// Valid only after [layout] has been called.
double get maxIntrinsicWidth;
/// {@template dart.ui.paragraph.alphabeticBaseline}
/// The distance from the top of the paragraph to the alphabetic
/// baseline of the first line, in logical pixels.
/// {@endtemplate}
///
/// Valid only after [layout] has been called.
double get alphabeticBaseline;
/// {@template dart.ui.paragraph.ideographicBaseline}
/// The distance from the top of the paragraph to the ideographic
/// baseline of the first line, in logical pixels.
/// {@endtemplate}
///
/// Valid only after [layout] has been called.
double get ideographicBaseline;
/// True if there is more vertical content, but the text was truncated, either
/// because we reached `maxLines` lines of text or because the `maxLines` was
/// null, `ellipsis` was not null, and one of the lines exceeded the width
/// constraint.
///
/// See the discussion of the `maxLines` and `ellipsis` arguments at [new
/// ParagraphStyle].
bool get didExceedMaxLines;
/// Computes the size and position of each glyph in the paragraph.
///
/// The [ParagraphConstraints] control how wide the text is allowed to be.
void layout(ParagraphConstraints constraints);
/// Returns a list of text boxes that enclose the given text range.
///
/// The [boxHeightStyle] and [boxWidthStyle] parameters allow customization
/// of how the boxes are bound vertically and horizontally. Both style
/// parameters default to the tight option, which will provide close-fitting
/// boxes and will not account for any line spacing.
///
/// The [boxHeightStyle] and [boxWidthStyle] parameters must not be null.
///
/// See [BoxHeightStyle] and [BoxWidthStyle] for full descriptions of each option.
List<TextBox> getBoxesForRange(int start, int end,
{BoxHeightStyle boxHeightStyle = BoxHeightStyle.tight,
BoxWidthStyle boxWidthStyle = BoxWidthStyle.tight});
/// Returns the text position closest to the given offset.
///
/// It does so by performing a binary search to find where the tap occurred
/// within the text.
TextPosition getPositionForOffset(Offset offset);
/// Returns the [TextRange] of the word at the given [TextPosition].
///
/// Characters not part of a word, such as spaces, symbols, and punctuation,
/// have word breaks on both sides. In such cases, this method will return
/// [offset, offset+1]. Word boundaries are defined more precisely in Unicode
/// Standard Annex #29 http://www.unicode.org/reports/tr29/#Word_Boundaries
TextRange getWordBoundary(TextPosition position);
/// Returns the [TextRange] of the line at the given [TextPosition].
///
/// The newline (if any) is returned as part of the range.
///
/// Not valid until after layout.
///
/// This can potentially be expensive, since it needs to compute the line
/// metrics, so use it sparingly.
TextRange getLineBoundary(TextPosition position);
/// Returns a list of text boxes that enclose all placeholders in the paragraph.
///
/// The order of the boxes are in the same order as passed in through [addPlaceholder].
///
/// Coordinates of the [TextBox] are relative to the upper-left corner of the paragraph,
/// where positive y values indicate down.
List<TextBox> getBoxesForPlaceholders();
/// Returns the full list of [LineMetrics] that describe in detail the various
/// metrics of each laid out line.
///
/// Not valid until after layout.
///
/// This can potentially return a large amount of data, so it is not recommended
/// to repeatedly call this. Instead, cache the results.
List<LineMetrics> computeLineMetrics();
}
/// Builds a [Paragraph] containing text with the given styling information.
///
/// To set the paragraph's alignment, truncation, and ellipsising behavior, pass
/// an appropriately-configured [ParagraphStyle] object to the [new
/// ParagraphBuilder] constructor.
///
/// Then, call combinations of [pushStyle], [addText], and [pop] to add styled
/// text to the object.
///
/// Finally, call [build] to obtain the constructed [Paragraph] object. After
/// this point, the builder is no longer usable.
///
/// After constructing a [Paragraph], call [Paragraph.layout] on it and then
/// paint it with [Canvas.drawParagraph].
abstract class ParagraphBuilder {
/// Creates a [ParagraphBuilder] object, which is used to create a
/// [Paragraph].
factory ParagraphBuilder(ParagraphStyle style) {
if (engine.experimentalUseSkia) {
return engine.SkParagraphBuilder(style);
} else {
return engine.EngineParagraphBuilder(style);
}
}
/// Applies the given style to the added text until [pop] is called.
///
/// See [pop] for details.
void pushStyle(TextStyle style);
/// Ends the effect of the most recent call to [pushStyle].
///
/// Internally, the paragraph builder maintains a stack of text styles. Text
/// added to the paragraph is affected by all the styles in the stack. Calling
/// [pop] removes the topmost style in the stack, leaving the remaining styles
/// in effect.
void pop();
/// Adds the given text to the paragraph.
///
/// The text will be styled according to the current stack of text styles.
void addText(String text);
/// Applies the given paragraph style and returns a [Paragraph] containing the
/// added text and associated styling.
///
/// After calling this function, the paragraph builder object is invalid and
/// cannot be used further.
Paragraph build();
/// The number of placeholders currently in the paragraph.
int get placeholderCount;
/// The scales of the placeholders in the paragraph.
List<double> get placeholderScales;
/// Adds an inline placeholder space to the paragraph.
///
/// The paragraph will contain a rectangular space with no text of the dimensions
/// specified.
///
/// The `width` and `height` parameters specify the size of the placeholder rectangle.
///
/// The `alignment` parameter specifies how the placeholder rectangle will be vertically
/// aligned with the surrounding text. When [PlaceholderAlignment.baseline],
/// [PlaceholderAlignment.aboveBaseline], and [PlaceholderAlignment.belowBaseline]
/// alignment modes are used, the baseline needs to be set with the `baseline`.
/// When using [PlaceholderAlignment.baseline], `baselineOffset` indicates the distance
/// of the baseline down from the top of of the rectangle. The default `baselineOffset`
/// is the `height`.
///
/// Examples:
///
/// * For a 30x50 placeholder with the bottom edge aligned with the bottom of the text, use:
/// `addPlaceholder(30, 50, PlaceholderAlignment.bottom);`
/// * For a 30x50 placeholder that is vertically centered around the text, use:
/// `addPlaceholder(30, 50, PlaceholderAlignment.middle);`.
/// * For a 30x50 placeholder that sits completely on top of the alphabetic baseline, use:
/// `addPlaceholder(30, 50, PlaceholderAlignment.aboveBaseline, baseline: TextBaseline.alphabetic)`.
/// * For a 30x50 placeholder with 40 pixels above and 10 pixels below the alphabetic baseline, use:
/// `addPlaceholder(30, 50, PlaceholderAlignment.baseline, baseline: TextBaseline.alphabetic, baselineOffset: 40)`.
///
/// Lines are permitted to break around each placeholder.
///
/// Decorations will be drawn based on the font defined in the most recently
/// pushed [TextStyle]. The decorations are drawn as if unicode text were present
/// in the placeholder space, and will draw the same regardless of the height and
/// alignment of the placeholder. To hide or manually adjust decorations to fit,
/// a text style with the desired decoration behavior should be pushed before
/// adding a placeholder.
///
/// Any decorations drawn through a placeholder will exist on the same canvas/layer
/// as the text. This means any content drawn on top of the space reserved by
/// the placeholder will be drawn over the decoration, possibly obscuring the
/// decoration.
///
/// Placeholders are represented by a unicode 0xFFFC "object replacement character"
/// in the text buffer. For each placeholder, one object replacement character is
/// added on to the text buffer.
void addPlaceholder(
double width,
double height,
PlaceholderAlignment alignment, {
double scale,
double baselineOffset,
TextBaseline baseline,
});
}
/// Loads a font from a buffer and makes it available for rendering text.
///
/// * `list`: A list of bytes containing the font file.
/// * `fontFamily`: The family name used to identify the font in text styles.
/// If this is not provided, then the family name will be extracted from the font file.
Future<void> loadFontFromList(Uint8List list, {String fontFamily}) {
if (engine.experimentalUseSkia) {
return engine.skiaFontCollection.loadFontFromList(list, fontFamily: fontFamily).then(
(_) => engine.sendFontChangeMessage()
);
} else {
return _fontCollection.loadFontFromList(list, fontFamily: fontFamily).then(
(_) => engine.sendFontChangeMessage()
);
}
}