Как измерить ширину цифры в пикселях заданного шрифта/размера (C#)

StackOverflow https://stackoverflow.com/questions/1833062

Вопрос

Я пытаюсь вычислить ширину столбцов Excel в пикселях, как описано в разделе эта почта, используя официальную формулу из спецификации OpenXML.Однако, чтобы применить эту формулу, мне нужно знать Максимальная ширина цифр принадлежащий Нормальный шрифт, который представляет собой ширину самой широкой числовой цифры в пикселях.Спецификация OpenXML дает этот пример в качестве пояснения:

Используя шрифт калибрии в качестве примера, максимальная цифра ширины 11 -балльного размера шрифта составляет 7 пикселей (при 96 dpi).

Я проверил, что это правильно, визуально изучив 11-значную цифру Calibri, и она действительно имеет ширину 7 пикселей.Итак, я пытаюсь создать метод, который будет возвращать максимальную ширину цифр для любого шрифта/размера.

Я выполнил рекомендации, изложенные в этот вопрос, но это не дает ожидаемых результатов.

Вот мой тестовый код:

var font = new Font("Calibri", 11.0f, FontStyle.Regular);

for (var i = 0; i < 10; i++)
{
    Debug.WriteLine(TextRenderer.MeasureText(i.ToString(), font));
}

Это сообщает, что все цифры имеют ширину 15.

Есть предложения, пожалуйста?

Спасибо, Тим

Это было полезно?

Решение

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

Я внес небольшое изменение в ваш код.Я измеряю длину двух символов подчеркивания.Затем в теле цикла for я измеряю нужный символ с подчеркиванием с обеих сторон и вычитаю длину только двух символов подчеркивания.Для тестового сценария я получаю результат 7.Основная цель этого заключалась в том, чтобы определить, учитывает ли MeasureText заполнение посимвольно или заполнение в начале/конце строки.Похоже, что это последнее.Возможно, это, в сочетании с другими полученными вами сведениями, приведет вас к более элегантному решению.

var font = new Font("Calibri", 11.0f, FontStyle.Regular);
int underscoreWidth = TextRenderer.MeasureText("__", font).Width; 
for (var i = 0; i < 10; i++)
   {
      int charWidth = TextRenderer.MeasureText(String.Concat("_", i.ToString(), "_"), font).Width - underscoreWidth;
      Debug.WriteLine(charWidth);
   }

Другие советы

TextRenderer не всегда точен, как вы ожидаете:

Из MSDN: http://msdn.microsoft.com/en-us/library/8wafk2kt.aspx

Например, поведение TextRenderer по умолчанию — добавление отступов к ограничивающему прямоугольнику нарисованного текста для размещения нависающих глифов.

У вас есть Graphics объект или объект управления?Если вы их используете, вы можете получить точные результаты, как и ожидали.

Взгляни на http://msdn.microsoft.com/en-us/library/6xe5hazb(VS.80).aspx

MeasureText добавляет отступы, величина довольно непредсказуема, хотя примерно линейно масштабируется в зависимости от размера шрифта.Хитрость заключается в том, чтобы вычесть два измерения, чтобы избавиться от заполнения.Этот код генерировал очень счастливые числа:

        float size = 5;
        for (int ix = 0; ix < 10; ++ix) {
            var font = new Font("Calibri", size, FontStyle.Regular);
            string txt = new string('0', 100);
            SizeF sz1 = TextRenderer.MeasureText("00", font) - TextRenderer.MeasureText("0", font);
            Console.WriteLine("{0} {1:N3}", size, sz1.Width);
            size += 2;
        }

Выход:

5 4.000
7 5.000
9 6.000
11 7.000
13 9.000
15 10.000
17 12.000
19 13.000
21 14.000
23 16.000

Это даст вам 7 пикселей, которые вы ищете.Имейте в виду, что вам придется использовать хинтинг TrueType: он может регулировать форму цифры так, чтобы ее основные основы попадали на границы пикселей.Добавьте хотя бы один пиксель.

Измерение ширины текста может оказаться кошмаром.Не знаю, почему они так запутали..

Вот самый точный способ измерения длины текста:

    protected int _MeasureDisplayStringWidth ( Graphics graphics, string text, Font font )
    {
        if ( text == "" )
            return 0;

        StringFormat format = new StringFormat ( StringFormat.GenericDefault );
        RectangleF rect = new RectangleF ( 0, 0, 1000, 1000 );
        CharacterRange[] ranges = { new CharacterRange ( 0, text.Length ) };
        Region[] regions = new Region[1];

        format.SetMeasurableCharacterRanges ( ranges );
        format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;

        regions = graphics.MeasureCharacterRanges ( text, font, rect, format );
        rect = regions[0].GetBounds ( graphics );

        return (int)( rect.Right );
    }

Я не на 100% уверен, что это именно то, что вам нужно.

Но вы можете взглянуть на эту страницу Microsoft:

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top