适合特定宽度的字符串长度

发布于 2024-10-09 12:45:48 字数 282 浏览 0 评论 0原文

我确信我错过了一些明显的东西,我有一个我打算在其中绘制文本的区域。我知道它(区域)的高度和宽度。我想知道宽度可以容纳多少个字符/单词,最好是字符。第二个问题,如果该行太长,我想绘制第二条线,所以我想我还需要获取文本的高度,包括它认为正确的垂直填充是什么?

我还想知道相反的情况,即在特定宽度中可以容纳多少个字符。

我认为 WPF 不受像素限制这一事实会对答案产生一些影响?

最终,我计划将文本包裹在嵌入文本中的不规则形状图像周围。

任何指向正确方向的指针都会很棒。

谢谢

I'm sure I'm missing something obvious, I have an area in which I intend to draw text. I know its (the area) height and width. I wish to know how many characters/Words will fit in the width, characters preferably. Second question, If the line is too long I'll want to draw a second line, so I guess I need to get the height of the text as well, including what ever it considers the right vertical padding?

I'd also rather like to know the inverse, i.e. how many characters I can fit in a specific width.

I assume the fact that WPF isn't constrained to pixels will have some bearing on the answer?

Ultimately I'm planning on wrapping text around irregular shaped images embedded in the text.

Any pointers in the right direction would be great.

Thanks

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

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

发布评论

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

