Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Programming_Windows_95_Part_I.pdf
Скачиваний:
96
Добавлен:
05.06.2014
Размер:
4.61 Mб
Скачать

162

принтере страницы) в миллиметрах. Если последний параметр имеет значение FALSE, то функция EzCreateFont использует эти значения для получения разрешающей способности устройств в точках на дюйм.

Здесь возможны два варианта. Вы можете получить разрешение устройства в точках на дюйм непосредственно, используя параметры LOGPIXELSX и LOGPIXELSY в функции GetDeviceCaps. Это называется "логическим разрешением" (logical resolution) устройства. Для печатающих устройств нормальное разрешение и логическое разрешение одинаковы (отбрасывая ошибки округления). Для дисплеев, однако, логическое разрешение лучше, чем нормальное разрешение. Например, для VGA наилучшим значением нормального разрешения является примерно 68 точек на дюйм. В то время, как логическое разрешение — 96 точек на дюйм.

Это различие, можно сказать, драматическое. Предположим, что мы работаем со шрифтом размера 12 пунктов, который имеет высоту 1/6 дюйма. Предположив, что нормальное разрешение равно 68 точкам на дюйм, полная высота символов будет около 11 пикселей. При логическом разрешении 96 точек на дюйм эта величина будет 16 пикселей. То есть разница составляет около 45%.

Чем объясняется такая разница? Если немного задуматься над этим, то на самом деле не существует истинного разрешения VGA. Стандартный VGA показывает 640 пикселей по горизонтали и 480 пикселей по вертикали, но размер экрана реального компьютера может быть различным — от маленького в компьютерах notebook до большого в VGA — проекторах. Windows не имеет способа самостоятельно определить действительный размер экрана. Значения HORZSIZE и VERTSIZE основаны на стандартных размерах настольных (desktop) дисплеев VGA, которые могли быть установлены для каких-нибудь ранних моделей IBM (году в 1987) путем простого измерения линейкой экрана каким-то программистом Microsoft.

Если же рассмотреть этот вопрос еще глубже, то в действительности вам и не нужно, чтобы шрифты были отображены на экране в их истинном размере. Предположим, что вы используете VGA проектор на презентации перед сотнями людей, и вы используете 12-пунктовый шрифт, реальный размер которого составляет 1/6 дюйма на проекционном экране. Нет сомнений, что ваша аудитория будет в замешательстве.

Люди, постоянно работающие с текстами, часто используют текстовые процессоры и настольные издательские системы. Довольно часто вывод осуществляется на бумаге размером 81/2х11 дюймов (или 8х10 дюймов с учетом отступов). Многие дисплеи VGA шире, чем 8 дюймов. Отображение на экране более крупных символов предпочтительнее, чем изображение в реальных размерах на экране.

Однако, если вы используете логическое разрешение шрифта, то могут возникнуть проблемы при совмещении текста и другой графики. Если вы используете функцию SetMapMode для рисования графики в дюймах или миллиметрах и одновременно логическое разрешение устройства для установки размера шрифта, то вы придете к несоответствию — не при выводе на принтере (т. к. здесь нормальное разрешение совпадает с логическим), а при выводе на экран, где существует разница в 45%. Решение этой проблемы будет продемонстрировано далее в этой главе в программе JUSTIFY1.

Структура LOGFONT, которую вы передаете функции CreateFontIndirect, требует задания высоты шрифта в логических единицах. Однажды получив это значение в пикселях, вы легко преобразуете его в логические единицы посредством вызова функции DPtoLP (device point to logical point, точка устройства в логическую точку). Но для того чтобы преобразование DPtoLP выполнялось правильно, должен быть установлен тот же режим отображения (mapping mode), с каким вы далее будете работать при отображении текста на экране, используя созданный шрифт. Это значит, что вы должны установить режим отображения до того, как будете вызывать функцию EzCreateFont. В большинстве случаев вы используете только один режим отображения для рисования в конкретной области окна, так что выполнение этого требования не является проблемой.

Форматирование простого текста

