text_painter_test.dart 38.9 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:ui' as ui;

7
import 'package:flutter/widgets.dart';
8 9
import 'package:flutter_test/flutter_test.dart';

10
void main() {
11
  test('TextPainter caret test', () {
12
    final TextPainter painter = TextPainter()
Ian Hickson's avatar
Ian Hickson committed
13
      ..textDirection = TextDirection.ltr;
14 15

    String text = 'A';
16
    painter.text = TextSpan(text: text);
17 18
    painter.layout();

19 20 21 22
    Offset caretOffset = painter.getOffsetForCaret(
      const ui.TextPosition(offset: 0),
      ui.Rect.zero,
    );
23
    expect(caretOffset.dx, 0);
24
    caretOffset = painter.getOffsetForCaret(ui.TextPosition(offset: text.length), ui.Rect.zero);
25 26
    expect(caretOffset.dx, painter.width);

27 28
    // Check that getOffsetForCaret handles a character that is encoded as a
    // surrogate pair.
29
    text = 'A\u{1F600}';
30
    painter.text = TextSpan(text: text);
31
    painter.layout();
32
    caretOffset = painter.getOffsetForCaret(ui.TextPosition(offset: text.length), ui.Rect.zero);
33
    expect(caretOffset.dx, painter.width);
34
  });
35

36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
  test('TextPainter null text test', () {
    final TextPainter painter = TextPainter()
      ..textDirection = TextDirection.ltr;

    List<TextSpan> children = <TextSpan>[const TextSpan(text: 'B'), const TextSpan(text: 'C')];
    painter.text = TextSpan(text: null, children: children);
    painter.layout();

    Offset caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 0), ui.Rect.zero);
    expect(caretOffset.dx, 0);
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 1), ui.Rect.zero);
    expect(caretOffset.dx, painter.width / 2);
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 2), ui.Rect.zero);
    expect(caretOffset.dx, painter.width);

    children = <TextSpan>[];
    painter.text = TextSpan(text: null, children: children);
    painter.layout();

    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 0), ui.Rect.zero);
    expect(caretOffset.dx, 0);
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 1), ui.Rect.zero);
    expect(caretOffset.dx, 0);
59
  });
60

61 62 63 64
  test('TextPainter caret emoji test', () {
    final TextPainter painter = TextPainter()
      ..textDirection = TextDirection.ltr;

65 66 67
    // Format: '👩‍<zwj>👩‍<zwj>👦👩‍<zwj>👩‍<zwj>👧‍<zwj>👧👏<modifier>'
    // One three-person family, one four-person family, one clapping hands (medium skin tone).
    const String text = '👩‍👩‍👦👩‍👩‍👧‍👧👏🏽';
68
    painter.text = const TextSpan(text: text);
69
    painter.layout(maxWidth: 10000);
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115

    expect(text.length, 23);

    Offset caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 0), ui.Rect.zero);
    expect(caretOffset.dx, 0); // 👩‍
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: text.length), ui.Rect.zero);
    expect(caretOffset.dx, painter.width);

    // Two UTF-16 codepoints per emoji, one codepoint per zwj
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 1), ui.Rect.zero);
    expect(caretOffset.dx, 42); // 👩‍
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 2), ui.Rect.zero);
    expect(caretOffset.dx, 42); // <zwj>
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 3), ui.Rect.zero);
    expect(caretOffset.dx, 42); // 👩‍
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 4), ui.Rect.zero);
    expect(caretOffset.dx, 42); // 👩‍
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 5), ui.Rect.zero);
    expect(caretOffset.dx, 42); // <zwj>
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 6), ui.Rect.zero);
    expect(caretOffset.dx, 42); // 👦
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 7), ui.Rect.zero);
    expect(caretOffset.dx, 42); // 👦
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 8), ui.Rect.zero);
    expect(caretOffset.dx, 42); // 👩‍
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 9), ui.Rect.zero);
    expect(caretOffset.dx, 98); // 👩‍
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 10), ui.Rect.zero);
    expect(caretOffset.dx, 98); // <zwj>
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 11), ui.Rect.zero);
    expect(caretOffset.dx, 98); // 👩‍
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 12), ui.Rect.zero);
    expect(caretOffset.dx, 98); // 👩‍
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 13), ui.Rect.zero);
    expect(caretOffset.dx, 98); // <zwj>
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 14), ui.Rect.zero);
    expect(caretOffset.dx, 98); // 👧‍
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 15), ui.Rect.zero);
    expect(caretOffset.dx, 98); // 👧‍
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 16), ui.Rect.zero);
    expect(caretOffset.dx, 98); // <zwj>
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 17), ui.Rect.zero);
    expect(caretOffset.dx, 98); // 👧
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 18), ui.Rect.zero);
    expect(caretOffset.dx, 98); // 👧
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 19), ui.Rect.zero);
116
    expect(caretOffset.dx, 98); // 👏
117
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 20), ui.Rect.zero);
118
    expect(caretOffset.dx, 98); // 👏
119
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 21), ui.Rect.zero);
120
    expect(caretOffset.dx, 98); // <medium skin tone modifier>
121
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 22), ui.Rect.zero);
122 123 124
    expect(caretOffset.dx, 98); // <medium skin tone modifier>
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 23), ui.Rect.zero);
    expect(caretOffset.dx, 126); // end of string
125
  }, skip: isBrowser); // https://github.com/flutter/flutter/issues/56308
126 127 128 129 130 131 132 133 134 135 136 137 138

  test('TextPainter caret center space test', () {
    final TextPainter painter = TextPainter()
      ..textDirection = TextDirection.ltr;

    const String text = 'test text with space at end   ';
    painter.text = const TextSpan(text: text);
    painter.textAlign = TextAlign.center;
    painter.layout();

    Offset caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 0), ui.Rect.zero);
    expect(caretOffset.dx, 21);
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: text.length), ui.Rect.zero);
139 140 141
    // The end of the line is 441, but the width is only 420, so the cursor is
    // stopped there without overflowing.
    expect(caretOffset.dx, painter.width);
142 143 144 145 146

    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 1), ui.Rect.zero);
    expect(caretOffset.dx, 35);
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 2), ui.Rect.zero);
    expect(caretOffset.dx, 49);
147
  }, skip: isBrowser); // https://github.com/flutter/flutter/issues/56308
148

149
  test('TextPainter error test', () {
150
    final TextPainter painter = TextPainter(textDirection: TextDirection.ltr);
151
    expect(() { painter.paint(MockCanvas(), Offset.zero); }, anyOf(throwsFlutterError, throwsAssertionError));
152
  });
153

Ian Hickson's avatar
Ian Hickson committed
154
  test('TextPainter requires textDirection', () {
155
    final TextPainter painter1 = TextPainter(text: const TextSpan(text: ''));
Ian Hickson's avatar
Ian Hickson committed
156
    expect(() { painter1.layout(); }, throwsAssertionError);
157
    final TextPainter painter2 = TextPainter(text: const TextSpan(text: ''), textDirection: TextDirection.rtl);
Ian Hickson's avatar
Ian Hickson committed
158 159 160
    expect(() { painter2.layout(); }, isNot(throwsException));
  });

161
  test('TextPainter size test', () {
162
    final TextPainter painter = TextPainter(
163
      text: const TextSpan(
164
        text: 'X',
165
        style: TextStyle(
166 167 168 169 170
          inherit: false,
          fontFamily: 'Ahem',
          fontSize: 123.0,
        ),
      ),
Ian Hickson's avatar
Ian Hickson committed
171
      textDirection: TextDirection.ltr,
172 173 174
    );
    painter.layout();
    expect(painter.size, const Size(123.0, 123.0));
175
  });
176

177
  test('TextPainter textScaleFactor test', () {
178
    final TextPainter painter = TextPainter(
179 180
      text: const TextSpan(
        text: 'X',
181
        style: TextStyle(
182 183 184 185 186 187 188 189 190 191
          inherit: false,
          fontFamily: 'Ahem',
          fontSize: 10.0,
        ),
      ),
      textDirection: TextDirection.ltr,
      textScaleFactor: 2.0,
    );
    painter.layout();
    expect(painter.size, const Size(20.0, 20.0));
192
  });
193

194 195 196 197 198 199 200 201 202 203 204 205
  test('TextPainter textScaleFactor null style test', () {
    final TextPainter painter = TextPainter(
      text: const TextSpan(
        text: 'X',
      ),
      textDirection: TextDirection.ltr,
      textScaleFactor: 2.0,
    );
    painter.layout();
    expect(painter.size, const Size(28.0, 28.0));
  });

