Длина строки, которая будет соответствовать определенной ширине - PullRequest
9 голосов
/ 28 декабря 2010

Я уверен, что упускаю что-то очевидное, у меня есть область, в которой я собираюсь рисовать текст. Я знаю его (площадь) высоту и ширину. Я хочу знать, сколько символов / слов будет соответствовать ширине, желательно символов. Второй вопрос: если линия слишком длинная, я хочу нарисовать вторую линию, поэтому я думаю, мне нужно также получить высоту текста, включая то, что он считает правильным вертикальным заполнением?

Я также хотел бы знать обратное, т. Е. Сколько символов я могу уместить на определенной ширине.

Я предполагаю, что факт, что WPF не ограничен пикселями, будет иметь какое-то отношение к ответу?

В конечном итоге я планирую обернуть текст вокруг изображений неправильной формы, встроенных в текст.

Любые указатели в правильном направлении были бы великолепны.

Спасибо

Ответы [ 3 ]

16 голосов
/ 28 декабря 2010

Для 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;
}
3 голосов
/ 24 февраля 2012

@ Ответ 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;
}
0 голосов
/ 28 декабря 2010

Это невозможно, потому что символы имеют разную длину, например W намного шире, чем я (если вы не используете шрифт, такой как Courier New).

...