Skip to content

Commit 221b049

Browse files
authored
Paint tab characters as a single space (flutter#34389)
This change makes Windows render tabs as a single space. Previously, tabs rendered differently depending on your platform: * Windows, Web - a tofu is rendered * iOS, Android, macOS, Linux - a single space is rendered Part of flutter/flutter#79153
1 parent e68a86c commit 221b049

File tree

2 files changed

+40
-0
lines changed

2 files changed

+40
-0
lines changed

testing/dart/canvas_test.dart

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,45 @@ void main() {
543543
}
544544
});
545545

546+
Future<Image> drawText(String text) {
547+
return toImage((Canvas canvas) {
548+
final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(
549+
fontFamily: 'RobotoSerif',
550+
fontStyle: FontStyle.normal,
551+
fontWeight: FontWeight.normal,
552+
fontSize: 15.0,
553+
));
554+
builder.pushStyle(TextStyle(color: const Color(0xFF0000FF)));
555+
builder.addText(text);
556+
557+
final Paragraph paragraph = builder.build();
558+
paragraph.layout(const ParagraphConstraints(width: 20 * 5.0));
559+
560+
canvas.drawParagraph(paragraph, Offset.zero);
561+
}, 100, 100);
562+
}
563+
564+
test('Canvas.drawParagraph renders tab as space instead of tofu', () async {
565+
// Skia renders a tofu if the font does not have a glyph for a character.
566+
// However, Flutter opts-in to a Skia feature to render tabs as a single space.
567+
// See: https://github.com/flutter/flutter/issues/79153
568+
final File file = File(path.join('flutter', 'testing', 'resources', 'RobotoSlab-VariableFont_wght.ttf'));
569+
final Uint8List fontData = await file.readAsBytes();
570+
await loadFontFromList(fontData, fontFamily: 'RobotoSerif');
571+
572+
// The backspace character, \b, does not have a corresponding glyph and is rendered as a tofu.
573+
final Image tabImage = await drawText('>\t<');
574+
final Image spaceImage = await drawText('> <');
575+
final Image tofuImage = await drawText('>\b<');
576+
577+
// The tab's image should be identical to the space's image but not the tofu's image.
578+
final bool tabToSpaceComparison = await fuzzyCompareImages(tabImage, spaceImage);
579+
final bool tabToTofuComparison = await fuzzyCompareImages(tabImage, tofuImage);
580+
581+
expect(tabToSpaceComparison, isTrue);
582+
expect(tabToTofuComparison, isFalse);
583+
});
584+
546585
Matcher closeToTransform(Float64List expected) => (dynamic v) {
547586
Expect.type<Float64List>(v);
548587
final Float64List value = v;

third_party/txt/src/skia/paragraph_builder_skia.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ skt::ParagraphStyle TxtToSkia(const ParagraphStyle& txt) {
7979
static_cast<skt::TextHeightBehavior>(txt.text_height_behavior));
8080

8181
skia.turnHintingOff();
82+
skia.setReplaceTabCharacters(true);
8283

8384
return skia;
8485
}

0 commit comments

Comments
 (0)