Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Графический интерфейс GDI в Microsoft Windows

© Александр Фролов, Григорий Фролов
Том 14, М.: Диалог-МИФИ, 1993, 288 стр.

[Назад] [Содеожание] [Дальше]

5.2. Выбор шрифта в контекст отображения

Для того чтобы написать строку текста заданным шрифтом, этот шрифт следует, подобно остальным объектам GDI, выбрать в контекст отображения. После этого функции TextOut, DrawText и аналогичные будут использовать для вывода текста нужный вам шрифт.

Приложения Windows могут использовать либо один из встроенных шрифтов, либо создать свой, описав требуемые характеристики шрифта. В любом случае в распоряжение пользователя будет предоставлен один из шрифтов, зарегистрированных при установке Windows или позже (с помощью Control Panel). Для выбора шрифта, соответствующего описанию, используется достаточно сложный алгоритм, учитывающий степень важности обеспечения соответствия параметров предоставленного шрифта запрошенным параметрам.

Обратим ваше внимание на одно важное обстоятельство.

Приложение заказывает шрифт, описывая его параметры. GDI анализирует запрошенные параметры и подбирает наиболее подходящий шрифт. При этом приложение не может "заставить" GDI выделить ему какой-то конкретный шрифт, указав его название или путь к файлу. Однако приложение может определить параметры шрифта, выбранного пользователем из состава установленных шрифтов, и запросить у GDI шрифт с этими параметрами. В последнем случае будет выделен шрифт, выбранный пользователем.

Выбор встроенного шрифта

По умолчанию в контекст отображения при его создании выбирается системный шрифт, основным (и почти единственным) преимуществом которого является то, что он всегда доступен. Системный шрифт не является масштабируемым, содержит буквы переменной ширины, не имеющие засечек, для него используется кодировка ANSI.

Однако в некоторых случаях вам может понадобиться шрифт с фиксированной шириной букв, или шрифт в кодировке OEM. Вы можете получить идентификатор одного из встроенных шрифтов при помощи макрокоманды GetStockFont , описанной в файле windowsx.h:

#define GetStockFont(i) ((HFONT)GetStockObject(i))

В качестве единственного параметра этой макрокоманде следует передать идентификатор одного из встроенных шрифтов:

Идентификатор Описание
SYSTEM_FONT Системный шрифт в кодировке ANSI с переменной шириной букв, используется операционной системой Windows для отображения текста в меню, заголовках окон и диалоговых панелях
SYSTEM_FIXED_FONT Шрифт в кодировке ANSI с фиксированной шириной букв. Использовался в старых версиях операционной системой Windows (до версии 3.0) как системный шрифт
ANSI_VAR_FONT Шрифт в кодировке ANSI с переменной шириной букв
ANSI_FIXED_FONT Шрифт в кодировке ANSI с фиксированной шириной букв
OEM_FIXED_FONT Шрифт в кодировке OEM с фиксированной шириной букв
DEVICE_DEFAULT_FONT Шрифт, который используется для данного устройства по умолчанию. Если устройство не имеет своих шрифтов, используется системный шрифт SYSTEM_FONT

После того как вы получили идентификатор шрифта, этот шрифт можно выбрать в контекст отображения макрокомандой SelectFont :

#define SelectFont(hdc, hfont) \
  ((HFONT)SelectObject((hdc), (HGDIOBJ)(HFONT)(hfont)))

Первый параметр этой макрокоманды определяет идентификатор контекста отображения, в который выбирается шрифт с идентификатором hfont. Она возвращает идентификатор шрифта, который был выбран в контекст отображения раньше, до вызова SelectFont.

Вам не нужно удалять встроенные шрифты, так же как не нужно удалять встроенные кисти и перья.

Определение логического шрифта

Если приложение ограничивает себя только встроенными шрифтами, оно не сможет воспользоваться всем многообразием созданных масштабируемых шрифтов и даже просто не сможет изменить размер букв или сделать текст жирным или наклонным. Для полного использования шрифтовых возможностей операционной системы Windows вы должны познакомиться с процедурой определения логических шрифтов.

