根据可用宽度和字体计算文本高度?

发布于 2024-07-22 12:11:32 字数 171 浏览 13 评论 0原文

我们使用 PDFsharp 从数据库动态创建 PDF 文档。

我需要知道计算文本区域高度的最佳方法 关于所使用的字体和可用宽度。

我需要知道高度,以便在需要时处理分页符。

We are creating PDF documents on the fly from the database using PDFsharp.

I need to know the best way to calculate the height of the text area based
on the font used and the available width.

I need to know the height so I can process page breaks when required.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

2024-07-29 12:11:32

PdfSharp.Drawing.XGraphics 对象有一个 MeasureString 方法,可以返回您需要的内容。

 var pdfDoc = new PdfSharp.Pdf.PdfDocument();
 var pdfPage = pdfDoc.AddPage();
 var pdfGfx = PdfSharp.Drawing.XGraphics.FromPdfPage(pdfPage);
 var pdfFont = new PdfSharp.Drawing.XFont("Helvetica", 20);

 while (pdfGfx.MeasureString("Hello World!").Width > pdfPage.Width)
      --pdfFont.Size;

 pdfGfx.DrawString("Hello World!", pdfFont
      , PdfSharp.Drawing.XBrushes.Black
      , new PdfSharp.Drawing.XPoint(100, 100));

这应该对你有帮助。 请注意,我没有测试此代码,因为我是为了提供帮助而临时编写的。 它可能包含一些编译时错误,但您可能会明白。

The PdfSharp.Drawing.XGraphics object has a MeasureString method that returns what you require.

 var pdfDoc = new PdfSharp.Pdf.PdfDocument();
 var pdfPage = pdfDoc.AddPage();
 var pdfGfx = PdfSharp.Drawing.XGraphics.FromPdfPage(pdfPage);
 var pdfFont = new PdfSharp.Drawing.XFont("Helvetica", 20);

 while (pdfGfx.MeasureString("Hello World!").Width > pdfPage.Width)
      --pdfFont.Size;

 pdfGfx.DrawString("Hello World!", pdfFont
      , PdfSharp.Drawing.XBrushes.Black
      , new PdfSharp.Drawing.XPoint(100, 100));

This should help you. Please consider that I didn't test this code as I wrote it on the fly in order to help. It might contain some compile-time errors, but you may get the idea.

忆梦 2024-07-29 12:11:32

在.NET中你可以调用
Graphics.MeasureString 了解如何
绘制的文本将会很大。

是的,但是当使用 PDFsharp 时,您会调用 XGraphics.MeasureString。

In .NET you can call
Graphics.MeasureString to find out how
large the drawn text is going to be.

Right, but when using PDFsharp you call XGraphics.MeasureString.

北斗星光 2024-07-29 12:11:32

我遇到了类似的问题,所以我实现了这个扩展方法:

public static double MeasureHeight(this PdfSharp.Drawing.XGraphics gfx, string text, PdfSharp.Drawing.XFont font, int width)
{
    var lines = text.Split('\n');

    double totalHeight = 0;

    foreach (string line in lines)
    {
        var size = gfx.MeasureString(line, font);
        double height = size.Height + (size.Height * Math.Floor(size.Width / width));

        totalHeight += height;
    }

    return totalHeight;
}

I had a similiar problem so I implemented this extension method:

public static double MeasureHeight(this PdfSharp.Drawing.XGraphics gfx, string text, PdfSharp.Drawing.XFont font, int width)
{
    var lines = text.Split('\n');

    double totalHeight = 0;

    foreach (string line in lines)
    {
        var size = gfx.MeasureString(line, font);
        double height = size.Height + (size.Height * Math.Floor(size.Width / width));

        totalHeight += height;
    }

    return totalHeight;
}
一袭白衣梦中忆 2024-07-29 12:11:32

在 .NET 中,您可以调用 Graphics.MeasureString找出绘制的文本有多大。

In .NET you can call Graphics.MeasureString to find out how large the drawn text is going to be.

梦行七里 2024-07-29 12:11:32

