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

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

На примере шрифта Calibri максимальная ширина цифры в 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.

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

Спасибо, Тим

Ответы [ 5 ]

5 голосов
/ 02 декабря 2009

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

5 голосов
/ 02 декабря 2009

Сначала позвольте мне сказать, что код, который я собираюсь показать, выглядит как ужасный взлом, и я не представляю его как решение. Скорее, я надеюсь, что это немного больше расскажет о поведении 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);
   }
5 голосов
/ 02 декабря 2009

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

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

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

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

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

3 голосов
/ 02 декабря 2009

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

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

    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 );
    }
1 голос
/ 02 декабря 2009

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

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

...