206
  test('TextPainter default text height is 14 pixels', () {
207
    final TextPainter painter = TextPainter(
Ian Hickson's avatar
Ian Hickson committed
208 209 210
      text: const TextSpan(text: 'x'),
      textDirection: TextDirection.ltr,
    );
211 212 213
    painter.layout();
    expect(painter.preferredLineHeight, 14.0);
    expect(painter.size, const Size(14.0, 14.0));
214
  });
215 216

  test('TextPainter sets paragraph size from root', () {
217
    final TextPainter painter = TextPainter(
218
      text: const TextSpan(text: 'x', style: TextStyle(fontSize: 100.0)),
Ian Hickson's avatar
Ian Hickson committed
219 220
      textDirection: TextDirection.ltr,
    );
221 222 223
    painter.layout();
    expect(painter.preferredLineHeight, 100.0);
    expect(painter.size, const Size(100.0, 100.0));
224
  });
225 226

  test('TextPainter intrinsic dimensions', () {
227
    const TextStyle style = TextStyle(
228 229 230 231 232 233
      inherit: false,
      fontFamily: 'Ahem',
      fontSize: 10.0,
    );
    TextPainter painter;

234
    painter = TextPainter(
235 236 237 238 239 240 241 242 243 244 245
      text: const TextSpan(
        text: 'X X X',
        style: style,
      ),
      textDirection: TextDirection.ltr,
    );
    painter.layout();
    expect(painter.size, const Size(50.0, 10.0));
    expect(painter.minIntrinsicWidth, 10.0);
    expect(painter.maxIntrinsicWidth, 50.0);

246
    painter = TextPainter(
247 248 249 250 251 252 253 254 255 256 257 258
      text: const TextSpan(
        text: 'X X X',
        style: style,
      ),
      textDirection: TextDirection.ltr,
      ellipsis: 'e',
    );
    painter.layout();
    expect(painter.size, const Size(50.0, 10.0));
    expect(painter.minIntrinsicWidth, 50.0);
    expect(painter.maxIntrinsicWidth, 50.0);

259
    painter = TextPainter(
260 261 262 263 264 265 266 267 268 269 270 271
      text: const TextSpan(
        text: 'X X XXXX',
        style: style,
      ),
      textDirection: TextDirection.ltr,
      maxLines: 2,
    );
    painter.layout();
    expect(painter.size, const Size(80.0, 10.0));
    expect(painter.minIntrinsicWidth, 40.0);
    expect(painter.maxIntrinsicWidth, 80.0);

272
    painter = TextPainter(
273 274 275 276 277 278 279 280 281 282 283 284
      text: const TextSpan(
        text: 'X X XXXX XX',
        style: style,
      ),
      textDirection: TextDirection.ltr,
      maxLines: 2,
    );
    painter.layout();
    expect(painter.size, const Size(110.0, 10.0));
    expect(painter.minIntrinsicWidth, 70.0);
    expect(painter.maxIntrinsicWidth, 110.0);

285
    painter = TextPainter(
286 287 288 289 290 291 292 293 294 295 296 297
      text: const TextSpan(
        text: 'XXXXXXXX XXXX XX X',
        style: style,
      ),
      textDirection: TextDirection.ltr,
      maxLines: 2,
    );
    painter.layout();
    expect(painter.size, const Size(180.0, 10.0));
    expect(painter.minIntrinsicWidth, 90.0);
    expect(painter.maxIntrinsicWidth, 180.0);

298
    painter = TextPainter(
299 300 301 302 303 304 305 306 307 308 309 310
      text: const TextSpan(
        text: 'X XX XXXX XXXXXXXX',
        style: style,
      ),
      textDirection: TextDirection.ltr,
      maxLines: 2,
    );
    painter.layout();
    expect(painter.size, const Size(180.0, 10.0));
    expect(painter.minIntrinsicWidth, 90.0);
    expect(painter.maxIntrinsicWidth, 180.0);
  }, skip: true); // https://github.com/flutter/flutter/issues/13512
311 312

  test('TextPainter handles newlines properly', () {
313
    final TextPainter painter = TextPainter()
314 315
      ..textDirection = TextDirection.ltr;

316
    const double SIZE_OF_A = 14.0; // square size of "a" character
317
    String text = 'aaa';
318
    painter.text = TextSpan(text: text);
319 320
    painter.layout();

321 322 323 324 325 326 327 328
    // getOffsetForCaret in a plain one-line string is the same for either affinity.
    int offset = 0;
    painter.text = TextSpan(text: text);
    painter.layout();
    Offset caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset),
      ui.Rect.zero,
    );
329 330
    expect(caretOffset.dx, moreOrLessEquals(SIZE_OF_A * offset, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001));
