text-renderingimagesharp

Rendering justified text - I cannot get the words to align by baseline


I am using the ImageSharp library to render multiple lines of text, and I want each line to be justified. I want the spacing to be dynamically calculated to ensure the first character is left aligned and the final character is right aligned.

I wrote some code to do this. This code divides the string up into words, sums up the total word widths, subtracts this total from the available width to determine the available space, and then assigns this space to each word break. It then renders each word separately.

The problem is the vertical alignment. Words can have different vertical heights depending upon the letters they have. The word 'in' has a different height to the word 'Mojo'. Here is how it's currently rendering. You can see vertically small words like 'over' are not aligned properly.

enter image description here

How can I render two words separately so each word's vertical baseline is aligned?

Here is my current code:

public static IPathCollection RenderJustifiedText(
    string text,
    PointF startPos,
    float width,
    Font font
)
{
    var textOptions = new TextOptions(font);
    var wordsAndWidths = text.Split(' ')
        .Select(
            w =>
            new
            {
                Word = w,
                Width = TextMeasurer.MeasureBounds(w, textOptions).Width,
            }
        ).ToArray();
    var spaceRemaining = width - wordsAndWidths.Select(ww => ww.Width).Sum();
    var eachSpace = spaceRemaining / (wordsAndWidths.Length - 1);
    float xPos = startPos.X;
    List<IPathCollection> allPaths = new();
    foreach (var w in wordsAndWidths)
    {
        var textPath = new PathBuilder()
            .AddLine(
                new PointF(
                    xPos,
                    startPos.Y
                ),
                new PointF(
                    xPos + w.Width,
                    startPos.Y
                )
            ).Build();
        allPaths.Add(
            TextBuilder.GenerateGlyphs(
                w.Word,
                textPath,
                textOptions
            )
        );
        xPos += w.Width + eachSpace;
    }
    return new PathCollection(allPaths.SelectMany(pathCollection => pathCollection));
}

Solution

  • As of v1.0.0-beta18 there is a new TextJustification enum in TextOptions. The options and implementation supporting the CSS Text Module Level 3 text-justify property. This allows you to easily justify text of mixed font sizes.

    I've added some example output below.

    Horizontal

    TextJustification.None

    none

    TextJustification.InterWord

    inter-word

    TextJustification.InterCharacter

    inter-char

    Vertical

    TextJustification.None

    v-none

    TextJustification.InterWord

    v-inter-word

    TextJustification.InterCharacter

    v-inter-char