Здавалка
Главная | Обратная связь

Определение метрик шрифта



Метрику шрифта, выбранного в контекст отображения hdc, определяют с помощью функции GetTextMetrics:

 

BOOL GetTextMetrics( HDC hdc, LPTEXTMETRIC lptm);

 

Параметр lptm указывает на структуру типа TEXTMETRIC, в которую нужно записать метрики шрифта. Все размеры записываются в логических единицах контекста отображения hdc. В случае успешного выполнения функция возвращает ненулевое значение.

Структура TEXTMETRIC предназначена для описания базовой информации о метриках шрифта и описана следующим образом:

 

typedef struct {

LONG tmHeight;

LONG tmAscent;

LONG tmDescent;

LONG tmInternalLeading;

LONG tmExtemalLeading;

LONG tmAveCharWidth;

LONG tmMaxCharWidth;

LONG tmWeight;

LONG tmOverhang;

LONG tmDigitizedAspectX;

LONG tmDigitizedAspectY;

BCHAR tmFirstChar;

BCHAR tmLastChar;

BCHAR tmDefaultChar;

BCHAR tmBreakChar;

BYTE tmItalic;

BYTE tmUnderlined;

BYTE tmStruckOut;

BYTE tmPitchAndFamily;

BYTE tmCharSet;

} TEXTMETRIC;

 

Назначение полей этой структуры:

1. tmHeight – общая высота букв, равна tmAscent+tmDescent.

2. tmAscent – часть высоты букв от базовой линии с учетом таких элементов, как тильда в букве "Й".

3. tmDescent – часть высоты букв ниже базовой линии.

4. tmInternalLeading – высота таких выступающих элементов, как тильда в букве "Й", и может быть равна нулю.

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

6. tmAveCharWidth – средняя ширина строчных букв, равна ширине латинской буквы "х".

7. tmMaxCharWidth – ширина самой широкой буквы.

8. tmWeight – жирность шрифта, может находиться в пределах от 0 до 1000, как и для логического шрифта.

9. tmOverhang – величина изменения ширины символов при построении наклонного или полужирного шрифта из нормального шрифта.

10. tmDigitizedAspectX – разрешение устройства отображения по горизонтали.

11. tmDigitizedAspectY – разрешение устройства отображения по вертикали.

12. tmFirstChar – код первого символа в шрифте.

13. tmLastChar – код последнего символа в шрифте.

14. tmDefaultChar – код символа, заменяющего любой отсутствующий в шрифте символ.

15. tmBreakChar – код символа переноса слов с одной строки на другую при выравнивании текста.

16. tmItalic ¹ 0 – означает наклонный шрифт.

17. tmUnderlined ¹ 0 – означает подчеркнутый шрифт.

18. tmStruckOut ¹ 0 – означает перечеркнутый шрифт.

19. tmPitchAndFamily – код семейства шрифта. Четыре младших бита этого кода комбинацией следующих констант определяют информацию о шаге и типе шрифта:

Константа Пояснение
TMPF_FIXED_PITCH Если этот бит установлен, то шрифт с переменным шагом, иначе – с постоянным
TMPF_VECTOR Если этот бит установлен, то шрифт векторного типа
TMPF_TRUETYPE Если этот бит установлен, то шрифт типа TrueType
TMPF_DEVICE Если этот бит установлен, шрифт определен устройством

 

Четыре старших бита кода tmPitchAndFamily описывают семейство шрифта, так же как и для логического шрифта.

20. tmCharSet – код используемого набора символов, такой же, как и для логического шрифта.

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

Листинг 3.7. Вывод таблицы в окно.

#include <windows.h>

#include <math.h>

#include <tchar.h>

 

BOOL RegClass(WNDPROC, LPCTSTR, UINT);

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

 

HINSTANCE hInstance;

TCHAR szClass[] = TEXT("TableClass");

//Структура столбца данных

typedef struct

{

TCHAR str[15]; //Поле имени столбца

double val[50]; //Массив данных столбца

} COLUMN; //Имя типа

 

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow)