331 332 333 334
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset, affinity: ui.TextAffinity.upstream),
      ui.Rect.zero,
    );
335 336
    expect(caretOffset.dx, moreOrLessEquals(SIZE_OF_A * offset, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001));
337 338 339 340 341
    offset = 1;
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset),
      ui.Rect.zero,
    );
342 343
    expect(caretOffset.dx, moreOrLessEquals(SIZE_OF_A * offset, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001));
344 345 346 347
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset, affinity: ui.TextAffinity.upstream),
      ui.Rect.zero,
    );
348 349
    expect(caretOffset.dx, moreOrLessEquals(SIZE_OF_A * offset, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001));
350 351 352 353 354
    offset = 2;
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset),
      ui.Rect.zero,
    );
355 356
    expect(caretOffset.dx, moreOrLessEquals(SIZE_OF_A * offset, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001));
357 358 359 360
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset, affinity: ui.TextAffinity.upstream),
      ui.Rect.zero,
    );
361 362
    expect(caretOffset.dx, moreOrLessEquals(SIZE_OF_A * offset, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001));
363 364 365 366 367
    offset = 3;
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset),
      ui.Rect.zero,
    );
368 369
    expect(caretOffset.dx, moreOrLessEquals(SIZE_OF_A * offset, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001));
370 371 372 373
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset, affinity: ui.TextAffinity.upstream),
      ui.Rect.zero,
    );
374 375
    expect(caretOffset.dx, moreOrLessEquals(SIZE_OF_A * offset, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001));
376

377 378 379
    // For explicit newlines, getOffsetForCaret places the caret at the location
    // indicated by offset regardless of affinity.
    text = '\n\n';
380
    painter.text = TextSpan(text: text);
381
    painter.layout();
382 383 384 385 386
    offset = 0;
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset),
      ui.Rect.zero,
    );
387 388
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001));
389 390 391 392
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset, affinity: ui.TextAffinity.upstream),
      ui.Rect.zero,
    );
393 394
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001));
395 396 397 398 399
    offset = 1;
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset),
      ui.Rect.zero,
    );
400 401
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A, epsilon: 0.0001));
402 403 404 405
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset, affinity: ui.TextAffinity.upstream),
      ui.Rect.zero,
    );
406 407
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A, epsilon: 0.0001));
408 409 410 411 412
    offset = 2;
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset),
      ui.Rect.zero,
    );
413 414
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A * 2, epsilon: 0.0001));
415 416 417 418
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset, affinity: ui.TextAffinity.upstream),
      ui.Rect.zero,
    );
419 420
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A * 2, epsilon: 0.0001));
421

422 423 424
    // getOffsetForCaret in an unwrapped string with explicit newlines is the
    // same for either affinity.
    text = '\naaa';
425
    painter.text = TextSpan(text: text);
426
    painter.layout();
427 428 429 430 431
    offset = 0;
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset),
      ui.Rect.zero,
    );
432 433
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001));
434 435 436 437
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset, affinity: ui.TextAffinity.upstream),
      ui.Rect.zero,
    );
438 439
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001));
440 441 442 443 444
    offset = 1;
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset),
      ui.Rect.zero,
    );
445 446
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A, epsilon: 0.0001));
447 448 449 450
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset, affinity: ui.TextAffinity.upstream),
      ui.Rect.zero,
    );
451 452
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A, epsilon: 0.0001));
453

454 455 456
    // When text wraps on its own, getOffsetForCaret disambiguates between the
    // end of one line and start of next using affinity.
    text = 'aaaaaaaa'; // Just enough to wrap one character down to second line
457
    painter.text = TextSpan(text: text);
458 459 460 461 462 463
    painter.layout(maxWidth: 100); // SIZE_OF_A * text.length > 100, so it wraps
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: text.length - 1),
      ui.Rect.zero,
    );
    // When affinity is downstream, cursor is at beginning of second line
464 465
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A, epsilon: 0.0001));
466 467 468 469 470
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: text.length - 1, affinity: ui.TextAffinity.upstream),
      ui.Rect.zero,
    );
    // When affinity is upstream, cursor is at end of first line
471 472
    expect(caretOffset.dx, moreOrLessEquals(98.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001));
473

474 475 476
    // When given a string with a newline at the end, getOffsetForCaret puts
    // the cursor at the start of the next line regardless of affinity
    text = 'aaa\n';
477
    painter.text = TextSpan(text: text);
