text.dart 82.5 KB
Newer Older
M
Michael Goderbauer 已提交
1
// Copyright 2013 The Flutter Authors. All rights reserved.
A
Adam Barth 已提交
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

A
Adam Barth 已提交
5
part of dart.ui;
A
Adam Barth 已提交
6 7 8 9 10 11 12 13 14 15 16

/// Whether to slant the glyphs in the font
enum FontStyle {
  /// Use the upright glyphs
  normal,

  /// Use glyphs designed for slanting
  italic,
}

/// The thickness of the glyphs used to draw the text
A
Adam Barth 已提交
17 18 19
class FontWeight {
  const FontWeight._(this.index);

A
Adam Barth 已提交
20
  /// The encoded integer value of this font weight.
A
Adam Barth 已提交
21 22
  final int index;

A
Adam Barth 已提交
23
  /// Thin, the least thick
D
Dan Field 已提交
24
  static const FontWeight w100 = FontWeight._(0);
A
Adam Barth 已提交
25 26

  /// Extra-light
D
Dan Field 已提交
27
  static const FontWeight w200 = FontWeight._(1);
A
Adam Barth 已提交
28 29

  /// Light
D
Dan Field 已提交
30
  static const FontWeight w300 = FontWeight._(2);
A
Adam Barth 已提交
31 32

  /// Normal / regular / plain
D
Dan Field 已提交
33
  static const FontWeight w400 = FontWeight._(3);
A
Adam Barth 已提交
34 35

  /// Medium
D
Dan Field 已提交
36
  static const FontWeight w500 = FontWeight._(4);
A
Adam Barth 已提交
37 38

  /// Semi-bold
D
Dan Field 已提交
39
  static const FontWeight w600 = FontWeight._(5);
A
Adam Barth 已提交
40 41

  /// Bold
D
Dan Field 已提交
42
  static const FontWeight w700 = FontWeight._(6);
A
Adam Barth 已提交
43 44

  /// Extra-bold
D
Dan Field 已提交
45
  static const FontWeight w800 = FontWeight._(7);
A
Adam Barth 已提交
46 47

  /// Black, the most thick
D
Dan Field 已提交
48
  static const FontWeight w900 = FontWeight._(8);
A
Adam Barth 已提交
49

A
Adam Barth 已提交
50
  /// The default font weight.
I
Ian Hickson 已提交
51
  static const FontWeight normal = w400;
A
Adam Barth 已提交
52 53

  /// A commonly used font weight that is heavier than normal.
I
Ian Hickson 已提交
54
  static const FontWeight bold = w700;
A
Adam Barth 已提交
55

A
Adam Barth 已提交
56
  /// A list of all the font weights.
D
Dan Field 已提交
57
  static const List<FontWeight> values = <FontWeight>[
A
Adam Barth 已提交
58 59 60
    w100, w200, w300, w400, w500, w600, w700, w800, w900
  ];

A
Adam Barth 已提交
61 62 63 64
  /// Linearly interpolates between two font weights.
  ///
  /// Rather than using fractional weights, the interpolation rounds to the
  /// nearest weight.
65
  ///
66 67
  /// If both `a` and `b` are null, then this method will return null. Otherwise,
  /// any null values for `a` or `b` are interpreted as equivalent to [normal]
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
  /// (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);
84 85
    if (a == null && b == null)
      return null;
86
    return values[lerpDouble(a?.index ?? normal.index, b?.index ?? normal.index, t).round().clamp(0, 8) as int];
I
Ian Hickson 已提交
87 88
  }

89
  @override
A
Adam Barth 已提交
90
  String toString() {
I
Ian Hickson 已提交
91 92 93 94 95 96 97 98 99 100
    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',
A
Adam Barth 已提交
101 102
    }[index];
  }
A
Adam Barth 已提交
103 104
}

105
/// A feature tag and value that affect the selection of glyphs in a font.
T
Tim Sneath 已提交
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
///
/// {@tool sample}
///
/// This example shows usage of several OpenType font features, including
/// Small Caps (smcp), old-style figures, fractional ligatures and stylistic
/// sets.
///
/// ```dart
///class TypePage extends StatelessWidget {
///  // The Cardo, Milonga and Raleway Dots fonts can be downloaded from
///  // Google Fonts (https://www.google.com/fonts).
///
///  final titleStyle = TextStyle(
///    fontSize: 18,
///    fontFeatures: [FontFeature.enable('smcp')],
///    color: Colors.blueGrey[600],
///  );
///
///  @override
///  Widget build(BuildContext context) {
///    return Scaffold(
///      body: Center(
///        child: Column(
///          mainAxisAlignment: MainAxisAlignment.center,
///          children: <Widget>[
///            Spacer(flex: 5),
///            Text('regular numbers have their place:', style: titleStyle),
///            Text('The 1972 cup final was a 1-1 draw.',
///                style: TextStyle(
///                  fontFamily: 'Cardo',
///                  fontSize: 24,
///                )),
///            Spacer(),
///            Text('but old-style figures blend well with lower case:',
///                style: titleStyle),
///            Text('The 1972 cup final was a 1-1 draw.',
///                style: TextStyle(
///                    fontFamily: 'Cardo',
///                    fontSize: 24,
///                    fontFeatures: [FontFeature.oldstyleFigures()])),
///            Spacer(),
///            Divider(),
///            Spacer(),
///            Text('fractions look better with a custom ligature:',
///                style: titleStyle),
///            Text('Add 1/2 tsp of flour and stir.',
///                style: TextStyle(
///                    fontFamily: 'Milonga',
///                    fontSize: 24,
///                    fontFeatures: [FontFeature.enable('frac')])),
///            Spacer(),
///            Divider(),
///            Spacer(),
///            Text('multiple stylistic sets in one font:', style: titleStyle),
///            Text('Raleway Dots',
///                style: TextStyle(fontFamily: 'Raleway Dots', fontSize: 48)),
///            Text('Raleway Dots',
///                style: TextStyle(
///                  fontFeatures: [FontFeature.stylisticSet(1)],
///                  fontFamily: 'Raleway Dots',
///                  fontSize: 48,
///                )),
///            Spacer(flex: 5),
///          ],
///        ),
///      ),
///    );
///  }
///}
/// ```
/// {@end-tool}
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
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;
303 304 305
    return other is FontFeature
        && other.feature == feature
        && other.value == value;
306 307 308 309 310 311 312 313 314
  }

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

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

315 316
/// Whether and how to align text horizontally.
// The order of this enum must match the order of the values in RenderStyleConstants.h's ETextAlign.
A
Adam Barth 已提交
317
enum TextAlign {
I
Ian Hickson 已提交
318
  /// Align the text on the left edge of the container.
A
Adam Barth 已提交
319 320
  left,

I
Ian Hickson 已提交
321
  /// Align the text on the right edge of the container.
A
Adam Barth 已提交
322 323
  right,

I
Ian Hickson 已提交
324
  /// Align the text in the center of the container.
325 326
  center,

I
Ian Hickson 已提交
327 328 329
  /// Stretch lines of text that end with a soft line break to fill the width of
  /// the container.
  ///
330
  /// Lines that end with hard line breaks are aligned towards the [start] edge.
I
Ian Hickson 已提交
331
  justify,
332 333 334 335 336

  /// Align the text on the leading edge of the container.
  ///
  /// For left-to-right text ([TextDirection.ltr]), this is the left edge.
  ///
J
Jason Simmons 已提交
337
  /// For right-to-left text ([TextDirection.rtl]), this is the right edge.
338 339 340 341 342 343
  start,

  /// Align the text on the trailing edge of the container.
  ///
  /// For left-to-right text ([TextDirection.ltr]), this is the right edge.
  ///
J
Jason Simmons 已提交
344
  /// For right-to-left text ([TextDirection.rtl]), this is the left edge.
345
  end,
A
Adam Barth 已提交
346 347
}

I
Ian Hickson 已提交
348
/// A horizontal line used for aligning text.
A
Adam Barth 已提交
349
enum TextBaseline {
350
  /// The horizontal line used to align the bottom of glyphs for alphabetic characters.
A
Adam Barth 已提交
351 352
  alphabetic,

353
  /// The horizontal line used to align ideographic characters.
354
  ideographic,
A
Adam Barth 已提交
355 356
}

