@@ -9,13 +9,16 @@ import '../embedder.dart';
99import '../html/bitmap_canvas.dart' ;
1010import '../profiler.dart' ;
1111import '../util.dart' ;
12+ import 'layout_fragmenter.dart' ;
1213import 'layout_service.dart' ;
1314import 'paint_service.dart' ;
1415import 'paragraph.dart' ;
1516import 'word_breaker.dart' ;
1617
1718const ui.Color _defaultTextColor = ui.Color (0xFFFF0000 );
1819
20+ final String _placeholderChar = String .fromCharCode (0xFFFC );
21+
1922/// A paragraph made up of a flat list of text spans and placeholders.
2023///
2124/// [CanvasParagraph] doesn't use a DOM element to represent the structure of
@@ -32,7 +35,7 @@ class CanvasParagraph implements ui.Paragraph {
3235 required this .plainText,
3336 required this .placeholderCount,
3437 required this .canDrawOnCanvas,
35- });
38+ }) : assert (spans.isNotEmpty) ;
3639
3740 /// The flat list of spans that make up this paragraph.
3841 final List <ParagraphSpan > spans;
@@ -168,38 +171,28 @@ class CanvasParagraph implements ui.Paragraph {
168171
169172 // 2. Append all spans to the paragraph.
170173
171- DomHTMLElement ? lastSpanElement;
172174 for (int i = 0 ; i < lines.length; i++ ) {
173175 final ParagraphLine line = lines[i];
174- final List <RangeBox > boxes = line.boxes;
175- final StringBuffer buffer = StringBuffer ();
176-
177- int j = 0 ;
178- while (j < boxes.length) {
179- final RangeBox box = boxes[j++ ];
180-
181- if (box is SpanBox ) {
182- lastSpanElement = domDocument.createElement ('flt-span' ) as
183- DomHTMLElement ;
184- applyTextStyleToElement (
185- element: lastSpanElement,
186- style: box.span.style,
187- isSpan: true ,
188- );
189- _positionSpanElement (lastSpanElement, line, box);
190- lastSpanElement.appendText (box.toText ());
191- rootElement.append (lastSpanElement);
192- buffer.write (box.toText ());
193- } else if (box is PlaceholderBox ) {
194- lastSpanElement = null ;
195- } else {
196- throw UnimplementedError ('Unknown box type: ${box .runtimeType }' );
176+ for (final LayoutFragment fragment in line.fragments) {
177+ if (fragment.isPlaceholder) {
178+ continue ;
179+ }
180+
181+ final String text = fragment.getText (this );
182+ if (text.isEmpty) {
183+ continue ;
197184 }
198- }
199185
200- final String ? ellipsis = line.ellipsis;
201- if (ellipsis != null ) {
202- (lastSpanElement ?? rootElement).appendText (ellipsis);
186+ final DomHTMLElement spanElement = domDocument.createElement ('flt-span' ) as DomHTMLElement ;
187+ applyTextStyleToElement (
188+ element: spanElement,
189+ style: fragment.style,
190+ isSpan: true ,
191+ );
192+ _positionSpanElement (spanElement, line, fragment);
193+
194+ spanElement.appendText (text);
195+ rootElement.append (spanElement);
203196 }
204197 }
205198
@@ -283,8 +276,8 @@ class CanvasParagraph implements ui.Paragraph {
283276 }
284277}
285278
286- void _positionSpanElement (DomElement element, ParagraphLine line, RangeBox box ) {
287- final ui.Rect boxRect = box. toTextBox (line, forPainting : true ).toRect ();
279+ void _positionSpanElement (DomElement element, ParagraphLine line, LayoutFragment fragment ) {
280+ final ui.Rect boxRect = fragment. toPaintingTextBox ( ).toRect ();
288281 element.style
289282 ..position = 'absolute'
290283 ..top = '${boxRect .top }px'
@@ -304,6 +297,9 @@ abstract class ParagraphSpan {
304297
305298 /// The index of the end of the range of text represented by this span.
306299 int get end;
300+
301+ /// The resolved style of the span.
302+ EngineTextStyle get style;
307303}
308304
309305/// Represent a span of text in the paragraph.
@@ -323,7 +319,7 @@ class FlatTextSpan implements ParagraphSpan {
323319 required this .end,
324320 });
325321
326- /// The resolved style of the span.
322+ @override
327323 final EngineTextStyle style;
328324
329325 @override
@@ -341,14 +337,24 @@ class FlatTextSpan implements ParagraphSpan {
341337
342338class PlaceholderSpan extends ParagraphPlaceholder implements ParagraphSpan {
343339 PlaceholderSpan (
344- int index,
345- super .width,
346- super .height,
347- super .alignment, {
348- required super .baselineOffset,
349- required super .baseline,
350- }) : start = index,
351- end = index;
340+ this .style,
341+ this .start,
342+ this .end,
343+ double width,
344+ double height,
345+ ui.PlaceholderAlignment alignment, {
346+ required double baselineOffset,
347+ required ui.TextBaseline baseline,
348+ }) : super (
349+ width,
350+ height,
351+ alignment,
352+ baselineOffset: baselineOffset,
353+ baseline: baseline,
354+ );
355+
356+ @override
357+ final EngineTextStyle style;
352358
353359 @override
354360 final int start;
@@ -624,10 +630,19 @@ class CanvasParagraphBuilder implements ui.ParagraphBuilder {
624630 alignment == ui.PlaceholderAlignment .belowBaseline ||
625631 alignment == ui.PlaceholderAlignment .baseline) || baseline != null );
626632
633+ final int start = _plainTextBuffer.length;
634+ _plainTextBuffer.write (_placeholderChar);
635+ final int end = _plainTextBuffer.length;
636+
637+ final EngineTextStyle style = _currentStyleNode.resolveStyle ();
638+ _updateCanDrawOnCanvas (style);
639+
627640 _placeholderCount++ ;
628641 _placeholderScales.add (scale);
629642 _spans.add (PlaceholderSpan (
630- _plainTextBuffer.length,
643+ style,
644+ start,
645+ end,
631646 width * scale,
632647 height * scale,
633648 alignment,
@@ -652,37 +667,54 @@ class CanvasParagraphBuilder implements ui.ParagraphBuilder {
652667
653668 @override
654669 void addText (String text) {
655- final EngineTextStyle style = _currentStyleNode.resolveStyle ();
656670 final int start = _plainTextBuffer.length;
657671 _plainTextBuffer.write (text);
658672 final int end = _plainTextBuffer.length;
659673
660- if (_canDrawOnCanvas) {
661- final ui.TextDecoration ? decoration = style.decoration;
662- if (decoration != null && decoration != ui.TextDecoration .none) {
663- _canDrawOnCanvas = false ;
664- }
674+ final EngineTextStyle style = _currentStyleNode.resolveStyle ();
675+ _updateCanDrawOnCanvas (style);
676+
677+ _spans.add (FlatTextSpan (style: style, start: start, end: end));
678+ }
679+
680+ void _updateCanDrawOnCanvas (EngineTextStyle style) {
681+ if (! _canDrawOnCanvas) {
682+ return ;
665683 }
666684
667- if (_canDrawOnCanvas) {
668- final List <ui.FontFeature >? fontFeatures = style.fontFeatures;
669- if (fontFeatures != null && fontFeatures.isNotEmpty) {
670- _canDrawOnCanvas = false ;
671- }
685+ final ui.TextDecoration ? decoration = style.decoration;
686+ if (decoration != null && decoration != ui.TextDecoration .none) {
687+ _canDrawOnCanvas = false ;
688+ return ;
672689 }
673690
674- if (_canDrawOnCanvas) {
675- final List <ui.FontVariation >? fontVariations = style.fontVariations;
676- if (fontVariations != null && fontVariations.isNotEmpty) {
677- _canDrawOnCanvas = false ;
678- }
691+ final List <ui.FontFeature >? fontFeatures = style.fontFeatures;
692+ if (fontFeatures != null && fontFeatures.isNotEmpty) {
693+ _canDrawOnCanvas = false ;
694+ return ;
679695 }
680696
681- _spans.add (FlatTextSpan (style: style, start: start, end: end));
697+ final List <ui.FontVariation >? fontVariations = style.fontVariations;
698+ if (fontVariations != null && fontVariations.isNotEmpty) {
699+ _canDrawOnCanvas = false ;
700+ return ;
701+ }
682702 }
683703
684704 @override
685705 CanvasParagraph build () {
706+ if (_spans.isEmpty) {
707+ // In case `addText` and `addPlaceholder` were never called.
708+ //
709+ // We want the paragraph to always have a non-empty list of spans to match
710+ // the expectations of the [LayoutFragmenter].
711+ _spans.add (FlatTextSpan (
712+ style: _rootStyleNode.resolveStyle (),
713+ start: 0 ,
714+ end: 0 ,
715+ ));
716+ }
717+
686718 return CanvasParagraph (
687719 _spans,
688720 paragraphStyle: _paragraphStyle,
0 commit comments