@@ -9,13 +9,16 @@ import '../embedder.dart';
9
9
import '../html/bitmap_canvas.dart' ;
10
10
import '../profiler.dart' ;
11
11
import '../util.dart' ;
12
+ import 'layout_fragmenter.dart' ;
12
13
import 'layout_service.dart' ;
13
14
import 'paint_service.dart' ;
14
15
import 'paragraph.dart' ;
15
16
import 'word_breaker.dart' ;
16
17
17
18
const ui.Color _defaultTextColor = ui.Color (0xFFFF0000 );
18
19
20
+ final String _placeholderChar = String .fromCharCode (0xFFFC );
21
+
19
22
/// A paragraph made up of a flat list of text spans and placeholders.
20
23
///
21
24
/// [CanvasParagraph] doesn't use a DOM element to represent the structure of
@@ -32,7 +35,7 @@ class CanvasParagraph implements ui.Paragraph {
32
35
required this .plainText,
33
36
required this .placeholderCount,
34
37
required this .canDrawOnCanvas,
35
- });
38
+ }) : assert (spans.isNotEmpty) ;
36
39
37
40
/// The flat list of spans that make up this paragraph.
38
41
final List <ParagraphSpan > spans;
@@ -168,38 +171,28 @@ class CanvasParagraph implements ui.Paragraph {
168
171
169
172
// 2. Append all spans to the paragraph.
170
173
171
- DomHTMLElement ? lastSpanElement;
172
174
for (int i = 0 ; i < lines.length; i++ ) {
173
175
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 ;
197
184
}
198
- }
199
185
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);
203
196
}
204
197
}
205
198
@@ -283,8 +276,8 @@ class CanvasParagraph implements ui.Paragraph {
283
276
}
284
277
}
285
278
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 ();
288
281
element.style
289
282
..position = 'absolute'
290
283
..top = '${boxRect .top }px'
@@ -304,6 +297,9 @@ abstract class ParagraphSpan {
304
297
305
298
/// The index of the end of the range of text represented by this span.
306
299
int get end;
300
+
301
+ /// The resolved style of the span.
302
+ EngineTextStyle get style;
307
303
}
308
304
309
305
/// Represent a span of text in the paragraph.
@@ -323,7 +319,7 @@ class FlatTextSpan implements ParagraphSpan {
323
319
required this .end,
324
320
});
325
321
326
- /// The resolved style of the span.
322
+ @override
327
323
final EngineTextStyle style;
328
324
329
325
@override
@@ -341,14 +337,24 @@ class FlatTextSpan implements ParagraphSpan {
341
337
342
338
class PlaceholderSpan extends ParagraphPlaceholder implements ParagraphSpan {
343
339
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;
352
358
353
359
@override
354
360
final int start;
@@ -624,10 +630,19 @@ class CanvasParagraphBuilder implements ui.ParagraphBuilder {
624
630
alignment == ui.PlaceholderAlignment .belowBaseline ||
625
631
alignment == ui.PlaceholderAlignment .baseline) || baseline != null );
626
632
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
+
627
640
_placeholderCount++ ;
628
641
_placeholderScales.add (scale);
629
642
_spans.add (PlaceholderSpan (
630
- _plainTextBuffer.length,
643
+ style,
644
+ start,
645
+ end,
631
646
width * scale,
632
647
height * scale,
633
648
alignment,
@@ -652,37 +667,54 @@ class CanvasParagraphBuilder implements ui.ParagraphBuilder {
652
667
653
668
@override
654
669
void addText (String text) {
655
- final EngineTextStyle style = _currentStyleNode.resolveStyle ();
656
670
final int start = _plainTextBuffer.length;
657
671
_plainTextBuffer.write (text);
658
672
final int end = _plainTextBuffer.length;
659
673
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 ;
665
683
}
666
684
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 ;
672
689
}
673
690
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 ;
679
695
}
680
696
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
+ }
682
702
}
683
703
684
704
@override
685
705
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
+
686
718
return CanvasParagraph (
687
719
_spans,
688
720
paragraphStyle: _paragraphStyle,
0 commit comments