I
Ian Hickson 已提交
357
/// A linear decoration to draw near the text.
A
Adam Barth 已提交
358 359 360
class TextDecoration {
  const TextDecoration._(this._mask);

H
Hixie 已提交
361
  /// Creates a decoration that paints the union of all the given decorations.
A
Adam Barth 已提交
362 363 364 365
  factory TextDecoration.combine(List<TextDecoration> decorations) {
    int mask = 0;
    for (TextDecoration decoration in decorations)
      mask |= decoration._mask;
D
Dan Field 已提交
366
    return TextDecoration._(mask);
A
Adam Barth 已提交
367 368 369 370 371 372 373 374 375
  }

  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;
  }

A
Adam Barth 已提交
376
  /// Do not draw a decoration
D
Dan Field 已提交
377
  static const TextDecoration none = TextDecoration._(0x0);
A
Adam Barth 已提交
378 379

  /// Draw a line underneath each line of text
D
Dan Field 已提交
380
  static const TextDecoration underline = TextDecoration._(0x1);
A
Adam Barth 已提交
381 382

  /// Draw a line above each line of text
D
Dan Field 已提交
383
  static const TextDecoration overline = TextDecoration._(0x2);
A
Adam Barth 已提交
384 385

  /// Draw a line through each line of text
D
Dan Field 已提交
386
  static const TextDecoration lineThrough = TextDecoration._(0x4);
A
Adam Barth 已提交
387

388
  @override
A
Adam Barth 已提交
389
  bool operator ==(dynamic other) {
390 391
    return other is TextDecoration
        && other._mask == _mask;
A
Adam Barth 已提交
392 393
  }

394
  @override
A
Adam Barth 已提交
395 396
  int get hashCode => _mask.hashCode;

397
  @override
A
Adam Barth 已提交
398 399 400
  String toString() {
    if (_mask == 0)
      return 'TextDecoration.none';
401
    final List<String> values = <String>[];
A
Adam Barth 已提交
402 403 404 405 406 407 408 409 410 411
    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(", ")}])';
  }
A
Adam Barth 已提交
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
}

/// 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
}

432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
/// {@template flutter.dart:ui.textHeightBehavior}
/// Defines how the paragraph will apply [TextStyle.height] to the ascent of the
/// first line and descent of the last line.
///
/// Each 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.
/// {@endtemplate}
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'
           ')';
  }
}

520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
/// Determines if lists [a] and [b] are deep equivalent.
///
/// Returns true if the lists are both null, or if they are both non-null, have
/// the same length, and contain the same elements in the same order. Returns
/// false otherwise.
bool _listEquals<T>(List<T> a, List<T> b) {
  if (a == null)
    return b == null;
  if (b == null || a.length != b.length)
    return false;
  for (int index = 0; index < a.length; index += 1) {
    if (a[index] != b[index])
      return false;
  }
  return true;
}

A
Adam Barth 已提交
537 538
// This encoding must match the C++ version of ParagraphBuilder::pushStyle.
//
A
Adam Barth 已提交
539
// The encoded array buffer has 8 elements.
A
Adam Barth 已提交
540
//
541
//  - Element 0: A bit field where the ith bit indicates whether the ith element
A
Adam Barth 已提交
542
//    has a non-null value. Bits 8 to 12 indicate whether |fontFamily|,
A
Adam Barth 已提交
543
//    |fontSize|, |letterSpacing|, |wordSpacing|, and |height| are non-null,
544
//    respectively. Bit 0 is unused.
A
Adam Barth 已提交
545 546 547 548 549 550 551 552 553
//
//  - Element 1: The |color| in ARGB with 8 bits per channel.
//
//  - Element 2: A bit field indicating which text decorations are present in
//    the |textDecoration| list. The ith bit is set if there's a TextDecoration
//    with enum index i in the list.
//
//  - Element 3: The |decorationColor| in ARGB with 8 bits per channel.
//
554
//  - Element 4: The bit field of the |decorationStyle|.
A
Adam Barth 已提交
555
//
556
//  - Element 5: The index of the |fontWeight|.
A
Adam Barth 已提交
557 558 559
//
//  - Element 6: The enum index of the |fontStyle|.
//
A
Adam Barth 已提交
560 561
//  - Element 7: The enum index of the |textBaseline|.
//
562 563 564 565 566
Int32List _encodeTextStyle(
  Color color,
  TextDecoration decoration,
  Color decorationColor,
  TextDecorationStyle decorationStyle,
567
  double decorationThickness,
568 569 570 571
  FontWeight fontWeight,
  FontStyle fontStyle,
  TextBaseline textBaseline,
  String fontFamily,
572
  List<String> fontFamilyFallback,
573 574 575 576
  double fontSize,
  double letterSpacing,
  double wordSpacing,
  double height,
577
  Locale locale,
578
  Paint background,
579
  Paint foreground,
580 581
  List<Shadow> shadows,
  List<FontFeature> fontFeatures,
582
) {
D
Dan Field 已提交
583
  final Int32List result = Int32List(8);
A
Adam Barth 已提交
584 585 586 587 588 589
  if (color != null) {
    result[0] |= 1 << 1;
    result[1] = color.value;
  }
  if (decoration != null) {
    result[0] |= 1 << 2;
A
Adam Barth 已提交
590
    result[2] = decoration._mask;
A
Adam Barth 已提交
591 592 593 594 595 596 597 598 599
  }
  if (decorationColor != null) {
    result[0] |= 1 << 3;
    result[3] = decorationColor.value;
  }
  if (decorationStyle != null) {
    result[0] |= 1 << 4;
    result[4] = decorationStyle.index;
  }
600
  if (fontWeight != null) {
601
    result[0] |= 1 << 5;
A
Adam Barth 已提交
602 603 604
    result[5] = fontWeight.index;
  }
  if (fontStyle != null) {
605
    result[0] |= 1 << 6;
A
Adam Barth 已提交
606 607
    result[6] = fontStyle.index;
  }
A
Adam Barth 已提交
608
  if (textBaseline != null) {
609
    result[0] |= 1 << 7;
A
Adam Barth 已提交
610 611
    result[7] = textBaseline.index;
  }
612 613 614
  if (decorationThickness != null) {
    result[0] |= 1 << 8;
  }
615
  if (fontFamily != null || (fontFamilyFallback != null && fontFamilyFallback.isNotEmpty)) {
616
    result[0] |= 1 << 9;
A
Adam Barth 已提交
617 618 619
    // Passed separately to native.
  }
  if (fontSize != null) {
620
    result[0] |= 1 << 10;
A
Adam Barth 已提交
621 622
    // Passed separately to native.
  }
A
Adam Barth 已提交
623
  if (letterSpacing != null) {
624
    result[0] |= 1 << 11;
A
Adam Barth 已提交
625 626
    // Passed separately to native.
  }
627
  if (wordSpacing != null) {
628
    result[0] |= 1 << 12;
629 630
    // Passed separately to native.
  }
A
Adam Barth 已提交
631
  if (height != null) {
632
    result[0] |= 1 << 13;
633 634
    // Passed separately to native.
  }
635
  if (locale != null) {
636
    result[0] |= 1 << 14;
637 638
    // Passed separately to native.
  }
639
  if (background != null) {
640
    result[0] |= 1 << 15;
641 642
    // Passed separately to native.
  }
643
  if (foreground != null) {
644
    result[0] |= 1 << 16;
G
Gary Qian 已提交
645 646
    // Passed separately to native.
  }
647
  if (shadows != null) {
648
    result[0] |= 1 << 17;
649 650
    // Passed separately to native.
  }
651 652 653 654
  if (fontFeatures != null) {
    result[0] |= 1 << 18;
    // Passed separately to native.
  }
A
Adam Barth 已提交
655 656 657
  return result;
}

A
Adam Barth 已提交
658
/// An opaque object that determines the size, position, and rendering of text.
659 660 661
///
/// See also:
///
662
///  * [TextStyle](https://api.flutter.dev/flutter/painting/TextStyle-class.html), the class in the [painting] library.
663
///
A
Adam Barth 已提交
664
class TextStyle {
H
Hixie 已提交
665
  /// Creates a new TextStyle object.
A
Adam Barth 已提交
666
  ///
667
  /// * `color`: The color to use when painting the text. If this is specified, `foreground` must be null.
668 669 670
  /// * `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).
671
  /// * `decorationThickness`: The thickness of the decoration as a muliplier on the thickness specified by the font.
672 673
  /// * `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).
674
  /// * `fontFamily`: The name of the font to use when painting the text (e.g., Roboto). If a `fontFamilyFallback` is
675
  ///   provided and `fontFamily` is not, then the first font family in `fontFamilyFallback` will take the position of
676 677 678 679 680 681 682
  ///   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.
683 684 685
  /// * `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).
A
Adam Barth 已提交
686
  /// * `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.
687 688
  /// * `height`: The height of this text span, as a multiplier of the font size. Omitting `height` will allow the line height
  ///   to take the height as defined by the font, which may not be exactly the height of the fontSize.
689
  /// * `locale`: The locale used to select region-specific glyphs.
690
  /// * `background`: The paint drawn as a background for the text.
691
  /// * `foreground`: The paint used to draw the text. If this is specified, `color` must be null.
692
  /// * `fontFeatures`: The font features that should be applied to the text.
A
Adam Barth 已提交
693 694
  TextStyle({
    Color color,
A
Adam Barth 已提交
695
    TextDecoration decoration,
A
Adam Barth 已提交
696 697
    Color decorationColor,
    TextDecorationStyle decorationStyle,
698
    double decorationThickness,
A
Adam Barth 已提交
699 700
    FontWeight fontWeight,
    FontStyle fontStyle,
A
Adam Barth 已提交
701
    TextBaseline textBaseline,
A
Adam Barth 已提交
702
    String fontFamily,
703
    List<String> fontFamilyFallback,
A
Adam Barth 已提交
704
    double fontSize,
705 706
    double letterSpacing,
    double wordSpacing,
707
    double height,
708
    Locale locale,
709
    Paint background,
710
    Paint foreground,
G
Gary Qian 已提交
711
    List<Shadow> shadows,
712
    List<FontFeature> fontFeatures,
L
liyuqian 已提交
713
  }) : assert(color == null || foreground == null,
714
         'Cannot provide both a color and a foreground\n'
D
Dan Field 已提交
715
         'The color argument is just a shorthand for "foreground: Paint()..color = color".'
716 717
       ),
       _encoded = _encodeTextStyle(
718 719 720 721
         color,
         decoration,
         decorationColor,
         decorationStyle,
722
         decorationThickness,
723 724 725 726
         fontWeight,
         fontStyle,
         textBaseline,
         fontFamily,
727
         fontFamilyFallback,
728 729 730 731
         fontSize,
         letterSpacing,
         wordSpacing,
         height,
732
         locale,
733
         background,
734
         foreground,
G
Gary Qian 已提交
735
         shadows,
736
         fontFeatures,
737
       ),
A
Adam Barth 已提交
738
       _fontFamily = fontFamily ?? '',
739
       _fontFamilyFallback = fontFamilyFallback,
A
Adam Barth 已提交
740
       _fontSize = fontSize,
741 742
       _letterSpacing = letterSpacing,
       _wordSpacing = wordSpacing,
743
       _height = height,
744
       _decorationThickness = decorationThickness,
745
       _locale = locale,
746
       _background = background,
G
Gary Qian 已提交
747
       _foreground = foreground,
748 749
       _shadows = shadows,
       _fontFeatures = fontFeatures;
A
Adam Barth 已提交
750 751 752

  final Int32List _encoded;
  final String _fontFamily;
753
  final List<String> _fontFamilyFallback;
A
Adam Barth 已提交
754
  final double _fontSize;
A
Adam Barth 已提交
755
  final double _letterSpacing;
756
  final double _wordSpacing;
A
Adam Barth 已提交
757
  final double _height;
758
  final double _decorationThickness;
759
  final Locale _locale;
760
  final Paint _background;
761
  final Paint _foreground;
G
Gary Qian 已提交
762
  final List<Shadow> _shadows;
763
  final List<FontFeature> _fontFeatures;
764

765
  @override
I
Ian Hickson 已提交
766 767 768
  bool operator ==(dynamic other) {
    if (identical(this, other))
      return true;
769 770 771 772 773 774 775 776 777 778 779 780 781 782
    return other is TextStyle
        && other._fontFamily == _fontFamily
        && other._fontSize == _fontSize
        && other._letterSpacing == _letterSpacing
        && other._wordSpacing == _wordSpacing
        && other._height == _height
        && other._decorationThickness == _decorationThickness
        && other._locale == _locale
        && other._background == _background
        && other._foreground == _foreground
        && _listEquals<int>(other._encoded, _encoded)
        && _listEquals<Shadow>(other._shadows, _shadows)
        && _listEquals<String>(other._fontFamilyFallback, _fontFamilyFallback)
        && _listEquals<FontFeature>(other._fontFeatures, _fontFeatures);
I
Ian Hickson 已提交
783 784
  }

785
  @override
786
  int get hashCode => hashValues(hashList(_encoded), _fontFamily, _fontFamilyFallback, _fontSize, _letterSpacing, _wordSpacing, _height, _locale, _background, _foreground, hashList(_shadows), _decorationThickness, hashList(_fontFeatures));
I
Ian Hickson 已提交
787

788
  @override
789
  String toString() {
A
Adam Barth 已提交
790
    return 'TextStyle('
D
Dan Field 已提交
791 792 793
             'color: ${              _encoded[0] & 0x00002 == 0x00002  ? Color(_encoded[1])                  : "unspecified"}, '
             'decoration: ${         _encoded[0] & 0x00004 == 0x00004  ? TextDecoration._(_encoded[2])       : "unspecified"}, '
             'decorationColor: ${    _encoded[0] & 0x00008 == 0x00008  ? Color(_encoded[3])                  : "unspecified"}, '
794
             'decorationStyle: ${    _encoded[0] & 0x00010 == 0x00010  ? TextDecorationStyle.values[_encoded[4]] : "unspecified"}, '
795 796 797 798 799
             // The decorationThickness is not in encoded order in order to keep it near the other decoration properties.
             'decorationThickness: ${_encoded[0] & 0x00100 == 0x00100  ? _decorationThickness                    : "unspecified"}, '
             'fontWeight: ${         _encoded[0] & 0x00020 == 0x00020  ? FontWeight.values[_encoded[5]]          : "unspecified"}, '
             'fontStyle: ${          _encoded[0] & 0x00040 == 0x00040  ? FontStyle.values[_encoded[6]]           : "unspecified"}, '
             'textBaseline: ${       _encoded[0] & 0x00080 == 0x00080  ? TextBaseline.values[_encoded[7]]        : "unspecified"}, '
800 801 802 803 804 805 806 807 808 809 810 811
             'fontFamily: ${         _encoded[0] & 0x00200 == 0x00200
                                     && _fontFamily != null            ? _fontFamily                             : "unspecified"}, '
             'fontFamilyFallback: ${ _encoded[0] & 0x00200 == 0x00200
                                     && _fontFamilyFallback != null
                                     && _fontFamilyFallback.isNotEmpty ? _fontFamilyFallback                     : "unspecified"}, '
             'fontSize: ${           _encoded[0] & 0x00400 == 0x00400  ? _fontSize                               : "unspecified"}, '
             'letterSpacing: ${      _encoded[0] & 0x00800 == 0x00800  ? "${_letterSpacing}x"                    : "unspecified"}, '
             'wordSpacing: ${        _encoded[0] & 0x01000 == 0x01000  ? "${_wordSpacing}x"                      : "unspecified"}, '
             'height: ${             _encoded[0] & 0x02000 == 0x02000  ? "${_height}x"                           : "unspecified"}, '
             'locale: ${             _encoded[0] & 0x04000 == 0x04000  ? _locale                                 : "unspecified"}, '
             'background: ${         _encoded[0] & 0x08000 == 0x08000  ? _background                             : "unspecified"}, '
             'foreground: ${         _encoded[0] & 0x10000 == 0x10000  ? _foreground                             : "unspecified"}, '
