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

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

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

Я проверил, что это правильно, визуально изучив калибровочную 11-точечную цифру, и она действительно имеет ширину 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.

любые предложения, пожалуйста?

спасибо, Тим!--8-->

5 ответов


сначала позвольте мне сказать, что код, который я собираюсь показать, выглядит как ужасный Хак, и я не представляю его как решение. Скорее, я надеюсь, что это покажет немного больше о поведении 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 по умолчанию заключается в добавлении отступа в ограничивающий прямоугольник нарисованного текста для размещения нависающих глиф

у вас есть


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, он может настроить форму цифры так, чтобы ее основные стебли падали на границы пикселей. Добавить в хотя бы один пиксель.


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

вот самый точный способ измерить длину текста:

    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: