DEV Community

loading...
Cover image for Text wrap with SkiaSharp

Text wrap with SkiaSharp

andris
・2 min read

Context

I am working on https://morepuzzles.com/ where we are drawing our puzzles with SkiaSharp. More on that in this post: https://medium.com/@brianelete/a-brief-experience-with-skiasharp-20478ca05ef2

How to wrap text lines

My main issue when displaying text with Skia was that the lines were sometimes long and went out of the canvas or the current column. Skia’s DrawText method works like that it starts at the given position and draws the text in one line with the given paint (what is containing the text size). Lucky for us we can measure text and break lines where we need them to be wrapped. That may be the edge of the page or at the start of a new column (we can add margins too).

We use bounding boxes inside the document to determine the position and the relative size of things. You can measure the text height and length using a given font size so you can calculate what font size fits the box and where you should wrap each line.

void WrapLines(string longLine, float lineLengthLimit, SKCanvas canvas, SKPaint defPaint)
{
    var wrappedLines = new List<string>();
    var lineLength = 0f;
    var line = "";
    foreach (var word in longLine.Split(' '))
    {
        var wordWithSpace = word + " ";
        var wordWithSpaceLength = defPaint.MeasureText(wordWithSpace);
        if (lineLength + wordWithSpaceLength > lineLengthLimit)
        {
            wrappedLines.Add(line);
            line = "" + wordWithSpace;
            lineLength = wordWithSpaceLength;
        }
        else
        {
            line += wordWithSpace;
            lineLength += wordWithSpaceLength;
        }
    }
    var x = 0;
    var y = 0;
    foreach (var wrappedLine in wrappedLines)
    {
        canvas.DrawText(wrappedLine, x, y, paint);
        y += paint.FontSpacing;
    }
}

This is a slightly modified example to fit this post’s topic. You can of course separate the line wrapping and the drawing itself if it better fits your needs.

Discussion (1)

Collapse
iains1986 profile image
Iain • Edited

There's a bug in your logic here that means you won't render the last line of text.

You add the text to the 'wrappedLines' everytime the string goes over the edge, but the last line doesn't get added so won't get drawn.

Need a simple, 'wrappedLines.Add(line);' after the foreach loop.

Also FYI for people reading, I'm sure you'll be aware, but ideally you want this logic calculating once in a different location than during the actual render - otherwise it could get expensive to continuously measure the lines to calculate the word wrap and do this again and again even though it keeps working it out the same.