Занимательное о шрифтах в .NET



Сегодня мы поговорим о замечательном классе Font. Иногда при работе с шрифтами возникают некоторые вопросы, которые не настолько очевидны, как хотелось бы. Рассмотрим несколько из них.


Q: Как опознать моноширинный шрифт?

A: Это не такой простой вопрос. Если немного погуглить, то можно найти следующий совет: описываем у себя в проекте класс LOGFONT и используем метод ToLogFont для конвертации шрифта в соответствующий объект. После этого (согласно легенде) в поле lfPitchAndFamily первый бит должен определять моноширинность шрифта. Так вот, это враньё, в современном мире поле всегда будет равно нулю. Когда-то где-то этот способ работал, но сейчас не работает. В реальности приходится использовать не очень красивое, но весьма эффективное решение типа такого:

// graphics — заранее созданный экземпляр класса Graphics
public static bool IsMonospace(Font font)
{
    return Math.Abs(graphics.MeasureString("iii", font).Width - 
                    graphics.MeasureString("WWW", font).Width) < 1e-3;
}

Q: А как узнать размеры, которые будет занимать строчка при рисовании данным шрифтом?

A: Нам понадобится Graphics, с помощью которого мы собираемся рисовать, а именно — его метод MeasureString. Передаём ему рисуемый текст и используемый шрифт — а он нам в ответ отдаёт его размеры.


Q: А если мне нужен размер не всей строчки, а только заданных её частей?

A: Это можно сделать с помощью метода Graphics.MeasureCharacterRanges. Но сначала (в msdn есть хороший пример) нужно задать целевые интервалы символов с помощью метода StringFormat.SetMeasurableCharacterRanges. Это метод обладает занимательным ограничением — ему нельзя передавать более 32-х интервалов.


Q: Этот метод выдаёт какие-то слишком большие границы. В них попадают не только сами символы, но и немного пространства около них. Что делать?

A: Действительно, возвращаемые регионы содержат интересующие нас символы так, как они идут в исходном шрифте — вместе с небольшой пустотой около них. Красивого способа получить точные границы нет. Придётся явно создавать картинку с целевыми символами, попиксельно её просмотреть (только не используйте метод Bitmap.GetPixel, он очень долгий, есть более быстрые способы) и найти крайние нарисованные символы нашей строки.


Q: Я создал шрифт, используя его строковое название, никаких исключений не вылетело. А этот шрифт точно есть в системе?

A: Не обязательно. Конструктор класса Font попытается подобрать самый подходящий (по его мнению) шрифт для данного названия. Лучше проверить, что создался правильный шрифт:

var font = new Font(fontName, 12);
vat isExist = font.Name == fontName;

А ещё не помешает проверить, поддерживает ли используемое вами семейство шрифтов ваш FontStyle:

var fontFamily = new FontFamily(fontName);
isExist &= fontFamily.IsStyleAvailable(fontStyle);
Поделиться:
Исходный код поста находится на GitHub:
https://github.com/AndreyAkinshin/aakinshin.net/blob/master/ru/_posts/dotnet/fonts.md