812 813
             'shadows: ${            _encoded[0] & 0x20000 == 0x20000  ? _shadows                                : "unspecified"}, '
             'fontFeatures: ${       _encoded[0] & 0x40000 == 0x40000  ? _fontFeatures                           : "unspecified"}'
814 815
           ')';
  }
A
Adam Barth 已提交
816 817 818 819
}

// This encoding must match the C++ version ParagraphBuilder::build.
//
G
Gary Qian 已提交
820
// The encoded array buffer has 6 elements.
A
Adam Barth 已提交
821
//
822 823 824 825
//  - Element 0: A bit mask indicating which fields are non-null.
//    Bit 0 is unused. Bits 1-n are set if the corresponding index in the
//    encoded array is non-null.  The remaining bits represent fields that
//    are passed separately from the array.
A
Adam Barth 已提交
826 827 828
//
//  - Element 1: The enum index of the |textAlign|.
//
G
Gary Qian 已提交
829
//  - Element 2: The enum index of the |textDirection|.
830
//
G
Gary Qian 已提交
831
//  - Element 3: The index of the |fontWeight|.
832
//
G
Gary Qian 已提交
833 834 835
//  - Element 4: The enum index of the |fontStyle|.
//
//  - Element 5: The value of |maxLines|.
836
//
837 838
//  - Element 6: The encoded value of |textHeightBehavior|.
//
839 840 841 842 843 844
Int32List _encodeParagraphStyle(
  TextAlign textAlign,
  TextDirection textDirection,
  int maxLines,
  String fontFamily,
  double fontSize,
G
Gary Qian 已提交
845
  double height,
846
  TextHeightBehavior textHeightBehavior,
G
Gary Qian 已提交
847 848 849
  FontWeight fontWeight,
  FontStyle fontStyle,
  StrutStyle strutStyle,
850
  String ellipsis,
851
  Locale locale,
852
) {
853
  final Int32List result = Int32List(7); // also update paragraph_builder.cc
A
Adam Barth 已提交
854 855 856 857
  if (textAlign != null) {
    result[0] |= 1 << 1;
    result[1] = textAlign.index;
  }
858
  if (textDirection != null) {
859
    result[0] |= 1 << 2;
860
    result[2] = textDirection.index;
A
Adam Barth 已提交
861
  }
862
  if (fontWeight != null) {
863
    result[0] |= 1 << 3;
864
    result[3] = fontWeight.index;
865
  }
866
  if (fontStyle != null) {
867
    result[0] |= 1 << 4;
868
    result[4] = fontStyle.index;
869
  }
870
  if (maxLines != null) {
871
    result[0] |= 1 << 5;
872 873
    result[5] = maxLines;
  }
874
  if (textHeightBehavior != null) {
D
Dan Field 已提交
875
    result[0] |= 1 << 6;
876 877 878 879
    result[6] = textHeightBehavior.encode();
  }
  if (fontFamily != null) {
    result[0] |= 1 << 7;
A
Adam Barth 已提交
880 881 882
    // Passed separately to native.
  }
  if (fontSize != null) {
883
    result[0] |= 1 << 8;
A
Adam Barth 已提交
884 885
    // Passed separately to native.
  }
G
Gary Qian 已提交
886
  if (height != null) {
887
    result[0] |= 1 << 9;
A
Adam Barth 已提交
888 889
    // Passed separately to native.
  }
G
Gary Qian 已提交
890
  if (strutStyle != null) {
891
    result[0] |= 1 << 10;
892 893
    // Passed separately to native.
  }
G
Gary Qian 已提交
894
  if (ellipsis != null) {
895
    result[0] |= 1 << 11;
896 897
    // Passed separately to native.
  }
G
Gary Qian 已提交
898
  if (locale != null) {
899
    result[0] |= 1 << 12;
G
Gary Qian 已提交
900 901
    // Passed separately to native.
  }
A
Adam Barth 已提交
902 903 904
  return result;
}

I
Ian Hickson 已提交
905 906
/// An opaque object that determines the configuration used by
/// [ParagraphBuilder] to position lines within a [Paragraph] of text.
A
Adam Barth 已提交
907
class ParagraphStyle {
H
Hixie 已提交
908
  /// Creates a new ParagraphStyle object.
A
Adam Barth 已提交
909
  ///
I
Ian Hickson 已提交
910 911 912 913 914 915
  /// * `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
  ///
916 917 918 919 920
  /// * `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.
  ///
I
Ian Hickson 已提交
921 922 923 924 925 926 927
  /// * `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.
  ///
G
Gary Qian 已提交
928 929
  /// * `fontFamily`: The name of the font family to apply when painting the text,
  ///   in the absence of a `textStyle` being attached to the span.
I
Ian Hickson 已提交
930
  ///
G
Gary Qian 已提交
931 932
  /// * `fontSize`: The fallback size of glyphs (in logical pixels) to
  ///   use when painting the text. This is used when there is no [TextStyle].
I
Ian Hickson 已提交
933
  ///
934 935 936 937 938
  /// * `height`: The fallback height of the spans as a multiplier of the font
  ///   size. The fallback height is used when no height is provided through
  ///   [TextStyle.height]. Omitting `height` here and in [TextStyle] will allow
  ///   the line height to take the height as defined by the font, which may not
  ///   be exactly the height of the `fontSize`.
G
Gary Qian 已提交
939
  ///
940 941 942
  /// * `textHeightBehavior`: Specifies how the `height` multiplier is
  ///   applied to ascent of the first line and the descent of the last line.
  ///
G
Gary Qian 已提交
943 944 945 946 947 948 949 950 951
  /// * `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.
I
Ian Hickson 已提交
952 953 954 955 956 957 958 959 960
  ///
  /// * `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.
961 962
  ///
  /// * `locale`: The locale used to select region-specific glyphs.
A
Adam Barth 已提交
963 964
  ParagraphStyle({
    TextAlign textAlign,
965
    TextDirection textDirection,
966
    int maxLines,
A
Adam Barth 已提交
967 968
    String fontFamily,
    double fontSize,
G
Gary Qian 已提交
969
    double height,
970
    TextHeightBehavior textHeightBehavior,
G
Gary Qian 已提交
971 972 973
    FontWeight fontWeight,
    FontStyle fontStyle,
    StrutStyle strutStyle,
I
Ian Hickson 已提交
974
    String ellipsis,
975
    Locale locale,
976 977 978 979 980 981
  }) : _encoded = _encodeParagraphStyle(
         textAlign,
         textDirection,
         maxLines,
         fontFamily,
         fontSize,
G
Gary Qian 已提交
982
         height,
983
         textHeightBehavior,
G
Gary Qian 已提交
984 985 986
         fontWeight,
         fontStyle,
         strutStyle,
987
         ellipsis,
988
         locale,
989
       ),
A
Adam Barth 已提交
990 991
       _fontFamily = fontFamily,
       _fontSize = fontSize,
G
Gary Qian 已提交
992 993
       _height = height,
       _strutStyle = strutStyle,
994 995
       _ellipsis = ellipsis,
       _locale = locale;
A
Adam Barth 已提交
996 997