{

MSG msg;

HWND hwnd;

hInstance = hInst;

if (!RegClass(WndProc, szClass, COLOR_WINDOW))

return FALSE;

hwnd = CreateWindow(szClass, TEXT("ТАБЛИЦА"), WS_OVERLAPPEDWINDOW | WS_VISIBLE,

CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0,0, hInstance, NULL);

if (!hwnd) return FALSE;

while(GetMessage(&msg, 0, 0, 0)) DispatchMessage(&msg);

return msg.wParam;

}

 

BOOL RegClass(WNDPROC Proc, LPCTSTR szName, UINT brBackground)

{

WNDCLASS wc;

wc.style = CS_HREDRAW | CS_VREDRAW;

wc.cbClsExtra=wc.cbWndExtra=0;

wc.lpfnWndProc = Proc;

wc.hInstance = hInstance;

wc.lpszClassName = szName;

wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

wc.hbrBackground = (HBRUSH)(brBackground+1);

wc.lpszMenuName = NULL;

return (RegisterClass(&wc) != 0);

}

 

BOOL DrawLine( HDC hdc, int x0, int y0, int x, int y)

{

MoveToEx(hdc, x0, y0, NULL);

return LineTo(hdc, x, y);

}

 

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{

//Описываем массив из 10 столбцов

static COLUMN cols[10];

static int cx, cy, cxChar, cyChar;

switch (msg)

{

case WM_SIZE:

{ cx=LOWORD(lParam); cy=HIWORD(lParam); return 0; }

case WM_CREATE:

{

//Заполняем заголовки столбцов

_tcscpy(cols[0].str, TEXT("i"));

_tcscpy(cols[1].str, TEXT("10*sin(i/10.)"));

_tcscpy(cols[2].str, TEXT("20*sin(i/20.)"));

_tcscpy(cols[3].str, TEXT("30*sin(i/30.)"));

_tcscpy(cols[4].str, TEXT("40*sin(i/40.)"));

_tcscpy(cols[5].str, TEXT("50*sin(i/50.)"));

_tcscpy(cols[6].str, TEXT("60*sin(i/60.)"));

_tcscpy(cols[7].str, TEXT("70*sin(i/70.)"));

_tcscpy(cols[8].str, TEXT("80*sin(i/80.)"));

_tcscpy(cols[9].str, TEXT("90*sin(i/90.)"));

//Заполняем массивы данных столбцов

for (int i=0; i<50; i++)

{

//В первом столбце будут номера строк

cols[0].val[i]=i;

//В остальных столбцах будут данные

for (int j=1; j<10; j++)

cols[j].val[i]=10*sin(i/10./j);

}

//Определяем средние высоту и ширину символов

{

TEXTMETRIC tm;

HDC hdc=GetDC(hwnd);

//Определяем метрики текста

GetTextMetrics(hdc, &tm);

ReleaseDC(hwnd, hdc);

//Количество пикселей в высоте символа

cyChar = tm.tmHeight + tm.tmExternalLeading;

//Количество пикселей в средней ширине

cxChar = tm.tmAveCharWidth+1;

}

return 0;

}

case WM_PAINT:

{

PAINTSTRUCT ps;

HDC hdc=BeginPaint(hwnd, &ps);

TCHAR str[20]={0};

//Установим начальные параметры для вывода

int left=cxChar, //Левый край

top=cyChar/2, //Верх

dx=cxChar, //Пробел по оси X

dy=cyChar/4, //Пробел по оси Y

hy=cyChar+dy+dy, //Высота для строки

right=cx-cxChar, //Правый край

bottom=cy-cyChar, //Низ

bounds[10], //Массив ширин столбцов

i=0, j=0;

//Заполняем массив ширинами столбцов

while (j<10)

{

// Для j-ro столбца выбираем ширину

bounds[j] = _tcslen(cols[j].str);

for (i=0; i<50; i++)

{

_sntprintf( str, 7, _T("%f"), cols[j].val[i] );

//_gcvt( cols[j].val[i], 7, str );

int ss = _tcslen(str);

if (bounds[j]<ss) bounds[j]=ss;

}

if (bounds[j]<=3) bounds[j]=4;

if (bounds[j]>10) bounds[j]-=2;

bounds[j] = cxChar*(bounds[j]);

j++;

}

//Определяем максимальное количество столбцов,

//которое помещается на ширине рабочей области

int dd=left, maxcol=0;

while (maxcol<10)

{

if (dd+bounds[maxcol]>right) break;

dd += bounds[maxcol];

maxcol++;

}

//right теперь указывает на правый край таблицы

right=dd;

//Подгоняем ширину окна к количеству столбцов

{

RECT rc;

GetWindowRect(hwnd, &rc);

MoveWindow(hwnd, rc.left, rc.top, //Изменяем только ширину окна

rc.right-rc.left-(cx-right)+dx, rc.bottom-rc.top, TRUE);

}

//////НАЧАЛО ВЫВОДА ТАБЛИЦЫ//////

//Устанавливаем режим выравнивания

SetTextAlign(hdc, TA_RIGHT);

//////Начало вывода шапки таблицы//////

int y=top; //Текущая координата по оси Y

//Горизонтальная линия на всю ширину

DrawLine(hdc, left, y, right, y);

//Заголовки столбцов

int x=left; //Текущая координата по оси X

//Вертикальная линия слева от столбца

DrawLine(hdc, x, y, x, y+hy);

for (j=0; j<maxcol; j++)

{

//Устанавливаем x на правой границе столбца

x+=bounds[j];

TextOut(hdc, x-dx, y+dy, cols[j].str, _tcslen(cols[j].str));

//Вертикальная линия справа от столбца

DrawLine(hdc, x, y, x, y+hy);

}

//Горизонтальная линия на всю ширину

y += hy;

DrawLine(hdc, left, y, right, y);

//////Конец вывода шапки таблицы//////

//////Начало вывода данных таблицы//////

i = 0;//Счетчик номера строки данных

while (i<50 && y<bottom)

{

//Вертикальная линия слева от столбца

x=left;

DrawLine(hdc, x, y, x, y+hy);

for (j=0; j<maxcol; j++)

{

x+=bounds[j];

//Преобразуем целое число в строку

if (j==0) _itot((int)cols[j].val[i], str, 10);

//Преобразуем вещественное число в строку

else _sntprintf( str, 7, _T("%f"), cols[j].val[i] );

//_gcvt( cols[j].val[i], 7, str);

TextOut(hdc, x-dx, y+dy, str, _tcslen(str));

//Вертикальная линия справа от столбца

DrawLine(hdc, x, y, x, y+hy);

}

//Горизонтальная линия на всю ширину

y += hy;

DrawLine(hdc, left, y, right, y);

i++;

}

//////Конец вывода данных таблицы//////

//////КОНЕЦ ВЫВОДА ТАБЛИЦЫ//////

EndPaint(hwnd, &ps);

return 0;

}

case WM_DESTROY: { PostQuitMessage(0); return 0; }

}

return DefWindowProc(hwnd, msg, wParam, lParam);

}

 