Приложение может получить идентификатор шрифта, указав его параметры (такие как размеры символов, семейство шрифта, наклон относительно горизонтальной оси и т. п.) функции CreateFont . Эта функция имеет 14 параметров, поэтому не слишком удобна в использовании. Вместо нее лучше пользоваться функцией CreateFontIndirect :

HFONT WINAPI CreateFontIndirect(const LOGFONT FAR* lplf);

Функция возвращает идентификатор созданного логического шрифта, который можно выбрать в контекст отображения макрокомандой SelectFont, при этом для вывода будет подобран наиболее подходящий физический шрифт.

В качестве параметра функции CreateFontIndirect передается указатель на структуру типа LOGFONT , определенную в файле windows.h:

typedef struct tagLOGFONT
{
  int  lfHeight;
  int  lfWidth;
  int  lfEscapement;
  int  lfOrientation;
  int  lfWeight;
  BYTE lfItalic;
  BYTE lfUnderline;
  BYTE lfStrikeOut;
  BYTE lfCharSet;
  BYTE lfOutPrecision;
  BYTE lfClipPrecision;
  BYTE lfQuality;
  BYTE lfPitchAndFamily;
  char lfFaceName[LF_FACESIZE];
} LOGFONT;
typedef LOGFONT*       PLOGFONT;
typedef LOGFONT NEAR* NPLOGFONT;
typedef LOGFONT FAR*  LPLOGFONT;

Перед вызовом функции CreateFontIndirect вы должны заполнить структуру LOGFONT нужными значениями, определяющими параметры шрифта. В неиспользованные поля следует записать нулевые значения. Можно записать нулевые значения во все поля, однако это едва ли имеет смысл.

Опишем назначение отдельных полей структуры LOGFONT. При этом мы будем пользоваться метриками шрифта, описанными в 11 томе "Библиотеки системного программиста" (стр. 144).

lfHeight

Высота шрифта в логических единицах (зависят от установленного режима отображения).

Можно указывать положительные и отрицательные значения, а также нуль. Если указано нулевое значение, выбирается шрифт размером в 12 пунктов (значение по умолчанию).

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

Абсолютная величина отрицательного значения определяет высоту символов, т. е. tmHeight - tmInternalLeading.

lfWidth

Ширина символов в логических единицах.

Если указано нулевое значение, используется значение по умолчанию, которое зависит от высоты шрифта и отношения масштабов по осям координат (aspect ratio) для шрифта и устройства вывода.

lfEscapement

Угол между базовой линией шрифта и координатной осью X в десятых долях градуса (угол отсчитывается в направлении против часовой стрелки).

Если в процессе отображения логического шрифта на физический будет выбран растровый или векторный шрифт, текст будет выведен в горизонтальном направлении, так как вращать можно только шрифты True Type и векторные шрифты.

lfOrientation

Это поле определяет ориентацию символов шрифта. К сожалению, операционная система Windows версии 3.1 игнорирует поле lfOrientation.

lfWeight

Вес шрифта. Определяет жирность символов шрифта и может находиться в пределах от 0 до 1000. Файл windows.h содержит определение символических констант для этого поля:

Константа Значение
FW_DONTCARE 0
FW_THIN 100
FW_EXTRALIGHT 200
FW_ULTRALIGHT 200
FW_LIGHT 300
FW_NORMAL 400
FW_REGULAR 400
FW_MEDIUM 500
FW_SEMIBOLD 600
FW_DEMIBOLD 600
FW_BOLD 700
FW_EXTRABOLD 800
FW_ULTRABOLD 800
FW_BLACK 900
FW_HEAVY 900

Вы можете использовать любое из указанных значений, однако следует иметь в виду, что многие шрифты содержат описания символов только для веса FW_NORMAL, FW_REGULAR и FW_BOLD.

lfItalic

Если содержимое этого поля не равно нулю, запрашивается шрифт с наклонными буквами.

lfUnderline

Если содержимое этого поля не равно нулю, запрашивается шрифт с подчеркиванием букв.

lfStrikeOut