  final Int32List _encoded;
A
Adam Barth 已提交
998 999
  final String _fontFamily;
  final double _fontSize;
G
Gary Qian 已提交
1000 1001
  final double _height;
  final StrutStyle _strutStyle;
1002
  final String _ellipsis;
1003
  final Locale _locale;
1004

1005
  @override
I
Ian Hickson 已提交
1006 1007 1008
  bool operator ==(dynamic other) {
    if (identical(this, other))
      return true;
1009
    if (other.runtimeType != runtimeType)
I
Ian Hickson 已提交
1010
      return false;
1011 1012 1013 1014 1015 1016 1017 1018
    return other is ParagraphStyle
        && other._fontFamily == _fontFamily
        && other._fontSize == _fontSize
        && other._height == _height
        && other._strutStyle == _strutStyle
        && other._ellipsis == _ellipsis
        && other._locale == _locale
        && _listEquals<int>(other._encoded, _encoded);
I
Ian Hickson 已提交
1019 1020
  }

1021
  @override
G
Gary Qian 已提交
1022
  int get hashCode => hashValues(hashList(_encoded), _fontFamily, _fontSize, _height, _ellipsis, _locale);
I
Ian Hickson 已提交
1023

1024
  @override
1025
  String toString() {
1026
    return 'ParagraphStyle('
1027 1028 1029 1030 1031
             'textAlign: ${     _encoded[0] & 0x002 == 0x002 ? TextAlign.values[_encoded[1]]     : "unspecified"}, '
             'textDirection: ${ _encoded[0] & 0x004 == 0x004 ? TextDirection.values[_encoded[2]] : "unspecified"}, '
             'fontWeight: ${    _encoded[0] & 0x008 == 0x008 ? FontWeight.values[_encoded[3]]    : "unspecified"}, '
             'fontStyle: ${     _encoded[0] & 0x010 == 0x010 ? FontStyle.values[_encoded[4]]     : "unspecified"}, '
             'maxLines: ${      _encoded[0] & 0x020 == 0x020 ? _encoded[5]                       : "unspecified"}, '
1032 1033 1034 1035 1036 1037 1038 1039
             'textHeightBehavior: ${
                                _encoded[0] & 0x040 == 0x040 ?
                                          TextHeightBehavior.fromEncoded(_encoded[6]).toString() : "unspecified"}, '
             'fontFamily: ${    _encoded[0] & 0x080 == 0x080 ? _fontFamily                       : "unspecified"}, '
             'fontSize: ${      _encoded[0] & 0x100 == 0x100 ? _fontSize                         : "unspecified"}, '
             'height: ${        _encoded[0] & 0x200 == 0x200 ? "${_height}x"                     : "unspecified"}, '
             'ellipsis: ${      _encoded[0] & 0x400 == 0x400 ? "\"$_ellipsis\""                  : "unspecified"}, '
             'locale: ${        _encoded[0] & 0x800 == 0x800 ? _locale                           : "unspecified"}'
1040 1041
           ')';
  }
A
Adam Barth 已提交
1042 1043
}

G
Gary Qian 已提交
1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114
// Serialize strut properties into ByteData. This encoding errs towards
// compactness. The first 8 bits is a bitmask that records which properties
// are null. The rest of the values are encoded in the same order encountered
// in the bitmask. The final returned value truncates any unused bytes
// at the end.
//
// We serialize this more thoroughly than ParagraphStyle because it is
// much more likely that the strut is empty/null and we wish to add
// minimal overhead for non-strut cases.
ByteData _encodeStrut(
  String fontFamily,
  List<String> fontFamilyFallback,
  double fontSize,
  double height,
  double leading,
  FontWeight fontWeight,
  FontStyle fontStyle,
  bool forceStrutHeight) {
  if (fontFamily == null &&
    fontSize == null &&
    height == null &&
    leading == null &&
    fontWeight == null &&
    fontStyle == null &&
    forceStrutHeight == null)
    return ByteData(0);

  final ByteData data = ByteData(15); // Max size is 15 bytes
  int bitmask = 0; // 8 bit mask
  int byteCount = 1;
  if (fontWeight != null) {
    bitmask |= 1 << 0;
    data.setInt8(byteCount, fontWeight.index);
    byteCount += 1;
  }
  if (fontStyle != null) {
    bitmask |= 1 << 1;
    data.setInt8(byteCount, fontStyle.index);
    byteCount += 1;
  }
  if (fontFamily != null || (fontFamilyFallback != null && fontFamilyFallback.isNotEmpty)){
    bitmask |= 1 << 2;
    // passed separately to native
  }
  if (fontSize != null) {
    bitmask |= 1 << 3;
    data.setFloat32(byteCount, fontSize, _kFakeHostEndian);
    byteCount += 4;
  }
  if (height != null) {
    bitmask |= 1 << 4;
    data.setFloat32(byteCount, height, _kFakeHostEndian);
    byteCount += 4;
  }
  if (leading != null) {
    bitmask |= 1 << 5;
    data.setFloat32(byteCount, leading, _kFakeHostEndian);
    byteCount += 4;
  }
  if (forceStrutHeight != null) {
    bitmask |= 1 << 6;
    // We store this boolean directly in the bitmask since there is
    // extra space in the 16 bit int.
    bitmask |= (forceStrutHeight ? 1 : 0) << 7;
  }

  data.setInt8(0, bitmask);

  return ByteData.view(data.buffer, 0,  byteCount);
}

1115 1116
/// See also:
///
1117
///  * [StrutStyle](https://api.flutter.dev/flutter/painting/StrutStyle-class.html), the class in the [painting] library.
1118
///
G
Gary Qian 已提交
1119 1120 1121 1122 1123 1124
class StrutStyle {
  /// Creates a new StrutStyle object.
  ///
  /// * `fontFamily`: The name of the font to use when painting the text (e.g.,
  ///   Roboto).
  ///
1125 1126
  /// * `fontFamilyFallback`: An ordered list of font family names that will be
  ///    searched for when the font in `fontFamily` cannot be found.
G
Gary Qian 已提交
1127 1128 1129 1130
  ///
  /// * `fontSize`: The size of glyphs (in logical pixels) to use when painting
  ///   the text.
  ///
G
Gary Qian 已提交
1131
  /// * `height`: The minimum height of the line boxes, as a multiplier of the
1132 1133 1134 1135 1136 1137 1138 1139
  ///   font size. The lines of the paragraph will be at least
  ///   `(height + leading) * fontSize` tall when `fontSize` is not null. Omitting
  ///   `height` will allow the minimum line height to take the height as defined
  ///   by the font, which may not be exactly the height of the `fontSize`. 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. The `fontSize` must
  ///   be provided for this property to take effect.
G
Gary Qian 已提交
1140 1141
  ///
  /// * `leading`: The minimum amount of leading between lines as a multiple of
1142
  ///   the font size. `fontSize` must be provided for this property to take effect.
G
Gary Qian 已提交
1143 1144 1145 1146 1147 1148 1149 1150
  ///
  /// * `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
G
Gary Qian 已提交
1151
  ///   `(height + leading) * fontSize` tall from baseline to baseline.
G
Gary Qian 已提交
1152
  ///   [TextStyle] is no longer able to influence the line height, and any tall
1153
  ///   glyphs may overlap with lines above. If a `fontFamily` is specified, the
G
Gary Qian 已提交
1154
  ///   total ascent of the first line will be the min of the `Ascent + half-leading`
1155
  ///   of the `fontFamily` and `(height + leading) * fontSize`. Otherwise, it
G
Gary Qian 已提交
1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189
  ///   will be determined by the Ascent + half-leading of the first text.
  StrutStyle({
    String fontFamily,
    List<String> fontFamilyFallback,
    double fontSize,
    double height,
    double leading,
    FontWeight fontWeight,
    FontStyle fontStyle,
    bool forceStrutHeight,
  }) : _encoded = _encodeStrut(
         fontFamily,
         fontFamilyFallback,
         fontSize,
         height,
         leading,
         fontWeight,
         fontStyle,
         forceStrutHeight,
       ),
       _fontFamily = fontFamily,
       _fontFamilyFallback = fontFamilyFallback;

  final ByteData _encoded; // Most of the data for strut is encoded.
  final String _fontFamily;
  final List<String> _fontFamilyFallback;


  @override
  bool operator ==(dynamic other) {
    if (identical(this, other))
      return true;
    if (other.runtimeType != runtimeType)
      return false;
1190 1191 1192 1193
    return other is StrutStyle
        && other._fontFamily == _fontFamily
        && _listEquals<String>(other._fontFamilyFallback, _fontFamilyFallback)
        && _listEquals<int>(other._encoded.buffer.asInt8List(), _encoded.buffer.asInt8List());
G
Gary Qian 已提交
1194 1195 1196 1197 1198 1199 1200
  }

  @override
  int get hashCode => hashValues(hashList(_encoded.buffer.asInt8List()), _fontFamily);

}

A
Adam Barth 已提交
1201
/// A direction in which text flows.
1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284
///
/// 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.
1285
// The order of this enum must match the order of the values in TextDirection.h's TextDirection.
A
Adam Barth 已提交
1286 1287 1288 1289 1290
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).
I
Ian Hickson 已提交
1291
  ltr,
A
Adam Barth 已提交
1292 1293 1294
}