Рассмотрим основные шаги решения данной задачи.

1. Таблицу сначала создают в памяти.

1.1. Описывают тип структуры столбца данных:

 

typedef struct

{ char str[15]; //Поле имени столбца

double val[50]; //Массив данных столбца

} COLUMN; //Имя типа

 

1.2. Описывают массив cols из 10 столбцов этого типа:

 

static COLUMN cols[10];

 

1.3. Заполняют этот массив некоторыми значениями. Сначала заполняются заголовки столбцов:

 

strcpy(cols[0].str, "i");

strcpy(cols[1].str, "10*sin(i/10.)");

...

strcpy(cols[9].str, "90*sin(i/90.)");

 

1.4. Затем заполняют массивы данных столбцов:

 

for (int i=0; i<50; i++)

{

cols[0].val[i]=i;

for (int j=1; j<10; j++)

cols[j].val[i]=10*sin(i/10./j);

}

 

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

2. Определяют геометрические параметры вывода таблицы в рабочую область.

2.1. Определяют среднюю высоту и ширину символов установленного шрифта:

 

TEXTMETRIC tm;

HDC hdc=GetDC(hwnd);

//Определяем метрики текста

GetTextMetrics(hdc, &tm);

ReleaseDC(hwnd, hdc);

//Количество пикселей в высоте символа

cyChar = tm.tmHeight + tm.tmExternalLeading;

