blob: 857d55a2d53f3cfc1cb474f48eac5ace556c8aa4 [file] [log] [blame]
// 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.
import 'package:ui/ui.dart' as ui;
import '../dom.dart';
import '../html/bitmap_canvas.dart';
import '../html/painting.dart';
import 'canvas_paragraph.dart';
import 'layout_fragmenter.dart';
import 'paragraph.dart';
/// 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 fragments and
// paint them. The fragment objects have enough information to be painted
// individually.
final List<ParagraphLine> lines = paragraph.lines;
for (final ParagraphLine line in lines) {
for (final LayoutFragment fragment in line.fragments) {
_paintBackground(canvas, offset, fragment);
_paintText(canvas, offset, line, fragment);
}
}
}
void _paintBackground(
BitmapCanvas canvas,
ui.Offset offset,
LayoutFragment fragment,
) {
final ParagraphSpan span = fragment.span;
if (span is FlatTextSpan) {
// Paint the background of the box, if the span has a background.
final SurfacePaint? background = span.style.background as SurfacePaint?;
if (background != null) {
final ui.Rect rect = fragment.toPaintingTextBox().toRect();
if (!rect.isEmpty) {
canvas.drawRect(rect.shift(offset), background.paintData);
}
}
}
}
void _paintText(
BitmapCanvas canvas,
ui.Offset offset,
ParagraphLine line,
LayoutFragment fragment,
) {
// There's no text to paint in placeholder spans.
if (fragment.isPlaceholder) {
return;
}
// Don't paint the text for space-only boxes. This is just an
// optimization, it doesn't have any effect on the output.
if (fragment.isSpaceOnly) {
return;
}
_prepareCanvasForFragment(canvas, fragment);
final double fragmentX = fragment.textDirection! == ui.TextDirection.ltr
? fragment.left
: fragment.right;
final double x = offset.dx + line.left + fragmentX;
final double y = offset.dy + line.baseline;
final EngineTextStyle style = fragment.style;
final String text = fragment.getText(paragraph);
final double? letterSpacing = style.letterSpacing;
if (letterSpacing == null || letterSpacing == 0.0) {
canvas.drawText(text, x, y,
style: style.foreground?.style, shadows: 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.drawText(char, charX.roundToDouble(), y,
style: style.foreground?.style,
shadows: style.shadows);
charX += letterSpacing + canvas.measureText(char).width!;
}
}
canvas.tearDownPaint();
}
void _prepareCanvasForFragment(BitmapCanvas canvas, LayoutFragment fragment) {
final EngineTextStyle style = fragment.style;
final SurfacePaint? paint;
final ui.Paint? foreground = style.foreground;
if (foreground != null) {
paint = foreground as SurfacePaint;
} else {
paint = (ui.Paint()..color = style.color!) as SurfacePaint;
}
canvas.setCssFont(style.cssFontString, fragment.textDirection!);
canvas.setUpPaint(paint.paintData, null);
}
}