/// A rectangle enclosing a run of text.
1295 1296
///
/// This is similar to [Rect] but includes an inherent [TextDirection].
S
Siva 已提交
1297
@pragma('vm:entry-point')
A
Adam Barth 已提交
1298
class TextBox {
1299
  /// Creates an object that describes a box containing text.
A
Adam Barth 已提交
1300 1301 1302 1303 1304
  const TextBox.fromLTRBD(
    this.left,
    this.top,
    this.right,
    this.bottom,
1305
    this.direction,
A
Adam Barth 已提交
1306 1307
  );

1308
  @pragma('vm:entry-point')
1309
  // ignore: unused_element
A
Adam Barth 已提交
1310 1311 1312 1313 1314
  TextBox._(
    this.left,
    this.top,
    this.right,
    this.bottom,
1315
    int directionIndex,
A
Adam Barth 已提交
1316 1317 1318
  ) : direction = TextDirection.values[directionIndex];

  /// The left edge of the text box, irrespective of direction.
1319 1320
  ///
  /// To get the leading edge (which may depend on the [direction]), consider [start].
A
Adam Barth 已提交
1321 1322 1323 1324 1325 1326
  final double left;

  /// The top edge of the text box.
  final double top;

  /// The right edge of the text box, irrespective of direction.
1327 1328
  ///
  /// To get the trailing edge (which may depend on the [direction]), consider [end].
A
Adam Barth 已提交
1329 1330 1331 1332 1333 1334 1335 1336 1337
  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.
D
Dan Field 已提交
1338
  Rect toRect() => Rect.fromLTRB(left, top, right, bottom);
A
Adam Barth 已提交
1339

1340 1341 1342 1343 1344
  /// 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.
A
Adam Barth 已提交
1345
  double get start {
J
Jason Simmons 已提交
1346
    return (direction == TextDirection.ltr) ? left : right;
A
Adam Barth 已提交
1347 1348
  }

1349 1350 1351 1352 1353
  /// 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.
A
Adam Barth 已提交
1354
  double get end {
J
Jason Simmons 已提交
1355
    return (direction == TextDirection.ltr) ? right : left;
A
Adam Barth 已提交
1356 1357
  }

1358
  @override
A
Adam Barth 已提交
1359 1360 1361
  bool operator ==(dynamic other) {
    if (identical(this, other))
      return true;
1362
    if (other.runtimeType != runtimeType)
A
Adam Barth 已提交
1363
      return false;
1364 1365 1366 1367 1368 1369
    return other is TextBox
        && other.left == left
        && other.top == top
        && other.right == right
        && other.bottom == bottom
        && other.direction == direction;
A
Adam Barth 已提交
1370 1371
  }

1372
  @override
A
Adam Barth 已提交
1373 1374
  int get hashCode => hashValues(left, top, right, bottom, direction);

1375
  @override
A
Adam Barth 已提交
1376 1377 1378
  String toString() => 'TextBox.fromLTRBD(${left.toStringAsFixed(1)}, ${top.toStringAsFixed(1)}, ${right.toStringAsFixed(1)}, ${bottom.toStringAsFixed(1)}, $direction)';
}

1379 1380
/// A way to disambiguate a [TextPosition] when its offset could match two
/// different locations in the rendered string.
A
Adam Barth 已提交
1381
///
1382 1383 1384 1385 1386 1387 1388 1389 1390 1391
/// 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.
///
1392
/// [TextAffinity] also affects bidirectional text at the interface between LTR
1393 1394 1395
/// 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
1396
/// would be ambiguous without a corresponding [TextAffinity].  Looking at the
1397 1398 1399 1400
/// 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".
A
Adam Barth 已提交
1401
enum TextAffinity {
1402
  /// The position has affinity for the upstream side of the text position, i.e.
1403 1404 1405 1406
  /// 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.
A
Adam Barth 已提交
1407
  ///
1408 1409 1410 1411
  /// 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.
A
Adam Barth 已提交
1412 1413
  upstream,

1414 1415
  /// The position has affinity for the downstream side of the text position,
  /// i.e. in the direction of the end of the string.
A
Adam Barth 已提交
1416
  ///
1417 1418 1419
  /// In the example of an offset at the place where text is wrapping,
  /// downstream indicates the beginning of the second line.
  ///
1420 1421 1422 1423
  /// 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.
1424
  downstream,
A
Adam Barth 已提交
1425 1426
}

1427 1428 1429 1430 1431 1432
/// 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]).
1433 1434 1435 1436 1437 1438 1439 1440 1441 1442
///
/// 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.
A
Adam Barth 已提交
1443
class TextPosition {
1444 1445 1446 1447 1448
  /// 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,
D
Dan Field 已提交
1449
    this.affinity = TextAffinity.downstream,
1450 1451 1452
  }) : assert(offset != null),
       assert(affinity != null);

1453 1454
  /// The index of the character that immediately follows the position in the
  /// string representation of the text.
1455 1456 1457 1458
  ///
  /// 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`.
A
Adam Barth 已提交
1459 1460
  final int offset;

1461 1462 1463 1464
  /// 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.
1465
  ///
1466 1467
  /// See the documentation for [TextAffinity] for more information on how
  /// TextAffinity disambiguates situations like these.
A
Adam Barth 已提交
1468 1469
  final TextAffinity affinity;

1470 1471 1472 1473
  @override
  bool operator ==(dynamic other) {
    if (other.runtimeType != runtimeType)
      return false;
1474 1475 1476
    return other is TextPosition
        && other.offset == offset
        && other.affinity == affinity;
1477 1478 1479 1480 1481 1482
  }

  @override
  int get hashCode => hashValues(offset, affinity);

  @override
A
Adam Barth 已提交
1483
  String toString() {
1484
    return 'TextPosition(offset: $offset, affinity: $affinity)';
A
Adam Barth 已提交
1485
  }
A
Adam Barth 已提交
1486 1487
}

1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557
/// 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.
  ///
  /// The text included in the range includes the character at [start], but not
  /// the one at [end].
  ///
  /// 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;
1558 1559 1560
    return other is TextRange
        && other.start == start
        && other.end == end;
1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572
  }

  @override
  int get hashCode => hashValues(
    start.hashCode,
    end.hashCode,
  );

  @override
  String toString() => 'TextRange(start: $start, end: $end)';
}

A
Adam Barth 已提交
1573 1574 1575
/// Layout constraints for [Paragraph] objects.
///
/// Instances of this class are typically used with [Paragraph.layout].
I
Ian Hickson 已提交
1576 1577 1578
///
/// The only constraint that can be specified is the [width]. See the discussion
/// at [width] for more details.
A
Adam Barth 已提交
1579
class ParagraphConstraints {
1580
  /// Creates constraints for laying out a paragraph.
A
Adam Barth 已提交
1581 1582
  ///
  /// The [width] argument must not be null.
1583
  const ParagraphConstraints({
1584 1585
    this.width,
  }) : assert(width != null);
A
Adam Barth 已提交
1586 1587 1588 1589 1590

  /// 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
I
Ian Hickson 已提交
1591 1592 1593 1594 1595 1596
  /// 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).
A
Adam Barth 已提交
1597
  ///
1598 1599
  /// The width influences how ellipses are applied. See the discussion at
  /// [new ParagraphStyle] for more details.
I
Ian Hickson 已提交
1600 1601 1602 1603
  ///
  /// 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].
A
Adam Barth 已提交
1604 1605
  final double width;

1606
  @override
A
Adam Barth 已提交
1607
  bool operator ==(dynamic other) {
1608
    if (other.runtimeType != runtimeType)
A
Adam Barth 已提交
1609
      return false;
1610 1611
    return other is ParagraphConstraints
        && other.width == width;
A
Adam Barth 已提交
1612 1613
  }

1614
  @override
A
Adam Barth 已提交
1615 1616
  int get hashCode => width.hashCode;

1617
  @override
1618
  String toString() => 'ParagraphConstraints(width: $width)';
A
Adam Barth 已提交
1619 1620
}

1621 1622 1623
/// Defines various ways to vertically bound the boxes returned by
/// [Paragraph.getBoxesForRange].
enum BoxHeightStyle {
1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673
  /// 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 [RectHeightStyle.includeLineSpacingTop], [RectHeightStyle.includeLineSpacingMiddle],
  /// and [RectHeightStyle.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.rectHeightStyle.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,
1674 1675 1676 1677 1678
}