Разобравшись с файлами EZFONT, наступило время потренироваться в форматировании текста. Процесс форматирования заключается в расположении каждой строки текста в пределах установленных полей одним из четырех способов: с выравниванием влево, с выравниванием вправо, с выравниванием по центру или с выравниванием по всей ширине (когда строка растягивается от левого края до правого края с формированием одинаковых интервалов между словами). Первые три задачи можно решить, используя функцию DrawText с параметром DT_WORDBREAK, но ее использование ограничено. Например, вы не можете определить, какую часть текста функция DrawText сможет разместить в прямоугольнике вывода. Функция DrawText удобна для некоторых простых задач, но для более сложных задач форматирования вы, вероятно, захотите применить функцию TextOut.

Одной из наиболее часто используемых функций работы с текстом является функция GetTextExtentPoint32. (Это функция, имя которой претерпело некоторые изменения со времени более ранних версий Windows.) Эта функция дает значения ширины и высоты строки символов, основываясь на текущем шрифте, выбранном в контексте устройства:

GetTextExtentPoint32(hdc, pString, iCount, &size);

163

Ширина и высота текста в логических единицах возвращается в поля cx и cy структуры SIZE. Начнем с примера, использующего одну строку текста. Предположим, что вы выбрали шрифт в контексте устройства и теперь хотите вывести текст:

char *szText [ ] = "Hello, how are you?";

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

GetTextExtentPoint32(hdc, szText, strlen(szText), &size);

Если значение size.cx больше, чем (xRight xLeft), то срока слишком длинна, чтобы поместиться в указанных границах. Предположим, что строка все же помещается.

Для того, чтобы выровнять текст влево, нужно просто установить значение xStart равным xLeft и затем вывести текст:

TextOut(hdc, xStart, yStart, szText, strlen(szText));

Это просто. Теперь вы можете прибавить значение size.cy к yStart и затем выводить следующую строку текста. Чтобы выровнять текст вправо, воспользуемся для вычисления xStart следующей формулой:

xStart = xRight — size.cx;

Чтобы выровнять текст по центру между левой и правой границами используем другую формулу:

xStart =(xLeft + xRight — size.cx) / 2;

Теперь перед нами самая трудная задача — выровнять текст по всей ширине между левой и правой границами. Расстояние между ними вычисляется как (xRight xLeft). Без растягивания по ширине текст имеет ширину size.cx. Разница между этими двумя величинами:

xRight — xLeft — size.cx

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

SetTextJustification(hdc, xRight — xLeft — size.cx, 3)

Второй параметр — это величина пробела, который должен быть распределен поровну между тремя символами пробела в символьной строке. Третий параметр — число символов пробела в строке, в нашем примере — 3.

Теперь установим значение xStart равным xLeft и выведем текст с помощью функции TextOut:

TextOut(hdc, xStart, yStart, szText, strlen(szText));

Текст будет выровнен по всей ширине между границами xLeft и xRight.

При каждом вызове функции SetTextJustification накапливается погрешность, если величина добавляемого пропуска не делится нацело на число символов пробела. Эта ошибка будет влиять на последующие вызовы функции GetTextExtent. Каждый раз перед тем, как начинать вывод новой строки, вы должны сбросить эту погрешность с помощью вызова функции:

SetTextJustification(hdc, 0, 0);

Работа с абзацами

Если вы работаете с целым абзацем, вы должны просматривать всю строку от начала, отыскивая символы пробела. Каждый раз, как вы встречаете символ пробела, вы вызываете функцию GettextExtentPoint32, чтобы определить, умещается ли текст по ширине между левой и правой границами. Когда текст не укладывается в отведенное для него пространство, вы возвращаетесь на один шаг назад к предыдущему найденному символу пробела. Теперь у вас есть законченная строка символов. Если вы хотите выровнять текст по ширине, то нужно вызвать функции SetTextJustification и TextOut, избавиться от ошибки и перейти к следующей строке.

Программа JUSTIFY1, приведенная на рис. 4.34, проделывает эту процедуру с первым параграфом книги автора Herman Melville, которая называется "Moby Dick". Программа использует встроенный шрифт Times New Roman размера 15 пунктов, но вы можете изменить его в функции MyCreateFont, описанной в начале программы, и перекомпилировать саму программу. Вы также можете изменить тип выравнивания, используя определение в начале программы. На рис. 4.35 показан вид текста, выведенного программой JUSTIFY1 на экран.

164

JUSTIFY1.MAK

#------------------------

