| // 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 'dart:math' as math; |
| |
| 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 Rect bounds = Rect.fromLTWH(0, 0, 800, 600); |
| |
| void main() { |
| internalBootstrapBrowserTest(() => testMain); |
| } |
| |
| Future<void> testMain() async { |
| setUpStableTestFonts(); |
| |
| test('paints spans and lines correctly', () { |
| final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); |
| |
| Offset offset = Offset.zero; |
| CanvasParagraph paragraph; |
| |
| // Single-line multi-span. |
| paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto'), (CanvasParagraphBuilder builder) { |
| builder.pushStyle(EngineTextStyle.only(color: blue)); |
| builder.addText('Lorem '); |
| builder.pushStyle(EngineTextStyle.only( |
| color: green, |
| background: Paint()..color = red, |
| )); |
| builder.addText('ipsum '); |
| builder.pop(); |
| builder.addText('.'); |
| }) |
| ..layout(constrain(double.infinity)); |
| canvas.drawParagraph(paragraph, offset); |
| offset = offset.translate(0, paragraph.height + 10); |
| |
| // Multi-line single-span. |
| paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto'), (CanvasParagraphBuilder builder) { |
| builder.addText('Lorem ipsum dolor sit'); |
| }) |
| ..layout(constrain(90.0)); |
| canvas.drawParagraph(paragraph, offset); |
| offset = offset.translate(0, paragraph.height + 10); |
| |
| // Multi-line multi-span. |
| paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto'), (CanvasParagraphBuilder builder) { |
| builder.pushStyle(EngineTextStyle.only(color: blue)); |
| builder.addText('Lorem ipsum '); |
| builder.pushStyle(EngineTextStyle.only(background: Paint()..color = red)); |
| builder.pushStyle(EngineTextStyle.only(color: green)); |
| builder.addText('dolor '); |
| builder.pop(); |
| builder.addText('sit'); |
| }) |
| ..layout(constrain(90.0)); |
| canvas.drawParagraph(paragraph, offset); |
| offset = offset.translate(0, paragraph.height + 10); |
| |
| return takeScreenshot(canvas, bounds, 'canvas_paragraph_general'); |
| }); |
| |
| test('respects alignment', () { |
| final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); |
| |
| Offset offset = Offset.zero; |
| CanvasParagraph paragraph; |
| |
| void build(CanvasParagraphBuilder builder) { |
| builder.pushStyle(EngineTextStyle.only(color: black)); |
| builder.addText('Lorem '); |
| builder.pushStyle(EngineTextStyle.only(color: blue)); |
| builder.addText('ipsum '); |
| builder.pushStyle(EngineTextStyle.only(color: green)); |
| builder.addText('dolor '); |
| builder.pushStyle(EngineTextStyle.only(color: red)); |
| builder.addText('sit'); |
| } |
| |
| paragraph = rich( |
| EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.left), |
| build, |
| )..layout(constrain(100.0)); |
| canvas.drawParagraph(paragraph, offset); |
| offset = offset.translate(0, paragraph.height + 10); |
| |
| paragraph = rich( |
| EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.center), |
| build, |
| )..layout(constrain(100.0)); |
| canvas.drawParagraph(paragraph, offset); |
| offset = offset.translate(0, paragraph.height + 10); |
| |
| paragraph = rich( |
| EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.right), |
| build, |
| )..layout(constrain(100.0)); |
| canvas.drawParagraph(paragraph, offset); |
| offset = offset.translate(0, paragraph.height + 10); |
| |
| return takeScreenshot(canvas, bounds, 'canvas_paragraph_align'); |
| }); |
| |
| test('respects alignment in DOM mode', () { |
| final DomCanvas canvas = DomCanvas(domDocument.createElement('flt-picture')); |
| |
| Offset offset = Offset.zero; |
| CanvasParagraph paragraph; |
| |
| void build(CanvasParagraphBuilder builder) { |
| builder.pushStyle(EngineTextStyle.only(color: black)); |
| builder.addText('Lorem '); |
| builder.pushStyle(EngineTextStyle.only(color: blue)); |
| builder.addText('ipsum '); |
| builder.pushStyle(EngineTextStyle.only(color: green)); |
| builder.addText('dolor '); |
| builder.pushStyle(EngineTextStyle.only(color: red)); |
| builder.addText('sit'); |
| } |
| |
| paragraph = rich( |
| EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.left), |
| build, |
| )..layout(constrain(100.0)); |
| canvas.drawParagraph(paragraph, offset); |
| offset = offset.translate(0, paragraph.height + 10); |
| |
| paragraph = rich( |
| EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.center), |
| build, |
| )..layout(constrain(100.0)); |
| canvas.drawParagraph(paragraph, offset); |
| offset = offset.translate(0, paragraph.height + 10); |
| |
| paragraph = rich( |
| EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.right), |
| build, |
| )..layout(constrain(100.0)); |
| canvas.drawParagraph(paragraph, offset); |
| offset = offset.translate(0, paragraph.height + 10); |
| |
| return takeScreenshot(canvas, bounds, 'canvas_paragraph_align_dom', maxDiffRatePercent: 0.3); |
| }); |
| |
| void testAlignAndTransform(EngineCanvas canvas) { |
| CanvasParagraph paragraph; |
| |
| void build(CanvasParagraphBuilder builder) { |
| builder.pushStyle(EngineTextStyle.only(color: white)); |
| builder.addText('Lorem '); |
| builder.pushStyle(EngineTextStyle.only(color: red)); |
| builder.addText('ipsum\n'); |
| builder.pushStyle(EngineTextStyle.only(color: yellow)); |
| builder.addText('dolor'); |
| } |
| |
| void drawParagraphAt(Offset offset, TextAlign align) { |
| paragraph = rich( |
| EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 20.0, textAlign: align), |
| build, |
| )..layout(constrain(150.0)); |
| canvas.save(); |
| canvas.translate(offset.dx, offset.dy); |
| canvas.rotate(math.pi / 4); |
| final Rect rect = Rect.fromLTRB(0.0, 0.0, 150.0, paragraph.height); |
| canvas.drawRect(rect, SurfacePaintData()..color = black); |
| canvas.drawParagraph(paragraph, Offset.zero); |
| canvas.restore(); |
| } |
| |
| drawParagraphAt(const Offset(50.0, 0.0), TextAlign.left); |
| drawParagraphAt(const Offset(150.0, 0.0), TextAlign.center); |
| drawParagraphAt(const Offset(250.0, 0.0), TextAlign.right); |
| } |
| |
| test('alignment and transform', () { |
| final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); |
| testAlignAndTransform(canvas); |
| return takeScreenshot(canvas, bounds, 'canvas_paragraph_align_transform'); |
| }); |
| |
| test('alignment and transform (DOM)', () { |
| final DomCanvas canvas = DomCanvas(domDocument.createElement('flt-picture')); |
| testAlignAndTransform(canvas); |
| return takeScreenshot(canvas, bounds, 'canvas_paragraph_align_transform_dom'); |
| }); |
| |
| void testGiantParagraphStyles(EngineCanvas canvas) { |
| final CanvasParagraph paragraph = rich( |
| EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 80.0), |
| (CanvasParagraphBuilder builder) { |
| builder.pushStyle(EngineTextStyle.only(color: yellow, fontSize: 24.0)); |
| builder.addText('Lorem '); |
| builder.pushStyle(EngineTextStyle.only(color: red, fontSize: 32.0)); |
| builder.addText('ipsum'); |
| }, |
| )..layout(constrain(double.infinity)); |
| final Rect rect = Rect.fromLTRB(0.0, 0.0, paragraph.maxIntrinsicWidth, paragraph.height); |
| canvas.drawRect(rect, SurfacePaintData()..color = black); |
| canvas.drawParagraph(paragraph, Offset.zero); |
| } |
| |
| test('giant paragraph style', () { |
| const Rect bounds = Rect.fromLTWH(0, 0, 300, 200); |
| final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); |
| testGiantParagraphStyles(canvas); |
| return takeScreenshot(canvas, bounds, 'canvas_paragraph_giant_paragraph_style'); |
| }); |
| |
| test('giant paragraph style (DOM)', () { |
| const Rect bounds = Rect.fromLTWH(0, 0, 300, 200); |
| final DomCanvas canvas = DomCanvas(domDocument.createElement('flt-picture')); |
| testGiantParagraphStyles(canvas); |
| return takeScreenshot(canvas, bounds, 'canvas_paragraph_giant_paragraph_style_dom'); |
| }); |
| |
| test('giant font size on the body tag (DOM)', () async { |
| const Rect bounds = Rect.fromLTWH(0, 0, 600, 200); |
| |
| // Store the old font size value on the body, and set a gaint font size. |
| final String oldBodyFontSize = domDocument.body!.style.fontSize; |
| domDocument.body!.style.fontSize = '100px'; |
| |
| final DomCanvas canvas = DomCanvas(domDocument.createElement('flt-picture')); |
| Offset offset = const Offset(10.0, 10.0); |
| |
| final CanvasParagraph paragraph = rich( |
| EngineParagraphStyle(fontFamily: 'Roboto'), |
| (CanvasParagraphBuilder builder) { |
| builder.pushStyle(EngineTextStyle.only(color: yellow, fontSize: 24.0)); |
| builder.addText('Lorem '); |
| builder.pushStyle(EngineTextStyle.only(color: red, fontSize: 48.0)); |
| builder.addText('ipsum'); |
| }, |
| )..layout(constrain(double.infinity)); |
| final Rect rect = Rect.fromLTWH(offset.dx, offset.dy, paragraph.maxIntrinsicWidth, paragraph.height); |
| canvas.drawRect(rect, SurfacePaintData()..color = black); |
| canvas.drawParagraph(paragraph, offset); |
| offset = offset.translate(paragraph.maxIntrinsicWidth, 0.0); |
| |
| // Add some extra padding between the two paragraphs. |
| offset = offset.translate(20.0, 0.0); |
| |
| // Use the same height as the previous paragraph so that the 2 paragraphs |
| // look nice in the screenshot. |
| final double placeholderHeight = paragraph.height; |
| final double placeholderWidth = paragraph.height * 2; |
| |
| final CanvasParagraph paragraph2 = rich( |
| EngineParagraphStyle(), |
| (CanvasParagraphBuilder builder) { |
| builder.addPlaceholder(placeholderWidth, placeholderHeight, PlaceholderAlignment.baseline, baseline: TextBaseline.alphabetic); |
| }, |
| )..layout(constrain(double.infinity)); |
| final Rect rect2 = Rect.fromLTWH(offset.dx, offset.dy, paragraph2.maxIntrinsicWidth, paragraph2.height); |
| canvas.drawRect(rect2, SurfacePaintData()..color = black); |
| canvas.drawParagraph(paragraph2, offset); |
| // Draw a rect in the placeholder. |
| // Leave some padding around the placeholder to make the black paragraph |
| // background visible. |
| const double padding = 5; |
| final TextBox placeholderBox = paragraph2.getBoxesForPlaceholders().single; |
| canvas.drawRect( |
| placeholderBox.toRect().shift(offset).deflate(padding), |
| SurfacePaintData()..color = red, |
| ); |
| |
| await takeScreenshot(canvas, bounds, 'canvas_paragraph_giant_body_font_size_dom'); |
| |
| // Restore the old font size value. |
| domDocument.body!.style.fontSize = oldBodyFontSize; |
| }); |
| |
| test('paints spans with varying heights/baselines', () { |
| final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); |
| |
| final CanvasParagraph paragraph = rich( |
| EngineParagraphStyle(fontFamily: 'Roboto'), |
| (CanvasParagraphBuilder builder) { |
| builder.pushStyle(EngineTextStyle.only(fontSize: 20.0)); |
| builder.addText('Lorem '); |
| builder.pushStyle(EngineTextStyle.only( |
| fontSize: 40.0, |
| background: Paint()..color = green, |
| )); |
| builder.addText('ipsum '); |
| builder.pushStyle(EngineTextStyle.only( |
| fontSize: 10.0, |
| color: white, |
| background: Paint()..color = black, |
| )); |
| builder.addText('dolor '); |
| builder.pushStyle(EngineTextStyle.only(fontSize: 30.0)); |
| builder.addText('sit '); |
| builder.pop(); |
| builder.pop(); |
| builder.pushStyle(EngineTextStyle.only( |
| fontSize: 20.0, |
| background: Paint()..color = blue, |
| )); |
| builder.addText('amet'); |
| }, |
| )..layout(constrain(220.0)); |
| canvas.drawParagraph(paragraph, Offset.zero); |
| |
| return takeScreenshot(canvas, bounds, 'canvas_paragraph_varying_heights'); |
| }); |
| |
| test('respects letter-spacing', () { |
| final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); |
| |
| final CanvasParagraph paragraph = rich( |
| EngineParagraphStyle(fontFamily: 'Roboto'), |
| (CanvasParagraphBuilder builder) { |
| builder.pushStyle(EngineTextStyle.only(color: blue)); |
| builder.addText('Lorem '); |
| builder.pushStyle(EngineTextStyle.only(color: green, letterSpacing: 1)); |
| builder.addText('Lorem '); |
| builder.pushStyle(EngineTextStyle.only(color: red, letterSpacing: 3)); |
| builder.addText('Lorem'); |
| }, |
| )..layout(constrain(double.infinity)); |
| canvas.drawParagraph(paragraph, Offset.zero); |
| |
| return takeScreenshot(canvas, bounds, 'canvas_paragraph_letter_spacing'); |
| }); |
| |
| test('draws text decorations', () { |
| final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); |
| final List<TextDecorationStyle> decorationStyles = <TextDecorationStyle>[ |
| TextDecorationStyle.solid, |
| TextDecorationStyle.double, |
| TextDecorationStyle.dotted, |
| TextDecorationStyle.dashed, |
| TextDecorationStyle.wavy, |
| ]; |
| |
| final CanvasParagraph paragraph = rich( |
| EngineParagraphStyle(fontFamily: 'Roboto'), |
| (CanvasParagraphBuilder builder) { |
| for (final TextDecorationStyle decorationStyle in decorationStyles) { |
| builder.pushStyle(EngineTextStyle.only( |
| color: const Color.fromRGBO(50, 50, 255, 1.0), |
| decoration: TextDecoration.underline, |
| decorationStyle: decorationStyle, |
| decorationColor: red, |
| fontFamily: 'Roboto', |
| fontSize: 30, |
| )); |
| builder.addText('Hello World'); |
| builder.pop(); |
| builder.addText(' '); |
| } |
| }, |
| )..layout(constrain(double.infinity)); |
| |
| canvas.drawParagraph(paragraph, Offset.zero); |
| return takeScreenshot(canvas, bounds, 'canvas_paragraph_decoration'); |
| }); |
| |
| void testFontFeatures(EngineCanvas canvas) { |
| const String text = 'Bb Difficult '; |
| const FontFeature enableSmallCaps = FontFeature('smcp'); |
| const FontFeature disableSmallCaps = FontFeature('smcp', 0); |
| |
| const String numeric = '123.4560'; |
| const FontFeature enableOnum = FontFeature('onum'); |
| |
| const FontFeature disableLigatures = FontFeature('liga', 0); |
| |
| final CanvasParagraph paragraph = rich( |
| EngineParagraphStyle(fontFamily: 'Roboto'), |
| (CanvasParagraphBuilder builder) { |
| // Small Caps |
| builder.pushStyle(EngineTextStyle.only( |
| height: 1.5, |
| color: black, |
| fontSize: 32.0, |
| )); |
| builder.pushStyle(EngineTextStyle.only( |
| color: blue, |
| fontFeatures: <FontFeature>[enableSmallCaps], |
| )); |
| builder.addText(text); |
| // Make sure disabling a font feature also works. |
| builder.pushStyle(EngineTextStyle.only( |
| color: black, |
| fontFeatures: <FontFeature>[disableSmallCaps], |
| )); |
| builder.addText(' (smcp)\n'); |
| builder.pop(); // disableSmallCaps |
| builder.pop(); // enableSmallCaps |
| |
| // No ligatures |
| builder.pushStyle(EngineTextStyle.only( |
| color: blue, |
| fontFeatures: <FontFeature>[disableLigatures], |
| )); |
| builder.addText(text); |
| builder.pop(); // disableLigatures |
| builder.addText(' (no liga)\n'); |
| |
| // No font features |
| builder.pushStyle(EngineTextStyle.only( |
| color: blue, |
| )); |
| builder.addText(text); |
| builder.pop(); // color: blue |
| builder.addText(' (none)\n'); |
| |
| // Onum |
| builder.pushStyle(EngineTextStyle.only( |
| color: blue, |
| fontFeatures: <FontFeature>[enableOnum], |
| )); |
| builder.addText(numeric); |
| builder.pop(); // enableOnum |
| builder.addText(' (onum)\n'); |
| |
| // No font features |
| builder.pushStyle(EngineTextStyle.only( |
| color: blue, |
| )); |
| builder.addText(numeric); |
| builder.pop(); // color: blue |
| builder.addText(' (none)\n\n'); |
| |
| // Multiple font features |
| builder.addText('Combined (smcp, onum):\n'); |
| builder.pushStyle(EngineTextStyle.only( |
| color: blue, |
| fontFeatures: <FontFeature>[ |
| enableSmallCaps, |
| enableOnum, |
| ], |
| )); |
| builder.addText('$text $numeric'); |
| builder.pop(); // enableSmallCaps, enableOnum |
| }, |
| )..layout(constrain(double.infinity)); |
| canvas.drawParagraph(paragraph, Offset.zero); |
| } |
| |
| test('font features', () { |
| const Rect bounds = Rect.fromLTWH(0, 0, 600, 500); |
| final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); |
| testFontFeatures(canvas); |
| return takeScreenshot(canvas, bounds, 'canvas_paragraph_font_features'); |
| }); |
| |
| test('font features (DOM)', () { |
| const Rect bounds = Rect.fromLTWH(0, 0, 600, 500); |
| final DomCanvas canvas = DomCanvas(domDocument.createElement('flt-picture')); |
| testFontFeatures(canvas); |
| return takeScreenshot(canvas, bounds, 'canvas_paragraph_font_features_dom'); |
| }); |
| |
| void testFontVariations(EngineCanvas canvas) { |
| const String text = 'ABCDE 12345\n'; |
| FontVariation weight(double w) => FontVariation('wght', w); |
| |
| final CanvasParagraph paragraph = rich( |
| EngineParagraphStyle(fontFamily: 'RobotoVariable'), |
| (CanvasParagraphBuilder builder) { |
| builder.pushStyle(EngineTextStyle.only( |
| fontSize: 48.0, |
| )); |
| builder.addText(text); |
| builder.pushStyle(EngineTextStyle.only( |
| fontSize: 48.0, |
| fontVariations: <FontVariation>[weight(900)], |
| )); |
| builder.addText(text); |
| builder.pushStyle(EngineTextStyle.only( |
| fontSize: 48.0, |
| fontVariations: <FontVariation>[weight(200)], |
| )); |
| builder.addText(text); |
| builder.pop(); |
| builder.pop(); |
| builder.pop(); |
| }, |
| )..layout(constrain(double.infinity)); |
| canvas.drawParagraph(paragraph, Offset.zero); |
| } |
| |
| test('font variations', () { |
| const Rect bounds = Rect.fromLTWH(0, 0, 600, 500); |
| final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); |
| testFontVariations(canvas); |
| return takeScreenshot(canvas, bounds, 'canvas_paragraph_font_variations'); |
| }); |
| |
| test('font variations (DOM)', () { |
| const Rect bounds = Rect.fromLTWH(0, 0, 600, 500); |
| final DomCanvas canvas = DomCanvas(domDocument.createElement('flt-picture')); |
| testFontVariations(canvas); |
| return takeScreenshot(canvas, bounds, 'canvas_paragraph_font_variations_dom'); |
| }); |
| |
| void testBackgroundStyle(EngineCanvas canvas) { |
| final CanvasParagraph paragraph = rich( |
| EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 40.0), |
| (CanvasParagraphBuilder builder) { |
| builder.pushStyle(EngineTextStyle.only(color: black)); |
| builder.pushStyle(EngineTextStyle.only(background: Paint()..color = blue)); |
| builder.addText('Lor'); |
| builder.pushStyle(EngineTextStyle.only(background: Paint()..color = black, color: white)); |
| builder.addText('em '); |
| builder.pop(); |
| builder.pushStyle(EngineTextStyle.only(background: Paint()..color = green)); |
| builder.addText('ipsu'); |
| builder.pushStyle(EngineTextStyle.only(background: Paint()..color = yellow)); |
| builder.addText('m\ndo'); |
| builder.pushStyle(EngineTextStyle.only(background: Paint()..color = red)); |
| builder.addText('lor sit'); |
| }, |
| ); |
| paragraph.layout(constrain(double.infinity)); |
| canvas.drawParagraph(paragraph, Offset.zero); |
| } |
| |
| test('background style', () { |
| const Rect bounds = Rect.fromLTWH(0, 0, 300, 200); |
| final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); |
| testBackgroundStyle(canvas); |
| return takeScreenshot(canvas, bounds, 'canvas_paragraph_background_style'); |
| }); |
| |
| test('background style (DOM)', () { |
| const Rect bounds = Rect.fromLTWH(0, 0, 300, 200); |
| final DomCanvas canvas = DomCanvas(domDocument.createElement('flt-picture')); |
| testBackgroundStyle(canvas); |
| return takeScreenshot(canvas, bounds, 'canvas_paragraph_background_style_dom'); |
| }); |
| |
| void testForegroundStyle(EngineCanvas canvas) { |
| final CanvasParagraph paragraph = rich( |
| EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 40.0), |
| (CanvasParagraphBuilder builder) { |
| builder.pushStyle(EngineTextStyle.only(color: blue)); |
| builder.addText('Lorem'); |
| builder.pop(); |
| builder.pushStyle(EngineTextStyle.only(foreground: Paint()..color = red..style = PaintingStyle.stroke)); |
| builder.addText('ipsum\n'); |
| builder.pop(); |
| builder.pushStyle(EngineTextStyle.only(foreground: Paint()..color = blue..style = PaintingStyle.stroke..strokeWidth = 0.0)); |
| builder.addText('dolor'); |
| builder.pop(); |
| builder.pushStyle(EngineTextStyle.only(foreground: Paint()..color = green..style = PaintingStyle.stroke..strokeWidth = 2.0)); |
| builder.addText('sit\n'); |
| builder.pop(); |
| builder.pushStyle(EngineTextStyle.only(foreground: Paint()..color = yellow..style = PaintingStyle.stroke..strokeWidth = 4.0)); |
| builder.addText('amet'); |
| }, |
| ); |
| paragraph.layout(constrain(double.infinity)); |
| canvas.drawParagraph(paragraph, Offset.zero); |
| } |
| |
| test('foreground style', () { |
| const Rect bounds = Rect.fromLTWH(0, 0, 300, 200); |
| final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); |
| testForegroundStyle(canvas); |
| return takeScreenshot(canvas, bounds, 'canvas_paragraph_foreground_style'); |
| }); |
| |
| test('foreground style (DOM)', () { |
| const Rect bounds = Rect.fromLTWH(0, 0, 300, 200); |
| final DomCanvas canvas = DomCanvas(domDocument.createElement('flt-picture')); |
| testForegroundStyle(canvas); |
| return takeScreenshot(canvas, bounds, 'canvas_paragraph_foreground_style_dom'); |
| }); |
| |
| test('paragraph bounds hug the text inside the paragraph', () async { |
| const Rect bounds = Rect.fromLTWH(0, 0, 150, 100); |
| |
| final CanvasParagraphBuilder builder = CanvasParagraphBuilder(EngineParagraphStyle( |
| fontFamily: 'Ahem', |
| fontSize: 20, |
| textAlign: TextAlign.center, |
| )); |
| |
| // Expected layout with center-alignment is something like this: |
| // |
| // _________________ |
| // | A | |
| // | |AAAAA| | |
| // | | AAA | | |
| // |----|-----|----| |
| // | |<--->| | |
| // | 100 | |
| // | | |
| // |<------------->| |
| // 110 |
| // |
| // The width of the paragraph is bigger than the actual content because the |
| // longest line "AAAAA" is 100px, which is smaller than 110px specified in |
| // the constraint. After the layout and centering the paint bounds would |
| // "hug" the text inside the paragraph more tightly than the box allocated |
| // for the paragraph. |
| builder.addText('A AAAAA AAA'); |
| |
| final CanvasParagraph paragraph = builder.build(); |
| paragraph.layout(const ParagraphConstraints(width: 110)); |
| final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); |
| canvas.translate(20, 20); |
| canvas.drawParagraph(paragraph, Offset.zero); |
| canvas.drawRect( |
| Rect.fromLTRB( |
| 0, |
| 0, |
| paragraph.width, |
| paragraph.height, |
| ), |
| SurfacePaintData() |
| ..style = PaintingStyle.stroke |
| ..strokeWidth = 1, |
| ); |
| |
| canvas.drawRect( |
| paragraph.paintBounds, |
| SurfacePaintData() |
| ..color = const Color(0xFF00FF00) |
| ..style = PaintingStyle.stroke |
| ..strokeWidth = 1, |
| ); |
| |
| await takeScreenshot(canvas, bounds, 'canvas_paragraph_bounds'); |
| }); |
| } |