Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PDFSharp: Measuring height of long text with word wrap

PDFSharp supports automatic text wrapping when drawing long text portions:

textFormatter.DrawString(text, font, XBrushes.Black, new XRect(x, y, textAreaWidth, 1000), XStringFormats.TopLeft);

This will wrap the text if it is longer than textAreaWidth.

How can I get the height of the text that has just been drawn?

I tried it with gfx.MeasureString(), but there is no overload that supports specifying a maximal width. gfx.MeasureString() returns the size of the text without text wrapping.

Thanks for any hints.

like image 879
cheesus Avatar asked Mar 17 '13 13:03

cheesus


2 Answers

This extension of PdfSharp didn't quite work for me. Don't know why but i was keeping getting a bigger height than expected (almost the double of the neededHeight). So I decided to write an extension method the the XGraphics object where i can specify a maxWidth and internally calculate the soft line breaks. The code uses the default XGraphics.MeasureString(string, XFont) to the inlined text Width and Aggregates with the words from the text to calclulate the Line breaks. The code to calculate the soft Line breaks looks like this :

/// <summary>
/// Calculate the number of soft line breaks
/// </summary>
private static int GetSplittedLineCount(this XGraphics gfx, string content, XFont font, double maxWidth)
{
    //handy function for creating list of string
    Func<string, IList<string>> listFor = val => new List<string> { val };
    // string.IsNullOrEmpty is too long :p
    Func <string, bool> nOe = str => string.IsNullOrEmpty(str);
    // return a space for an empty string (sIe = Space if Empty)
    Func<string, string> sIe = str => nOe(str) ? " " : str;
    // check if we can fit a text in the maxWidth
    Func<string, string, bool> canFitText = (t1, t2) => gfx.MeasureString($"{(nOe(t1) ? "" : $"{t1} ")}{sIe(t2)}", font).Width <= maxWidth;

    Func<IList<string>, string, IList<string>> appendtoLast =
            (list, val) => list.Take(list.Count - 1)
                               .Concat(listFor($"{(nOe(list.Last()) ? "" : $"{list.Last()} ")}{sIe(val)}"))
                               .ToList();

    var splitted = content.Split(' ');

    var lines = splitted.Aggregate(listFor(""),
            (lfeed, next) => canFitText(lfeed.Last(), next) ? appendtoLast(lfeed, next) : lfeed.Concat(listFor(next)).ToList(),
            list => list.Count());

    return lines;
}

See the following Gist for the complete code : https://gist.github.com/erichillah/d198f4a1c9e8f7df0739b955b245512a

like image 111
Eric H. Avatar answered Nov 13 '22 20:11

Eric H.


The XTextFormatter class (source code included with PDFsharp) is meant to get you started. Modify it if it doesn't suit your needs.

Since XTextFormatter keeps the Y position internally, it would be a rather simple change to return the height of the text that was just drawn.

Instead of modifying XTextFormatter, consider using MigraDoc Foundation (also included) instead.

like image 3
I liked the old Stack Overflow Avatar answered Nov 13 '22 20:11

I liked the old Stack Overflow