[web] Calculate align offset for each paragraph line (LineMetrics.left) (#14537)

This commit is contained in:
Mouad Debbar 2019-12-18 14:29:51 -08:00 committed by GitHub
parent 105eb66b91
commit 1ecfdcb94c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 291 additions and 107 deletions

View File

@ -666,6 +666,7 @@ class BitmapCanvas extends EngineCanvas with SaveStackTracking {
double x,
double y,
) {
x += line.left;
final double letterSpacing = style.letterSpacing;
if (letterSpacing == null || letterSpacing == 0.0) {
ctx.fillText(line.text, x, y);
@ -712,11 +713,10 @@ class BitmapCanvas extends EngineCanvas with SaveStackTracking {
}
_applyPaint(paragraph._paint.paintData);
final double x = offset.dx + paragraph._alignOffset;
double y = offset.dy + paragraph.alphabeticBaseline;
final int len = lines.length;
for (int i = 0; i < len; i++) {
_drawTextLine(style, lines[i], x, y);
_drawTextLine(style, lines[i], offset.dx, y);
y += paragraph._lineHeight;
}
_resetPaint();

View File

@ -416,6 +416,11 @@ class DomTextMeasurementService extends TextMeasurementService {
List<EngineLineMetrics> lines;
if (text != null) {
final double lineWidth = maxIntrinsicWidth;
final double alignOffset = _calculateAlignOffsetForLine(
paragraph: paragraph,
lineWidth: lineWidth,
maxWidth: width,
);
lines = <EngineLineMetrics>[
EngineLineMetrics.withText(
text,
@ -423,6 +428,7 @@ class DomTextMeasurementService extends TextMeasurementService {
endIndex: text.length,
hardBreak: true,
width: lineWidth,
left: alignOffset,
lineNumber: 0,
),
];
@ -440,6 +446,8 @@ class DomTextMeasurementService extends TextMeasurementService {
alphabeticBaseline: alphabeticBaseline,
ideographicBaseline: ideographicBaseline,
lines: lines,
textAlign: paragraph._textAlign,
textDirection: paragraph._textDirection,
);
}
@ -488,6 +496,8 @@ class DomTextMeasurementService extends TextMeasurementService {
alphabeticBaseline: alphabeticBaseline,
ideographicBaseline: ideographicBaseline,
lines: null,
textAlign: paragraph._textAlign,
textDirection: paragraph._textDirection,
);
}
@ -546,7 +556,7 @@ class CanvasTextMeasurementService extends TextMeasurementService {
// TODO(mdebbar): Check if the whole text can fit in a single-line. Then avoid all this ceremony.
_canvasContext.font = style.cssFontString;
final LinesCalculator linesCalculator =
LinesCalculator(_canvasContext, text, style, constraints.width);
LinesCalculator(_canvasContext, paragraph, constraints.width);
final MinIntrinsicCalculator minIntrinsicCalculator =
MinIntrinsicCalculator(_canvasContext, text, style);
final MaxIntrinsicCalculator maxIntrinsicCalculator =
@ -597,6 +607,8 @@ class CanvasTextMeasurementService extends TextMeasurementService {
maxIntrinsicWidth: maxIntrinsicCalculator.value,
width: constraints.width,
lines: linesCalculator.lines,
textAlign: paragraph._textAlign,
textDirection: paragraph._textDirection,
);
return result;
}
@ -702,16 +714,18 @@ int _excludeTrailing(String text, int start, int end, CharPredicate predicate) {
/// During the text layout phase, this class splits the lines of text so that it
/// ends up fitting into the given width constraint.
///
/// It mimicks the Flutter engine's behavior when it comes to handling ellipsis
/// and max lines.
/// It implements the Flutter engine's behavior when it comes to handling
/// ellipsis and max lines.
class LinesCalculator {
LinesCalculator(this._canvasContext, this._text, this._style, this._maxWidth);
LinesCalculator(this._canvasContext, this._paragraph, this._maxWidth);
final html.CanvasRenderingContext2D _canvasContext;
final String _text;
final ParagraphGeometricStyle _style;
final EngineParagraph _paragraph;
final double _maxWidth;
String get _text => _paragraph._plainText;
ParagraphGeometricStyle get _style => _paragraph._geometricStyle;
/// The lines that have been consumed so far.
List<EngineLineMetrics> lines = <EngineLineMetrics>[];
@ -768,12 +782,20 @@ class LinesCalculator {
start: _lineStart,
end: chunkEndWithoutSpace,
);
final double widthOfResultingLine =
measureSubstring(_lineStart, breakingPoint) + _ellipsisWidth;
final double alignOffset = _calculateAlignOffsetForLine(
paragraph: _paragraph,
lineWidth: widthOfResultingLine,
maxWidth: _maxWidth,
);
lines.add(EngineLineMetrics.withText(
_text.substring(_lineStart, breakingPoint) + _style.ellipsis,
startIndex: _lineStart,
endIndex: chunkEnd,
hardBreak: false,
width: measureSubstring(_lineStart, breakingPoint) + _ellipsisWidth,
width: widthOfResultingLine,
left: alignOffset,
lineNumber: lines.length,
));
} else if (isChunkTooLong) {
@ -826,12 +848,19 @@ class LinesCalculator {
_whitespacePredicate,
);
final int lineNumber = lines.length;
final double lineWidth = measureSubstring(_lineStart, endWithoutSpace);
final double alignOffset = _calculateAlignOffsetForLine(
paragraph: _paragraph,
lineWidth: lineWidth,
maxWidth: _maxWidth,
);
final EngineLineMetrics metrics = EngineLineMetrics.withText(
_text.substring(_lineStart, endWithoutNewlines),
startIndex: _lineStart,
endIndex: lineEnd,
hardBreak: isHardBreak,
width: measureSubstring(_lineStart, endWithoutSpace),
width: lineWidth,
left: alignOffset,
lineNumber: lineNumber,
);
lines.add(metrics);
@ -958,3 +987,30 @@ class MaxIntrinsicCalculator {
_lastHardLineEnd = hardLineEnd;
}
}
/// Calculates the offset necessary for the given line to be correctly aligned.
double _calculateAlignOffsetForLine({
@required EngineParagraph paragraph,
@required double lineWidth,
@required double maxWidth,
}) {
final double emptySpace = maxWidth - lineWidth;
// WARNING: the [paragraph] may not be laid out yet at this point. This
// function must not use layout metrics, such as [paragraph.height].
switch (paragraph._textAlign) {
case ui.TextAlign.center:
return emptySpace / 2.0;
case ui.TextAlign.right:
return emptySpace;
case ui.TextAlign.start:
return paragraph._textDirection == ui.TextDirection.rtl
? emptySpace
: 0.0;
case ui.TextAlign.end:
return paragraph._textDirection == ui.TextDirection.rtl
? 0.0
: emptySpace;
default:
return 0.0;
}
}

View File

@ -29,12 +29,13 @@ class EngineLineMetrics implements ui.LineMetrics {
this.unscaledAscent,
this.height,
@required this.width,
this.left,
@required this.left,
this.baseline,
@required this.lineNumber,
}) : assert(text != null),
assert(hardBreak != null),
assert(width != null),
assert(left != null),
assert(lineNumber != null && lineNumber >= 0);
/// The textual content representing this line.

View File

@ -800,7 +800,9 @@ class ParagraphRuler {
final int len = constraintCache.length;
for (int i = 0; i < len; i++) {
final MeasurementResult item = constraintCache[i];
if (item.constraintWidth == constraints.width) {
if (item.constraintWidth == constraints.width &&
item.textAlign == paragraph._textAlign &&
item.textDirection == paragraph._textDirection) {
return item;
}
}
@ -852,7 +854,13 @@ class MeasurementResult {
/// of each laid out line.
final List<EngineLineMetrics> lines;
const MeasurementResult(
/// The text align value of the paragraph.
final ui.TextAlign textAlign;
/// The text direction of the paragraph.
final ui.TextDirection textDirection;
MeasurementResult(
this.constraintWidth, {
@required this.isSingleLine,
@required this.width,
@ -864,6 +872,8 @@ class MeasurementResult {
@required this.alphabeticBaseline,
@required this.ideographicBaseline,
@required this.lines,
@required this.textAlign,
@required this.textDirection,
}) : assert(constraintWidth != null),
assert(isSingleLine != null),
assert(width != null),
@ -872,5 +882,7 @@ class MeasurementResult {
assert(minIntrinsicWidth != null),
assert(maxIntrinsicWidth != null),
assert(alphabeticBaseline != null),
assert(ideographicBaseline != null);
assert(ideographicBaseline != null),
assert(textAlign != null),
assert(textDirection != null);
}

View File

@ -156,7 +156,7 @@ void main() async {
expect(result.minIntrinsicWidth, 30);
expect(result.height, 10);
expect(result.lines, <EngineLineMetrics>[
line(' abc', 0, 6, hardBreak: true, width: 60.0, lineNumber: 0),
line(' abc', 0, 6, hardBreak: true, width: 60.0, lineNumber: 0, left: 0.0),
]);
// trailing whitespaces
@ -167,13 +167,13 @@ void main() async {
expect(result.height, 10);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('abc ', 0, 6, hardBreak: true, width: 30.0, lineNumber: 0),
line('abc ', 0, 6, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0),
]);
} else {
// DOM-based measurement always includes trailing whitespace in the
// width, while Flutter and Canvas-based measurement don't.
expect(result.lines, <EngineLineMetrics>[
line('abc ', 0, 6, hardBreak: true, width: 60.0, lineNumber: 0),
line('abc ', 0, 6, hardBreak: true, width: 60.0, lineNumber: 0, left: 0.0),
]);
}
@ -185,13 +185,13 @@ void main() async {
expect(result.height, 10);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line(' ab c ', 0, 10, hardBreak: true, width: 80.0, lineNumber: 0),
line(' ab c ', 0, 10, hardBreak: true, width: 80.0, lineNumber: 0, left: 0.0),
]);
} else {
// DOM-based measurement always includes trailing whitespace in the
// width, while Flutter and Canvas-based measurement don't.
expect(result.lines, <EngineLineMetrics>[
line(' ab c ', 0, 10, hardBreak: true, width: 100.0, lineNumber: 0),
line(' ab c ', 0, 10, hardBreak: true, width: 100.0, lineNumber: 0, left: 0.0),
]);
}
@ -203,13 +203,13 @@ void main() async {
expect(result.height, 10);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line(' ', 0, 1, hardBreak: true, width: 0.0, lineNumber: 0),
line(' ', 0, 1, hardBreak: true, width: 0.0, lineNumber: 0, left: 0.0),
]);
} else {
// DOM-based measurement always includes trailing whitespace in the
// width, while Flutter and Canvas-based measurement don't.
expect(result.lines, <EngineLineMetrics>[
line(' ', 0, 1, hardBreak: true, width: 10.0, lineNumber: 0),
line(' ', 0, 1, hardBreak: true, width: 10.0, lineNumber: 0, left: 0.0),
]);
}
@ -221,13 +221,13 @@ void main() async {
expect(result.height, 10);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line(' ', 0, 5, hardBreak: true, width: 0.0, lineNumber: 0),
line(' ', 0, 5, hardBreak: true, width: 0.0, lineNumber: 0, left: 0.0),
]);
} else {
// DOM-based measurement always includes trailing whitespace in the
// width, while Flutter and Canvas-based measurement don't.
expect(result.lines, <EngineLineMetrics>[
line(' ', 0, 5, hardBreak: true, width: 50.0, lineNumber: 0),
line(' ', 0, 5, hardBreak: true, width: 50.0, lineNumber: 0, left: 0.0),
]);
}
},
@ -246,7 +246,7 @@ void main() async {
expect(result.width, 50);
expect(result.height, 10);
expect(result.lines, <EngineLineMetrics>[
line('12345', 0, 5, hardBreak: true, width: 50.0, lineNumber: 0),
line('12345', 0, 5, hardBreak: true, width: 50.0, lineNumber: 0, left: 0.0),
]);
},
);
@ -267,8 +267,8 @@ void main() async {
expect(result.height, 20);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('foo bar ', 0, 8, hardBreak: false, width: 70.0, lineNumber: 0),
line('baz', 8, 11, hardBreak: true, width: 30.0, lineNumber: 1),
line('foo bar ', 0, 8, hardBreak: false, width: 70.0, lineNumber: 0, left: 0.0),
line('baz', 8, 11, hardBreak: true, width: 30.0, lineNumber: 1, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -292,8 +292,8 @@ void main() async {
expect(result.height, 20);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('12345', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0),
line('67890', 5, 10, hardBreak: true, width: 50.0, lineNumber: 1),
line('12345', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0),
line('67890', 5, 10, hardBreak: true, width: 50.0, lineNumber: 1, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -311,9 +311,9 @@ void main() async {
expect(result.height, 30);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('abcde', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0),
line('fghij', 5, 10, hardBreak: false, width: 50.0, lineNumber: 1),
line('k lm', 10, 14, hardBreak: true, width: 40.0, lineNumber: 2),
line('abcde', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0),
line('fghij', 5, 10, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0),
line('k lm', 10, 14, hardBreak: true, width: 40.0, lineNumber: 2, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -333,8 +333,8 @@ void main() async {
expect(result.height, 20);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('A', 0, 1, hardBreak: false, width: 10.0, lineNumber: 0),
line('A', 1, 2, hardBreak: true, width: 10.0, lineNumber: 1),
line('A', 0, 1, hardBreak: false, width: 10.0, lineNumber: 0, left: 0.0),
line('A', 1, 2, hardBreak: true, width: 10.0, lineNumber: 1, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -351,9 +351,9 @@ void main() async {
expect(result.height, 30);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('A', 0, 1, hardBreak: false, width: 10.0, lineNumber: 0),
line('A', 1, 3, hardBreak: true, width: 10.0, lineNumber: 1),
line('A', 3, 4, hardBreak: true, width: 10.0, lineNumber: 2),
line('A', 0, 1, hardBreak: false, width: 10.0, lineNumber: 0, left: 0.0),
line('A', 1, 3, hardBreak: true, width: 10.0, lineNumber: 1, left: 0.0),
line('A', 3, 4, hardBreak: true, width: 10.0, lineNumber: 2, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -370,10 +370,10 @@ void main() async {
expect(result.height, 40);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('A', 0, 1, hardBreak: false, width: 10.0, lineNumber: 0),
line('A', 1, 2, hardBreak: false, width: 10.0, lineNumber: 1),
line('A', 2, 4, hardBreak: true, width: 10.0, lineNumber: 2),
line('', 4, 4, hardBreak: true, width: 0.0, lineNumber: 3),
line('A', 0, 1, hardBreak: false, width: 10.0, lineNumber: 0, left: 0.0),
line('A', 1, 2, hardBreak: false, width: 10.0, lineNumber: 1, left: 0.0),
line('A', 2, 4, hardBreak: true, width: 10.0, lineNumber: 2, left: 0.0),
line('', 4, 4, hardBreak: true, width: 0.0, lineNumber: 3, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -397,8 +397,8 @@ void main() async {
expect(result.height, 20);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('12', 0, 3, hardBreak: true, width: 20.0, lineNumber: 0),
line('34', 3, 5, hardBreak: true, width: 20.0, lineNumber: 1),
line('12', 0, 3, hardBreak: true, width: 20.0, lineNumber: 0, left: 0.0),
line('34', 3, 5, hardBreak: true, width: 20.0, lineNumber: 1, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -418,9 +418,9 @@ void main() async {
expect(result.height, 30);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('', 0, 1, hardBreak: true, width: 0.0, lineNumber: 0),
line('', 1, 2, hardBreak: true, width: 0.0, lineNumber: 1),
line('1234', 2, 6, hardBreak: true, width: 40.0, lineNumber: 2),
line('', 0, 1, hardBreak: true, width: 0.0, lineNumber: 0, left: 0.0),
line('', 1, 2, hardBreak: true, width: 0.0, lineNumber: 1, left: 0.0),
line('1234', 2, 6, hardBreak: true, width: 40.0, lineNumber: 2, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -435,9 +435,9 @@ void main() async {
expect(result.height, 30);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('12', 0, 3, hardBreak: true, width: 20.0, lineNumber: 0),
line('', 3, 4, hardBreak: true, width: 0.0, lineNumber: 1),
line('345', 4, 7, hardBreak: true, width: 30.0, lineNumber: 2),
line('12', 0, 3, hardBreak: true, width: 20.0, lineNumber: 0, left: 0.0),
line('', 3, 4, hardBreak: true, width: 0.0, lineNumber: 1, left: 0.0),
line('345', 4, 7, hardBreak: true, width: 30.0, lineNumber: 2, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -453,9 +453,9 @@ void main() async {
// This can only be done correctly in the canvas-based implementation.
expect(result.height, 30);
expect(result.lines, <EngineLineMetrics>[
line('1234', 0, 5, hardBreak: true, width: 40.0, lineNumber: 0),
line('', 5, 6, hardBreak: true, width: 0.0, lineNumber: 1),
line('', 6, 6, hardBreak: true, width: 0.0, lineNumber: 2),
line('1234', 0, 5, hardBreak: true, width: 40.0, lineNumber: 0, left: 0.0),
line('', 5, 6, hardBreak: true, width: 0.0, lineNumber: 1, left: 0.0),
line('', 6, 6, hardBreak: true, width: 0.0, lineNumber: 2, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -479,8 +479,8 @@ void main() async {
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('123', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0),
line('456 789', 4, 11, hardBreak: true, width: 70.0, lineNumber: 1),
line('123', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0),
line('456 789', 4, 11, hardBreak: true, width: 70.0, lineNumber: 1, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -544,9 +544,9 @@ void main() async {
expect(result.minIntrinsicWidth, 40);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('abc ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0),
line('de ', 4, 7, hardBreak: false, width: 20.0, lineNumber: 1),
line('fghi', 7, 11, hardBreak: true, width: 40.0, lineNumber: 2),
line('abc ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0, left: 0.0),
line('de ', 4, 7, hardBreak: false, width: 20.0, lineNumber: 1, left: 0.0),
line('fghi', 7, 11, hardBreak: true, width: 40.0, lineNumber: 2, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -559,9 +559,9 @@ void main() async {
expect(result.minIntrinsicWidth, 40);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('abcd', 0, 5, hardBreak: true, width: 40.0, lineNumber: 0),
line('ef', 5, 8, hardBreak: true, width: 20.0, lineNumber: 1),
line('ghi', 8, 11, hardBreak: true, width: 30.0, lineNumber: 2),
line('abcd', 0, 5, hardBreak: true, width: 40.0, lineNumber: 0, left: 0.0),
line('ef', 5, 8, hardBreak: true, width: 20.0, lineNumber: 1, left: 0.0),
line('ghi', 8, 11, hardBreak: true, width: 30.0, lineNumber: 2, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -574,8 +574,8 @@ void main() async {
expect(result.minIntrinsicWidth, 40);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('abcd ', 0, 10, hardBreak: false, width: 40.0, lineNumber: 0),
line('efg', 10, 13, hardBreak: true, width: 30.0, lineNumber: 1),
line('abcd ', 0, 10, hardBreak: false, width: 40.0, lineNumber: 0, left: 0.0),
line('efg', 10, 13, hardBreak: true, width: 30.0, lineNumber: 1, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -588,8 +588,8 @@ void main() async {
expect(result.minIntrinsicWidth, 40);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('abc ', 0, 8, hardBreak: true, width: 30.0, lineNumber: 0),
line('defg', 8, 12, hardBreak: true, width: 40.0, lineNumber: 1),
line('abc ', 0, 8, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0),
line('defg', 8, 12, hardBreak: true, width: 40.0, lineNumber: 1, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -602,9 +602,9 @@ void main() async {
expect(result.minIntrinsicWidth, 120);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('AAAAA', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0),
line('AAAAA', 5, 10, hardBreak: false, width: 50.0, lineNumber: 1),
line('AA', 10, 12, hardBreak: true, width: 20.0, lineNumber: 2),
line('AAAAA', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0),
line('AAAAA', 5, 10, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0),
line('AA', 10, 12, hardBreak: true, width: 20.0, lineNumber: 2, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -621,9 +621,9 @@ void main() async {
expect(result.maxIntrinsicWidth, 110);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('abc ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0),
line('de ', 4, 7, hardBreak: false, width: 20.0, lineNumber: 1),
line('fghi', 7, 11, hardBreak: true, width: 40.0, lineNumber: 2),
line('abc ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0, left: 0.0),
line('de ', 4, 7, hardBreak: false, width: 20.0, lineNumber: 1, left: 0.0),
line('fghi', 7, 11, hardBreak: true, width: 40.0, lineNumber: 2, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -636,9 +636,9 @@ void main() async {
expect(result.maxIntrinsicWidth, 40);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('abcd', 0, 5, hardBreak: true, width: 40.0, lineNumber: 0),
line('ef', 5, 8, hardBreak: true, width: 20.0, lineNumber: 1),
line('ghi', 8, 11, hardBreak: true, width: 30.0, lineNumber: 2),
line('abcd', 0, 5, hardBreak: true, width: 40.0, lineNumber: 0, left: 0.0),
line('ef', 5, 8, hardBreak: true, width: 20.0, lineNumber: 1, left: 0.0),
line('ghi', 8, 11, hardBreak: true, width: 30.0, lineNumber: 2, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -651,8 +651,8 @@ void main() async {
expect(result.maxIntrinsicWidth, 100);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('abcd ', 0, 7, hardBreak: false, width: 40.0, lineNumber: 0),
line('efg', 7, 10, hardBreak: true, width: 30.0, lineNumber: 1),
line('abcd ', 0, 7, hardBreak: false, width: 40.0, lineNumber: 0, left: 0.0),
line('efg', 7, 10, hardBreak: true, width: 30.0, lineNumber: 1, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -665,8 +665,8 @@ void main() async {
expect(result.maxIntrinsicWidth, 100);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('abc ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0),
line('def ', 4, 10, hardBreak: true, width: 30.0, lineNumber: 1),
line('abc ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0, left: 0.0),
line('def ', 4, 10, hardBreak: true, width: 30.0, lineNumber: 1, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -679,8 +679,8 @@ void main() async {
expect(result.maxIntrinsicWidth, 60);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('abc ', 0, 5, hardBreak: true, width: 30.0, lineNumber: 0),
line('def ', 5, 11, hardBreak: true, width: 30.0, lineNumber: 1),
line('abc ', 0, 5, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0),
line('def ', 5, 11, hardBreak: true, width: 30.0, lineNumber: 1, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -693,9 +693,9 @@ void main() async {
expect(result.maxIntrinsicWidth, 120);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('AAAAA', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0),
line('AAAAA', 5, 10, hardBreak: false, width: 50.0, lineNumber: 1),
line('AA', 10, 12, hardBreak: true, width: 20.0, lineNumber: 2),
line('AAAAA', 0, 5, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0),
line('AAAAA', 5, 10, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0),
line('AA', 10, 12, hardBreak: true, width: 20.0, lineNumber: 2, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -727,7 +727,7 @@ void main() async {
expect(result.height, 10);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('AA...', 0, 48, hardBreak: false, width: 50.0, lineNumber: 0),
line('AA...', 0, 48, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0),
]);
} else {
// DOM-based measurement can't handle the ellipsis case very well. The
@ -747,8 +747,8 @@ void main() async {
expect(result.height, 20);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('AAA', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0),
line('AA...', 4, 49, hardBreak: false, width: 50.0, lineNumber: 1),
line('AAA', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0),
line('AA...', 4, 49, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0),
]);
} else {
// DOM-based measurement can't handle the ellipsis case very well. The
@ -766,7 +766,7 @@ void main() async {
expect(result.height, 10);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('...', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0),
line('...', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0, left: 0.0),
]);
} else {
// DOM-based measurement can't handle the ellipsis case very well. The
@ -784,10 +784,10 @@ void main() async {
if (instance.isCanvas) {
// TODO(flutter_web): https://github.com/flutter/flutter/issues/34346
// expect(result.lines, <EngineLineMetrics>[
// line('.', hardBreak: false, width: 10.0, lineNumber: 0),
// line('.', 0, 4, hardBreak: false, width: 10.0, lineNumber: 0, left: 0.0),
// ]);
expect(result.lines, <EngineLineMetrics>[
line('...', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0),
line('...', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0, left: 0.0),
]);
} else {
// DOM-based measurement can't handle the ellipsis case very well. The
@ -811,7 +811,7 @@ void main() async {
result = instance.measure(oneline, infiniteConstraints);
expect(result.height, 10);
expect(result.lines, <EngineLineMetrics>[
line('One line', 0, 8, hardBreak: true, width: 80.0, lineNumber: 0),
line('One line', 0, 8, hardBreak: true, width: 80.0, lineNumber: 0, left: 0.0),
]);
// The height should respect max lines and be limited to two lines here.
@ -821,8 +821,8 @@ void main() async {
expect(result.height, 20);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('First', 0, 6, hardBreak: true, width: 50.0, lineNumber: 0),
line('Second', 6, 13, hardBreak: true, width: 60.0, lineNumber: 1),
line('First', 0, 6, hardBreak: true, width: 50.0, lineNumber: 0, left: 0.0),
line('Second', 6, 13, hardBreak: true, width: 60.0, lineNumber: 1, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -839,8 +839,8 @@ void main() async {
expect(result.height, 20);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('Lorem ', 0, 6, hardBreak: false, width: 50.0, lineNumber: 0),
line('ipsum ', 6, 12, hardBreak: false, width: 50.0, lineNumber: 1),
line('Lorem ', 0, 6, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0),
line('ipsum ', 6, 12, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -857,8 +857,8 @@ void main() async {
expect(result.height, 20);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('AAA ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0),
line('AAAAA', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1),
line('AAA ', 0, 4, hardBreak: false, width: 30.0, lineNumber: 0, left: 0.0),
line('AAAAA', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -893,7 +893,7 @@ void main() async {
result = instance.measure(p, constraints);
expect(result.height, 10);
expect(result.lines, <EngineLineMetrics>[
line('abcdef', 0, 6, hardBreak: true, width: 60.0, lineNumber: 0),
line('abcdef', 0, 6, hardBreak: true, width: 60.0, lineNumber: 0, left: 0.0),
]);
// Simple overflow case.
@ -902,7 +902,7 @@ void main() async {
expect(result.height, 10);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('abc...', 0, 8, hardBreak: false, width: 60.0, lineNumber: 0),
line('abc...', 0, 8, hardBreak: false, width: 60.0, lineNumber: 0, left: 0.0),
]);
} else {
// DOM-based measurement can't handle the ellipsis case very well. The
@ -916,7 +916,7 @@ void main() async {
expect(result.height, 10);
if (instance.isCanvas) {
expect(result.lines, <EngineLineMetrics>[
line('a b...', 0, 10, hardBreak: false, width: 60.0, lineNumber: 0),
line('a b...', 0, 10, hardBreak: false, width: 60.0, lineNumber: 0, left: 0.0),
]);
} else {
// DOM-based measurement can't handle the ellipsis case very well. The
@ -933,8 +933,8 @@ void main() async {
expect(result.height, 20);
expect(result.lines, <EngineLineMetrics>[
line('abcdef ', 0, 7, hardBreak: false, width: 60.0, lineNumber: 0),
line('ghijkl', 7, 13, hardBreak: true, width: 60.0, lineNumber: 1),
line('abcdef ', 0, 7, hardBreak: false, width: 60.0, lineNumber: 0, left: 0.0),
line('ghijkl', 7, 13, hardBreak: true, width: 60.0, lineNumber: 1, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -950,8 +950,8 @@ void main() async {
expect(result.height, 20);
expect(result.lines, <EngineLineMetrics>[
line('abcd ', 0, 5, hardBreak: false, width: 40.0, lineNumber: 0),
line('efg...', 5, 13, hardBreak: false, width: 60.0, lineNumber: 1),
line('abcd ', 0, 5, hardBreak: false, width: 40.0, lineNumber: 0, left: 0.0),
line('efg...', 5, 13, hardBreak: false, width: 60.0, lineNumber: 1, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -968,8 +968,8 @@ void main() async {
expect(result.height, 20);
expect(result.lines, <EngineLineMetrics>[
line('abcde ', 0, 6, hardBreak: false, width: 50.0, lineNumber: 0),
line('f g...', 6, 14, hardBreak: false, width: 60.0, lineNumber: 1),
line('abcde ', 0, 6, hardBreak: false, width: 50.0, lineNumber: 0, left: 0.0),
line('f g...', 6, 14, hardBreak: false, width: 60.0, lineNumber: 1, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -985,8 +985,8 @@ void main() async {
expect(result.height, 20);
expect(result.lines, <EngineLineMetrics>[
line('abcdef', 0, 6, hardBreak: false, width: 60.0, lineNumber: 0),
line('g hijk', 6, 12, hardBreak: true, width: 60.0, lineNumber: 1),
line('abcdef', 0, 6, hardBreak: false, width: 60.0, lineNumber: 0, left: 0.0),
line('g hijk', 6, 12, hardBreak: true, width: 60.0, lineNumber: 1, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -1002,8 +1002,8 @@ void main() async {
expect(result.height, 20);
expect(result.lines, <EngineLineMetrics>[
line('abcdef', 0, 6, hardBreak: false, width: 60.0, lineNumber: 0),
line('g h...', 6, 17, hardBreak: false, width: 60.0, lineNumber: 1),
line('abcdef', 0, 6, hardBreak: false, width: 60.0, lineNumber: 0, left: 0.0),
line('g h...', 6, 17, hardBreak: false, width: 60.0, lineNumber: 1, left: 0.0),
]);
} else {
// DOM-based measurement can't produce line metrics for multi-line
@ -1012,6 +1012,119 @@ void main() async {
}
},
);
test('handles textAlign', () {
TextMeasurementService instance = TextMeasurementService.canvasInstance;
ui.Paragraph p;
MeasurementResult result;
ui.ParagraphStyle createStyle(ui.TextAlign textAlign) {
return ui.ParagraphStyle(
fontFamily: 'ahem',
fontSize: 10,
textAlign: textAlign,
textDirection: ui.TextDirection.ltr,
);
}
p = build(createStyle(ui.TextAlign.start), 'abc\ndefghi');
result = instance.measure(p, constraints);
expect(result.lines, <EngineLineMetrics>[
line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0),
line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0),
line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 0.0),
]);
p = build(createStyle(ui.TextAlign.end), 'abc\ndefghi');
result = instance.measure(p, constraints);
expect(result.lines, <EngineLineMetrics>[
line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 20.0),
line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0),
line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 40.0),
]);
p = build(createStyle(ui.TextAlign.center), 'abc\ndefghi');
result = instance.measure(p, constraints);
expect(result.lines, <EngineLineMetrics>[
line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 10.0),
line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0),
line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 20.0),
]);
p = build(createStyle(ui.TextAlign.left), 'abc\ndefghi');
result = instance.measure(p, constraints);
expect(result.lines, <EngineLineMetrics>[
line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0),
line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0),
line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 0.0),
]);
p = build(createStyle(ui.TextAlign.right), 'abc\ndefghi');
result = instance.measure(p, constraints);
expect(result.lines, <EngineLineMetrics>[
line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 20.0),
line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0),
line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 40.0),
]);
});
testMeasurements(
'handles rtl with textAlign',
(TextMeasurementService instance) {
TextMeasurementService instance = TextMeasurementService.canvasInstance;
ui.Paragraph p;
MeasurementResult result;
ui.ParagraphStyle createStyle(ui.TextAlign textAlign) {
return ui.ParagraphStyle(
fontFamily: 'ahem',
fontSize: 10,
textAlign: textAlign,
textDirection: ui.TextDirection.rtl,
);
}
p = build(createStyle(ui.TextAlign.start), 'abc\ndefghi');
result = instance.measure(p, constraints);
expect(result.lines, <EngineLineMetrics>[
line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 20.0),
line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0),
line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 40.0),
]);
p = build(createStyle(ui.TextAlign.end), 'abc\ndefghi');
result = instance.measure(p, constraints);
expect(result.lines, <EngineLineMetrics>[
line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0),
line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0),
line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 0.0),
]);
p = build(createStyle(ui.TextAlign.center), 'abc\ndefghi');
result = instance.measure(p, constraints);
expect(result.lines, <EngineLineMetrics>[
line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 10.0),
line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0),
line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 20.0),
]);
p = build(createStyle(ui.TextAlign.left), 'abc\ndefghi');
result = instance.measure(p, constraints);
expect(result.lines, <EngineLineMetrics>[
line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 0.0),
line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0),
line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 0.0),
]);
p = build(createStyle(ui.TextAlign.right), 'abc\ndefghi');
result = instance.measure(p, constraints);
expect(result.lines, <EngineLineMetrics>[
line('abc', 0, 4, hardBreak: true, width: 30.0, lineNumber: 0, left: 20.0),
line('defgh', 4, 9, hardBreak: false, width: 50.0, lineNumber: 1, left: 0.0),
line('i', 9, 10, hardBreak: true, width: 10.0, lineNumber: 2, left: 40.0),
]);
},
);
});
}
@ -1023,6 +1136,7 @@ EngineLineMetrics line(
double width,
int lineNumber,
bool hardBreak,
double left,
}) {
return EngineLineMetrics.withText(
text,
@ -1031,5 +1145,6 @@ EngineLineMetrics line(
hardBreak: hardBreak,
width: width,
lineNumber: lineNumber,
left: left,
);
}