【问题标题】:Length of string that will fit in a specific width适合特定宽度的字符串长度
【发布时间】:2010-12-27 23:50:42
【问题描述】:

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

我还想知道逆向,即我可以在特定宽度中容纳多少个字符。

我认为 WPF 不受像素限制的事实会对答案有一定影响吗?

最终我打算将文本环绕在嵌入在文本中的不规则形状的图像周围。

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

谢谢

【问题讨论】:

    标签: wpf wpf-controls


    【解决方案1】:

    对于 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;
    }
    

    【讨论】:

    • 很好的开始,我们是否知道如何做相反的事情,或者我需要应用某种分而治之的策略?即测量“我是否太长而无法容纳在这个空间中”,然后如果 > 可用宽度,则测量“我是否太长”,如果太短,请尝试介于两者之间?
    • @Ian:编写一个返回适合给定宽度的字符数的方法应该很简单,我将从上面的编辑(显然简化)开始,如果性能是一个更复杂的问题 - 我没有做过任何测量。
    • 是的。未知字符串的每个字符到底有多宽?不可能,只能“假设”(平均)。
    • @Tom Tom:为什么投反对票?如果您检查解决方案,您会发现它确实取决于实际文本,我从一开始就明确表示。
    • 迭代算法完全是矫枉过正,伙计们。您需要做的就是计算字符串对于给定空间过小或过大的百分比,并使用该因子缩放字体大小
    【解决方案2】:

    @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;
    }
    

    【讨论】:

    • 您的答案过于复杂,有一个更简单的解决方案。您需要做的就是计算字符串对于给定空间过小或过大的百分比,并使用该因子缩放字体大小。
    • OP 或我们的应用程序都不接受更改字体大小。
    【解决方案3】:

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

    【讨论】:

    • 这不是不可能的。整个排版取决于能否做到这一点。从历史上看,我们有诸如 MeasureText 和 MeasureString 之类的东西,以及项目上的 Measure 事件。想一想,看看你正在阅读的网页,注意文字是如何适应可用空间的。
    • 啊,'但是排版不像你那样。这就像从屋顶盖房子。你不知道有多少个字符适合一个宽度,你知道一个 SPECIFIC TEXT 有多宽。在不知道文本的情况下,无法计算宽度。 Typogrpahy 不会以要测量的“未知随机文本”开头。
    • 对不起,我不是故意的。我知道文本,我知道它必须进入的行的宽度。我知道字体、字体大小等等。所以我有我需要的所有位来计算它,我只是不知道如何:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-03
    • 1970-01-01
    • 1970-01-01
    • 2017-06-13
    • 1970-01-01
    • 2014-05-09
    相关资源
    最近更新 更多