评论(3

月牙弯弯 2024-10-16 12:45:48

对于 WPF,您可以使用 FormattedText 类来计算给定文本字符串将使用多少宽度 - 这将取决于实际文本。

示例:

FormattedText formattedText = new FormattedText("Hello Stackoverflow", 
                                                System.Globalization.CultureInfo.GetCultureInfo("en-us"), 
                                                FlowDirection.LeftToRight, 
                                                new Typeface("Arial"), FontSize =14, Brushes.Black);
double textWidth = formattedText.Width;

获取给定宽度的子字符串(简化):

string text  = GetSubStringForWidth("Hello Stackoverflow", 55);
...
public string GetSubStringForWidth(string text, double width)
{
    if (width <= 0)
        return "";

    int length = text.Length;
    string testString;

    while(true)//0 length string will always fit
    {
        testString = text.Substring(0, length);
        FormattedText formattedText = new FormattedText(testString, 
                                                        CultureInfo.GetCultureInfo("en-us"), 
                                                        FlowDirection.LeftToRight, 
                                                        new Typeface("Arial"), 
                                                        FontSize = 14, 
                                                        Brushes.Black);
        if(formattedText.Width <= width)
            break;
        else
            length--;
    }
    return testString;
}

For WPF you can use the FormattedText class to calculate how much width a given text string will use - it will depend on the actual text.

Example:

FormattedText formattedText = new FormattedText("Hello Stackoverflow", 
                                                System.Globalization.CultureInfo.GetCultureInfo("en-us"), 
                                                FlowDirection.LeftToRight, 
                                                new Typeface("Arial"), FontSize =14, Brushes.Black);
double textWidth = formattedText.Width;

Getting a sub string for a given width (simplified):

string text  = GetSubStringForWidth("Hello Stackoverflow", 55);
...
public string GetSubStringForWidth(string text, double width)
{
    if (width <= 0)
        return "";

    int length = text.Length;
    string testString;

    while(true)//0 length string will always fit
    {
        testString = text.Substring(0, length);
        FormattedText formattedText = new FormattedText(testString, 
                                                        CultureInfo.GetCultureInfo("en-us"), 
                                                        FlowDirection.LeftToRight, 
                                                        new Typeface("Arial"), 
                                                        FontSize = 14, 
                                                        Brushes.Black);
        if(formattedText.Width <= width)
            break;
        else
            length--;
    }
    return testString;
}
烏雲後面有陽光 2024-10-16 12:45:48

@BrokenGlass 的答案很好,但根据您的应用程序的特征,您可能会发现通过二分搜索可以获得更好的性能。如果大多数字符串都适合可用宽度,或者通常只需要修剪一两个字符,那么线性搜索是最好的。但是,如果您有很多将被严重截断的长字符串,则以下二分查找将效果很好。

请注意,availableWidth 和 fontSize 均以与设备无关的单位(1/96 英寸)指定。另外,请使用与您绘制文本的方式相匹配的 TextFormattingMode。

public static string TruncateTextToFitAvailableWidth(
    string text, 
    double availableWidth, 
    string fontName, 
    double fontSize)
{
    if(availableWidth <= 0)
        return string.Empty;

    Typeface typeface = new Typeface(fontName);

    int foundCharIndex = BinarySearch(
        text.Length,
        availableWidth,
        predicate: (idxValue1, value2) =>
        {
            FormattedText ft = new FormattedText(
                text.Substring(0, idxValue1 + 1), 
                CultureInfo.CurrentCulture, 
                FlowDirection.LeftToRight, 
                typeface, 
                fontSize, 
                Brushes.Black,
                numberSubstitution: null,
                textFormattingMode: TextFormattingMode.Ideal);

            return ft.WidthIncludingTrailingWhitespace.CompareTo(value2);
        });

    int numChars = (foundCharIndex < 0) ? ~foundCharIndex : foundCharIndex + 1;

    return text.Substring(0, numChars);
}

/**
<summary>
See <see cref="T:System.Array.BinarySearch"/>. This implementation is exactly the same,
except that it is not bound to any specific type of collection. The behavior of the
supplied predicate should match that of the T.Compare method (for example, 
<see cref="T:System.String.Compare"/>).
</summary>
*/      
public static int BinarySearch<T>(
    int               length,
    T                 value,
    Func<int, T, int> predicate) // idxValue1, value2, compareResult
{
    return BinarySearch(0, length, value, predicate);
}

public static int BinarySearch<T>(
    int               index,
    int               length,
    T                 value,
    Func<int, T, int> predicate)
{
    int lo = index;
    int hi = (index + length) - 1;

    while(lo <= hi)
    {
        int mid = lo + ((hi - lo) / 2);

        int compareResult = predicate(mid, value);

        if(compareResult == 0)
            return mid;
        else if(compareResult < 0)
            lo = mid + 1;
        else
            hi = mid - 1;
    }

    return ~lo;
}

@BrokenGlass's answer is great, but depending on the characteristics of your application, you may find you get better performance with a binary search. If the majority of your strings fit in the available width, or generally only need to be trimmed by a character or two, then a linear search is best. However, if you have a lot of long strings that will be severely truncated, the following binary search will work well.

Note that both availableWidth and fontSize are specified in device-independent units (1/96ths of an inch). Also, use the TextFormattingMode that matches the way you draw your text.

public static string TruncateTextToFitAvailableWidth(
    string text, 
    double availableWidth, 
    string fontName, 
    double fontSize)
{
    if(availableWidth <= 0)
        return string.Empty;

    Typeface typeface = new Typeface(fontName);

    int foundCharIndex = BinarySearch(
        text.Length,
        availableWidth,
        predicate: (idxValue1, value2) =>
        {
            FormattedText ft = new FormattedText(
                text.Substring(0, idxValue1 + 1), 
                CultureInfo.CurrentCulture, 
                FlowDirection.LeftToRight, 
                typeface, 
                fontSize, 
                Brushes.Black,
                numberSubstitution: null,
                textFormattingMode: TextFormattingMode.Ideal);

            return ft.WidthIncludingTrailingWhitespace.CompareTo(value2);
        });

    int numChars = (foundCharIndex < 0) ? ~foundCharIndex : foundCharIndex + 1;

    return text.Substring(0, numChars);
}

/**
<summary>
See <see cref="T:System.Array.BinarySearch"/>. This implementation is exactly the same,
except that it is not bound to any specific type of collection. The behavior of the
supplied predicate should match that of the T.Compare method (for example, 
<see cref="T:System.String.Compare"/>).
</summary>
*/      
public static int BinarySearch<T>(
    int               length,
    T                 value,
    Func<int, T, int> predicate) // idxValue1, value2, compareResult
{
    return BinarySearch(0, length, value, predicate);
}

public static int BinarySearch<T>(
    int               index,
    int               length,
    T                 value,
    Func<int, T, int> predicate)
{
    int lo = index;
    int hi = (index + length) - 1;

    while(lo <= hi)
    {
        int mid = lo + ((hi - lo) / 2);

        int compareResult = predicate(mid, value);

        if(compareResult == 0)
            return mid;
        else if(compareResult < 0)
            lo = mid + 1;
        else
            hi = mid - 1;
    }

    return ~lo;
}
凯凯我们等你回来 2024-10-16 12:45:48

这是不可能的,因为字符具有不同的长度,例如 W 比 i 宽得多(除非您使用像 Courier New 这样的字体)。

This is impossible because characters have different length, for example W is much wider than i (unless you're using a font like Courier New).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文