/// Defines various ways to horizontally bound the boxes returned by
/// [Paragraph.getBoxesForRange].
enum BoxWidthStyle {
1679 1680
  /// Provide tight bounding boxes that fit widths to the runs of each line
  /// independently.
1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716
  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,
}

/// 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,

1717
  /// Align the top edge of the placeholder with the top edge of the text.
1718 1719 1720 1721 1722
  ///
  /// When the placeholder is very tall, the extra space will hang from
  /// the top and extend through the bottom of the line.
  top,

1723
  /// Align the bottom edge of the placeholder with the bottom edge of the text.
1724 1725 1726 1727 1728 1729 1730 1731 1732 1733
  ///
  /// 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,
1734 1735
}

1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762
/// [LineMetrics] stores the measurements and statistics of a single line in the
/// paragraph.
///
/// The measurements here are for the line as a whole, and represent the maximum
/// extent of the line instead of per-run or per-glyph metrics. For more detailed
/// metrics, see [TextBox] and [Paragraph.getBoxesForRange].
///
/// [LineMetrics] should be obtained directly from the [Paragraph.computeLineMetrics]
/// method.
class LineMetrics {
  /// Creates a [LineMetrics] object with only the specified values.
  ///
  /// Omitted values will remain null. [Paragraph.computeLineMetrics] produces
  /// fully defined [LineMetrics] with no null values.
  LineMetrics({
    this.hardBreak,
    this.ascent,
    this.descent,
    this.unscaledAscent,
    this.height,
    this.width,
    this.left,
    this.baseline,
    this.lineNumber,
  });

  @pragma('vm:entry-point')
1763
  // ignore: unused_element
1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808
  LineMetrics._(
    this.hardBreak,
    this.ascent,
    this.descent,
    this.unscaledAscent,
    this.height,
    this.width,
    this.left,
    this.baseline,
    this.lineNumber,
  );

  /// True if this line ends with an explicit line break (e.g. '\n') or is the end
  /// of the paragraph. False otherwise.
  final bool hardBreak;

  /// 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`.
  final double ascent;

  /// 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`.
  final double descent;

  /// 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.
  final double unscaledAscent;

  /// Total height of the line from the top edge to the bottom edge.
  ///
1809 1810 1811
  /// This is equivalent to `round(ascent + descent)`. This value is provided
  /// separately due to rounding causing sub-pixel differences from the unrounded
  /// values.
1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830
  final double height;

  /// 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.
  final double width;

  /// The x coordinate of left edge of the line.
  ///
  /// The right edge can be obtained with `left + width`.
  final double left;

  /// The y coordinate of the baseline for this line from the top of the paragraph.
1831 1832 1833
  ///
  /// The bottom edge of the paragraph up to and including this line may be obtained
  /// through `baseline + descent`.
1834 1835 1836 1837 1838 1839 1840 1841 1842
  final double baseline;

  /// 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.
  final int lineNumber;
}

A
Adam Barth 已提交
1843 1844 1845 1846
/// A paragraph of text.
///
/// A paragraph retains the size and position of each glyph in the text and can
/// be efficiently resized and painted.
H
Hixie 已提交
1847
///
I
Ian Hickson 已提交
1848
/// To create a [Paragraph] object, use a [ParagraphBuilder].
H
Hixie 已提交
1849
///
I
Ian Hickson 已提交
1850 1851
/// Paragraphs can be displayed on a [Canvas] using the [Canvas.drawParagraph]
/// method.
S
sjindel-google 已提交
1852
@pragma('vm:entry-point')
1853 1854 1855
class Paragraph extends NativeFieldWrapperClass2 {
  /// This class is created by the engine, and should not be instantiated
  /// or extended directly.
H
Hixie 已提交
1856
  ///
1857
  /// To create a [Paragraph] object, use a [ParagraphBuilder].
1858
  @pragma('vm:entry-point')
1859
  Paragraph._();
H
Hixie 已提交
1860

A
Adam Barth 已提交
1861 1862 1863
  /// The amount of horizontal space this paragraph occupies.
  ///
  /// Valid only after [layout] has been called.
1864
  double get width native 'Paragraph_width';
A
Adam Barth 已提交
1865 1866 1867 1868

  /// The amount of vertical space this paragraph occupies.
  ///
  /// Valid only after [layout] has been called.
1869
  double get height native 'Paragraph_height';
A
Adam Barth 已提交
1870

1871 1872 1873 1874
  /// 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.
1875
  double get longestLine native 'Paragraph_longestLine';
1876

A
Adam Barth 已提交
1877 1878 1879 1880
  /// The minimum width that this paragraph could be without failing to paint
  /// its contents within itself.
  ///
  /// Valid only after [layout] has been called.
1881
  double get minIntrinsicWidth native 'Paragraph_minIntrinsicWidth';
A
Adam Barth 已提交
1882 1883 1884 1885 1886

  /// Returns the smallest width beyond which increasing the width never
  /// decreases the height.
  ///
  /// Valid only after [layout] has been called.
1887
  double get maxIntrinsicWidth native 'Paragraph_maxIntrinsicWidth';
A
Adam Barth 已提交
1888

H
Hixie 已提交
1889 1890
  /// The distance from the top of the paragraph to the alphabetic
  /// baseline of the first line, in logical pixels.
1891
  double get alphabeticBaseline native 'Paragraph_alphabeticBaseline';
A
Adam Barth 已提交
1892

H
Hixie 已提交
1893
  /// The distance from the top of the paragraph to the ideographic
1894
  /// baseline of the first line, in logical pixels.
1895
  double get ideographicBaseline native 'Paragraph_ideographicBaseline';
A
Adam Barth 已提交
1896

I
Ian Hickson 已提交
1897 1898 1899 1900 1901
  /// 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.
  ///
1902 1903
  /// See the discussion of the `maxLines` and `ellipsis` arguments at
  /// [new ParagraphStyle].
1904
  bool get didExceedMaxLines native 'Paragraph_didExceedMaxLines';
1905

A
Adam Barth 已提交
1906
  /// Computes the size and position of each glyph in the paragraph.
I
Ian Hickson 已提交
1907 1908
  ///
  /// The [ParagraphConstraints] control how wide the text is allowed to be.
1909
  void layout(ParagraphConstraints constraints) => _layout(constraints.width);
1910
  void _layout(double width) native 'Paragraph_layout';
A
Adam Barth 已提交
1911

A
Adam Barth 已提交
1912
  /// Returns a list of text boxes that enclose the given text range.
1913 1914 1915 1916 1917 1918
  ///
  /// 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.
  ///
1919 1920 1921
  /// Coordinates of the TextBox are relative to the upper-left corner of the paragraph,
  /// where positive y values indicate down.
  ///
1922 1923 1924 1925 1926 1927 1928 1929 1930
  /// 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}) {
    assert(boxHeightStyle != null);
    assert(boxWidthStyle != null);
    return _getBoxesForRange(start, end, boxHeightStyle.index, boxWidthStyle.index);
  }
  List<TextBox> _getBoxesForRange(int start, int end, int boxHeightStyle, int boxWidthStyle) native 'Paragraph_getRectsForRange';
A
Adam Barth 已提交
1931

1932 1933 1934 1935 1936 1937 1938 1939
  /// 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() native 'Paragraph_getRectsForPlaceholders';

A
Adam Barth 已提交
1940 1941
  /// Returns the text position closest to the given offset.
  TextPosition getPositionForOffset(Offset offset) {
1942
    final List<int> encoded = _getPositionForOffset(offset.dx, offset.dy);
D
Dan Field 已提交
1943
    return TextPosition(offset: encoded[0], affinity: TextAffinity.values[encoded[1]]);
A
Adam Barth 已提交
1944
  }
1945
  List<int> _getPositionForOffset(double dx, double dy) native 'Paragraph_getPositionForOffset';
1946

1947 1948 1949 1950 1951 1952 1953 1954 1955
  /// 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) {
    final List<int> boundary = _getWordBoundary(position.offset);
    return TextRange(start: boundary[0], end: boundary[1]);
1956 1957
  }
  List<int> _getWordBoundary(int offset) native 'Paragraph_getWordBoundary';
1958