我为 XGraphic 对象编写了一个小扩展方法来执行此操作:通过指定 maxWidth 来计算确切的文本高度(和宽度)。
请参阅以下代码要点: https://gist.github.com/ericillah/d198f4a1c9e8f7df0739b955b245512a

I wrote a small extension method to the XGraphic object to do just that : Calclulate the exact text height (and width) by specifiying the maxWidth.
See the following gist for the code : https://gist.github.com/erichillah/d198f4a1c9e8f7df0739b955b245512a

可爱咩 2024-07-29 12:11:32

OP询问如何根据可用宽度和字体计算文本高度。 Windows .NET 为此提供了一个 API 调用,它需要一个宽度参数; 我使用的 PDFsharp 版本(0.9.653,.NET 1.1)没有。

我的解决方案 - 使用 .NET API 调用以及为自定义创建的位图对象分配的 Graphics 对象来获取答案。

对我有用的是使用具有 100 DPI 分辨率(关键)并且恰好是纵向页面大小(可能不太关键)的位图。

然后我只是询问 .NET 在该位图上绘制的像素大小是多少。

然后,您可能需要将单位从 1/100 英寸转换为点(对于 PDFsharp)。

''' Adapted Code - this not tested or even compiled - Caveat Emptor!
''' Target: Visual Basic, .NET 1.1 (VS2003) [adapt as necessary]

'  '  '  '  '  '  '  '  '  '  '  '  '  '  '  '
'  GraphicsAlt.MeasureString() does substantially what System.Drawing MeasureString(...,Integer) does.
'  '  '  '  '  '  '  '  '  '  '  '  '  '  '  '
Public Module GraphicsAlt

    '
    ' Static data used Only to compute MeasureString() below.
    '
    '     Cache a single copy of these two objects, to address an otherwise unexplained intermittent exception.
    '
    Private Shared myImage As Bitmap = Nothing
    Private Shared myGraphics As Graphics = Nothing

    Public Shared Function GetMeasureGraphics() As Graphics
        If myImage Is Nothing Then
            myImage = New Bitmap(1700, 2200)  '' ... Specify 8.5x11 
            myImage.SetResolution(100, 100)   '' ... and 100 DPI (if you want different units, you might change this)
            myGraphics = Graphics.FromImage(myImage)
        End If
        Return myGraphics
    End Function

    'Given 1/100TH inch max width, return Rect to hold with units 1/100TH inch
    '
    Public Function MeasureString(ByVal text As String, ByVal aFont As System.Drawing.Font, ByVal width As Integer) As System.Drawing.SizeF
        Return (GraphicsAlt.GetMeasureGraphics()).MeasureString(text, aFont, width)
    End Function

End Module

The OP asked how to calculate text height based on available width and font. Windows .NET provides an API call for this which takes a width argument; the version of PDFsharp I'm using (0.9.653, .NET 1.1) does not.

My solution - use the .NET API call with a Graphics object allocated for a custom-created Bitmap object to get the answer.

What worked for me was to use a Bitmap that had 100 DPI resolution (critical) and happened to be the size of a Portrait page (probably less critical).

Then I just asked .NET what the pixel size would be for painting on that bitmap.

You probably will then want to convert the units from 1/100th of an inch to Points (for PDFsharp).

''' Adapted Code - this not tested or even compiled - Caveat Emptor!
''' Target: Visual Basic, .NET 1.1 (VS2003) [adapt as necessary]

'  '  '  '  '  '  '  '  '  '  '  '  '  '  '  '
'  GraphicsAlt.MeasureString() does substantially what System.Drawing MeasureString(...,Integer) does.
'  '  '  '  '  '  '  '  '  '  '  '  '  '  '  '
Public Module GraphicsAlt

    '
    ' Static data used Only to compute MeasureString() below.
    '
    '     Cache a single copy of these two objects, to address an otherwise unexplained intermittent exception.
    '
    Private Shared myImage As Bitmap = Nothing
    Private Shared myGraphics As Graphics = Nothing

    Public Shared Function GetMeasureGraphics() As Graphics
        If myImage Is Nothing Then
            myImage = New Bitmap(1700, 2200)  '' ... Specify 8.5x11 
            myImage.SetResolution(100, 100)   '' ... and 100 DPI (if you want different units, you might change this)
            myGraphics = Graphics.FromImage(myImage)
        End If
        Return myGraphics
    End Function

    'Given 1/100TH inch max width, return Rect to hold with units 1/100TH inch
    '
    Public Function MeasureString(ByVal text As String, ByVal aFont As System.Drawing.Font, ByVal width As Integer) As System.Drawing.SizeF
        Return (GraphicsAlt.GetMeasureGraphics()).MeasureString(text, aFont, width)
    End Function