Если содержимое этого поля не равно нулю, запрашивается шрифт с перечеркнутыми буквами.

lfCharSet

Набор символов.

Можно использовать одну из следующих констант, определенных в файле windows.h:

Константа Значение Описание
ANSI_CHARSET 0 Набор символов в кодировке ANSI
DEFAULT_CHARSET 1 Не используется при отображении шрифтов. Определяется при необходимости запросить шрифт с заданным именем и размером шрифта. Следует использовать с осторожностью, так как если указанного шрифта нет, GDI может выделить шрифт с любым набором символов
SYMBOL_CHARSET 2 Символьный шрифт, такой как, например, Wingdings
SHIFTJIS_CHARSET 128 Шрифт, в котором для представления символов используется двухбайтовая кодировка. Нужен для работы с японской версией Windows
OEM_CHARSET 255 Набор символов в кодировке OEM

lfOutPrecision

Требуемая степень соответствия параметров шрифта.

Это поле используется для того, чтобы указать GDI способ выбора между двумя шрифтами, имеющими одинаковое название, но разный тип. Например, для удовлетворения запроса можно использовать растровый или масштабируемый шрифт с названием OddType. Если в поле lfOutPrecision указать константу OUT_TT_PRECIS, будет выбран масштабируемый шрифт.

Можно указывать одну из следующих констант:

Константа Значение Описание
OUT_DEFAULT_PRECIS 0 Используется точность, заданная по умолчанию
OUT_STRING_PRECIS 1 Выбирается шрифт, для которого соблюдается наибольшее соответствие в размерах символов
OUT_CHARACTER_PRECIS 2 Аналогично OUT_STRING_PRECIS
OUT_STROKE_PRECIS 3 Требуется точное соответствие между запрошенными атрибутами и атрибутами полученного шрифта
OUT_TT_PRECIS 4 Выбирается масштабируемый шрифт True Type, даже если есть подходящий растровый или векторный шрифт
OUT_DEVICE_PRECIS 5 Выбирается шрифт устройства вывода
OUT_RASTER_PRECIS 6 Выбирается растровый шрифт
OUT_TT_ONLY_PRECIS 7 Используются только шрифты True Type

lfClipPrecision

Поле используется для определения способа, при помощи которого обрезается изображение символа, частично попавшего за пределы области ограничения вывода (clipping region), выбранную в контекст отображения.

Можно использовать следующие константы: CLIP_DEFAULT_PRECIS , CLIP_CHARACTER_PRECIS , CLIP_STROKE_PRECIS , CLIP_MASK , CLIP_LH_ANGLES , CLIP_TT_ALWAYS , CLIP_EMBEDDED .

Если указана константа CLIP_LH_ANGLES , направление вращения текста зависит от установленного режима отображения.

lfQuality

Качество шрифта, полученного при отображении.

Можно указывать одну из следующих констант:

Константа Описание
DEFAULT_QUALITY Качество не имеет значения
DRAFT_QUALITY Низкое качество. Допустимо масштабирование шрифтов, синтезирование наклонных, жирных, перечеркнутых и подчеркнутых символов
PROOF_QUALITY Высокое качество. Масштабирование шрифтов не допускается. При этом могут быть получены символы, имеющие размер, немного меньший запрошенного

lfPitchAndFamily

С помощью этого поля можно определить, нужна ли фиксированная или переменна ширина символов. Кроме этого, можно определить семейство, к которому должен принадлежать полученный шрифт.

Фиксированная или переменная ширина символов задается при помощи следующих констант:

Константа Описание
DEFAULT_PITCH Не имеет значения, будет ли шрифт иметь фиксированную или переменную ширину символов
FIXED_PITCH Нужен шрифт с фиксированной шириной символов
VARIABLE_PITCH Нужен шрифт с переменной шириной символов

Вы можете объединить при помощи логической операции ИЛИ эти константы с константами, соответствующими семейству шрифта:

Константа Описание
FF_DECORATIVE Шрифт, содержащий маленькие рисунки (пиктограммы). Примером такого шрифта может послужить шрифт Wingdings, поставляемый в составе Windows
FF_DONTCARE Семейство шрифта не имеет значения
FF_MODERN Семейство Modern. Фиксированная ширина символов, могут быть засечки (но могут и не быть)
FF_ROMAN Семейство Roman. Переменная ширина букв, есть засечки
FF_SCRIPT Семейство Script. Рукописный шрифт
FF_SWISS Семейство Swiss. Переменная ширина букв, нет засечек

lfFaceName

Строка, закрытая двоичным нулем, которая служит названием внешнего вида шрифта. Размер строки (включая закрывающий строку нуль) не должен превышать LF_FACESIZE байт.

Вы можете указать, что вам нужен, например, шрифт "Arial", однако это вовсе не гарантирует, что именно этот шрифт будет предоставлен в распоряжение приложения.

Выбор созданного шрифта в контекст отображения

Если вы заполнили все нужные поля в структуре LOGFONT и затем передали адрес структуры функции CreateFontIndirect, эта функция вернет идентификатор шрифта. Вы должны выбрать шрифт с этим идентификатором в контекст отображения с помощью макрокоманды SelectFont (точно так же, как для встроенных шрифтов):

hfontOldFont = SelectFont(hdc, hfont);

Как только в созданном шрифте отпадет необходимость, его следует удалить при помощи макрокоманды DeleteFont , предварительно выбрав в контекст отображения тот шрифт, который был выбран в него раньше:

#define DeleteFont(hfont) \
   DeleteObject((HGDIOBJ)(HFONT)(hfont))

Процесс отображения логического шрифта достаточно сложен. GDI сравнивает заданные в структуре LOGFONT параметры с параметрами различных шрифтов, которые можно использовать для данного устройства отображения, выбирая наиболее подходящий шрифт. Для сравнения используются пенальти (штрафные очки), которые имеют разные весовые коэффициенты. Выбирается тот шрифт, для которого сумма пенальти наименьшая.

Наиболее важное поле в структуре LOGFONT - поле lfCharSet. Если в этом поле будет установлено нулевое значение, будет выбран шрифт ANSI_CHARACTER, так как значение соответствующей ему константы равно нулю. Понятно, почему это поле самое важное - если приложение запрашивает шрифт OEM_CHARSET, оно предполагает использовать для вывода кодировку OEM. Если бы GDI предоставил приложению шрифт в кодировке ANSI, скорее всего, строку было бы невозможно прочесть. Если же в Windows нет ни одного шрифта с кодировкой OEM, приложение все равно получит какой-нибудь шрифт, однако результат вывода текста может оказаться неудовлетворительным.

Учтите, что растровые шрифты семейств Modern, Roman и Script, которые пришли из Windows версии 3.0, отмечены как имеющие кодировку OEM, хотя в действительности для этих шрифтов используется кодировка ANSI. Это сделано для того, чтобы в процессе выбора GDI вначале использовал масштабируемые шрифты перечисленных семейств, и только в крайнем случае остановил свой выбор на растровых шрифтах.

Следующее по важности поле в структуре LOGFONT - это поле lfPitchAndFamily. Оно имеет большое значение потому, что приложение, запрашивающее шрифт с фиксированной шириной букв, может работать неправильно, если ему будет выделен шрифт с переменной шириной букв.

Далее следует поле lfFaceName, а после него - поле lfFamily.

После сравнения всех описанных полей GDI сравнивает высоту букв шрифта (поле lfHeight), затем в сравнении принимают участие поля lfWidth, lfItalic, lfUnderline, lfStrikeOut.

Функция ChooseFont

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

DLL-библиотека commdlg.dll содержит функцию ChooseFont , специально предназначенную для выбора одного из зарегистрированных в системе шрифтов. Эта функция выводит на экран диалоговую панель "Font", с помощью которой пользователь может выбрать шрифт, стиль шрифта, размер шрифта, цвет букв, может выбрать подчеркнутый или перечеркнутый шрифт (рис. 5.2).

Рис. 5.2. Диалоговая панель "Font"

