blob: 49b5aca52290863f741ec97bcc44fb0527871e86 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
import 'package:ui/src/engine.dart';
import 'package:ui/ui.dart' hide window;
import 'helper.dart';
import 'text_scuba.dart';
typedef CanvasTest = FutureOr<void> Function(EngineCanvas canvas);
const String _rtlWord1 = 'واحد';
const String _rtlWord2 = 'اثنان';
void main() {
internalBootstrapBrowserTest(() => testMain);
}
Future<void> testMain() async {
setUpStableTestFonts();
void paintBasicBidiStartingWithLtr(
EngineCanvas canvas,
Rect bounds,
double y,
TextDirection textDirection,
TextAlign textAlign,
) {
// The text starts with a left-to-right word.
const String text = 'One 12 $_rtlWord1 $_rtlWord2 34 two 56';
final EngineParagraphStyle paragraphStyle = EngineParagraphStyle(
fontFamily: 'Roboto',
fontSize: 20.0,
textDirection: textDirection,
textAlign: textAlign,
);
final CanvasParagraph paragraph = plain(
paragraphStyle,
text,
textStyle: EngineTextStyle.only(color: blue),
);
final double maxWidth = bounds.width - 10;
paragraph.layout(constrain(maxWidth));
canvas.drawParagraph(paragraph, Offset(bounds.left + 5, bounds.top + y + 5));
}
test('basic bidi starting with ltr', () {
const Rect bounds = Rect.fromLTWH(0, 0, 340, 600);
final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy());
const double height = 40;
// Border for ltr paragraphs.
final Rect ltrBox = const Rect.fromLTWH(0, 0, 320, 5 * height).inflate(5).translate(10, 10);
canvas.drawRect(
ltrBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// LTR with different text align values:
paintBasicBidiStartingWithLtr(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left);
paintBasicBidiStartingWithLtr(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right);
paintBasicBidiStartingWithLtr(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center);
paintBasicBidiStartingWithLtr(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start);
paintBasicBidiStartingWithLtr(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end);
// Border for rtl paragraphs.
final Rect rtlBox = ltrBox.translate(0, ltrBox.height + 10);
canvas.drawRect(
rtlBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// RTL with different text align values:
paintBasicBidiStartingWithLtr(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left);
paintBasicBidiStartingWithLtr(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right);
paintBasicBidiStartingWithLtr(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center);
paintBasicBidiStartingWithLtr(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start);
paintBasicBidiStartingWithLtr(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end);
return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_start_ltr');
});
test('basic bidi starting with ltr (DOM)', () {
const Rect bounds = Rect.fromLTWH(0, 0, 340, 600);
final DomCanvas canvas = DomCanvas(domRenderer.createElement('flt-picture'));
const double height = 40;
// Border for ltr paragraphs.
final Rect ltrBox = const Rect.fromLTWH(0, 0, 320, 5 * height).inflate(5).translate(10, 10);
canvas.drawRect(
ltrBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// LTR with different text align values:
paintBasicBidiStartingWithLtr(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left);
paintBasicBidiStartingWithLtr(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right);
paintBasicBidiStartingWithLtr(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center);
paintBasicBidiStartingWithLtr(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start);
paintBasicBidiStartingWithLtr(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end);
// Border for rtl paragraphs.
final Rect rtlBox = ltrBox.translate(0, ltrBox.height + 10);
canvas.drawRect(
rtlBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// RTL with different text align values:
paintBasicBidiStartingWithLtr(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left);
paintBasicBidiStartingWithLtr(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right);
paintBasicBidiStartingWithLtr(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center);
paintBasicBidiStartingWithLtr(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start);
paintBasicBidiStartingWithLtr(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end);
return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_start_ltr_dom');
});
void paintBasicBidiStartingWithRtl(
EngineCanvas canvas,
Rect bounds,
double y,
TextDirection textDirection,
TextAlign textAlign,
) {
// The text starts with a right-to-left word.
const String text = '$_rtlWord1 12 one 34 $_rtlWord2 56 two';
final EngineParagraphStyle paragraphStyle = EngineParagraphStyle(
fontFamily: 'Roboto',
fontSize: 20.0,
textDirection: textDirection,
textAlign: textAlign,
);
final CanvasParagraph paragraph = plain(
paragraphStyle,
text,
textStyle: EngineTextStyle.only(color: blue),
);
final double maxWidth = bounds.width - 10;
paragraph.layout(constrain(maxWidth));
canvas.drawParagraph(paragraph, Offset(bounds.left + 5, bounds.top + y + 5));
}
test('basic bidi starting with rtl', () {
const Rect bounds = Rect.fromLTWH(0, 0, 340, 600);
final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy());
const double height = 40;
// Border for ltr paragraphs.
final Rect ltrBox = const Rect.fromLTWH(0, 0, 320, 5 * height).inflate(5).translate(10, 10);
canvas.drawRect(
ltrBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// LTR with different text align values:
paintBasicBidiStartingWithRtl(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left);
paintBasicBidiStartingWithRtl(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right);
paintBasicBidiStartingWithRtl(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center);
paintBasicBidiStartingWithRtl(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start);
paintBasicBidiStartingWithRtl(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end);
// Border for rtl paragraphs.
final Rect rtlBox = ltrBox.translate(0, ltrBox.height + 10);
canvas.drawRect(
rtlBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// RTL with different text align values:
paintBasicBidiStartingWithRtl(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left);
paintBasicBidiStartingWithRtl(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right);
paintBasicBidiStartingWithRtl(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center);
paintBasicBidiStartingWithRtl(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start);
paintBasicBidiStartingWithRtl(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end);
return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_start_rtl');
});
test('basic bidi starting with rtl (DOM)', () {
const Rect bounds = Rect.fromLTWH(0, 0, 340, 600);
final DomCanvas canvas = DomCanvas(domRenderer.createElement('flt-picture'));
const double height = 40;
// Border for ltr paragraphs.
final Rect ltrBox = const Rect.fromLTWH(0, 0, 320, 5 * height).inflate(5).translate(10, 10);
canvas.drawRect(
ltrBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// LTR with different text align values:
paintBasicBidiStartingWithRtl(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left);
paintBasicBidiStartingWithRtl(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right);
paintBasicBidiStartingWithRtl(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center);
paintBasicBidiStartingWithRtl(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start);
paintBasicBidiStartingWithRtl(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end);
// Border for rtl paragraphs.
final Rect rtlBox = ltrBox.translate(0, ltrBox.height + 10);
canvas.drawRect(
rtlBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// RTL with different text align values:
paintBasicBidiStartingWithRtl(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left);
paintBasicBidiStartingWithRtl(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right);
paintBasicBidiStartingWithRtl(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center);
paintBasicBidiStartingWithRtl(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start);
paintBasicBidiStartingWithRtl(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end);
return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_start_rtl_dom');
});
void paintMultilineBidi(
EngineCanvas canvas,
Rect bounds,
double y,
TextDirection textDirection,
TextAlign textAlign,
) {
// '''
// Lorem 12 $_rtlWord1
// $_rtlWord2 34 ipsum
// dolor 56
// '''
const String text = 'Lorem 12 $_rtlWord1 $_rtlWord2 34 ipsum dolor 56';
final EngineParagraphStyle paragraphStyle = EngineParagraphStyle(
fontFamily: 'Roboto',
fontSize: 20.0,
textDirection: textDirection,
textAlign: textAlign,
);
final CanvasParagraph paragraph = plain(
paragraphStyle,
text,
textStyle: EngineTextStyle.only(color: blue),
);
final double maxWidth = bounds.width - 10;
paragraph.layout(constrain(maxWidth));
canvas.drawParagraph(paragraph, Offset(bounds.left + 5, bounds.top + y + 5));
}
test('multiline bidi', () {
const Rect bounds = Rect.fromLTWH(0, 0, 400, 500);
final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy());
const double height = 95;
// Border for ltr paragraphs.
final Rect ltrBox = const Rect.fromLTWH(0, 0, 150, 5 * height).inflate(5).translate(10, 10);
canvas.drawRect(
ltrBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// LTR with different text align values:
paintMultilineBidi(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left);
paintMultilineBidi(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right);
paintMultilineBidi(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center);
paintMultilineBidi(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start);
paintMultilineBidi(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end);
// Border for rtl paragraphs.
final Rect rtlBox = ltrBox.translate(ltrBox.width + 10, 0);
canvas.drawRect(
rtlBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// RTL with different text align values:
paintMultilineBidi(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left);
paintMultilineBidi(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right);
paintMultilineBidi(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center);
paintMultilineBidi(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start);
paintMultilineBidi(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end);
return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_multiline');
});
test('multiline bidi (DOM)', () {
const Rect bounds = Rect.fromLTWH(0, 0, 400, 500);
final DomCanvas canvas = DomCanvas(domRenderer.createElement('flt-picture'));
const double height = 95;
final Rect ltrBox = const Rect.fromLTWH(0, 0, 150, 5 * height).inflate(5).translate(10, 10);
canvas.drawRect(
ltrBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// LTR with different text align values:
paintMultilineBidi(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left);
paintMultilineBidi(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right);
paintMultilineBidi(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center);
paintMultilineBidi(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start);
paintMultilineBidi(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end);
// Border for rtl paragraphs.
final Rect rtlBox = ltrBox.translate(ltrBox.width + 10, 0);
canvas.drawRect(
rtlBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// RTL with different text align values:
paintMultilineBidi(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left);
paintMultilineBidi(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right);
paintMultilineBidi(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center);
paintMultilineBidi(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start);
paintMultilineBidi(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end);
return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_multiline_dom');
});
void paintMultSpanBidi(
EngineCanvas canvas,
Rect bounds,
double y,
TextDirection textDirection,
TextAlign textAlign,
) {
final EngineParagraphStyle paragraphStyle = EngineParagraphStyle(
fontFamily: 'Roboto',
fontSize: 20.0,
textDirection: textDirection,
textAlign: textAlign,
);
// '''
// Lorem 12 $_rtlWord1
// $_rtlWord2 34 ipsum
// dolor 56
// '''
final CanvasParagraph paragraph = rich(paragraphStyle, (CanvasParagraphBuilder builder) {
builder.pushStyle(EngineTextStyle.only(color: blue));
builder.addText('Lorem ');
builder.pushStyle(EngineTextStyle.only(color: green));
builder.addText('12 ');
builder.pushStyle(EngineTextStyle.only(color: red));
builder.addText('$_rtlWord1 ');
builder.pushStyle(EngineTextStyle.only(color: black));
builder.addText('$_rtlWord2 ');
builder.pushStyle(EngineTextStyle.only(color: blue));
builder.addText('34 ipsum ');
builder.pushStyle(EngineTextStyle.only(color: green));
builder.addText('dolor 56 ');
});
final double maxWidth = bounds.width - 10;
paragraph.layout(constrain(maxWidth));
canvas.drawParagraph(paragraph, Offset(bounds.left + 5, bounds.top + y + 5));
}
test('multi span bidi', () {
const Rect bounds = Rect.fromLTWH(0, 0, 400, 900);
final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy());
const double height = 95;
// Border for ltr paragraphs.
final Rect ltrBox = const Rect.fromLTWH(0, 0, 150, 5 * height).inflate(5).translate(10, 10);
canvas.drawRect(
ltrBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// LTR with different text align values:
paintMultSpanBidi(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left);
paintMultSpanBidi(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right);
paintMultSpanBidi(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center);
paintMultSpanBidi(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start);
paintMultSpanBidi(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end);
// Border for rtl paragraphs.
final Rect rtlBox = ltrBox.translate(ltrBox.width + 10, 0);
canvas.drawRect(
rtlBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// RTL with different text align values:
paintMultSpanBidi(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left);
paintMultSpanBidi(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right);
paintMultSpanBidi(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center);
paintMultSpanBidi(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start);
paintMultSpanBidi(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end);
return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_multispan');
});
test('multi span bidi (DOM)', () {
const Rect bounds = Rect.fromLTWH(0, 0, 400, 900);
final DomCanvas canvas = DomCanvas(domRenderer.createElement('flt-picture'));
const double height = 95;
// Border for ltr paragraphs.
final Rect ltrBox = const Rect.fromLTWH(0, 0, 150, 5 * height).inflate(5).translate(10, 10);
canvas.drawRect(
ltrBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// LTR with different text align values:
paintMultSpanBidi(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left);
paintMultSpanBidi(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right);
paintMultSpanBidi(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center);
paintMultSpanBidi(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start);
paintMultSpanBidi(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end);
// Border for rtl paragraphs.
final Rect rtlBox = ltrBox.translate(ltrBox.width + 10, 0);
canvas.drawRect(
rtlBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// RTL with different text align values:
paintMultSpanBidi(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left);
paintMultSpanBidi(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right);
paintMultSpanBidi(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center);
paintMultSpanBidi(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start);
paintMultSpanBidi(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end);
return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_multispan_dom');
});
void paintBidiWithSelection(
EngineCanvas canvas,
Rect bounds,
double y,
TextDirection textDirection,
TextAlign textAlign,
) {
// '''
// Lorem 12 $_rtlWord1
// $_rtlWord2 34 ipsum
// dolor 56
// '''
const String text = 'Lorem 12 $_rtlWord1 $_rtlWord2 34 ipsum dolor 56';
final EngineParagraphStyle paragraphStyle = EngineParagraphStyle(
fontFamily: 'Roboto',
fontSize: 20.0,
textDirection: textDirection,
textAlign: textAlign,
);
final CanvasParagraph paragraph = plain(
paragraphStyle,
text,
textStyle: EngineTextStyle.only(color: blue),
);
final double maxWidth = bounds.width - 10;
paragraph.layout(constrain(maxWidth));
final Offset offset = Offset(bounds.left + 5, bounds.top + y + 5);
// Range for "em 12 " and the first character of `_rtlWord1`.
paintBoxes(canvas, offset, paragraph.getBoxesForRange(3, 10), lightBlue);
// Range for the second half of `_rtlWord1` and all of `_rtlWord2` and " 3".
paintBoxes(canvas, offset, paragraph.getBoxesForRange(11, 21), lightPurple);
// Range for "psum dolo".
paintBoxes(canvas, offset, paragraph.getBoxesForRange(24, 33), green);
canvas.drawParagraph(paragraph, offset);
}
test('bidi with selection', () {
const Rect bounds = Rect.fromLTWH(0, 0, 400, 500);
final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy());
const double height = 95;
// Border for ltr paragraphs.
final Rect ltrBox = const Rect.fromLTWH(0, 0, 150, 5 * height).inflate(5).translate(10, 10);
canvas.drawRect(
ltrBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// LTR with different text align values:
paintBidiWithSelection(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left);
paintBidiWithSelection(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right);
paintBidiWithSelection(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center);
paintBidiWithSelection(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start);
paintBidiWithSelection(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end);
// Border for rtl paragraphs.
final Rect rtlBox = ltrBox.translate(ltrBox.width + 10, 0);
canvas.drawRect(
rtlBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// RTL with different text align values:
paintBidiWithSelection(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left);
paintBidiWithSelection(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right);
paintBidiWithSelection(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center);
paintBidiWithSelection(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start);
paintBidiWithSelection(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end);
return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_selection');
});
test('bidi with selection (DOM)', () {
const Rect bounds = Rect.fromLTWH(0, 0, 400, 500);
final DomCanvas canvas = DomCanvas(domRenderer.createElement('flt-picture'));
const double height = 95;
// Border for ltr paragraphs.
final Rect ltrBox = const Rect.fromLTWH(0, 0, 150, 5 * height).inflate(5).translate(10, 10);
canvas.drawRect(
ltrBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// LTR with different text align values:
paintBidiWithSelection(canvas, ltrBox, 0 * height, TextDirection.ltr, TextAlign.left);
paintBidiWithSelection(canvas, ltrBox, 1 * height, TextDirection.ltr, TextAlign.right);
paintBidiWithSelection(canvas, ltrBox, 2 * height, TextDirection.ltr, TextAlign.center);
paintBidiWithSelection(canvas, ltrBox, 3 * height, TextDirection.ltr, TextAlign.start);
paintBidiWithSelection(canvas, ltrBox, 4 * height, TextDirection.ltr, TextAlign.end);
// Border for rtl paragraphs.
final Rect rtlBox = ltrBox.translate(ltrBox.width + 10, 0);
canvas.drawRect(
rtlBox,
SurfacePaintData()
..color = black
..style = PaintingStyle.stroke,
);
// RTL with different text align values:
paintBidiWithSelection(canvas, rtlBox, 0 * height, TextDirection.rtl, TextAlign.left);
paintBidiWithSelection(canvas, rtlBox, 1 * height, TextDirection.rtl, TextAlign.right);
paintBidiWithSelection(canvas, rtlBox, 2 * height, TextDirection.rtl, TextAlign.center);
paintBidiWithSelection(canvas, rtlBox, 3 * height, TextDirection.rtl, TextAlign.start);
paintBidiWithSelection(canvas, rtlBox, 4 * height, TextDirection.rtl, TextAlign.end);
return takeScreenshot(canvas, bounds, 'canvas_paragraph_bidi_selection_dom');
});
}
void paintBoxes(EngineCanvas canvas, Offset offset, List<TextBox> boxes, Color color) {
for (final TextBox box in boxes) {
final Rect rect = box.toRect().shift(offset);
canvas.drawRect(rect, SurfacePaintData()..color = color);
}
}