End Module
つ可否回来 2024-07-29 12:11:32

如果有人仍然想找到答案,我已经实现了一种相当容易理解的方法来找出结果文本的高度。

Public Function PrintString(text As String, ft As XFont, rect As XRect, graph As XGraphics, b As SolidBrush, Optional tf As XTextFormatter = Nothing) As Integer
    If Not IsNothing(tf) Then
        tf.DrawString(text, ft, b, rect)
    Else
        Dim drawLeft As New XStringFormat
        drawLeft.Alignment = XStringAlignment.Near

        graph.DrawString(text, ft, b, rect, drawLeft)
    End If

    Dim width As Double = graph.MeasureString(text, ft).Width
    Dim multiplier As Integer = 0

    While width > 0
        multiplier += 1

        width -= rect.Width
    End While

    Dim height As Double = (graph.MeasureString(text, ft).Height) * multiplier
    Return height
End Function

解释代码:

首先,打印文本。 我包含了一个名为 tf 的可选 XTextFormatter,因为我在应用程序中可以互换使用 XGraphics 或 XTextFormatters。

然后,通过MeasureString().Width计算文本的长度。

然后,计算有多少行文本。 这是通过将先前找到的文本的总长度除以所提供的打印税费的矩形(框)的宽度来完成的。 我在这里用 while 循环做到了。

将文本的高度(使用 graph.MeasureString().Height)乘以行数。 这是文本的最终高度。

返回高度值。 现在,调用 PrintString() 函数将打印提供的文本,同时返回打印文本的高度。

In case anyone still wants to find an answer, I've implemented a reasonably easy-to-understand method to find out the height of the resulting text.

Public Function PrintString(text As String, ft As XFont, rect As XRect, graph As XGraphics, b As SolidBrush, Optional tf As XTextFormatter = Nothing) As Integer
    If Not IsNothing(tf) Then
        tf.DrawString(text, ft, b, rect)
    Else
        Dim drawLeft As New XStringFormat
        drawLeft.Alignment = XStringAlignment.Near

        graph.DrawString(text, ft, b, rect, drawLeft)
    End If

    Dim width As Double = graph.MeasureString(text, ft).Width
    Dim multiplier As Integer = 0

    While width > 0
        multiplier += 1

        width -= rect.Width
    End While

    Dim height As Double = (graph.MeasureString(text, ft).Height) * multiplier
    Return height
End Function

Explaining the code:

First, print the text. I included an Optional XTextFormatter called tf because I use either XGraphics or XTextFormatters interchangeably in my application.

Then, calculate how long the text was by MeasureString().Width.

Then, calculate how many lines of text there were. This is done by dividing the total length of the text found earlier by the width of the provided rectangle (box) where the tax is printed. I did it with a while loop here.

Multiply the height of the text (using graph.MeasureString().Height) by the number of lines there were. This is the final height of your text.

Return the height value. Now, calling the PrintString() function will print the text provided out while returning the height of the printed text afterward.

对岸观火 2024-07-29 12:11:32

PDFsharp 包含一个 XTextFormatter 类,可用于绘制带有换行符的文本。

但是它无法确定文本所需的高度。 受到 @Wakka02 评论的启发,我改进了这个类,生成了 XTextFormatterEx 类。
在我看来,它也回答了原来的问题,因此我发布了一个答案。
我知道这是一个老问题,答案可能对OP没有帮助,但这是一个常见问题,答案可能对其他人有帮助。

新类有 500 行代码 - 我认为这对于这篇文章来说太多了。

源代码可以在PDFsharp论坛上找到:
http://forum.pdfsharp.net/viewtopic.php?p=9213#p9213