1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972
  /// 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) {
    final List<int> boundary = _getLineBoundary(position.offset);
    return TextRange(start: boundary[0], end: boundary[1]);
  }
  List<int> _getLineBoundary(int offset) native 'Paragraph_getLineBoundary';

1973 1974 1975
  // Redirecting the paint function in this way solves some dependency problems
  // in the C++ code. If we straighten out the C++ dependencies, we can remove
  // this indirection.
1976
  void _paint(Canvas canvas, double x, double y) native 'Paragraph_paint';
1977 1978 1979 1980 1981 1982 1983 1984 1985

  /// 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() native 'Paragraph_computeLineMetrics';
A
Adam Barth 已提交
1986 1987
}

A
Adam Barth 已提交
1988
/// Builds a [Paragraph] containing text with the given styling information.
I
Ian Hickson 已提交
1989
///
1990
/// To set the paragraph's alignment, truncation, and ellipsizing behavior, pass
1991 1992
/// an appropriately-configured [ParagraphStyle] object to the
/// [new ParagraphBuilder] constructor.
I
Ian Hickson 已提交
1993 1994 1995 1996 1997 1998 1999 2000 2001
///
/// 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].
A
Adam Barth 已提交
2002
class ParagraphBuilder extends NativeFieldWrapperClass2 {
G
Gary Qian 已提交
2003

D
Dan Field 已提交
2004
  /// Creates a new [ParagraphBuilder] object, which is used to create a
H
Hixie 已提交
2005
  /// [Paragraph].
2006
  @pragma('vm:entry-point')
G
Gary Qian 已提交
2007
  ParagraphBuilder(ParagraphStyle style) {
2008
    _placeholderCount = 0;
G
Gary Qian 已提交
2009 2010 2011 2012 2013 2014 2015 2016
    List<String> strutFontFamilies;
    if (style._strutStyle != null) {
      strutFontFamilies = <String>[];
      if (style._strutStyle._fontFamily != null)
        strutFontFamilies.add(style._strutStyle._fontFamily);
      if (style._strutStyle._fontFamilyFallback != null)
        strutFontFamilies.addAll(style._strutStyle._fontFamilyFallback);
    }
2017 2018 2019 2020 2021 2022 2023 2024 2025 2026
    _constructor(
      style._encoded,
      style._strutStyle?._encoded,
      style._fontFamily,
      strutFontFamilies,
      style._fontSize,
      style._height,
      style._ellipsis,
      _encodeLocale(style._locale)
    );
G
Gary Qian 已提交
2027
  }
2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038

  void _constructor(
    Int32List encoded,
    ByteData strutData,
    String fontFamily,
    List<dynamic> strutFontFamily,
    double fontSize,
    double height,
    String ellipsis,
    String locale
  ) native 'ParagraphBuilder_constructor';
A
Adam Barth 已提交
2039

2040 2041 2042 2043 2044 2045 2046 2047
  /// The number of placeholders currently in the paragraph.
  int get placeholderCount => _placeholderCount;
  int _placeholderCount;

  /// The scales of the placeholders in the paragraph.
  List<double> get placeholderScales => _placeholderScales;
  List<double> _placeholderScales = <double>[];

A
Adam Barth 已提交
2048 2049 2050
  /// Applies the given style to the added text until [pop] is called.
  ///
  /// See [pop] for details.
2051 2052 2053 2054 2055
  void pushStyle(TextStyle style) {
    final List<String> fullFontFamilies = <String>[];
    if (style._fontFamily != null)
      fullFontFamilies.add(style._fontFamily);
    if (style._fontFamilyFallback != null)
2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067
    fullFontFamilies.addAll(style._fontFamilyFallback);

    ByteData encodedFontFeatures;
    if (style._fontFeatures != null) {
      encodedFontFeatures = ByteData(style._fontFeatures.length * FontFeature._kEncodedSize);
      int byteOffset = 0;
      for (FontFeature feature in style._fontFeatures) {
        feature._encode(ByteData.view(encodedFontFeatures.buffer, byteOffset, FontFeature._kEncodedSize));
        byteOffset += FontFeature._kEncodedSize;
      }
    }

2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080
    _pushStyle(
      style._encoded,
      fullFontFamilies,
      style._fontSize,
      style._letterSpacing,
      style._wordSpacing,
      style._height,
      style._decorationThickness,
      _encodeLocale(style._locale),
      style._background?._objects,
      style._background?._data,
      style._foreground?._objects,
      style._foreground?._data,
2081 2082
      Shadow._encodeShadows(style._shadows),
      encodedFontFeatures,
2083
    );
2084
  }
2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098

  void _pushStyle(
    Int32List encoded,
    List<dynamic> fontFamilies,
    double fontSize,
    double letterSpacing,
    double wordSpacing,
    double height,
    double decorationThickness,
    String locale,
    List<dynamic> backgroundObjects,
    ByteData backgroundData,
    List<dynamic> foregroundObjects,
    ByteData foregroundData,
2099 2100
    ByteData shadowsData,
    ByteData fontFeaturesData,
2101
  ) native 'ParagraphBuilder_pushStyle';
2102 2103

  static String _encodeLocale(Locale locale) => locale?.toString() ?? '';
A
Adam Barth 已提交
2104

A
Adam Barth 已提交
2105 2106 2107 2108 2109 2110
  /// 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.
2111
  void pop() native 'ParagraphBuilder_pop';
2112

A
Adam Barth 已提交
2113 2114 2115
  /// Adds the given text to the paragraph.
  ///
  /// The text will be styled according to the current stack of text styles.
2116
  void addText(String text) {
2117
    final String error = _addText(text);
2118
    if (error != null)
D
Dan Field 已提交
2119
      throw ArgumentError(error);
2120
  }
2121
  String _addText(String text) native 'ParagraphBuilder_addText';
A
Adam Barth 已提交
2122

2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187
  /// 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.
  ///
  /// The `scale` parameter will scale the `width` and `height` by the specified amount,
  /// and keep track of the scale. The scales of placeholders added can be accessed
  /// through [placeholderScales]. This is primarily used for acessibility scaling.
  void addPlaceholder(double width, double height, PlaceholderAlignment alignment, {
    double scale = 1.0,
    double baselineOffset,
    TextBaseline baseline,
  }) {
    // Require a baseline to be specified if using a baseline-based alignment.
    assert((alignment == PlaceholderAlignment.aboveBaseline ||
            alignment == PlaceholderAlignment.belowBaseline ||
            alignment == PlaceholderAlignment.baseline) ? baseline != null : true);
    // Default the baselineOffset to height if null. This will place the placeholder
    // fully above the baseline, similar to [PlaceholderAlignment.aboveBaseline].
    baselineOffset = baselineOffset ?? height;
    _addPlaceholder(width * scale, height * scale, alignment.index, (baselineOffset == null ? height : baselineOffset) * scale, baseline == null ? null : baseline.index);
    _placeholderCount++;
    _placeholderScales.add(scale);
  }
  String _addPlaceholder(double width, double height, int alignment, double baselineOffset, int baseline) native 'ParagraphBuilder_addPlaceholder';

I
Ian Hickson 已提交
2188 2189
  /// Applies the given paragraph style and returns a [Paragraph] containing the
  /// added text and associated styling.
A
Adam Barth 已提交
2190 2191 2192
  ///
  /// After calling this function, the paragraph builder object is invalid and
  /// cannot be used further.
2193
  Paragraph build() native 'ParagraphBuilder_build';
A
Adam Barth 已提交
2194
}
2195 2196 2197 2198 2199 2200 2201 2202 2203

/// 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}) {
  return _futurize(
    (_Callback<void> callback) => _loadFontFromList(list, callback, fontFamily)
2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217
  ).then((_) => _sendFontChangeMessage());
}

final ByteData _fontChangeMessage = utf8.encoder.convert(
  json.encode(<String, dynamic>{'type': 'fontsChange'})
).buffer.asByteData();

FutureOr<void> _sendFontChangeMessage() async {
  if (window.onPlatformMessage != null)
    window.onPlatformMessage(
      'flutter/system',
      _fontChangeMessage,
      (_) {},
    );
2218 2219 2220
}

String _loadFontFromList(Uint8List list, _Callback<void> callback, String fontFamily) native 'loadFontFromList';