//Количество пикселей в средней ширине

cxChar=tm.tmAveCharWidth+1;

 

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

2.2. Устанавливают границы вывода таблицы. Здесь left указывает на левую, top – на верхнюю, right – на правую, bottom – на нижнюю границу таблицы в рабочей области. Параметр dx равен величине пробела по оси X, a dy – по оси Y, hy задает высоту строки таблицы. Также описывают переменные bounds[10], i и j.

2.3. Массив bounds заполняют значениями ширины столбцов:

 

while (j<10)

{

bounds[j]=strlen(cols[j].str);

for (i=0; i<50; i++)

{

_gcvt( cols[j].val[i], 7, str);

int ss=strlen(str);

if (bounds0]<ss) bounds[j]=ss;

}

if (bounds[j]<=3) bounds[j]=4;

if (bounds[j]>10) bounds[j]-=2;

bounds[j] = cxChar*(bounds[j]);

j++;

}

 

Для j-го столбца вычисляют количество символов заголовка:

 

bounds[j]=strlen(cols[j].str);

 

Далее определяют максимально необходимое количество символов для вывода в этот столбец:

 

_gcvt( cols[j].val[i], 7, str);

int ss=strien(str);

if (bounds[j]<ss) bounds[j]=ss;

 

После определения необходимого количества символов вносят незначительные коррекции. Например, в столбце выделяют место для вывода не менее четырех символов:

 

if (bounds[j]<=3) bounds[j]=4;

 

А в случае большого (более 10) количества символов уменьшают количество выделяемых позиций:

 

if (bounds[j]>10) bounds[j]-=2;

 

Вычисляют ширину столбца в пикселях:

 

Bounds[j] = cxChar*bounds[j];

 

2.4. Определяют максимальное количество maxcol столбцов, которое помещается в установленных границах:

 

int dd=left, maxcol=0;

while (maxcol<10)

{

if (dd+bounds[maxcol]>right) break;

dd += bounds[maxcol];

maxcol++;

}

 

2.5. Значение right переносят на правую границу столбца maxcol:

 

right=dd;

 

2.6. Уменьшают ширину окна так, чтобы она была достаточна для вывода только maxcol столбцов:

 

RECT rc;

GetWindowRect(hwnd, &rc);

MoveWindow(hwnd, rc.left, rc.top,

rc.right-rc.left-(cx-right)+dx, rc.bottom-rc.top, TRUE);

 

3. Выводят таблицу.

3.1. Устанавливают режим выравнивания по правой границе:

 

SetTextAlign(hdp, TA_RIGHT);

 

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

3.2. Рисуют заголовки столбцов таблицы.

В первую очередь рисуют горизонтальную линию на всю ширину таблицы (от левой границы – left до правой границы – right):

 

DrawLine(hdc, left, y, right, y);

 

Для рисования заголовков столбцов вспомогательную координату по оси X устанавливают на левой границе (x = left). После этого рисуют вертикальную линию на левой границе таблицы высотой hy:

 

DrawLine(hdc, х, у, х, y+hy);

 

Вывод заголовка j-го столбца содержит следующие шаги:

3.2.1. Устанавливают х на правой границе столбца:

 

x+=bounds[j];

 

3.2.2. Выводят текст заголовка j-го столбца:

 

TextOut( hdc, x-dx, y+dy, cols[j].str, strlen(cols[j].str));

 

При этом координата x-dx указывает на правую границу j-го столбца минус величина пробела по оси X.

3.2.3. Рисуют вертикальную линию справа от j-го столбца:

 

DrawLine( hdc, х, у, х, y+hy);

 

После вывода всех maxcol заголовков рисуют горизонтальную линию на всю ширину таблицы:

 

y+=hy;

DrawLine(hdc, left, у, right, у);

 

Этим завершают вывод заголовков таблицы.

3.3. Выводят данные таблицы. Этот шаг практически ничем не отличается от шага 3.2. Здесь добавляют счетчик номера строки данных и операторы преобразования числовых данных в строковые. Кроме этого, вывод ограничивают 50 строками и значением bottom нижней границы вывода таблицы в рабочей области.







©2015 arhivinfo.ru Все права принадлежат авторам размещенных материалов.