Из списка "Font", который расположен в левой верхней части этой диалоговой панели, пользователь может выбрать название шрифта. Список "Font Style" позволяет выбрать один из доступных стилей, например, наклонный или жирный шрифт. Список "Size" предназначен для выбора размера шрифта. С помощью переключателей "Strikeout" и "Underline", расположенных в поле "Effects", можно создать, соответственно, перечеркнутый и подчеркнутый шрифт. И, наконец, из меню "Color" можно выбрать цвет букв.

Образец выбранного шрифта отображается в поле "Sample".

Обратите внимание на то, что в списке "Font" некоторые шрифты отмечены двойной буквой "T". Это масштабируемые шрифты True Type.

Приведем прототип функции ChooseFont:

BOOL WINAPI ChooseColor(CHOOSEFONT FAR* lpcf);

Единственный параметр функции является указателем на структуру типа CHOOSEFONT. Эта структура, а также сама функция ChooseFont, определены в файле commdlg.h. Структура определена следующим образом:

typedef struct tagCHOOSEFONT
{
  DWORD           lStructSize;
  HWND            hwndOwner;
  HDC             hDC;
  LOGFONT FAR*    lpLogFont;
  int             iPointSize;
  DWORD           Flags;
  COLORREF        rgbColors;
  LPARAM          lCustData;
  UINT (CALLBACK* lpfnHook)(HWND, UINT, WPARAM, LPARAM);
  LPCSTR          lpTemplateName;
  HINSTANCE       hInstance;
  LPSTR           lpszStyle;
  UINT            nFontType;
  int             nSizeMin;
  int             nSizeMax;
} CHOOSEFONT;
typedef CHOOSEFONT FAR *LPCHOOSEFONT;

Перед вызовом функции ChooseFont вы должны проинициализировать нужные поля структуры CHOOSEFONT, записав в остальные поля нулевые значения.

Опишем назначение отдельных полей структуры CHOOSEFONT:

Поле Описание
lStructSize Размер структуры в байтах. Это поле необходимо заполнить перед вызовом функции ChooseFont
hwndOwner Идентификатор окна, которому будет принадлежать диалоговая панель. Если в поле Flags не указан флаг CF_SHOWHELP, в это поле можно записать значение NULL. Поле заполняется до вызова функции ChooseFont
hDC Идентификатор контекста отображения или информационного контекста для принтера. Если установлен флаг CF_PRINTERFONTS, в списке появятся шрифты, доступные в данном контексте
lpLogFont Указатель на структуру LOGFONT. Приложение может заполнить нужные поля в этой структуре перед вызовом функции ChooseFont. Если при этом будет установлен флаг CF_INITTOLOGFONTSTRUCT, выбранные значения будут использоваться в качестве начальных.
iPointSize Размер букв выбранного шрифта в десятых долях пункта. Содержимое этого поля устанавливается после возврата из функции ChooseFont
Flags Флаги инициализации диалоговой панели. Можно использовать следующие значения:CF_APPLY - разрешается использование кнопки "Apply";CF_ANSIONLY - в списке выбора появляются только шрифты в кодировке ANSI;CF_BOTH - в списке шрифтов появляются экранные и принтерные шрифты;CF_TTONLY - можно выбирать только масштабируемые шрифты True Type;CF_EFFECTS - если указан этот флаг, с помощью диалоговой панели можно определять цвет букв создавать подчеркнутые и перечеркнутые шрифты. В этом случае необходимо перед вызовом функции проинициализировать содержимое полей lfStrikeOut, lfUnderline, rgbColors;CF_ENABLEHOOK - разрешается использовать функцию фильтра адрес которой указан в поле lpfnHook;CF_ENABLETEMPLATE - разрешается использование шаблона диалоговой панели, определяемого содержимым полей hInstance и lpTemplateName;CF_ENABLETEMPLATEHANDLE - флаг указывает, что поле hInstance содержит идентификатор загруженного шаблона диалоговой панели. Содержимое поля lpTemplateName игнорируется;CF_FIXEDPITCHONLY - можно выбрать только шрифты с фиксированной шириной символов:CF_FORCEFONTEXIST - выдается сообщение об ошибке, если пользователь пытается выбрать несуществующий шрифт;CF_INITTOLOGFONTSTRUCT - для инициализации диалоговой панели используется содержимое структуры LOGFONT, адрес которой передается через поле lpLogFont;CF_LIMITSIZE - при выборе шрифта учитывается содержимое полей nSizeMin и nSizeMax;CF_NOFACESEL - отменяется выбор в списке "Font";CF_NOOEMFONTS - нельзя выбирать векторные шрифты, этот флаг аналогичен флагу CF_NOVECTORFONTS;CF_NOSIMULATIONS - запрещается эмуляция шрифтов;CF_NOSIZESEL - отменяется выбор размера шрифта;CF_NOSTYLESEL - отменяется выбор стиля шрифта;CF_NOVECTORFONTS - нельзя выбирать векторные шрифты, этот флаг аналогичен флагу CF_NOOEMFONTS;CF_PRINTERFONTS - в списке появляются только такие шрифты, которые поддерживаются принтером, контекст отображения для которого задан в поле hDC;CF_SCALABLEONLY - можно выбирать только масштабируемые и векторные шрифты;CF_SCREENFONTS - можно выбирать только экранные шрифты;CF_SHOWHELP - в диалоговой панели отображается кнопка "Help";CF_USESTYLE - строка lpszStyle содержит указатель на буфер, который содержит строку описания стиля. Эта строка используется для инициализации списка "Font Style" диалоговой панели "Font";CF_WYSIWYG - можно выбирать только такие шрифты, которые доступны и для отображения на экране, и для печати на принтере. Если установлен этот флаг, следует также установить флаги CF_BOTH и CF_SCALABLEONLY
rgbColors Цвет символов шрифта, который будет выбран в меню "Colors" диалоговой панели "Fonts" сразу после отображения диалоговой панели. Должен использоваться флаг CF_EFFECTS. Поле заполняется до вызова функции ChooseFont, после возврата из функции поле содержит значение выбранного цвета
lCustData Произвольные данные, передаваемые функции фильтра, определенной содержимым поля lpfnHook
lpfnHook Указатель на функцию фильтра, обрабатывающую сообщения, поступающие в диалоговую панель. Для работы с фильтром необходимо в поле Flags указать флаг CF_ENABLEHOOK
lpTemplateName Строка, закрытая двоичным нулем, которая содержит идентификатор шаблона диалоговой панели. Для использования этого поля необходимо указать флаг CF_ENABLETEMPLATE
hInstance Идентификатор модуля, который содержит шаблон диалоговой панели в качестве ресурса. Поле используется только в тех случаях, когда в поле Flags указаны значения CF_ENABLETEMPLATE или CF_ENABLETEMPLATEHANDLE. Поле заполняется до вызова функции ChooseFont
lpszStyle Указатель на буфер, содержащий строку, описывающую шрифт. Если указан флаг CF_USESTYLE, эта строка используется для инициализации списка "Font Style". Размер буфера должен быть не меньше LF_FACESIZE байт
nFontType Тип выбираемого шрифта. Можно использовать одно из следующих значений:SIMULATED_FONTTYPE - GDI может эмулировать этот шрифт;PRINTER_FONTTYPE - принтерный шрифт;SCREEN_FONTTYPE - экранный шрифт;BOLD_FONTTYPE - жирный шрифт, используется только для шрифтов True Type;ITALIC_FONTTYPE - наклонный шрифт, используется только для шрифтов True Type;REGULAR_FONTTYPE - не жирный и не наклонный шрифт, используется только для шрифтов True Type
nSizeMin Минимальный размер шрифта, который можно выбрать. Для использования этого поля необходимо установить флаг CF_LIMITSIZE
nSizeMax Максимальный размер шрифта, который можно выбрать. Для использования этого поля необходимо установить флаг CF_LIMITSIZE

Если пользователь выбрал шрифт, функция ChooseFont возвращает значение TRUE. Если пользователь отказался от выбора, нажав кнопку "Cancel" или клавишу <Esc>, возвращается значение FALSE.

[Назад] [Содеожание] [Дальше]