Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit e8b05bf

Browse files
authored
[web] Fix text rendering issue when paragraph style is ginormous (#24159)
1 parent 082dbd6 commit e8b05bf

File tree

5 files changed

+82
-21
lines changed

5 files changed

+82
-21
lines changed

lib/web_ui/dev/goldens_lock.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
repository: https://github.com/flutter/goldens.git
2-
revision: 6839c709f859a1abe50e6322dfee17a3a3817c5c
2+
revision: 92381b8ca48729e9b21c6b19dd39ed605a98d4ee

lib/web_ui/lib/src/engine/text/canvas_paragraph.dart

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ class CanvasParagraph implements EngineParagraph {
133133
domRenderer.createElement('p') as html.HtmlElement;
134134

135135
// 1. Set paragraph-level styles.
136-
_applyParagraphStyleToElement(element: rootElement, style: paragraphStyle);
136+
_applyNecessaryParagraphStyles(element: rootElement, style: paragraphStyle);
137137
final html.CssStyleDeclaration cssStyle = rootElement.style;
138138
cssStyle
139139
..position = 'absolute'
@@ -259,6 +259,31 @@ class CanvasParagraph implements EngineParagraph {
259259
}
260260
}
261261

262+
/// Applies a paragraph [style] to an [element], translating the properties to
263+
/// their corresponding CSS equivalents.
264+
///
265+
/// As opposed to [_applyParagraphStyleToElement], this method only applies
266+
/// styles that are necessary at the paragraph level. Other styles (e.g. font
267+
/// size) are always applied at the span level so they aren't needed at the
268+
/// paragraph level.
269+
void _applyNecessaryParagraphStyles({
270+
required html.HtmlElement element,
271+
required EngineParagraphStyle style,
272+
}) {
273+
final html.CssStyleDeclaration cssStyle = element.style;
274+
275+
if (style._textAlign != null) {
276+
cssStyle.textAlign = textAlignToCssValue(
277+
style._textAlign, style._textDirection ?? ui.TextDirection.ltr);
278+
}
279+
if (style._lineHeight != null) {
280+
cssStyle.lineHeight = '${style._lineHeight}';
281+
}
282+
if (style._textDirection != null) {
283+
cssStyle.direction = _textDirectionToCss(style._textDirection);
284+
}
285+
}
286+
262287
/// A common interface for all types of spans that make up a paragraph.
263288
///
264289
/// These spans are stored as a flat list in the paragraph object.

lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -334,15 +334,18 @@ void testMain() async {
334334
// Yellow text should be behind image and rectangle.
335335
// Cyan text should be above everything.
336336
test('Paints text above and below image', () async {
337+
// Use a non-Ahem font so that text is visible.
338+
debugEmulateFlutterTesterEnvironment = false;
337339
final RecordingCanvas rc =
338340
RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300));
339341
rc.save();
340342
Image testImage = createTestImage();
341343
double testWidth = testImage.width.toDouble();
342344
double testHeight = testImage.height.toDouble();
345+
final Color orange = Color(0xFFFF9800);
343346
final Paragraph paragraph1 = createTestParagraph(
344-
'should be below...............',
345-
color: Color(0xFFFFFF40));
347+
'Should be below below below below below',
348+
color: orange);
346349
paragraph1.layout(const ParagraphConstraints(width: 400.0));
347350
rc.drawParagraph(paragraph1, const Offset(20, 100));
348351
rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight),
@@ -352,14 +355,19 @@ void testMain() async {
352355
Paint()
353356
..strokeWidth = 3
354357
..color = Color(0xA0000000));
358+
final Color cyan = Color(0xFF0097A7);
355359
final Paragraph paragraph2 = createTestParagraph(
356-
'Should be above...............',
357-
color: Color(0xFF00FFFF));
360+
'Should be above above above above above',
361+
color: cyan);
358362
paragraph2.layout(const ParagraphConstraints(width: 400.0));
359363
rc.drawParagraph(paragraph2, const Offset(20, 150));
360364
rc.restore();
361-
await _checkScreenshot(rc, 'draw_text_composite_order_below',
362-
maxDiffRatePercent: 1.0);
365+
await _checkScreenshot(
366+
rc,
367+
'draw_text_composite_order_below',
368+
maxDiffRatePercent: 1.0,
369+
region: Rect.fromLTWH(0, 0, 350, 300),
370+
);
363371
});
364372