# JUSTIFY1.MAK make file

#------------------------

justify1.exe : justify1.obj ezfont.obj

$(LINKER) $(GUIFLAGS) -OUT:justify1.exe justify1.obj ezfont.obj $(GUILIBS)

justify1.obj : justify1.c $(CC) $(CFLAGS) justify1.c

ezfont.obj : ezfont.c

$(CC) $(CFLAGS) ezfont.c

JUSTIFY1.C

/*-----------------------------------------

 

 

JUSTIFY1.C --

Justified Type Program

 

(c) Charles Petzold, 1996

-----------------------------------------

 

*/

#include <windows.h>

 

#include "ezfont.h"

 

#define LEFT

 

0

#define RIGHT

 

1

#define CENTER

 

2

#define JUSTIFIED

3

#define ALIGN

 

JUSTIFIED

#define MyCreateFont

EzCreateFont(hdc, "Times New Roman", 150, 0, 0, TRUE)

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

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)

{

 

static

char szAppName[] = "Justify1";

HWND

hwnd;

MSG

msg;

WNDCLASSEX wndclass;

 

wndclass.cbSize

= sizeof(wndclass);

wndclass.style

= CS_HREDRAW | CS_VREDRAW;

wndclass.lpfnWndProc

= WndProc;

wndclass.cbClsExtra

= 0;

wndclass.cbWndExtra

= 0;

wndclass.hInstance

= hInstance;

wndclass.hIcon

= LoadIcon(NULL, IDI_APPLICATION);

wndclass.hCursor

= LoadCursor(NULL, IDC_ARROW);

wndclass.hbrBackground

=(HBRUSH) GetStockObject(WHITE_BRUSH);

wndclass.lpszMenuName

= szAppName;

wndclass.lpszClassName

= szAppName;

wndclass.hIconSm

= LoadIcon(NULL, IDI_APPLICATION);

RegisterClassEx(&wndclass);

hwnd = CreateWindow(szAppName, "Justified Type", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

ShowWindow(hwnd, iCmdShow);

165

UpdateWindow(hwnd);

while(GetMessage(&msg, NULL, 0, 0))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

return msg.wParam;

}

void DrawRuler(HDC hdc, RECT *prc)

{

static int iRuleSize [16] = { 360, 72, 144, 72, 216, 72, 144, 72, 288, 72, 144, 72, 216, 72, 144, 72 };

int

i, j;

POINT

ptClient;

SaveDC(hdc);

// Set Logical Twips mapping mode

SetMapMode(hdc, MM_ANISOTROPIC);

SetWindowExtEx(hdc, 1440, 1440, NULL);

SetViewportExtEx(hdc, GetDeviceCaps(hdc, LOGPIXELSX),

GetDeviceCaps(hdc, LOGPIXELSY), NULL);

// Move the origin to a half inch from upper left

SetWindowOrgEx(hdc, -720, -720, NULL);

// Find the right margin(quarter inch from right)

ptClient.x = prc->right; ptClient.y = prc->bottom; DPtoLP(hdc, &ptClient, 1); ptClient.x -= 360;

 

// Draw the rulers

MoveToEx(hdc, 0,

-360, NULL);

LineTo

(hdc, ptClient.x,

-360);

MoveToEx(hdc, -360,

0, NULL);

LineTo

(hdc, -360, ptClient.y);

for(i = 0, j = 0; i <= ptClient.x; i += 1440 / 16, j++)

{

MoveToEx(hdc, i, -360, NULL);

LineTo (hdc, i, -360 - iRuleSize [j % 16]);

}

for(i = 0, j = 0; i <= ptClient.y; i += 1440 / 16, j++)

{

MoveToEx(hdc, -360, i, NULL);

LineTo (hdc, -360 - iRuleSize [j % 16], i);

}

RestoreDC(hdc, -1);

}

void Justify(HDC hdc, PSTR pText, RECT *prc, int iAlign)

{

int xStart, yStart, iBreakCount; PSTR pBegin, pEnd;

SIZE size;

yStart = prc->top;

166

do

// for each text line

{

 

iBreakCount = 0;

 

while(*pText == ' ')

// skip over leading blanks

pText++;

 

pBegin = pText;

 

do

// until the line is known

{

 

pEnd = pText;

 

while(*pText != '\0' && *pText++ != ' '); if(*pText == '\0')

break;

// for each space, calculate extents

iBreakCount++; SetTextJustification(hdc, 0, 0);

GetTextExtentPoint32(hdc, pBegin, pText - pBegin - 1, &size);

}

while((int) size.cx <(prc->right - prc->left));

iBreakCount--;

while(*(pEnd - 1) == ' ') // eliminate trailing blanks

{

pEnd--; iBreakCount--;

}

if(*pText == '\0' || iBreakCount <= 0) pEnd = pText;

SetTextJustification(hdc, 0, 0); GetTextExtentPoint32(hdc, pBegin, pEnd - pBegin, &size);

switch(iAlign)

// use alignment for xStart

{

 

case LEFT:

 

xStart = prc->left; break;

case RIGHT:

xStart = prc->right - size.cx; break;

case CENTER:

xStart =(prc->right + prc->left - size.cx) / 2; break;

case JUSTIFIED:

if(*pText != '\0' && iBreakCount > 0) SetTextJustification(hdc,

prc->right - prc->left - size.cx, iBreakCount);

xStart = prc->left; break;

}

TextOut(hdc, xStart, yStart, pBegin, pEnd - pBegin); yStart += size.cy;

pText = pEnd;

}

while(*pText && yStart < prc->bottom);

167

}

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

{

static char szText[] = "Call me Ishmael. Some years ago -- never mind " "how long precisely -- having little or no money " "in my purse, and nothing particular to interest " "me on shore, I thought I would sail about a " "little and see the watery part of the world. It " "is a way I have of driving off the spleen, and " "regulating the circulation. Whenever I find " "myself growing grim about the mouth; whenever " "it is a damp, drizzly November in my soul; " "whenever I find myself involuntarily pausing " "before coffin warehouses, and bringing up the " "rear of every funeral I meet; and especially " "whenever my hypos get such an upper hand of me, " "that it requires a strong moral principle to " "prevent me from deliberately stepping into the " "street, and methodically knocking people's hats " "off -- then, I account it high time to get to sea " "as soon as I can. This is my substitute for " "pistol and ball. With a philosophical flourish " "Cato throws himself upon his sword; I quietly " "take to the ship. There is nothing surprising " "in this. If they but knew it, almost all men in " "their degree, some time or other, cherish very " "nearly the same feelings towards the ocean with " "me.";

HDC hdc;

PAINTSTRUCT ps;

RECT rcClient;

switch(iMsg)

{

case WM_PAINT:

hdc = BeginPaint(hwnd, &ps);

GetClientRect(hwnd, &rcClient);

DrawRuler(hdc, &rcClient);

rcClient.left += GetDeviceCaps(hdc, LOGPIXELSX) / 2; rcClient.top += GetDeviceCaps(hdc, LOGPIXELSY) / 2; rcClient.right -= GetDeviceCaps(hdc, LOGPIXELSX) / 4;

SelectObject(hdc, MyCreateFont);

Justify(hdc, szText, &rcClient, ALIGN);

DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));

EndPaint(hwnd, &ps);

return 0;

case WM_DESTROY: PostQuitMessage(0); return 0;

}

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

}

Рис. 4.34 Файлы JUSTIFY1

168

Рис. 4.35 Вывод программы JUSTIFY1

Программа JUSTIFY1 выводит на экран линейки (в логических дюймах, конечно) вдоль верхнего края и вдоль левого края рабочей области. Функция DrawRuler рисует линейки. Структура прямоугольника определяет область, в которой может быть расположен текст.

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

Эта программа не является совершенной. В частности, выравнивание по ширине логически бессмысленно, когда в каждой строке меньше, чем два слова. Но даже если мы решим этот вопрос (что не особенно сложно), программа все еще не будет работать как следует, если одно слово будет слишком длинным, чтобы уместиться между заданными полями. Конечно, ситуация может стать более сложной, если вы работаете с программами, использующими несколько шрифтов в одной строке (как с кажущейся легкостью делают это текстовые процессоры в Windows). Но никто никогда и не требовал, чтобы это было легко. Это проще только по сравнению с самостоятельным выполнением той же работы.

Часть II

Средства ввода

Соседние файлы в предмете Операционные системы