也可以在我的简陋博客中找到:
http: //developer.th-soft.com/developer/pdfsharp-improving-the-xtextformatter-class-measuring-the-height-of-the-text/

使用新类时,可以先调用PrepareDrawString 找出适合的文本大小以及适合文本的高度。 然后,您的解码器可以绘制准备好的文本或准备另一个文本或使用不同的矩形准备相同的文本。

我在工作中的新课程:
XTextFormatterEx tf = new XTextFormatterEx(gfx);
int 最后一个字符索引;
双倍需要高度;

// Draw the text in a box with the optimal height
// (magic: we know that one page is enough).
XRect rect = new XRect(40, 100, 250, double.MaxValue);
//tf.Alignment = ParagraphAlignment.Left;
tf.PrepareDrawString(text, font, rect,
                     out lastCharIndex, out neededHeight);
rect = new XRect(40, 100, 250, neededHeight);
gfx.DrawRectangle(XBrushes.SeaShell, rect);
// Both variants should look the same.

// Optimized version: draw the prepared string.
tf.DrawString(XBrushes.Black, XStringFormats.TopLeft);

准备文本会多次调用 MeasureString。 稍后可以绘制准备好的文本,而无需再次调用 MeasureString。

截至今天(2015 年 7 月 17 日),XTextFormatterEx 类(与原始 XTextFormatter 类似)使用 XFont 类的内部字段。 这需要在编译类时进行特殊处理。 下载 PDFsharp 1.32 的完整源代码包后,我决定将 XTextFormatterEx 类复制到 PDFsharp 文件夹中。
任何尝试修改 XTextFormatter 或 XTextFormatterEx 类的人都会面临同样的问题。
我希望 PDFsharp 的未来版本能够解决这个问题,允许这些类的修改版本包含在应用程序项目中。

PDFsharp includes a class XTextFormatter that can be used to draw text with linebreaks.

However it can not determine the height needed for the text. Inspired by a comment from @Wakka02 I improved this class, generating class XTextFormatterEx.
In my opinion it also answers the original question, therefore I post an answer.
I know this is an old question and the answer may not help the OP, but it is a frequently asked question and the answer may help others.

The new class has 500 lines of code - and I think this would be too much for this post.

The source code can be found on the PDFsharp forum:
http://forum.pdfsharp.net/viewtopic.php?p=9213#p9213

It can also be found in my humble blog:
http://developer.th-soft.com/developer/pdfsharp-improving-the-xtextformatter-class-measuring-the-height-of-the-text/

When using the new class, you can first call PrepareDrawString to find out how much of the text fits and which height the fitting text has. Then your decoder can draw the prepared text or prepare another text or prepare the same text with a different rectangle.

My new class at work:
XTextFormatterEx tf = new XTextFormatterEx(gfx);
int lastCharIndex;
double neededHeight;

// Draw the text in a box with the optimal height
// (magic: we know that one page is enough).
XRect rect = new XRect(40, 100, 250, double.MaxValue);
//tf.Alignment = ParagraphAlignment.Left;
tf.PrepareDrawString(text, font, rect,
                     out lastCharIndex, out neededHeight);
rect = new XRect(40, 100, 250, neededHeight);
gfx.DrawRectangle(XBrushes.SeaShell, rect);
// Both variants should look the same.

// Optimized version: draw the prepared string.
tf.DrawString(XBrushes.Black, XStringFormats.TopLeft);

Preparing the text invokes MeasureString many times. Later the prepared text can be drawn without invoking MeasureString again.

As of today (Juli 17, 2015) the class XTextFormatterEx (like the original XTextFormatter) uses internal fields of the XFont class. This requires special treatment when compiling the class. I decided to copy my XTextFormatterEx class into the PDFsharp folder after downloading the complete source package for PDFsharp 1.32.
Anybody trying to modify either the XTextFormatter or XTextFormatterEx class will face the same problem.
I hope this issue will be solved with future versions of PDFsharp, allowing modified versions of these classes to be included in the application project.

故事与诗 2024-07-29 12:11:32