478
    painter.layout();
479 480 481 482
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: text.length),
      ui.Rect.zero,
    );
483 484
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A, epsilon: 0.0001));
485 486 487 488 489
    offset = text.length;
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset, affinity: TextAffinity.upstream),
      ui.Rect.zero,
    );
490 491
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A, epsilon: 0.0001));
492

493 494 495 496 497 498 499 500 501 502 503 504
    // Given a one-line right aligned string, positioning the cursor at offset 0
    // means that it appears at the "end" of the string, after the character
    // that was typed first, at x=0.
    painter.textAlign = TextAlign.right;
    text = 'aaa';
    painter.text = TextSpan(text: text);
    painter.layout();
    offset = 0;
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset),
      ui.Rect.zero,
    );
505 506
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001));
507
    painter.textAlign = TextAlign.left;
508

509 510 511 512 513 514 515 516 517 518 519
    // When given an offset after a newline in the middle of a string,
    // getOffsetForCaret returns the start of the next line regardless of
    // affinity.
    text = 'aaa\naaa';
    painter.text = TextSpan(text: text);
    painter.layout();
    offset = 4;
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset),
      ui.Rect.zero,
    );
520 521
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A, epsilon: 0.0001));
522 523 524 525
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset, affinity: TextAffinity.upstream),
      ui.Rect.zero,
    );
526 527
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A, epsilon: 0.0001));
528

529 530 531 532 533 534 535 536
    // When given a string with multiple trailing newlines, places the caret
    // in the position given by offset regardless of affinity.
    text = 'aaa\n\n\n';
    offset = 3;
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset),
      ui.Rect.zero,
    );
537 538
    expect(caretOffset.dx, moreOrLessEquals(SIZE_OF_A * 3, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001));
539 540 541 542
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset, affinity: TextAffinity.upstream),
      ui.Rect.zero,
    );
543 544
    expect(caretOffset.dx, moreOrLessEquals(SIZE_OF_A * 3, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001));
545

546
    offset = 4;
547
    painter.text = TextSpan(text: text);
548
    painter.layout();
549 550 551 552
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset),
      ui.Rect.zero,
    );
553 554
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A, epsilon: 0.001));
555 556 557 558
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset, affinity: TextAffinity.upstream),
      ui.Rect.zero,
    );
559 560
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A, epsilon: 0.0001));
561

562 563 564 565 566
    offset = 5;
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset),
      ui.Rect.zero,
    );
567 568
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A * 2, epsilon: 0.001));
569 570 571 572
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset, affinity: TextAffinity.upstream),
      ui.Rect.zero,
    );
573 574
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A * 2, epsilon: 0.0001));
575

576 577 578 579 580
    offset = 6;
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset),
      ui.Rect.zero,
    );
581 582
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A * 3, epsilon: 0.0001));
583

584 585 586 587
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset, affinity: TextAffinity.upstream),
      ui.Rect.zero,
    );
588 589
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A * 3, epsilon: 0.0001));
590

591 592 593 594 595 596 597 598 599 600
    // When given a string with multiple leading newlines, places the caret in
    // the position given by offset regardless of affinity.
    text = '\n\n\naaa';
    offset = 3;
    painter.text = TextSpan(text: text);
    painter.layout();
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset),
      ui.Rect.zero,
    );
601 602
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A * 3, epsilon: 0.0001));
603 604 605 606
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset, affinity: TextAffinity.upstream),
      ui.Rect.zero,
    );
607 608
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A * 3, epsilon: 0.0001));
609

610 611 612 613 614
    offset = 2;
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset),
      ui.Rect.zero,
    );
615 616
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A * 2, epsilon: 0.0001));
617 618 619 620
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset, affinity: TextAffinity.upstream),
      ui.Rect.zero,
    );
621 622
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A * 2, epsilon: 0.0001));
623

624 625 626 627 628
    offset = 1;
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset),
      ui.Rect.zero,
    );
629 630
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy,moreOrLessEquals(SIZE_OF_A, epsilon: 0.0001));
631 632 633 634
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset, affinity: TextAffinity.upstream),
      ui.Rect.zero,
    );
635 636
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(SIZE_OF_A, epsilon: 0.0001));
637

638 639 640 641 642
    offset = 0;
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset),
      ui.Rect.zero,
    );
643 644
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001));
645 646 647 648
    caretOffset = painter.getOffsetForCaret(
      ui.TextPosition(offset: offset, affinity: TextAffinity.upstream),
      ui.Rect.zero,
    );
