Skip to content

Commit

Permalink
[web] Use DOM to render paragraphs with letter spacing (#45651)
Browse files Browse the repository at this point in the history
<**_Only applies to the HTML renderer_**>

When a paragraph has letter spacing that's greater than 0, we attempt to render each character individually on the canvas, while adding extra gaps between the characters (because the browser's canvas API doesn't support letter spacing).

This works well-ish in English but causes issues with international text. It also makes text rendering very slow.

With this PR, the idea is to force all paragraphs with letter spacing to be rendered to a DOM element.

Fixes flutter/flutter#51234
Fixes flutter/flutter#71220
  • Loading branch information
mdebbar committed Sep 13, 2023
1 parent cd90cc8 commit b94b83b
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 18 deletions.
6 changes: 6 additions & 0 deletions lib/web_ui/lib/src/engine/text/canvas_paragraph.dart
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,12 @@ class CanvasParagraphBuilder implements ui.ParagraphBuilder {
return;
}

final double? letterSpacing = style.letterSpacing;
if (letterSpacing != null && letterSpacing != 0.0) {
_canDrawOnCanvas = false;
return;
}

final ui.TextDecoration? decoration = style.decoration;
if (decoration != null && decoration != ui.TextDecoration.none) {
_canDrawOnCanvas = false;
Expand Down
19 changes: 1 addition & 18 deletions lib/web_ui/lib/src/engine/text/paint_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import 'package:ui/ui.dart' as ui;

import '../dom.dart';
import '../html/bitmap_canvas.dart';
import '../html/painting.dart';
import 'canvas_paragraph.dart';
Expand Down Expand Up @@ -78,23 +77,7 @@ class TextPaintService {
final EngineTextStyle style = fragment.style;

final String text = fragment.getText(paragraph);
final double? letterSpacing = style.letterSpacing;
if (letterSpacing == null || letterSpacing == 0.0) {
canvas.drawText(text, x, y,
style: style.foreground?.style, shadows: style.shadows);
} else {
// TODO(mdebbar): Implement letter-spacing on canvas more efficiently:
// https://github.com/flutter/flutter/issues/51234
double charX = x;
final int len = text.length;
for (int i = 0; i < len; i++) {
final String char = text[i];
canvas.drawText(char, charX.roundToDouble(), y,
style: style.foreground?.style,
shadows: style.shadows);
charX += letterSpacing + canvas.measureText(char).width!;
}
}
canvas.drawText(text, x, y, style: style.foreground?.style, shadows: style.shadows);

canvas.tearDownPaint();
}
Expand Down
22 changes: 22 additions & 0 deletions lib/web_ui/test/html/paragraph/general_golden_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,28 @@ Future<void> testMain() async {
return takeScreenshot(canvas, bounds, 'canvas_paragraph_letter_spacing');
});

test('letter-spacing Thai', () {
final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy());

const String yes = '\u0e43\u0e0a\u0e48';
const String no = '\u0e44\u0e21\u0e48';

final CanvasParagraph paragraph = rich(
EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 36),
(CanvasParagraphBuilder builder) {
builder.pushStyle(EngineTextStyle.only(color: blue));
builder.addText('$yes $no ');
builder.pushStyle(EngineTextStyle.only(color: green, letterSpacing: 1));
builder.addText('$yes $no ');
builder.pushStyle(EngineTextStyle.only(color: red, letterSpacing: 3));
builder.addText('$yes $no');
},
)..layout(constrain(double.infinity));
canvas.drawParagraph(paragraph, const Offset(20, 20));

return takeScreenshot(canvas, bounds, 'canvas_paragraph_letter_spacing_thai');
});

test('draws text decorations', () {
final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy());
final List<TextDecorationStyle> decorationStyles = <TextDecorationStyle>[
Expand Down

0 comments on commit b94b83b

Please sign in to comment.