Рис. 8.2. Окно программы, использующей встроенные шрифты
Хотя на первый взгляд это может показаться сложным, на самом деле создавать собственные шрифты довольно просто. При этом Вы получите два существенных преимущества. Во-первых, собственные шрифты придают программе уникальный внешний вид, отличающий ее от других. Во-вторых, собственные шрифты позволяют лучше управлять выводом текста. Прежде чем начать писать программу создания собственного шрифта, необходимо отметить, что его создание не означает определение нового шрифта. Скорее это выглядит как клонирование существующего шрифта и подгонка его параметров согласно задаваемым Вами спецификациям (то есть не нужно определять форму каждого символа создаваемого шрифта.)
Для создания собственного шрифта используется функция API CreateFont():
HFONT CreateFont(int Height, int Width,
int Escapement, int Orientation,
int Weight, DWORD Ital,
DWORD Underline, DWORD StrikeThru,
DWORD Charset, DWORD Precision,
DWORD ClipPreqision, DWORD Quality,
DWORD Pitch, LPCSTR FontName);
Требуемая высота шрифта задается параметром Height, а ширина – параметром Width. Если параметр Width равен 0, Windows автоматически подставляет значение, определяемое текущими пропорциями шрифта. Значения Height и Width задаются в логических единицах.
Угол наклона текста в окне может быть любым. Угол по отношению к горизонтальной оси задается параметром Escapement. Для текста, отображаемого горизонтально, этот параметр должен быть равен 0. В противном случае он определяет угол наклона текста в десятых долях градуса. Так, значение 900 определяет угол наклона, равный 90 градусам.
Угол наклона для каждого отдельного символа можно указать при помощи параметра Orientation. Этот параметр задает угол наклона каждого символа по отношению к горизонтальной оси также в десятых долях градуса.
Параметр Weight задает насыщенность (жирность) шрифта и может принимать значения в диапазоне от 0 до 1000. Значение 0 определяет насыщенность по умолчанию, значение 400 – нормальную насыщенность, а 700 – жирный шрифт. Для задания насыщенности шрифта можно также использовать следующие макросы:
FW_DONTCARE
FW_EXTRALIGHT
FW_LIGHT
FW_NORMAL
FW_MEDIUM
FW_SEMIBOLD
FW_BOLD
FW_EXTRABOLD
FW_HEAVY
Для создания наклонного шрифта следует задать ненулевое значение параметра Ital. Если параметр Underline не равен нулю, создается подчеркнутый шрифт. Для создания шрифта, все символы которого зачеркнуты горизонтальной линией, необходимо задать ненулевое значение для параметра StrikeThru.
Параметр Charset определяет множество символов шрифта. В следующем примере мы будем использовать ANSI_CHARSET. Параметр Precision задает точность отображения шрифта, которая определяет, насколько точно созданный шрифт соответствует задаваемым характеристикам. В примере, приведенном в данной главе, параметр принимает значение OUT_DEFAULT_PRECIS. Значение параметра ClipPrecision определяет «точность отсечения» шрифта, иначе говоря – как будут «отсекаться» символы шрифта, не попадающие в видимую область вывода. В следующем примере этот параметр принимает значение CLIP_DEFAULT_PRECIS.
Для подробного дальнейшего изучения того, какие значения могут принимать параметры Charset, Precision и ClipPrecision, обратитесь к Руководству по функциям и форматам данных API (или к MSDN Library Help – приложению Visual C++ 6.0).
Параметр Quality определяет, в какой степени создаваемый логический шрифт будет соответствовать физическим шрифтам, допустимым для данного устройства вывода. Этот параметр может принимать одно из следующих значений:
DEFAULT_QUALITY
DRAFT_QUALITY
PROOF_QUALITY
Параметр Pitch задает тип и семейство шрифта. Имеется три варианта задания типа шрифта:
DEFAULT_PITCH
FIXED_PITCH
VARIABLE_PITCH
Семейство шрифта может быть задано одним из шести возможных значений:
FF_DECORATIVE
FF_DONTCARE
FF_MODERN
FF_ROMAN
FF_SCRIPT
FF_SWISS
Значение параметра Pitch формируется комбинацией типа и семейства шрифта при помощи операции логического ИЛИ.
В параметре FontName передается указатель на строку, содержащую имя шрифта. Такая строка может содержать не более 32 символов. Указываемый шрифт должен быть установлен в Вашей системе.
При успешном завершении функция CreateFont() возвращает дескриптор созданного логического шрифта. В случае возникновения ошибки возвращается NULL.
Замечание: Функция CreateFont() физически не создает новый шрифт, – она только преобразует шрифт, реально существующий в системе, таким образом, чтобы полученный в результате шрифт максимально соответствовал задаваемым спецификациям.
Шрифты, создаваемые при помощи функции CreateFont(), должны быть удалены, прежде чем программа завершит свое выполнение. Для удаления шрифта используется функция DeleteObject().
Пример 8-4. Ниже приводится текст программы, демонстрирующей работу с двумя логическими шрифтами. Один из шрифтов базируется на шрифте Times New RomanCyr, а другой – на шрифте Parsek Cyrillic. Каждый раз при выборе команды меню Сменить шрифт выбирается и отображается новый шрифт. Эта программа использует тот же файл ресурсов, что и пример из предыдущего раздела.
// Создание собственных шрифтов
#include <Windows.h>
#include <String.h>
#include <Stdio.h>
#include "Text.h"
LRESULT CALLBACK WindowFunc(HWND,UINT,WPARAM,LPARAM);
char szWinName[] = "МоеОкно"; // Имя класса окна
char str[255]; // Буфер строки вывода
char frame[40]= "По умолчанию"; // Имя шрифта
int X=0, Y=0; // Текущие координаты строки
int maxX, maxY; // Размеры экрана
HDC memdc; // DC виртуального окна
HBITMAP hbit; // Растр - это виртуальное окно
HBRUSH hbrush; // Дескриптор кисти
HFONT holdf, hnewf1, hnewf2; // Дескрипторы шрифтов
int WINAPI WinMain (HINSTANCE hThisInst,
HINSTANCE hPrevInst,
LPSTR lpszArgs,
int nWinMode)
{
HWND hwnd;
MSG msg;
WNDCLASS wcl;
HACCEL hAccel;
// Определить класс окна
wcl.hInstance=hThisInst; // Дескриптор приложения
wcl.lpszClassName=szWinName; // Имя класса окна
wcl.lpfnWndProc=WindowFunc; // Функция окна
wcl.style=0; // Стиль по умолчанию
wcl.hIcon=LoadIcon(NULL,IDI_APPLICATION); // Иконка
wcl.hCursor=LoadCursor(NULL,IDC_ARROW); // Курсор
wcl.lpszMenuName="MYMENU"; // Меню
wcl.cbClsExtra=0; // Без дополнительной
wcl.cbWndExtra=0; // информации
// Определить заполнение окна белым цветом
wcl.hbrBackground=
(HBRUSH)GetStockObject(WHITE_BRUSH);
if(!RegisterClass(&wcl)) // Зарегистр. класс окна
return 0;
// Создать окно
hwnd=CreateWindow(szWinName, // Имя класса
"Создание собственных шрифтов",
WS_OVERLAPPEDWINDOW,// Стиль окна
CW_USEDEFAULT, // Х-координата
CW_USEDEFAULT, // Y-координата
CW_USEDEFAULT, // Ширина окна
CW_USEDEFAULT, // Высота окна
HWND_DESKTOP, // Нет родит. окна
NULL, // Нет меню
hThisInst, // Дескрип. приложения
NULL); // Без дополит. аргументов
// Загрузить акселераторы
hAccel=LoadAccelerators(hThisInst,"MYMENU");
ShowWindow(hwnd,nWinMode); // Показать окно и
UpdateWindow(hwnd); // перерисовать содержимое
// Запустить цикл обработки сообщений
while(GetMessage (&msg,NULL,0,0))
if(!TranslateAccelerator(hwnd,hAccel,&msg))
{
TranslateMessage(&msg); // Использ.Клавиатуры
DispatchMessage (&msg); // Возврат к Windows
}
return msg.wParam;
}
// Следующая функция вызывается операционной системой
// Windows и получает в качестве параметров сообщения
// из очереди сообщений данного приложения
LRESULT CALLBACK WindowFunc(HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT paintstruct;
TEXTMETRIC tm;
SIZE size;
static fontswitch=0;
switch(message)
{
case WM_CREATE: // Получаем размеры экрана
maxX=GetSystemMetrics(SM_CXSCREEN);
maxY=GetSystemMetrics(SM_CYSCREEN);
hdc=GetDC(hwnd); // Совмест. с окном растр
memdc=CreateCompatibleDC(hdc);
hbit=CreateCompatibleBitmap(hdc,maxX,maxY);
SelectObject(memdc,hbit);
hbrush=(HBRUSH)GetStockObject(WHITE_BRUSH);
SelectObject(memdc,hbrush);
PatBlt(memdc,0,0,maxX,maxY,PATCOPY);
// Создать новые шрифты
hnewfl=CreateFont(16,0,0,0,FW_NORMAL,0,0,0,
AHSI_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAOLT_QUALITY,
DEFAULT_PITCH|FW_DONTCARE,
"Times New Roman Cyr");
hnewf2=CreateFont(24,0,0,0,FW_SEMIBOLD,0,0,
0,ANSI_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH|FW_DONTCARE,
"Parsek Cyrillic");
ReleaseDC(hwnd,hdc);
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_SHOW:
SetTextColor(memdc,RGB(0,0,0)); // Черн
SetBkMode(memdc,TRANSPARENT); // Прозра
GetTextMetrics(memdc,&tm); // Метрики
sprintf(str,
"Высота шрифта %s равна %ld пикселей.",
fname,tm.tmHeight);
TextOut(memdc,X,Y,str,strlen(str));
Y=Y+tm.tmHeight // Следующая
+tm.tmExternalLeading; // строка
strcpy(str,"Это следующая строка. ");
TextOut(memdc,X,Y,str,strlen(str));
GetTextExtentPoint32(memdc,str, //Длина
strlen(str),
&size);
sprintf(str,
"Длина предыдущей строки %ld.",
size.cx);
X=size.cx; // В конец предыдущей строки
TextOut(memdc,X,Y,str,strlen(str));
Y=Y+tm.tmHeight // Следующая
+tm.tmExternalLeading; // строка
X=0; // X опять в начало
sprintf(str,"Размеры экрана %d на %d.",
maxX,maxY);
TextOut(memdc,X,Y,str,strlen(str));
Y=Y+tm.tmHeight // Следующая
+tm.tmExternalLeading; // строка
InvalidateRect(hwnd,NULL,1); //Сообщить
break;
case ID_FONT:
switch(fontswitch) // На новый шрифт
{
case 0: // Переключиться на шрифт 1
holdf=(HFONT)SelectObject(memdc,
hnewfl);
fontswitch=1;
strcpy(fname,
"Times New Roman Cyr");
break;
case 1: // Переключиться на шрифт 1
SelectObject(memdc,hnewf2);
fontswitch=2;
strcpy(fname,
"Parsek Cyrillic");
break;
case 2: // Переключиться на старый
SelectObject(memdc,holdf);
fontswitch=0;
strcpy(fname,"Default");
break;
}
break;
case ID_RESET:
X=Y=0; // Стереть перерисовкой фона
PatBlt(memdc,0,0,maxX,maxY,PATCOPY);
InvalidateRect(hwnd,NULL,1); //Сообщить
break;
case ID_HELP:
MessageBox(hwnd,"F2: вывести текст\n"
"F3: сменить шрифт\n"
"F4: с начала экрана",
"Помощь",MB_OK);
break;
}
break;
case WM_PAINT: // Перерисовка окна
hdc=BeginPaint(hwnd,&paintstruct); // Пол. DC
// Теперь копируем растр из памяти на экран
BitBlt(hdc,0,0,maxX,maxY,memdc,0,0,SRCCOPY);
EndPaint(hwnd,&paintstruct); // Освободить DC
break;
case WM_DESTROY: // Завершение программы
DeleteDC(memdc); // Удалить виртуальное окно
PostQuitMessage(0);
break;
default:
// Все сообщения, не обрабатываемые в данной
// функции, направляются на обработку по
// умолчанию
return DefWindowProc(hwnd,message,
wParam,lParam);
}
return 0;
}
Рис. 8.3. Окно программы, работающей с логическими шрифтами
Пример вывода этой программы показан на рис. 8.3.
В заключение надо отметить, что Windows обладает весьма богатыми возможностями работы со шрифтами и текстом. В следующей главе мы будем продолжать рассматривать вопросы, связанные с отображением графической информации.