649 650
    expect(caretOffset.dx, moreOrLessEquals(0.0, epsilon: 0.0001));
    expect(caretOffset.dy, moreOrLessEquals(0.0, epsilon: 0.0001));
651
  });
652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676

  test('TextPainter widget span', () {
    final TextPainter painter = TextPainter()
      ..textDirection = TextDirection.ltr;

    const String text = 'test';
    painter.text = const TextSpan(
      text: text,
      children: <InlineSpan>[
        WidgetSpan(child: SizedBox(width: 50, height: 30)),
        TextSpan(text: text),
        WidgetSpan(child: SizedBox(width: 50, height: 30)),
        WidgetSpan(child: SizedBox(width: 50, height: 30)),
        TextSpan(text: text),
        WidgetSpan(child: SizedBox(width: 50, height: 30)),
        WidgetSpan(child: SizedBox(width: 50, height: 30)),
        WidgetSpan(child: SizedBox(width: 50, height: 30)),
        WidgetSpan(child: SizedBox(width: 50, height: 30)),
        WidgetSpan(child: SizedBox(width: 50, height: 30)),
        WidgetSpan(child: SizedBox(width: 50, height: 30)),
        WidgetSpan(child: SizedBox(width: 50, height: 30)),
        WidgetSpan(child: SizedBox(width: 50, height: 30)),
        WidgetSpan(child: SizedBox(width: 50, height: 30)),
        WidgetSpan(child: SizedBox(width: 50, height: 30)),
        WidgetSpan(child: SizedBox(width: 50, height: 30)),
677
      ],
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731
    );

    // We provide dimensions for the widgets
    painter.setPlaceholderDimensions(const <PlaceholderDimensions>[
      PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
      PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
      PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
      PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
      PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
      PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
      PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
      PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
      PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
      PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
      PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
      PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
      PlaceholderDimensions(size: Size(51, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
      PlaceholderDimensions(size: Size(50, 30), baselineOffset: 25, alignment: ui.PlaceholderAlignment.bottom),
    ]);

    painter.layout(maxWidth: 500);

    // Now, each of the WidgetSpans will have their own placeholder 'hole'.
    Offset caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 1), ui.Rect.zero);
    expect(caretOffset.dx, 14);
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 4), ui.Rect.zero);
    expect(caretOffset.dx, 56);
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 5), ui.Rect.zero);
    expect(caretOffset.dx, 106);
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 6), ui.Rect.zero);
    expect(caretOffset.dx, 120);
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 10), ui.Rect.zero);
    expect(caretOffset.dx, 212);
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 11), ui.Rect.zero);
    expect(caretOffset.dx, 262);
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 12), ui.Rect.zero);
    expect(caretOffset.dx, 276);
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 13), ui.Rect.zero);
    expect(caretOffset.dx, 290);
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 14), ui.Rect.zero);
    expect(caretOffset.dx, 304);
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 15), ui.Rect.zero);
    expect(caretOffset.dx, 318);
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 16), ui.Rect.zero);
    expect(caretOffset.dx, 368);
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 17), ui.Rect.zero);
    expect(caretOffset.dx, 418);
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 18), ui.Rect.zero);
    expect(caretOffset.dx, 0);
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 19), ui.Rect.zero);
    expect(caretOffset.dx, 50);
    caretOffset = painter.getOffsetForCaret(const ui.TextPosition(offset: 23), ui.Rect.zero);
    expect(caretOffset.dx, 250);

732 733 734 735 736 737
    expect(painter.inlinePlaceholderBoxes!.length, 14);
    expect(painter.inlinePlaceholderBoxes![0], const TextBox.fromLTRBD(56, 0, 106, 30, TextDirection.ltr));
    expect(painter.inlinePlaceholderBoxes![2], const TextBox.fromLTRBD(212, 0, 262, 30, TextDirection.ltr));
    expect(painter.inlinePlaceholderBoxes![3], const TextBox.fromLTRBD(318, 0, 368, 30, TextDirection.ltr));
    expect(painter.inlinePlaceholderBoxes![4], const TextBox.fromLTRBD(368, 0, 418, 30, TextDirection.ltr));
    expect(painter.inlinePlaceholderBoxes![5], const TextBox.fromLTRBD(418, 0, 468, 30, TextDirection.ltr));
738
    // line should break here
