При подборе шрифта используется система “пенальти”: для каждого из шрифтов он вычисляет “пенальти”, соответствующие отличию данного шрифта от желаемого. Шрифт с минимальным пенальти считается наиболее точно соответствующим желаемому.
Пенальти вычисляются следующим образом: для существенных параметров вводятся бальные оценки. Если заказанный параметр соответствует шрифту, пенальти равно 0, а если отличается, то размер пенальти зависит от параметра. Значения пенальти приведены в следующей таблице:
параметр | пенальти |
fCharSet | 4 |
fPitchAndFamily: pitchfamily | 33 |
lfFaceName | 3 |
lfHeight | 2 |
lfWidth | 2 |
lfItalic | 1 |
lfUnderline | 1 |
lfStrikeOut | 1 |
В этой таблице можно заметить несколько интересных особенностей. Например, пенальти за несоответствие кодовой таблице больше, а за не–принадлежность к семейству равна пенальти за несоответствие имени шрифта. Практически, если Вы хотите использовать шрифт с конкретным именем, то Вы должны обязательно указать правильные кодовую страницу и семейство шрифтов, иначе Windows может использовать шрифт с другим именем.
1.4 Функции для работы со шрифтами
После того, как Вы получили хендл шрифта, Вы можете осуществить вывод текста, используя этот шрифт. Для этого Вы должны выбрать шрифт в контекст устройства с помощью функции
SelectObject( hDC, hFont );
Все последующие операции вывода будут использовать тот шрифт, который Вы выбрали в контекст устройства. Если Вам надо получить информацию о загруженном шрифте, то Вы можете воспользоваться функциями:
int GetTextFace( hDC, nMaxBuffer, lpsBuffer );
int GetObject( hFont, sizeof(LOGFONT), &stLogfont );
Функция GetTextFace() заполняет указанный буфер именем применяемого шрифта, а функция GetObject() позволяет заполнить структуру LOGFONT информацией о конкретном шрифте.
Более сложным представляется все-таки выбор шрифта и определение характеристик вновь создаваемого шрифта. Это связано с тем, что в большинстве случаев Вы заранее не знаете, какие шрифты используются в данном комплекте Windows, то есть Вы должны уметь выбирать нужный шрифт из числа имеющихся в Windows.
Это можно осуществить двумя разными способами - Вы можете перебирать все шрифты и выбрать из них нужный Вам (например создать меню, содержащее имена шрифтов), или Вы можете воспользоваться функцией ChooseFont() для вызова стандартного диалога выбора шрифта.
Сначала мы разберемся с основными правилами перебора шрифтов. В Windows для этих целей существует две функции:
int EnumFonts( hDC, lpszFace, lpfnEnumProc, lParam );
int EnumFontFamilies( hDC, lpszFace, lpfnEnumProc, lParam );
Обе эти функции осуществляют перебор шрифтов, которые могут быть применены на указанном устройстве, и имеющие заданное имя (значение NULL указывает на перебор всех доступных шрифтов). Эти функции осуществляют перебор чуть-чуть различающимся образом.
Разница связана с тем, что TrueType шрифты обычно существуют в нескольких вариантах, отличающихся начертанием (жирный, наклонный и др.). Эти варианты существуют как отдельные шрифты, поэтому функция EnumFonts() перечисляет один и тот–же TrueType шрифт несколько раз, соответственно с количеством разных начертаний. Функция EnumFontFamilies() перебирает только по одному начертанию каждого TrueType шрифта.
Для каждого перечисляемого шрифта вызывается функция lpfnEnumProc(), которой передается структуры типа LOGFONT и TEXTMETRIC (функция EnumFontFamilies() передает структуры NEWLOGFONT и NEWTEXTMETRIC, содержащие дополнительные данные), номер типа шрифта (DEVICE_FONTTYPE, RASTER_FONTTYPE или TRUETYPE_FONTTYPE) и параметр lParam, который Вы указали для функции EnumFonts() или EnumFontFamilies().
Общий вид функции, вызываемой при переборе шрифтов, следующий:
intCALLBACK _exportEnumFontProc( lpLF, lpTM, nType, lParam );
intCALLBACK _exportEnumFontFamProc( lpNLF, lpNTM, nType, lParam );
Параметры:
lpLF является дальним адресом структуры LOGFONT
lpNLF является дальним адресом структуры NEWLOGFONT
lpTM является дальним адресом структуры TEXTMETRIC
lpNTM является дальним адресом структуры NEWTEXTMETRIC
nType указывает тип шрифта
lParam дополнительный параметр, определяемый Вами.
Осуществляя перебор шрифтов, Вы можете, например, заполнить меню, содержащее имена нужных Вам шрифтов, или выбрать тот шрифт, который Вас устраивает и т.д.
Во многих случаях удобнее, однако, не перебирать шрифты, а воспользоваться каким-либо диалогом для выбора нужного шрифта. Это можно сделать с помощью функции ChooseFont(), описанной в файле COMMDLG.H.
Файл COMMDLG.H содержит описания нескольких функций и структур данных, позволяющих вызывать “диалоги общего пользования” (COMMon DiaLoGs). Помимо файла COMMDLG.H Вы должны включить в Ваше приложение файл COMMDLG.LIB, с помощью которого осуществляется связывание Вашего приложения с динамической библиотекой COMMDLG.DLL, содержащей требуемые функции.
Функция, вызывающая диалог для выбора шрифта выглядит следующим образом:
BOOL ChooseFont( lpChooseFont );
причем параметр lpChooseFont указывает на структуру типа CHOOSEFONT:
typedef struct tagCHOOSEFONT { /* cf */
DWORD lStructSize; // = sizeof(CHOOSEFONT)
HWND hwndOwner;
HDC hDC; // используется только для принтера
LOGFONTFAR* lpLogFont;
intiPointSize;
DWORDFlags;
COLORREFrgbColors;
LPARAMlCustData;
UINT (CALLBACK* lpfnHook)(HWND, UINT, WPARAM, LPARAM);
LPCSTRlpTemplateName;
HINSTANCEhInstance;
LPSTRlpszStyle;
UINTnFontType;
intnSizeMin;
intnSizeMax;
} CHOOSEFONT;
Вы должны заполнить нужные поля этой структуры и вызвать функцию ChooseFont() для выбора нужного шрифта. Функция возвращает результат TRUE (не 0), если шрифт был выбран, или FALSE (0), если была нажата кнопка “Cancel”.
Заполнение полей этой функции элементарно, рассмотреть стоит только поле Flags, описывающее характеристики диалога и выбираемого шрифта. С помощью этого поля Вы можете уточнить, из какой группы Вы собираетесь выбирать шрифт:
по устройствам:
CF_PRINTERFONTS шрифты принтера (Вы должны указать hDC принтера)
CF_SCREENFONTS дисплейные шрифты
CF_BOTH все
по типам шрифтов:
CF_TTONLY только TrueType
CF_NOVECTORFONTS растровые и TrueType
CF_SCALABLEONLY векторные, TrueType и некоторые шрифты принтера
CF_WYSIWYG шрифты, используемые и дисплеем и принтером. (вместе с CF_WYSIWYG надо установить CF_BOTH|CF_SCALABLEONLY)
по кодировке:
CF_ANSIONLY только ANSI шрифты
CF_NOOEMFONTS все шрифты кроме OEM
по особенностям
CF_FIXEDPITCHONLY только моноширинные шрифты
CF_FORCEFONTEXIST шрифт с выбранными атрибутами должен существовать (не допускается автоматическое преобразование) по размеру
CF_LIMITSIZE установив этот флаг Вы должны задать поля nSizeMin и nSizeMax, которые определят допустимые размеры шрифтов.
А также Вы можете несколько видоизменять диалог:
по наличию кнопок:
CF_APPLY присутствует кнопка “Apply” (Применить)
CF_USEHELP присутствует кнопка “Help” (Справка)
по правилам инициализации:
CF_INITTOLOGFONTSTRUCT использовать данные структуры LOGFONT (указанной в CHOOSEFONT) для инициализации диалога
по возможности выбирать параметры:
CF_EFFECTS диалог позволит установить стили подчеркивание (underline) и перечеркивание (overstrike)
CF_NOFACESEL нельзя выбирать имя шрифта из списка
CF_NOSIMUALTIONS запрещена имитация шрифта с помощью GDI
CF_NOSIZESEL нельзя выбирать размер шрифта
CF_NOSTYLESEL нельзя выбирать стиль шрифта.
Дополнительные возможности этой функции связаны с возможностью видоизменять сам диалог, (в том числе самостоятельно спроектировать шаблон диалога), и функцию диалога, для выполнения дополнительных действий.
2. Шрифты в качестве ресурсов
2.1 Применение шрифтовых ресурсов
Сейчас мы рассмотрим последнюю тему, связанную со шрифтами, а именно - создание собственных шрифтовых ресурсов. Ранее мы встречались с одной из разновидностей ресурсов - битмапом. Тогда битмап включался в ресурс и становился доступным приложению. Для этого мы в файле описания ресурсов включали строку вида:
name BITMAP “file.bmp”
По аналогии хочется поступить также и со шрифтом, тем более, что существует такой вид ресурсов - FONT. Однако этот метод не работает. Это связано с тем, что все шрифты в Windows доступны всем приложениям. В этом случае включать шрифт в приложение становиться невозможным - так как в момент его завершения шрифт может использоваться другим приложением. Поэтому шрифтовые ресурсы в Windows оформляются в виде отдельных файлов.
Так как шрифты доступны всем приложениям, то мы сначала должны включить свой шрифт в системную таблицу шрифтов. При этом шрифт становится доступным всем приложениям Windows (в том числе и нашему). Теперь мы можем вызвать функцию CreateFont() или CreateFontIndirect() для получения хендла шрифта, а в конце работы, после уничтожения созданного шрифта, мы должны удалить его из системной таблицы.
В некоторых случаях может быть удобным добавление шрифта в список шрифтов, автоматически попадающих в системную таблицу при запуске Windows. Для этого Вы должны добавить строку в файл WIN.INI, секция [fonts] (как это делается - позже, когда будем рассматривать настройку приложений). При этом все последующие запуски Windows будет автоматически добавлять Ваш шрифт в системную таблицу. Однако в текущем сеансе этого автоматически не происходит, так что Вы должны сами добавить его в таблицу.
Для включения шрифта в системную таблицу надо воспользоваться функцией:
int AddFontResource( lpszFileName );
возвращаемое значение указывает число шрифтов, добавленных в системную таблицу из этого файла, значение 0 указывает на ошибку.
Обычно шрифтовые файлы имеют расширение .FON; Такой файл может содержать несколько шрифтов с общим именем, но разными размерами символов. Windows будет использовать шрифт того размера, который наиболее точно подходит к запрашиваемому.
Если добавленный шрифт не предназначен строго для внутреннего использования, то Вы должны послать всем приложениям сообщение о смене шрифта:
SendMessage( HWND_BROADCAST, WM_FONTCHANGE, 0, 0L );
Для удаления шрифта из системной таблицы Вы должны воспользоваться функцией
BOOL RemoveFontResource( lpszFileName );
Как и при добавлении шрифта, если этот шрифт может применяться другими приложениями, то Вы должны послать сообщение WM_FONTCHANGE.
Добавлять или удалять шрифты из системной таблицы удобно либо в начале и конце приложения, либо при создании и удалении главного окна приложения.