From e2d3f2d663c1b55ccb1d478522b203374bc568cc Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Thu, 22 Jun 2023 13:58:47 -0700 Subject: [PATCH 1/7] Add ParagraphStyle.applyRoundingHack for SkParagraph --- lib/ui/text.dart | 6 +++- lib/ui/text/paragraph_builder.cc | 4 ++- .../src/engine/canvaskit/canvaskit_api.dart | 2 ++ .../lib/src/engine/canvaskit/renderer.dart | 3 +- lib/web_ui/lib/src/engine/canvaskit/text.dart | 4 +++ lib/web_ui/lib/src/engine/html/renderer.dart | 4 ++- lib/web_ui/lib/src/engine/renderer.dart | 1 + .../engine/skwasm/skwasm_impl/paragraph.dart | 2 ++ .../raw/text/raw_paragraph_style.dart | 3 ++ .../engine/skwasm/skwasm_impl/renderer.dart | 4 ++- .../engine/skwasm/skwasm_stub/renderer.dart | 2 +- lib/web_ui/lib/text.dart | 2 ++ lib/web_ui/skwasm/text/paragraph_style.cpp | 5 +++ testing/dart/paragraph_test.dart | 34 +++++++++++++++++-- .../txt/src/skia/paragraph_builder_skia.cc | 1 + third_party/txt/src/txt/paragraph_style.h | 8 +++++ 16 files changed, 77 insertions(+), 8 deletions(-) diff --git a/lib/ui/text.dart b/lib/ui/text.dart index d163f3a078776..93d2c01f927f8 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -1762,6 +1762,7 @@ class ParagraphStyle { StrutStyle? strutStyle, String? ellipsis, Locale? locale, + bool applyRoundingHack = true, }) : _encoded = _encodeParagraphStyle( textAlign, textDirection, @@ -1782,6 +1783,7 @@ class ParagraphStyle { _strutStyle = strutStyle, _ellipsis = ellipsis, _locale = locale, + _applyRoundingHack = applyRoundingHack, _leadingDistribution = textHeightBehavior?.leadingDistribution ?? TextLeadingDistribution.proportional; final Int32List _encoded; @@ -1792,6 +1794,7 @@ class ParagraphStyle { final String? _ellipsis; final Locale? _locale; final TextLeadingDistribution _leadingDistribution; + final bool _applyRoundingHack; @override bool operator ==(Object other) { @@ -1809,11 +1812,12 @@ class ParagraphStyle { && other._ellipsis == _ellipsis && other._locale == _locale && other._leadingDistribution == _leadingDistribution + && other._applyRoundingHack == _applyRoundingHack && _listEquals(other._encoded, _encoded); } @override - int get hashCode => Object.hash(Object.hashAll(_encoded), _fontFamily, _fontSize, _height, _ellipsis, _locale, _leadingDistribution); + int get hashCode => Object.hash(Object.hashAll(_encoded), _fontFamily, _fontSize, _height, _ellipsis, _locale, _leadingDistribution, _applyRoundingHack); @override String toString() { diff --git a/lib/ui/text/paragraph_builder.cc b/lib/ui/text/paragraph_builder.cc index 3857a10572d62..5740ead7926c0 100644 --- a/lib/ui/text/paragraph_builder.cc +++ b/lib/ui/text/paragraph_builder.cc @@ -230,7 +230,8 @@ ParagraphBuilder::ParagraphBuilder( double fontSize, double height, const std::u16string& ellipsis, - const std::string& locale) { + const std::string& locale, + bool applyRoundingHack) { int32_t mask = 0; txt::ParagraphStyle style; { @@ -291,6 +292,7 @@ ParagraphBuilder::ParagraphBuilder( if (mask & kPSLocaleMask) { style.locale = locale; } + style.apply_rounding_hack = applyRoundingHack; FontCollection& font_collection = UIDartState::Current() ->platform_configuration() diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart index 4aa608f4e23c9..f486eaed0a301 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart @@ -2722,6 +2722,8 @@ extension SkParagraphStylePropertiesExtension on SkParagraphStyleProperties { @JS('replaceTabCharacters') external set _replaceTabCharacters(JSBoolean? bool); set replaceTabCharacters(bool? bool) => _replaceTabCharacters = bool?.toJS; + + external set applyRoundingHack(bool applyRoundingHack); } @JS() diff --git a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart index 834446ef26d82..4ba883274ce1a 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart @@ -314,7 +314,8 @@ class CanvasKitRenderer implements Renderer { ui.FontStyle? fontStyle, ui.StrutStyle? strutStyle, String? ellipsis, - ui.Locale? locale + ui.Locale? locale, + bool applyRoundingHack = true, }) => CkParagraphStyle( textAlign: textAlign, textDirection: textDirection, diff --git a/lib/web_ui/lib/src/engine/canvaskit/text.dart b/lib/web_ui/lib/src/engine/canvaskit/text.dart index f177031d6afcc..f8f3a4b33fa84 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/text.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/text.dart @@ -32,6 +32,7 @@ class CkParagraphStyle implements ui.ParagraphStyle { ui.StrutStyle? strutStyle, String? ellipsis, ui.Locale? locale, + bool applyRoundingHack = true, }) : skParagraphStyle = toSkParagraphStyle( textAlign, textDirection, @@ -45,6 +46,7 @@ class CkParagraphStyle implements ui.ParagraphStyle { strutStyle, ellipsis, locale, + applyRoundingHack, ), _fontFamily = _effectiveFontFamily(fontFamily), _fontSize = fontSize, @@ -144,6 +146,7 @@ class CkParagraphStyle implements ui.ParagraphStyle { ui.StrutStyle? strutStyle, String? ellipsis, ui.Locale? locale, + bool applyRoundingHack, ) { final SkParagraphStyleProperties properties = SkParagraphStyleProperties(); @@ -180,6 +183,7 @@ class CkParagraphStyle implements ui.ParagraphStyle { properties.replaceTabCharacters = true; properties.textStyle = toSkTextStyleProperties( fontFamily, fontSize, height, fontWeight, fontStyle); + properties.applyRoundingHack = applyRoundingHack; return canvasKit.ParagraphStyle(properties); } diff --git a/lib/web_ui/lib/src/engine/html/renderer.dart b/lib/web_ui/lib/src/engine/html/renderer.dart index 728c8bc367ce2..c7ad2c515466f 100644 --- a/lib/web_ui/lib/src/engine/html/renderer.dart +++ b/lib/web_ui/lib/src/engine/html/renderer.dart @@ -283,7 +283,9 @@ class HtmlRenderer implements Renderer { ui.FontStyle? fontStyle, ui.StrutStyle? strutStyle, String? ellipsis, - ui.Locale? locale + ui.Locale? locale, + // Not used: the HTML renderer does not use SkParagraph. + bool applyRoundingHack = true, }) => EngineParagraphStyle( textAlign: textAlign, textDirection: textDirection, diff --git a/lib/web_ui/lib/src/engine/renderer.dart b/lib/web_ui/lib/src/engine/renderer.dart index 20fd055b1c13d..7309e7ee3cf48 100644 --- a/lib/web_ui/lib/src/engine/renderer.dart +++ b/lib/web_ui/lib/src/engine/renderer.dart @@ -210,6 +210,7 @@ abstract class Renderer { ui.StrutStyle? strutStyle, String? ellipsis, ui.Locale? locale, + bool applyRoundingHack = true, }); ui.StrutStyle createStrutStyle({ diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart index c00c1bcac9b17..061eb1abded07 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart @@ -475,6 +475,7 @@ class SkwasmParagraphStyle extends SkwasmObjectWrapper implem ui.StrutStyle? strutStyle, String? ellipsis, ui.Locale? locale, + bool applyRoundingHack = true, }) { final ParagraphStyleHandle handle = paragraphStyleCreate(); if (textAlign != null) { @@ -535,6 +536,7 @@ class SkwasmParagraphStyle extends SkwasmObjectWrapper implem skStringFree(localeHandle); } paragraphStyleSetTextStyle(handle, textStyleHandle); + paragraphStyleSetApplyRoundingHack(handle, applyRoundingHack); return SkwasmParagraphStyle._(handle, textStyle, fontFamily); } diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph_style.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph_style.dart index e65a5f7690066..77a7d64328638 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph_style.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph_style.dart @@ -49,3 +49,6 @@ external void paragraphStyleSetStrutStyle(ParagraphStyleHandle handle, StrutStyl @Native(symbol: 'paragraphStyle_setTextStyle', isLeaf: true) external void paragraphStyleSetTextStyle(ParagraphStyleHandle handle, TextStyleHandle textStyle); + +@Native(symbol: 'paragraphStyle_setApplyRoundingHack', isLeaf: true) +external void paragraphStyleSetApplyRoundingHack(ParagraphStyleHandle handle, bool applyRoundingHack); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart index e8228815ac806..3e57596613230 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart @@ -154,7 +154,8 @@ class SkwasmRenderer implements Renderer { ui.FontStyle? fontStyle, ui.StrutStyle? strutStyle, String? ellipsis, - ui.Locale? locale + ui.Locale? locale, + bool applyRoundingHack = true, }) => SkwasmParagraphStyle( textAlign: textAlign, textDirection: textDirection, @@ -168,6 +169,7 @@ class SkwasmRenderer implements Renderer { strutStyle: strutStyle, ellipsis: ellipsis, locale: locale, + applyRoundingHack: applyRoundingHack, ); @override diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart index 3d4823f49cc2c..14a2e511b139d 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart @@ -77,7 +77,7 @@ class SkwasmRenderer implements Renderer { } @override - ui.ParagraphStyle createParagraphStyle({ui.TextAlign? textAlign, ui.TextDirection? textDirection, int? maxLines, String? fontFamily, double? fontSize, double? height, ui.TextHeightBehavior? textHeightBehavior, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, ui.StrutStyle? strutStyle, String? ellipsis, ui.Locale? locale}) { + ui.ParagraphStyle createParagraphStyle({ui.TextAlign? textAlign, ui.TextDirection? textDirection, int? maxLines, String? fontFamily, double? fontSize, double? height, ui.TextHeightBehavior? textHeightBehavior, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, ui.StrutStyle? strutStyle, String? ellipsis, ui.Locale? locale, bool applyRoundingHack = true}) { throw UnimplementedError('Skwasm not implemented on this platform.'); } diff --git a/lib/web_ui/lib/text.dart b/lib/web_ui/lib/text.dart index 491966a0f88c2..f91a26029c827 100644 --- a/lib/web_ui/lib/text.dart +++ b/lib/web_ui/lib/text.dart @@ -380,6 +380,7 @@ abstract class ParagraphStyle { StrutStyle? strutStyle, String? ellipsis, Locale? locale, + bool applyRoundingHack = true, }) => engine.renderer.createParagraphStyle( textAlign: textAlign, textDirection: textDirection, @@ -393,6 +394,7 @@ abstract class ParagraphStyle { strutStyle: strutStyle, ellipsis: ellipsis, locale: locale, + applyRoundingHack: applyRoundingHack, ); } diff --git a/lib/web_ui/skwasm/text/paragraph_style.cpp b/lib/web_ui/skwasm/text/paragraph_style.cpp index 9a9dc82fac616..a319287bebeba 100644 --- a/lib/web_ui/skwasm/text/paragraph_style.cpp +++ b/lib/web_ui/skwasm/text/paragraph_style.cpp @@ -78,3 +78,8 @@ SKWASM_EXPORT void paragraphStyle_setTextStyle(ParagraphStyle* style, TextStyle* textStyle) { style->setTextStyle(*textStyle); } + +SKWASM_EXPORT void paragraphStyle_setApplyRoundingHack(ParagraphStyle* style, + bool applyRoundingHack) { + style->setApplyRoundingHack(applyRoundingHack); +} diff --git a/testing/dart/paragraph_test.dart b/testing/dart/paragraph_test.dart index 8c8e865770c59..48f6eb4b631e5 100644 --- a/testing/dart/paragraph_test.dart +++ b/testing/dart/paragraph_test.dart @@ -27,8 +27,7 @@ void main() { expect(paragraph.minIntrinsicWidth, closeTo(fontSize * 4.0, 0.001)); expect(paragraph.maxIntrinsicWidth, closeTo(fontSize * 4.0, 0.001)); expect(paragraph.alphabeticBaseline, closeTo(fontSize * .8, 0.001)); - expect( - paragraph.ideographicBaseline, + expect(paragraph.ideographicBaseline, closeTo(paragraph.alphabeticBaseline * kAhemBaselineRatio, 0.001), ); } @@ -233,4 +232,35 @@ void main() { expect(callback, throwsStateError); } }); + + test('applyRoundingHack defaults to true', () { + const double fontSize = 1.25; + const String text = '12345'; + assert((fontSize * text.length).truncate() != fontSize * text.length); + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(fontSize: fontSize)); + builder.addText('test'); + final Paragraph paragraph = builder.build() + ..layout(const ParagraphConstraints(width: text.length * fontSize)); + + expect(paragraph.maxIntrinsicWidth, greaterThan(text.length * fontSize)); + expect(paragraph.computeLineMetrics(), hasLength(2)); + }); + + test('applyRoundingHack works', () { + const double fontSize = 1.25; + const String text = '12345'; + assert((fontSize * text.length).truncate() != fontSize * text.length); + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(fontSize: fontSize, applyRoundingHack: false)); + builder.addText('test'); + final Paragraph paragraph = builder.build() + ..layout(const ParagraphConstraints(width: text.length * fontSize)); + + expect(paragraph.maxIntrinsicWidth, text.length * fontSize); + switch (paragraph.computeLineMetrics()) { + case [LineMetrics(width: final double width)]: + expect(width, text.length * fontSize); + case final List metrics: + expect(metrics, hasLength(1)); + } + }); } diff --git a/third_party/txt/src/skia/paragraph_builder_skia.cc b/third_party/txt/src/skia/paragraph_builder_skia.cc index 96d98f3f4c29f..641725ec6ccfb 100644 --- a/third_party/txt/src/skia/paragraph_builder_skia.cc +++ b/third_party/txt/src/skia/paragraph_builder_skia.cc @@ -138,6 +138,7 @@ skt::ParagraphStyle ParagraphBuilderSkia::TxtToSkia(const ParagraphStyle& txt) { skia.turnHintingOff(); skia.setReplaceTabCharacters(true); + skia.setApplyRoundingHack(txt.apply_rounding_hack); return skia; } diff --git a/third_party/txt/src/txt/paragraph_style.h b/third_party/txt/src/txt/paragraph_style.h index 7c3043b8ad899..e1915323a0bd3 100644 --- a/third_party/txt/src/txt/paragraph_style.h +++ b/third_party/txt/src/txt/paragraph_style.h @@ -95,6 +95,14 @@ class ParagraphStyle { std::u16string ellipsis; std::string locale; + // Temporary flag that indicates whether the Paragraph should report its + // metrics with rounding hacks applied. + // + // This flag currently defaults to true and will be flipped to false once the + // migration is complete. + // TODO(LongCatIsLooong): https://github.com/flutter/flutter/issues/31707 + bool apply_rounding_hack = true; + TextStyle GetTextStyle() const; bool unlimited_lines() const; From 997fc907a37cccdf8d0907508a61159471b662f4 Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Thu, 22 Jun 2023 17:42:24 -0700 Subject: [PATCH 2/7] dart:ui non-web --- lib/ui/dart_ui.cc | 2 +- lib/ui/text.dart | 8 +++++--- lib/ui/text/paragraph_builder.cc | 5 +++-- lib/ui/text/paragraph_builder.h | 6 ++++-- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/ui/dart_ui.cc b/lib/ui/dart_ui.cc index 77b126d797346..efb5af0a30a69 100644 --- a/lib/ui/dart_ui.cc +++ b/lib/ui/dart_ui.cc @@ -78,7 +78,7 @@ typedef CanvasPath Path; V(Gradient::Create, 1) \ V(ImageFilter::Create, 1) \ V(ImageShader::Create, 1) \ - V(ParagraphBuilder::Create, 9) \ + V(ParagraphBuilder::Create, 10) \ V(PathMeasure::Create, 3) \ V(Path::Create, 1) \ V(PictureRecorder::Create, 1) \ diff --git a/lib/ui/text.dart b/lib/ui/text.dart index 93d2c01f927f8..4ed14b42919bb 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -3135,11 +3135,12 @@ base class _NativeParagraphBuilder extends NativeFieldWrapperClass1 implements P style._fontSize ?? 0, style._height ?? 0, style._ellipsis ?? '', - _encodeLocale(style._locale) + _encodeLocale(style._locale), + style._applyRoundingHack, ); } - @Native(symbol: 'ParagraphBuilder::Create') + @Native(symbol: 'ParagraphBuilder::Create') external void _constructor( Int32List encoded, ByteData? strutData, @@ -3148,7 +3149,8 @@ base class _NativeParagraphBuilder extends NativeFieldWrapperClass1 implements P double fontSize, double height, String ellipsis, - String locale); + String locale, + bool applyRoundingHack); @override int get placeholderCount => _placeholderCount; diff --git a/lib/ui/text/paragraph_builder.cc b/lib/ui/text/paragraph_builder.cc index 5740ead7926c0..eb01511772d49 100644 --- a/lib/ui/text/paragraph_builder.cc +++ b/lib/ui/text/paragraph_builder.cc @@ -151,11 +151,12 @@ void ParagraphBuilder::Create(Dart_Handle wrapper, double fontSize, double height, const std::u16string& ellipsis, - const std::string& locale) { + const std::string& locale, + bool applyRoundingHack) { UIDartState::ThrowIfUIOperationsProhibited(); auto res = fml::MakeRefCounted( encoded_handle, strutData, fontFamily, strutFontFamilies, fontSize, - height, ellipsis, locale); + height, ellipsis, locale, applyRoundingHack); res->AssociateWithDartWrapper(wrapper); } diff --git a/lib/ui/text/paragraph_builder.h b/lib/ui/text/paragraph_builder.h index 0018f969da68f..6fc7c58ad97aa 100644 --- a/lib/ui/text/paragraph_builder.h +++ b/lib/ui/text/paragraph_builder.h @@ -30,7 +30,8 @@ class ParagraphBuilder : public RefCountedDartWrappable { double fontSize, double height, const std::u16string& ellipsis, - const std::string& locale); + const std::string& locale, + bool applyRoundingHack); ~ParagraphBuilder() override; @@ -76,7 +77,8 @@ class ParagraphBuilder : public RefCountedDartWrappable { double fontSize, double height, const std::u16string& ellipsis, - const std::string& locale); + const std::string& locale, + bool applyRoundingHack); std::unique_ptr m_paragraphBuilder; }; From 6de46a37c829e17ac73bbfb9d9b4251a25eeb7ef Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Thu, 22 Jun 2023 18:34:16 -0700 Subject: [PATCH 3/7] fix tests --- testing/dart/paragraph_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/dart/paragraph_test.dart b/testing/dart/paragraph_test.dart index d174ad4652cb0..7fbe06406b41e 100644 --- a/testing/dart/paragraph_test.dart +++ b/testing/dart/paragraph_test.dart @@ -239,7 +239,7 @@ void main() { const String text = '12345'; assert((fontSize * text.length).truncate() != fontSize * text.length); final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(fontSize: fontSize)); - builder.addText('test'); + builder.addText(text); final Paragraph paragraph = builder.build() ..layout(const ParagraphConstraints(width: text.length * fontSize)); @@ -252,7 +252,7 @@ void main() { const String text = '12345'; assert((fontSize * text.length).truncate() != fontSize * text.length); final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(fontSize: fontSize, applyRoundingHack: false)); - builder.addText('test'); + builder.addText(text); final Paragraph paragraph = builder.build() ..layout(const ParagraphConstraints(width: text.length * fontSize)); From 43c80b938f6d8b29479ac42ac1d0d014045f0a45 Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Thu, 22 Jun 2023 18:40:02 -0700 Subject: [PATCH 4/7] tests --- .../lib/src/engine/canvaskit/renderer.dart | 1 + lib/web_ui/test/canvaskit/text_test.dart | 35 ++++++++++- testing/dart/paragraph_test.dart | 59 +++++++++---------- 3 files changed, 64 insertions(+), 31 deletions(-) diff --git a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart index 4ba883274ce1a..6ab2296300252 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart @@ -329,6 +329,7 @@ class CanvasKitRenderer implements Renderer { strutStyle: strutStyle, ellipsis: ellipsis, locale: locale, + applyRoundingHack: applyRoundingHack, ); @override diff --git a/lib/web_ui/test/canvaskit/text_test.dart b/lib/web_ui/test/canvaskit/text_test.dart index d14ad7b75e83d..ad8403e4526f7 100644 --- a/lib/web_ui/test/canvaskit/text_test.dart +++ b/lib/web_ui/test/canvaskit/text_test.dart @@ -122,7 +122,40 @@ void testMain() { } }); }); + + test('applyRoundingHack defaults to true', () { + const double fontSize = 1.25; + const String text = '12345'; + assert((fontSize * text.length).truncate() != fontSize * text.length); + final ui.ParagraphBuilder builder = ui.ParagraphBuilder( + ui.ParagraphStyle(fontSize: fontSize, fontFamily: 'FlutterTest'), + ); + builder.addText(text); + final ui.Paragraph paragraph = builder.build() + ..layout(const ui.ParagraphConstraints(width: text.length * fontSize)); + + expect(paragraph.computeLineMetrics(), hasLength(2)); + }); + + test('applyRoundingHack works', () { + const double fontSize = 1.25; + const String text = '12345'; + assert((fontSize * text.length).truncate() != fontSize * text.length); + final ui.ParagraphBuilder builder = ui.ParagraphBuilder( + ui.ParagraphStyle(fontSize: fontSize, fontFamily: 'FlutterTest', applyRoundingHack: false), + ); + builder.addText(text); + final ui.Paragraph paragraph = builder.build() + ..layout(const ui.ParagraphConstraints(width: text.length * fontSize)); + + expect(paragraph.maxIntrinsicWidth, text.length * fontSize); + switch (paragraph.computeLineMetrics()) { + case [ui.LineMetrics(width: final double width)]: + expect(width, text.length * fontSize); + case final List metrics: + expect(metrics, hasLength(1)); + } + }); // TODO(hterkelsen): https://github.com/flutter/flutter/issues/71520 }, skip: isSafari || isFirefox); - } diff --git a/testing/dart/paragraph_test.dart b/testing/dart/paragraph_test.dart index 7fbe06406b41e..c3d4ac6978adb 100644 --- a/testing/dart/paragraph_test.dart +++ b/testing/dart/paragraph_test.dart @@ -234,34 +234,33 @@ void main() { } }); - test('applyRoundingHack defaults to true', () { - const double fontSize = 1.25; - const String text = '12345'; - assert((fontSize * text.length).truncate() != fontSize * text.length); - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(fontSize: fontSize)); - builder.addText(text); - final Paragraph paragraph = builder.build() - ..layout(const ParagraphConstraints(width: text.length * fontSize)); - - expect(paragraph.maxIntrinsicWidth, greaterThan(text.length * fontSize)); - expect(paragraph.computeLineMetrics(), hasLength(2)); - }); - - test('applyRoundingHack works', () { - const double fontSize = 1.25; - const String text = '12345'; - assert((fontSize * text.length).truncate() != fontSize * text.length); - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(fontSize: fontSize, applyRoundingHack: false)); - builder.addText(text); - final Paragraph paragraph = builder.build() - ..layout(const ParagraphConstraints(width: text.length * fontSize)); - - expect(paragraph.maxIntrinsicWidth, text.length * fontSize); - switch (paragraph.computeLineMetrics()) { - case [LineMetrics(width: final double width)]: - expect(width, text.length * fontSize); - case final List metrics: - expect(metrics, hasLength(1)); - } - }); + test('applyRoundingHack defaults to true', () { + const double fontSize = 1.25; + const String text = '12345'; + assert((fontSize * text.length).truncate() != fontSize * text.length); + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(fontSize: fontSize)); + builder.addText(text); + final Paragraph paragraph = builder.build() + ..layout(const ParagraphConstraints(width: text.length * fontSize)); + + expect(paragraph.computeLineMetrics(), hasLength(2)); + }); + + test('applyRoundingHack works', () { + const double fontSize = 1.25; + const String text = '12345'; + assert((fontSize * text.length).truncate() != fontSize * text.length); + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(fontSize: fontSize, applyRoundingHack: false)); + builder.addText(text); + final Paragraph paragraph = builder.build() + ..layout(const ParagraphConstraints(width: text.length * fontSize)); + + expect(paragraph.maxIntrinsicWidth, text.length * fontSize); + switch (paragraph.computeLineMetrics()) { + case [LineMetrics(width: final double width)]: + expect(width, text.length * fontSize); + case final List metrics: + expect(metrics, hasLength(1)); + } + }); } From 622fffae20d9fa301432a344c8d66562ef8de5b1 Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Fri, 23 Jun 2023 11:45:33 -0700 Subject: [PATCH 5/7] Merge remote-tracking branch 'upstream/main' into apply-rounding-hack-plumbing --- lib/ui/text.dart | 24 ++++++++++++++++++- .../lib/src/engine/canvaskit/renderer.dart | 3 +-- lib/web_ui/lib/src/engine/html/renderer.dart | 2 -- lib/web_ui/lib/src/engine/renderer.dart | 1 - .../engine/skwasm/skwasm_impl/paragraph.dart | 2 -- .../raw/text/raw_paragraph_style.dart | 3 --- .../engine/skwasm/skwasm_impl/renderer.dart | 2 -- .../engine/skwasm/skwasm_stub/renderer.dart | 2 +- lib/web_ui/lib/text.dart | 15 ++++++++++-- lib/web_ui/skwasm/text/paragraph_style.cpp | 5 ---- lib/web_ui/test/canvaskit/text_test.dart | 19 ++++----------- testing/dart/paragraph_test.dart | 17 ++++--------- 12 files changed, 46 insertions(+), 49 deletions(-) diff --git a/lib/ui/text.dart b/lib/ui/text.dart index 4ed14b42919bb..0d3a586c877a8 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -2801,7 +2801,7 @@ abstract class Paragraph { /// This only returns a valid value if asserts are enabled, and must not be /// used otherwise. bool get debugDisposed; - } +} @pragma('vm:entry-point') base class _NativeParagraph extends NativeFieldWrapperClass1 implements Paragraph { @@ -3018,6 +3018,28 @@ abstract class ParagraphBuilder { /// [Paragraph]. factory ParagraphBuilder(ParagraphStyle style) = _NativeParagraphBuilder; + /// Whether the rounding hack enabled by default in SkParagraph and TextPainter + /// is disabled. + /// + /// Do not rely on this getter as it exists for migration purposes only and + /// will soon be removed. + static bool get shouldDisableRoundingHack { + return const bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') + || _roundingHackDisabledInDebugMode; + } + static bool _roundingHackDisabledInDebugMode = true; + + /// Only works in debug mode. Do not call this method as it is for migration + /// purposes only and will soon be removed. + static void setDisableRoundingHack(bool disableRoundingHack) { + // bool.hasEnvironment does not work in internal tests so an additional flag + // is needed for tests. + assert(() { + _roundingHackDisabledInDebugMode = disableRoundingHack; + return true; + }()); + } + /// The number of placeholders currently in the paragraph. int get placeholderCount; diff --git a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart index 6ab2296300252..045a73d935531 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart @@ -315,7 +315,6 @@ class CanvasKitRenderer implements Renderer { ui.StrutStyle? strutStyle, String? ellipsis, ui.Locale? locale, - bool applyRoundingHack = true, }) => CkParagraphStyle( textAlign: textAlign, textDirection: textDirection, @@ -329,7 +328,7 @@ class CanvasKitRenderer implements Renderer { strutStyle: strutStyle, ellipsis: ellipsis, locale: locale, - applyRoundingHack: applyRoundingHack, + applyRoundingHack: !ui.ParagraphBuilder.shouldDisableRoundingHack, ); @override diff --git a/lib/web_ui/lib/src/engine/html/renderer.dart b/lib/web_ui/lib/src/engine/html/renderer.dart index c7ad2c515466f..75818266d1f7e 100644 --- a/lib/web_ui/lib/src/engine/html/renderer.dart +++ b/lib/web_ui/lib/src/engine/html/renderer.dart @@ -284,8 +284,6 @@ class HtmlRenderer implements Renderer { ui.StrutStyle? strutStyle, String? ellipsis, ui.Locale? locale, - // Not used: the HTML renderer does not use SkParagraph. - bool applyRoundingHack = true, }) => EngineParagraphStyle( textAlign: textAlign, textDirection: textDirection, diff --git a/lib/web_ui/lib/src/engine/renderer.dart b/lib/web_ui/lib/src/engine/renderer.dart index 7309e7ee3cf48..20fd055b1c13d 100644 --- a/lib/web_ui/lib/src/engine/renderer.dart +++ b/lib/web_ui/lib/src/engine/renderer.dart @@ -210,7 +210,6 @@ abstract class Renderer { ui.StrutStyle? strutStyle, String? ellipsis, ui.Locale? locale, - bool applyRoundingHack = true, }); ui.StrutStyle createStrutStyle({ diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart index 061eb1abded07..c00c1bcac9b17 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart @@ -475,7 +475,6 @@ class SkwasmParagraphStyle extends SkwasmObjectWrapper implem ui.StrutStyle? strutStyle, String? ellipsis, ui.Locale? locale, - bool applyRoundingHack = true, }) { final ParagraphStyleHandle handle = paragraphStyleCreate(); if (textAlign != null) { @@ -536,7 +535,6 @@ class SkwasmParagraphStyle extends SkwasmObjectWrapper implem skStringFree(localeHandle); } paragraphStyleSetTextStyle(handle, textStyleHandle); - paragraphStyleSetApplyRoundingHack(handle, applyRoundingHack); return SkwasmParagraphStyle._(handle, textStyle, fontFamily); } diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph_style.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph_style.dart index 77a7d64328638..e65a5f7690066 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph_style.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph_style.dart @@ -49,6 +49,3 @@ external void paragraphStyleSetStrutStyle(ParagraphStyleHandle handle, StrutStyl @Native(symbol: 'paragraphStyle_setTextStyle', isLeaf: true) external void paragraphStyleSetTextStyle(ParagraphStyleHandle handle, TextStyleHandle textStyle); - -@Native(symbol: 'paragraphStyle_setApplyRoundingHack', isLeaf: true) -external void paragraphStyleSetApplyRoundingHack(ParagraphStyleHandle handle, bool applyRoundingHack); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart index 3e57596613230..c31228dd5cd43 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart @@ -155,7 +155,6 @@ class SkwasmRenderer implements Renderer { ui.StrutStyle? strutStyle, String? ellipsis, ui.Locale? locale, - bool applyRoundingHack = true, }) => SkwasmParagraphStyle( textAlign: textAlign, textDirection: textDirection, @@ -169,7 +168,6 @@ class SkwasmRenderer implements Renderer { strutStyle: strutStyle, ellipsis: ellipsis, locale: locale, - applyRoundingHack: applyRoundingHack, ); @override diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart index 14a2e511b139d..3d4823f49cc2c 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart @@ -77,7 +77,7 @@ class SkwasmRenderer implements Renderer { } @override - ui.ParagraphStyle createParagraphStyle({ui.TextAlign? textAlign, ui.TextDirection? textDirection, int? maxLines, String? fontFamily, double? fontSize, double? height, ui.TextHeightBehavior? textHeightBehavior, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, ui.StrutStyle? strutStyle, String? ellipsis, ui.Locale? locale, bool applyRoundingHack = true}) { + ui.ParagraphStyle createParagraphStyle({ui.TextAlign? textAlign, ui.TextDirection? textDirection, int? maxLines, String? fontFamily, double? fontSize, double? height, ui.TextHeightBehavior? textHeightBehavior, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, ui.StrutStyle? strutStyle, String? ellipsis, ui.Locale? locale}) { throw UnimplementedError('Skwasm not implemented on this platform.'); } diff --git a/lib/web_ui/lib/text.dart b/lib/web_ui/lib/text.dart index f91a26029c827..dea0c720db9ec 100644 --- a/lib/web_ui/lib/text.dart +++ b/lib/web_ui/lib/text.dart @@ -380,7 +380,6 @@ abstract class ParagraphStyle { StrutStyle? strutStyle, String? ellipsis, Locale? locale, - bool applyRoundingHack = true, }) => engine.renderer.createParagraphStyle( textAlign: textAlign, textDirection: textDirection, @@ -394,7 +393,6 @@ abstract class ParagraphStyle { strutStyle: strutStyle, ellipsis: ellipsis, locale: locale, - applyRoundingHack: applyRoundingHack, ); } @@ -687,6 +685,19 @@ abstract class Paragraph { abstract class ParagraphBuilder { factory ParagraphBuilder(ParagraphStyle style) => engine.renderer.createParagraphBuilder(style); + + static bool get shouldDisableRoundingHack { + return const bool.hasEnvironment('SKPARAGRAPH_REMOVE_ROUNDING_HACK') + || _roundingHackDisabledInDebugMode; + } + static bool _roundingHackDisabledInDebugMode = true; + static void setDisableRoundingHack(bool disableRoundingHack) { + assert(() { + _roundingHackDisabledInDebugMode = disableRoundingHack; + return true; + }()); + } + void pushStyle(TextStyle style); void pop(); void addText(String text); diff --git a/lib/web_ui/skwasm/text/paragraph_style.cpp b/lib/web_ui/skwasm/text/paragraph_style.cpp index a319287bebeba..9a9dc82fac616 100644 --- a/lib/web_ui/skwasm/text/paragraph_style.cpp +++ b/lib/web_ui/skwasm/text/paragraph_style.cpp @@ -78,8 +78,3 @@ SKWASM_EXPORT void paragraphStyle_setTextStyle(ParagraphStyle* style, TextStyle* textStyle) { style->setTextStyle(*textStyle); } - -SKWASM_EXPORT void paragraphStyle_setApplyRoundingHack(ParagraphStyle* style, - bool applyRoundingHack) { - style->setApplyRoundingHack(applyRoundingHack); -} diff --git a/lib/web_ui/test/canvaskit/text_test.dart b/lib/web_ui/test/canvaskit/text_test.dart index ad8403e4526f7..e9f4797fb498c 100644 --- a/lib/web_ui/test/canvaskit/text_test.dart +++ b/lib/web_ui/test/canvaskit/text_test.dart @@ -123,26 +123,14 @@ void testMain() { }); }); - test('applyRoundingHack defaults to true', () { - const double fontSize = 1.25; - const String text = '12345'; - assert((fontSize * text.length).truncate() != fontSize * text.length); - final ui.ParagraphBuilder builder = ui.ParagraphBuilder( - ui.ParagraphStyle(fontSize: fontSize, fontFamily: 'FlutterTest'), - ); - builder.addText(text); - final ui.Paragraph paragraph = builder.build() - ..layout(const ui.ParagraphConstraints(width: text.length * fontSize)); - - expect(paragraph.computeLineMetrics(), hasLength(2)); - }); - test('applyRoundingHack works', () { const double fontSize = 1.25; const String text = '12345'; assert((fontSize * text.length).truncate() != fontSize * text.length); + final bool roundingHackWasDisabled = ui.ParagraphBuilder.shouldDisableRoundingHack; + ui.ParagraphBuilder.setDisableRoundingHack(true); final ui.ParagraphBuilder builder = ui.ParagraphBuilder( - ui.ParagraphStyle(fontSize: fontSize, fontFamily: 'FlutterTest', applyRoundingHack: false), + ui.ParagraphStyle(fontSize: fontSize, fontFamily: 'FlutterTest'), ); builder.addText(text); final ui.Paragraph paragraph = builder.build() @@ -155,6 +143,7 @@ void testMain() { case final List metrics: expect(metrics, hasLength(1)); } + ui.ParagraphBuilder.setDisableRoundingHack(roundingHackWasDisabled); }); // TODO(hterkelsen): https://github.com/flutter/flutter/issues/71520 }, skip: isSafari || isFirefox); diff --git a/testing/dart/paragraph_test.dart b/testing/dart/paragraph_test.dart index c3d4ac6978adb..ec8a30cd1cad8 100644 --- a/testing/dart/paragraph_test.dart +++ b/testing/dart/paragraph_test.dart @@ -234,22 +234,12 @@ void main() { } }); - test('applyRoundingHack defaults to true', () { - const double fontSize = 1.25; - const String text = '12345'; - assert((fontSize * text.length).truncate() != fontSize * text.length); - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(fontSize: fontSize)); - builder.addText(text); - final Paragraph paragraph = builder.build() - ..layout(const ParagraphConstraints(width: text.length * fontSize)); - - expect(paragraph.computeLineMetrics(), hasLength(2)); - }); - - test('applyRoundingHack works', () { + test('disableRoundingHack works', () { const double fontSize = 1.25; const String text = '12345'; assert((fontSize * text.length).truncate() != fontSize * text.length); + final bool roundingHackWasDisabled = ParagraphBuilder.shouldDisableRoundingHack; + ParagraphBuilder.setDisableRoundingHack(true); final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(fontSize: fontSize, applyRoundingHack: false)); builder.addText(text); final Paragraph paragraph = builder.build() @@ -262,5 +252,6 @@ void main() { case final List metrics: expect(metrics, hasLength(1)); } + ParagraphBuilder.setDisableRoundingHack(roundingHackWasDisabled); }); } From 75ce8aeabe52095a7559a6f540312a176c110b65 Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Fri, 23 Jun 2023 11:47:45 -0700 Subject: [PATCH 6/7] remove additional changes --- lib/ui/text.dart | 8 ++------ lib/web_ui/lib/src/engine/canvaskit/renderer.dart | 2 +- lib/web_ui/lib/src/engine/html/renderer.dart | 2 +- .../lib/src/engine/skwasm/skwasm_impl/renderer.dart | 2 +- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/ui/text.dart b/lib/ui/text.dart index 0d3a586c877a8..c109c420b2343 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -1762,7 +1762,6 @@ class ParagraphStyle { StrutStyle? strutStyle, String? ellipsis, Locale? locale, - bool applyRoundingHack = true, }) : _encoded = _encodeParagraphStyle( textAlign, textDirection, @@ -1783,7 +1782,6 @@ class ParagraphStyle { _strutStyle = strutStyle, _ellipsis = ellipsis, _locale = locale, - _applyRoundingHack = applyRoundingHack, _leadingDistribution = textHeightBehavior?.leadingDistribution ?? TextLeadingDistribution.proportional; final Int32List _encoded; @@ -1794,7 +1792,6 @@ class ParagraphStyle { final String? _ellipsis; final Locale? _locale; final TextLeadingDistribution _leadingDistribution; - final bool _applyRoundingHack; @override bool operator ==(Object other) { @@ -1812,12 +1809,11 @@ class ParagraphStyle { && other._ellipsis == _ellipsis && other._locale == _locale && other._leadingDistribution == _leadingDistribution - && other._applyRoundingHack == _applyRoundingHack && _listEquals(other._encoded, _encoded); } @override - int get hashCode => Object.hash(Object.hashAll(_encoded), _fontFamily, _fontSize, _height, _ellipsis, _locale, _leadingDistribution, _applyRoundingHack); + int get hashCode => Object.hash(Object.hashAll(_encoded), _fontFamily, _fontSize, _height, _ellipsis, _locale, _leadingDistribution); @override String toString() { @@ -3158,7 +3154,7 @@ base class _NativeParagraphBuilder extends NativeFieldWrapperClass1 implements P style._height ?? 0, style._ellipsis ?? '', _encodeLocale(style._locale), - style._applyRoundingHack, + !ParagraphBuilder.shouldDisableRoundingHack, ); } diff --git a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart index 045a73d935531..96da491f69af4 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart @@ -314,7 +314,7 @@ class CanvasKitRenderer implements Renderer { ui.FontStyle? fontStyle, ui.StrutStyle? strutStyle, String? ellipsis, - ui.Locale? locale, + ui.Locale? locale }) => CkParagraphStyle( textAlign: textAlign, textDirection: textDirection, diff --git a/lib/web_ui/lib/src/engine/html/renderer.dart b/lib/web_ui/lib/src/engine/html/renderer.dart index 75818266d1f7e..728c8bc367ce2 100644 --- a/lib/web_ui/lib/src/engine/html/renderer.dart +++ b/lib/web_ui/lib/src/engine/html/renderer.dart @@ -283,7 +283,7 @@ class HtmlRenderer implements Renderer { ui.FontStyle? fontStyle, ui.StrutStyle? strutStyle, String? ellipsis, - ui.Locale? locale, + ui.Locale? locale }) => EngineParagraphStyle( textAlign: textAlign, textDirection: textDirection, diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart index c31228dd5cd43..e8228815ac806 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart @@ -154,7 +154,7 @@ class SkwasmRenderer implements Renderer { ui.FontStyle? fontStyle, ui.StrutStyle? strutStyle, String? ellipsis, - ui.Locale? locale, + ui.Locale? locale }) => SkwasmParagraphStyle( textAlign: textAlign, textDirection: textDirection, From 6215574dbebeaa8244b35c6efd0af4d1dc42feb4 Mon Sep 17 00:00:00 2001 From: LongCat is Looong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Fri, 23 Jun 2023 12:01:09 -0700 Subject: [PATCH 7/7] fix tests --- testing/dart/paragraph_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/dart/paragraph_test.dart b/testing/dart/paragraph_test.dart index ec8a30cd1cad8..075bfa2993c4f 100644 --- a/testing/dart/paragraph_test.dart +++ b/testing/dart/paragraph_test.dart @@ -240,7 +240,7 @@ void main() { assert((fontSize * text.length).truncate() != fontSize * text.length); final bool roundingHackWasDisabled = ParagraphBuilder.shouldDisableRoundingHack; ParagraphBuilder.setDisableRoundingHack(true); - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(fontSize: fontSize, applyRoundingHack: false)); + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(fontSize: fontSize)); builder.addText(text); final Paragraph paragraph = builder.build() ..layout(const ParagraphConstraints(width: text.length * fontSize));