365373
// Creates a picture
@@ -723,7 +731,7 @@ HtmlImage createTestImage({int width = 100, int height = 50}) {
723731
Paragraph createTestParagraph(String text,
724732
{Color color = const Color(0xFF000000)}) {
725733
final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(
726-
fontFamily: 'Ahem',
734+
fontFamily: 'Roboto',
727735
fontStyle: FontStyle.normal,
728736
fontWeight: FontWeight.normal,
729737
fontSize: 14.0,

lib/web_ui/test/golden_tests/engine/canvas_paragraph/general_test.dart

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ void testMain() async {
151151
canvas.drawParagraph(paragraph, offset);
152152
offset = offset.translate(0, paragraph.height + 10);
153153

154-
return takeScreenshot(canvas, bounds, 'canvas_paragraph_align_dom');
154+
return takeScreenshot(canvas, bounds, 'canvas_paragraph_align_dom', maxDiffRatePercent: 0.3);
155155
});
156156

157157
void testAlignAndTransform(EngineCanvas canvas) {
@@ -174,8 +174,7 @@ void testMain() async {
174174
canvas.save();
175175
canvas.translate(offset.dx, offset.dy);
176176
canvas.rotate(math.pi / 4);
177-
final Rect rect =
178-
Rect.fromLTRB(0.0, 0.0, 150.0, paragraph.height);
177+
final Rect rect = Rect.fromLTRB(0.0, 0.0, 150.0, paragraph.height);
179178
canvas.drawRect(rect, SurfacePaintData()..color = black);
180179
canvas.drawParagraph(paragraph, Offset.zero);
181180
canvas.restore();
@@ -198,6 +197,35 @@ void testMain() async {
198197
return takeScreenshot(canvas, bounds, 'canvas_paragraph_align_transform_dom');
199198
});
200199

200+
void testGiantParagraphStyles(EngineCanvas canvas) {
201+
final CanvasParagraph paragraph = rich(
202+
ParagraphStyle(fontFamily: 'Roboto', fontSize: 80.0),
203+
(CanvasParagraphBuilder builder) {
204+
builder.pushStyle(EngineTextStyle.only(color: yellow, fontSize: 24.0));
205+
builder.addText('Lorem ');
206+
builder.pushStyle(EngineTextStyle.only(color: red, fontSize: 32.0));
207+
builder.addText('ipsum');
208+
},
209+
)..layout(constrain(double.infinity));
210+
final Rect rect = Rect.fromLTRB(0.0, 0.0, paragraph.maxIntrinsicWidth, paragraph.height);
211+
canvas.drawRect(rect, SurfacePaintData()..color = black);
212+
canvas.drawParagraph(paragraph, Offset.zero);
213+
}
214+
215+
test('giant paragraph style', () {
216+
const Rect bounds = Rect.fromLTWH(0, 0, 300, 200);
217+
final canvas = BitmapCanvas(bounds, RenderStrategy());
218+
testGiantParagraphStyles(canvas);
219+
return takeScreenshot(canvas, bounds, 'canvas_paragraph_giant_paragraph_style');
220+
});
221+
222+
test('giant paragraph style (DOM)', () {
223+
const Rect bounds = Rect.fromLTWH(0, 0, 300, 200);
224+
final canvas = DomCanvas(domRenderer.createElement('flt-picture'));
225+
testGiantParagraphStyles(canvas);
226+
return takeScreenshot(canvas, bounds, 'canvas_paragraph_giant_paragraph_style_dom');
227+
});
228+
201229
test('paints spans with varying heights/baselines', () {
202230
final canvas = BitmapCanvas(bounds, RenderStrategy());
203231

lib/web_ui/test/text/canvas_paragraph_builder_test.dart

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ String get defaultFontFamily {
2323
const String defaultColor = 'color: rgb(255, 0, 0);';
2424
const String defaultFontSize = 'font-size: 14px;';
2525
final String paragraphStyle =
26-
'$defaultFontFamily position: absolute; white-space: pre;';
26+
'position: absolute; white-space: pre;';
2727

2828
void main() {
2929
internalBootstrapBrowserTest(() => testMain);
@@ -50,7 +50,7 @@ void testMain() async {
5050
paragraph.layout(ParagraphConstraints(width: double.infinity));
5151
expect(
5252
paragraph.toDomElement().outerHtml,
53-
'<p style="font-size: 13px; $paragraphStyle">'
53+
'<p style="$paragraphStyle">'
5454
'<span style="$defaultColor font-size: 13px; $defaultFontFamily">'
5555
'Hello'
5656
'</span>'
@@ -61,7 +61,7 @@ void testMain() async {
6161
paragraph.layout(ParagraphConstraints(width: 39.0));
6262
expect(
6363
paragraph.toDomElement().outerHtml,
64-
'<p style="font-size: 13px; $paragraphStyle">'
64+
'<p style="$paragraphStyle">'
6565
'<span style="$defaultColor font-size: 13px; $defaultFontFamily">'
6666
'Hel<br>lo'
6767
'</span>'
@@ -168,7 +168,7 @@ void testMain() async {
168168
paragraph.layout(ParagraphConstraints(width: double.infinity));
169169
expect(
170170
paragraph.toDomElement().outerHtml,
171-
'<p style="line-height: 1.5; font-size: 13px; $paragraphStyle">'
171+
'<p style="line-height: 1.5; $paragraphStyle">'
172172
'<span style="$defaultColor line-height: 1.5; font-size: 9px; font-weight: bold; font-style: italic; $defaultFontFamily letter-spacing: 2px;">'
173173
'Hello'
174174
'</span>'
@@ -206,7 +206,7 @@ void testMain() async {
206206
paragraph.layout(ParagraphConstraints(width: double.infinity));
207207
expect(
208208
paragraph.toDomElement().outerHtml,
209-
'<p style="font-size: 13px; $paragraphStyle">'
209+
'<p style="$paragraphStyle">'
210210
'<span style="$defaultColor font-size: 13px; font-weight: bold; $defaultFontFamily">'
211211
'Hello'
212212
'</span>'
@@ -220,7 +220,7 @@ void testMain() async {
220220
paragraph.layout(ParagraphConstraints(width: 75.0));
221221
expect(
222222
paragraph.toDomElement().outerHtml,
223-
'<p style="font-size: 13px; $paragraphStyle width: 75px;">'
223+
'<p style="$paragraphStyle width: 75px;">'
224224
'<span style="$defaultColor font-size: 13px; font-weight: bold; $defaultFontFamily">'
225225
'Hello'
226226
'</span>'
@@ -271,7 +271,7 @@ void testMain() async {
271271
paragraph.layout(ParagraphConstraints(width: double.infinity));
272272
expect(
273273
paragraph.toDomElement().outerHtml,
274-
'<p style="font-size: 13px; $paragraphStyle">'
274+
'<p style="$paragraphStyle">'
275275
'<span style="$defaultColor line-height: 2; font-size: 13px; font-weight: bold; $defaultFontFamily">'
276276
'Hello'
277277
'</span>'
@@ -335,7 +335,7 @@ void testMain() async {
335335
paragraph.layout(ParagraphConstraints(width: double.infinity));
336336
expect(
337337
paragraph.toDomElement().outerHtml,
338-
'<p style="font-size: 13px; $paragraphStyle">'
338+
'<p style="$paragraphStyle">'
339339
'<span style="$defaultColor font-size: 13px; $defaultFontFamily">'
340340
'First<br>Second '
341341
'</span>'
@@ -349,7 +349,7 @@ void testMain() async {
349349
paragraph.layout(ParagraphConstraints(width: 180.0));
350350
expect(
351351
paragraph.toDomElement().outerHtml,
352-
'<p style="font-size: 13px; $paragraphStyle width: 180px;">'
352+
'<p style="$paragraphStyle width: 180px;">'
353353
'<span style="$defaultColor font-size: 13px; $defaultFontFamily">'
354354
'First<br>Second <br>'
355355
'</span>'

0 commit comments

Comments
 (0)