这是 PdfSharp 的 XGraphics 的扩展,用于测量带有换行符的文本框的高度。 它受到上面Eric-H的回答的启发,但考虑了段落的行间距规则,并且还实现了更准确的文本换行逻辑。

// Enhanced version of MeasureString function for PdfSharp XGraphics taking to account line breaks within a restricted width (maxWidth)
// The returned height will be the font's Content height - from the Ascend of the first line to the Descend of the last line, i.e.
// the height will include the necessary spacing between the lines, but not the usual spacing under the last line.
// The returned width will be less than maxWidth if the text could be wrapped, but it can be greater than maxWidth
// if the text contained a word that could did not fit and so could not be wrapped
public static XSize MeasureWrappedText(
    this XGraphics gfx,
    string text,
    ParagraphFormat format,
    Unit maxWidth,
    out int lineCount
)
{
    XFont font = new XFont(format.Font.Name, format.Font.Size, GetXStyle(format.Font));
    XSize space = gfx.MeasureString(" ", font);

    // Wrap the text by line breaks, white-spaces and hyphens and count the resulting lines
    // Note that this logic may not be exactly what PdfSharp's renderer implements so a discrepancy may occur in some rare cases
    double width = 0;
    double lineWidth = 0;
    lineCount = 0;
    var p = new (string Word, double WordWidth, double Width, bool IsSpace, double LineWidth)[3]; // to remember 3 previous steps
    foreach (string word in SplitIntoWordsAndSpaces(text))
    {
        double wordWidth = word == " " ? space.Width : gfx.MeasureString(word, font).Width;
        bool isNewLine = word is "\n" or "\r" or "\r\n";
        bool isSpace = word.Length == 1 && char.IsWhiteSpace(word[0]);
        lineWidth += wordWidth;
        if (isNewLine || lineWidth > maxWidth && maxWidth > space.Width) // if our maxWidth is smaller than the space width (e.g. 0), we don't wrap at all
        {
            lineWidth = isSpace || isNewLine ? 0 : wordWidth; // if we are breaking at a white-space or a new line, forget its width
            int previous = 0;
            if (p[0].IsSpace)
                width = p[1].Width; // roll-back any width increase due to the space before this word
            else if (word == "-" && p[0].Word != "-") // a special case for a hyphen - we break after it, not before
            {
                lineWidth += p[0].WordWidth; // keep the hyphen together with the previous word
                width = p[p[1].IsSpace ? 2 : 1].Width; // roll-back any width increase due to our word followed by the hyphen
                previous = 1;
            }
            if (isNewLine || p[previous].LineWidth > 0)
                lineCount++;
        }
        width = Math.Max(width, lineWidth);
        p[2] = p[1];
        p[1] = p[0];
        p[0] = (word, wordWidth, width, isSpace, lineWidth);
    }
    if (lineWidth > 0)
        lineCount++;

    if (lineCount > 1)
    {
        double singleLineSpace = font.GetHeight();
        double lineHeight = format.LineSpacingRule switch
        {
            LineSpacingRule.Single => singleLineSpace,
            LineSpacingRule.OnePtFive => 1.5 * singleLineSpace,
            LineSpacingRule.Double => 2.0 * singleLineSpace,
            LineSpacingRule.Multiple => format.LineSpacing * singleLineSpace,
            LineSpacingRule.AtLeast => Math.Max(singleLineSpace, format.LineSpacing),
            LineSpacingRule.Exactly => format.LineSpacing,
            _ => 0
        };
        return new XSize(width, space.Height + (lineCount - 1) * lineHeight);
    }

    return new XSize(width, space.Height);
}

private static IEnumerable<string> SplitIntoWordsAndSpaces(string s)
{
    int i = 0;
    int j = 0;
    while (j < s.Length)
    {
        if (char.IsWhiteSpace(s[j]) || s[j] == '-')
        {
            int len = j < s.Length - 1 && s.Substring(j, 2) == "\r\n" ? 2 : 1;
            if (i < j)
                yield return s[i..j];
            yield return s.Substring(j, len);
            j += len;
            i = j;
        }
        else
            j++;
    }
    if (i < j)
        yield return s[i..j];
}