739 740 741 742 743 744
    expect(painter.inlinePlaceholderBoxes![6], const TextBox.fromLTRBD(0, 30, 50, 60, TextDirection.ltr));
    expect(painter.inlinePlaceholderBoxes![7], const TextBox.fromLTRBD(50, 30, 100, 60, TextDirection.ltr));
    expect(painter.inlinePlaceholderBoxes![10], const TextBox.fromLTRBD(200, 30, 250, 60, TextDirection.ltr));
    expect(painter.inlinePlaceholderBoxes![11], const TextBox.fromLTRBD(250, 30, 300, 60, TextDirection.ltr));
    expect(painter.inlinePlaceholderBoxes![12], const TextBox.fromLTRBD(300, 30, 351, 60, TextDirection.ltr));
    expect(painter.inlinePlaceholderBoxes![13], const TextBox.fromLTRBD(351, 30, 401, 60, TextDirection.ltr));
745
  }, skip: isBrowser); // https://github.com/flutter/flutter/issues/42086
746

747 748 749 750 751 752 753 754 755
  // Null values are valid. See https://github.com/flutter/flutter/pull/48346#issuecomment-584839221
  test('TextPainter set TextHeightBehavior null test', () {
    final TextPainter painter = TextPainter(textHeightBehavior: null)
      ..textDirection = TextDirection.ltr;

    painter.textHeightBehavior = const TextHeightBehavior();
    painter.textHeightBehavior = null;
  });

756 757 758 759 760 761 762 763 764 765 766
  test('TextPainter line metrics', () {
    final TextPainter painter = TextPainter()
      ..textDirection = TextDirection.ltr;

    const String text = 'test1\nhello line two really long for soft break\nfinal line 4';
    painter.text = const TextSpan(
      text: text,
    );

    painter.layout(maxWidth: 300);

767 768 769
    expect(painter.text, const TextSpan(text: text));
    expect(painter.preferredLineHeight, 14);

770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817
    final List<ui.LineMetrics> lines = painter.computeLineMetrics();

    expect(lines.length, 4);

    expect(lines[0].hardBreak, true);
    expect(lines[1].hardBreak, false);
    expect(lines[2].hardBreak, true);
    expect(lines[3].hardBreak, true);

    expect(lines[0].ascent, 11.199999809265137);
    expect(lines[1].ascent, 11.199999809265137);
    expect(lines[2].ascent, 11.199999809265137);
    expect(lines[3].ascent, 11.199999809265137);

    expect(lines[0].descent, 2.799999952316284);
    expect(lines[1].descent, 2.799999952316284);
    expect(lines[2].descent, 2.799999952316284);
    expect(lines[3].descent, 2.799999952316284);

    expect(lines[0].unscaledAscent, 11.199999809265137);
    expect(lines[1].unscaledAscent, 11.199999809265137);
    expect(lines[2].unscaledAscent, 11.199999809265137);
    expect(lines[3].unscaledAscent, 11.199999809265137);

    expect(lines[0].baseline, 11.200000047683716);
    expect(lines[1].baseline, 25.200000047683716);
    expect(lines[2].baseline, 39.200000047683716);
    expect(lines[3].baseline, 53.200000047683716);

    expect(lines[0].height, 14);
    expect(lines[1].height, 14);
    expect(lines[2].height, 14);
    expect(lines[3].height, 14);

    expect(lines[0].width, 70);
    expect(lines[1].width, 294);
    expect(lines[2].width, 266);
    expect(lines[3].width, 168);

    expect(lines[0].left, 0);
    expect(lines[1].left, 0);
    expect(lines[2].left, 0);
    expect(lines[3].left, 0);

    expect(lines[0].lineNumber, 0);
    expect(lines[1].lineNumber, 1);
    expect(lines[2].lineNumber, 2);
    expect(lines[3].lineNumber, 3);
818
  }, skip: true); // https://github.com/flutter/flutter/issues/62819
