Определение метрик шрифта
Метрику шрифта, выбранного в контекст отображения 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 – код семейства шрифта. Четыре младших бита этого кода комбинацией следующих констант определяют информацию о шаге и типе шрифта:
Четыре старших бита кода 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 Все права принадлежат авторам размещенных материалов.
|