Here's an extension to PdfSharp's XGraphics to measure the height of a box of text with line breaks. It was inspired by Eric-H's answer above, but takes into account the line spacing rules of the paragraph, and also implements a more accurate text wrapping logic.

// Enhanced version of MeasureString function for PdfSharp XGraphics taking to account line breaks within a restricted width (maxWidth)
// The returned height will be the font's Content height - from the Ascend of the first line to the Descend of the last line, i.e.
// the height will include the necessary spacing between the lines, but not the usual spacing under the last line.
// The returned width will be less than maxWidth if the text could be wrapped, but it can be greater than maxWidth
// if the text contained a word that could did not fit and so could not be wrapped
public static XSize MeasureWrappedText(
    this XGraphics gfx,
    string text,
    ParagraphFormat format,
    Unit maxWidth,
    out int lineCount
)
{
    XFont font = new XFont(format.Font.Name, format.Font.Size, GetXStyle(format.Font));
    XSize space = gfx.MeasureString(" ", font);

    // Wrap the text by line breaks, white-spaces and hyphens and count the resulting lines
    // Note that this logic may not be exactly what PdfSharp's renderer implements so a discrepancy may occur in some rare cases
    double width = 0;
    double lineWidth = 0;
    lineCount = 0;
    var p = new (string Word, double WordWidth, double Width, bool IsSpace, double LineWidth)[3]; // to remember 3 previous steps
    foreach (string word in SplitIntoWordsAndSpaces(text))
    {
        double wordWidth = word == " " ? space.Width : gfx.MeasureString(word, font).Width;
        bool isNewLine = word is "\n" or "\r" or "\r\n";
        bool isSpace = word.Length == 1 && char.IsWhiteSpace(word[0]);
        lineWidth += wordWidth;
        if (isNewLine || lineWidth > maxWidth && maxWidth > space.Width) // if our maxWidth is smaller than the space width (e.g. 0), we don't wrap at all
        {
            lineWidth = isSpace || isNewLine ? 0 : wordWidth; // if we are breaking at a white-space or a new line, forget its width
            int previous = 0;
            if (p[0].IsSpace)
                width = p[1].Width; // roll-back any width increase due to the space before this word
            else if (word == "-" && p[0].Word != "-") // a special case for a hyphen - we break after it, not before
            {
                lineWidth += p[0].WordWidth; // keep the hyphen together with the previous word
                width = p[p[1].IsSpace ? 2 : 1].Width; // roll-back any width increase due to our word followed by the hyphen
                previous = 1;
            }
            if (isNewLine || p[previous].LineWidth > 0)
                lineCount++;
        }
        width = Math.Max(width, lineWidth);
        p[2] = p[1];
        p[1] = p[0];
        p[0] = (word, wordWidth, width, isSpace, lineWidth);
    }
    if (lineWidth > 0)
        lineCount++;

    if (lineCount > 1)
    {
        double singleLineSpace = font.GetHeight();
        double lineHeight = format.LineSpacingRule switch
        {
            LineSpacingRule.Single => singleLineSpace,
            LineSpacingRule.OnePtFive => 1.5 * singleLineSpace,
            LineSpacingRule.Double => 2.0 * singleLineSpace,
            LineSpacingRule.Multiple => format.LineSpacing * singleLineSpace,
            LineSpacingRule.AtLeast => Math.Max(singleLineSpace, format.LineSpacing),
            LineSpacingRule.Exactly => format.LineSpacing,
            _ => 0
        };
        return new XSize(width, space.Height + (lineCount - 1) * lineHeight);
    }

    return new XSize(width, space.Height);
}

private static IEnumerable<string> SplitIntoWordsAndSpaces(string s)
{
    int i = 0;
    int j = 0;
    while (j < s.Length)
    {
        if (char.IsWhiteSpace(s[j]) || s[j] == '-')
        {
            int len = j < s.Length - 1 && s.Substring(j, 2) == "\r\n" ? 2 : 1;
            if (i < j)
                yield return s[i..j];
            yield return s.Substring(j, len);
            j += len;
            i = j;
        }
        else
            j++;
    }
    if (i < j)
        yield return s[i..j];
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文