819 820 821 822 823 824 825 826 827 828 829 830 831

  test('TextPainter caret height and line height', () {
    final TextPainter painter = TextPainter()
      ..textDirection = TextDirection.ltr
      ..strutStyle = const StrutStyle(fontSize: 50.0);

    const String text = 'A';
    painter.text = const TextSpan(text: text, style: TextStyle(height: 1.0));
    painter.layout();

    final double caretHeight = painter.getFullHeightForCaret(
      const ui.TextPosition(offset: 0),
      ui.Rect.zero,
832
    )!;
833
    expect(caretHeight, 50.0);
834
  }, skip: isBrowser); // https://github.com/flutter/flutter/issues/56308
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954

  group('TextPainter line-height', () {
    test('half-leading', (){
      const TextStyle style = TextStyle(
        height: 20,
        fontSize: 1,
        leadingDistribution: TextLeadingDistribution.even,
      );

      final TextPainter painter = TextPainter()
        ..textDirection = TextDirection.ltr
        ..text = const TextSpan(text: 'A', style: style)
        ..layout();

      final Rect glyphBox = painter.getBoxesForSelection(
        const TextSelection(baseOffset: 0, extentOffset: 1),
      ).first.toRect();

      final RelativeRect insets = RelativeRect.fromSize(glyphBox, painter.size);
      // The glyph box is centered.
      expect(insets.top, insets.bottom);
      // The glyph box is exactly 1 logical pixel high.
      expect(insets.top, (20 - 1) / 2);
    });

    test('half-leading with small height', (){
      const TextStyle style = TextStyle(
        height: 0.1,
        fontSize: 10,
        leadingDistribution: TextLeadingDistribution.even,
      );

      final TextPainter painter = TextPainter()
        ..textDirection = TextDirection.ltr
        ..text = const TextSpan(text: 'A', style: style)
        ..layout();

      final Rect glyphBox = painter.getBoxesForSelection(
        const TextSelection(baseOffset: 0, extentOffset: 1),
      ).first.toRect();

      final RelativeRect insets = RelativeRect.fromSize(glyphBox, painter.size);
      // The glyph box is still centered.
      expect(insets.top, insets.bottom);
      // The glyph box is exactly 10 logical pixel high (the height multiplier
      // does not scale the glyph). Negative leading.
      expect(insets.top, (1 - 10) / 2);
    });

    test('half-leading with leading trim', (){
      const TextStyle style = TextStyle(
        height: 0.1,
        fontSize: 10,
        leadingDistribution: TextLeadingDistribution.even,
      );

      final TextPainter painter = TextPainter()
        ..textDirection = TextDirection.ltr
        ..text = const TextSpan(text: 'A', style: style)
        ..textHeightBehavior = const TextHeightBehavior(
            applyHeightToFirstAscent: false,
            applyHeightToLastDescent: false,
            leadingDistribution: TextLeadingDistribution.proportional,
          )
        ..layout();

      final Rect glyphBox = painter.getBoxesForSelection(
        const TextSelection(baseOffset: 0, extentOffset: 1),
      ).first.toRect();

      expect(painter.size, glyphBox.size);
      // The glyph box is still centered.
      expect(glyphBox.topLeft, Offset.zero);
    });

    test('TextLeadingDistribution falls back to paragraph style', (){
      const TextStyle style = TextStyle(height: 20, fontSize: 1);
      final TextPainter painter = TextPainter()
        ..textDirection = TextDirection.ltr
        ..text = const TextSpan(text: 'A', style: style)
        ..textHeightBehavior = const TextHeightBehavior(
            leadingDistribution: TextLeadingDistribution.even,
          )
        ..layout();

      final Rect glyphBox = painter.getBoxesForSelection(
        const TextSelection(baseOffset: 0, extentOffset: 1),
      ).first.toRect();

      // Still uses half-leading.
      final RelativeRect insets = RelativeRect.fromSize(glyphBox, painter.size);
      expect(insets.top, insets.bottom);
      expect(insets.top, (20 - 1) / 2);
    });

    test('TextLeadingDistribution does nothing if height multiplier is null', (){
      const TextStyle style = TextStyle(fontSize: 1);
      final TextPainter painter = TextPainter()
        ..textDirection = TextDirection.ltr
        ..text = const TextSpan(text: 'A', style: style)
        ..textHeightBehavior = const TextHeightBehavior(
            leadingDistribution: TextLeadingDistribution.even,
          )
        ..layout();

      final Rect glyphBox = painter.getBoxesForSelection(
        const TextSelection(baseOffset: 0, extentOffset: 1),
      ).first.toRect();

      painter.textHeightBehavior = const TextHeightBehavior(
        leadingDistribution: TextLeadingDistribution.proportional,
      );
      painter.layout();

      final Rect newGlyphBox = painter.getBoxesForSelection(
        const TextSelection(baseOffset: 0, extentOffset: 1),
      ).first.toRect();
      expect(glyphBox, newGlyphBox);
    });
  }, skip: isBrowser);
955
}
956 957 958 959

class MockCanvas extends Fake implements Canvas {

}