| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| part of engine; |
| |
| /// Responsible for painting a [CanvasParagraph] on a [BitmapCanvas]. |
| class TextPaintService { |
| TextPaintService(this.paragraph); |
| |
| final CanvasParagraph paragraph; |
| |
| void paint(BitmapCanvas canvas, ui.Offset offset) { |
| // Loop through all the lines, for each line, loop through all the boxes and |
| // paint them. The boxes have enough information so they can be painted |
| // individually. |
| final List<EngineLineMetrics> lines = paragraph.computeLineMetrics(); |
| |
| for (final EngineLineMetrics line in lines) { |
| for (final RangeBox box in line.boxes!) { |
| _paintBox(canvas, offset, line, box); |
| } |
| } |
| } |
| |
| void _paintBox( |
| BitmapCanvas canvas, |
| ui.Offset offset, |
| EngineLineMetrics line, |
| RangeBox box, |
| ) { |
| // Placeholder spans don't need any painting. Their boxes should remain |
| // empty so that their underlying widgets do their own painting. |
| if (box is SpanBox) { |
| final FlatTextSpan span = box.span; |
| |
| // Paint the background of the box, if the span has a background. |
| final SurfacePaint? background = span.style._background as SurfacePaint?; |
| if (background != null) { |
| canvas.drawRect( |
| box.toTextBox(line).toRect().shift(offset), |
| background.paintData, |
| ); |
| } |
| |
| // Paint the actual text. |
| _applySpanStyleToCanvas(span, canvas); |
| final double x = offset.dx + line.left + box.left; |
| final double y = offset.dy + line.baseline; |
| final String text = paragraph.toPlainText().substring( |
| box.start.index, |
| box.end.indexWithoutTrailingNewlines, |
| ); |
| final double? letterSpacing = span.style._letterSpacing; |
| if (letterSpacing == null || letterSpacing == 0.0) { |
| canvas.fillText(text, x, y, shadows: span.style._shadows); |
| } else { |
| // TODO(mdebbar): Implement letter-spacing on canvas more efficiently: |
| // https://github.com/flutter/flutter/issues/51234 |
| double charX = x; |
| final int len = text.length; |
| for (int i = 0; i < len; i++) { |
| final String char = text[i]; |
| canvas.fillText(char, charX.roundToDouble(), y, |
| shadows: span.style._shadows); |
| charX += letterSpacing + canvas.measureText(char).width!; |
| } |
| } |
| |
| // Paint the ellipsis using the same span styles. |
| final String? ellipsis = line.ellipsis; |
| if (ellipsis != null && box == line.boxes!.last) { |
| final double x = offset.dx + line.left + box.right; |
| canvas.fillText(ellipsis, x, y); |
| } |
| |
| canvas._tearDownPaint(); |
| } |
| } |
| |
| void _applySpanStyleToCanvas(FlatTextSpan span, BitmapCanvas canvas) { |
| final SurfacePaint? paint; |
| final ui.Paint? foreground = span.style._foreground; |
| if (foreground != null) { |
| paint = foreground as SurfacePaint; |
| } else { |
| paint = (ui.Paint()..color = span.style._color!) as SurfacePaint; |
| } |
| |
| canvas.setCssFont(span.style.cssFontString); |
| canvas._setUpPaint(paint.paintData, null); |
| } |
| } |