Семейства шрифтов в Windows
По трем из рассмотренных признаков (ширина штриха, ширина символов и наличию засечек) в Windows принято выделять так называемые семейства шрифтов. Для того, что бы пояснить разницу между принятыми семействами, приведем небольшую табличку, содержащую сводку характеристик разных семейств шрифтов в Windows:
Семейство | Ширина штриха | Ширина символов | Наличие засечек | примеры |
MODERN | постоянная | постоянная | — | Courier New |
ROMAN | переменная | переменная | есть | Times New Roman, Antiqua, Garamond, Palatino, Bodoni |
SWISS | переменная | переменная | нет | Arial, Helvetica, Futura, Avantgarde, Optima, Swiss |
SCRIPT | — | — | — | Script, Odessa Script FWF, Decor, Jikharev, Parsek |
DECORATIVE | — | — | — | Windings, MusicalSymbols, Symbol |
DONTCARE | — |
Обычно к семейству MODERN относят все шрифты фиксированной ширины. Название MODERN указывает на сравнительно недавнюю историю этих шрифтов - они получили распространение с развитием печатающей техники и компьютеров, тогда как другие виды шрифтов возникали с XV века, когда заканчивалась эпоха готического шрифта.
В те времена начинал формироваться шрифт, похожий по своему начертанию на один из древнейших шрифтов - римский капитальный. Это был пропорциональный шрифт с засечками и нормальной контрастностью, позже он получил очень широкое распространение в типографском деле и дожил до наших дней. За свою долгую историю он многократно видоизменялся и стал родоначальником большого числа поколений шрифтов. В Windows такие шрифты относятся к семейству ROMAN.
Реально этому семейству соответствует очень большое число разных шрифтов, выделяемых в других классификационных системах. В частности, можно выделить так называемые брусковые шрифты, обычно со слабо выраженным контрастом и засечками, перпендикулярными штрихам и имеющими примерно такую же ширину (пример - Courier); В XX веке возникли шрифты, получившие очень широкое распространение. Наиболее распространенное название для этих шрифтов - рубленые. Эти шрифты не имеют контраста и засечек (sans serif), в Windows им соответствует семейство SWISS. Семейство DONTCARE реально не соответствует никакой группе шрифтов. Оно используется только при указании, из какого семейства надо выбирать шрифт - при этом оно обозначает “любое семейство”.
Стандартные шрифты Windows
В стандартной поставке Windows присутствует небольшой набор шрифтов, представляющий все (определенные в Windows) семейства шрифтов. Этот набор включает в себя растровые, векторные и TrueType шрифты, информация о которых сведена в небольшую таблицу:
Имя шрифта | семейство | кодовая таблица | файл(ы) |
растровые шрифты | |||
System | SWISS | ANSI | xxxSYS.FON |
FixedSys | DONTCARE | ANSI | xxxFIX.FON |
Terminal | MODERN | OEM | xxxOEM.FON |
Courier | MODERN | ANSI | COURy.FON |
MS Sans Serif | SWISS | ANSI | SSERIFy.FON |
MS Serif | ROMAN | ANSI | SERIFy.FON |
Small Fonts | ROMAN | ANSI | SMALLy.FON |
Symbol | DECORATIVE | SYMBOL | SYMBOLy.FON |
векторные шрифты | |||
Modern | MODERN | OEM | MODERN.FON |
Roman | ROMAN | OEM | ROMAN.FON |
Script | SCRIPT | OEM | SCRIPT.FON |
TrueType | |||
Arial | SWISS | ANSI | ARIALzz.TTF(.FOT) |
Courier New | MODERN | ANSI | COURzz.TTF(.FOT) |
Times New Roman | ROMAN | ANSI | TMSRMN.TTF(.FOT) |
Windings | DONTCARE | ANSI | WINDINGS.TTF(.FOT) |
В этой таблице следует внимательно рассмотреть имена файлов. В этих именах маленькими буквами (xxx,y,zz) обозначены изменяющиеся части.
Так xxx обозначает устройство, для которого был спроектирован шрифт. Вместо этой последовательности реально написано CGA, EGA, VGA или 8514.
Символ y обозначает категорию устройств, к которой относится данный шрифт. Возможные значения можно найти в таблице:
Буква | Соотношение сторон | Разрешающая способность X/Y (пиксел/дюйм) | Устройство |
A | 200 | 96/48 | CGA |
B | 133 | 96/72 | EGA |
C | 83 | 60/72 | Okidata printers |
D | 167 | 120/72 | IBM, Epson printers |
E | 100 | 96/96 | VGA |
F | 100 | 120/120 | IBM 8514 |
Последовательность zz указывает, какого типа шрифт описан в этом файле: нормальный (пустая последовательность), жирный BD, наклонный I или жирный наклонный BI.
Получение хендла шрифта
Для того, что бы Вы могли применять шрифт в Вашей программе, Вы должны сначала получить хендл соответствующего шрифта. Шрифт является объектом GDI, поэтому работа с ним похожа на работу с другими объектами GDI. Как правило вы должны выполнить следующие действия:
получить хендл либо стандартного шрифта, либо создав "новый" шрифт
При создании "нового" шрифта создается соответствующий объект GDI, а не новый файл описания шрифта.
выбрать шрифт в контекст устройства
осуществить вывод, используя текущий шрифт
если шрифт был создан, то его надо уничтожить.
Создание шрифтов занимает некоторое время (особенно для отображения TrueType шрифтов - Windows автоматически генерирует промежуточный растровый шрифт, который и используется при выводе). Если надо создавать шрифты, то это удобно делать при создании окна или даже при запуске приложения, а уничтожать - при закрытии окна или при завершении приложения (как и все объекты GDI, созданный шрифт уничтожается с помощью функции DeleteObject).
Если Вам надо получить хендл стандартного шрифта, то Вы можете воспользоваться функцией:
HFONT GetStockObject( nIndex );
Параметр nIndex может быть:
ANSI_FIXED_FONT | соответствует шрифту Courier |
ANSI_VAR_FONT | соответствует шрифту MS Sans Serif |
OEM_FIXED_FONT | соответствует шрифту Terminal |
SYSTEM_FONT | соответствует шрифту System; этот шрифт используется по умолчанию |
SYSTEM_FIXED_FONT | соответствует шрифту FixedSys; До версии Windows 3.0 системный шрифт был фиксированной ширины, он включен в Windows 3.1 для совместимости. |
DEVICE_DEFAULT_FONT | соответствует шрифту, загруженному в устройство; для дисплея не определен |
Если Вам надо создавать собственный шрифт, то Вы можете воспользоваться одной из двух функций:
HFONT CreateFont(
nHeight, nWidth, nEscapement, nOrientation, nWeight,
bItalic, bUnderline, bStrikeOut,
bCharSet, bOutputPrecision, bClipPrecision, bQuality, bPitchAndFamily,
lpszFacename);
или
HFONT CreateFontIndirect( lpLogFont );
Чаще применяется функция CreateFontIndirect(), получающая в качестве параметра указатель на структуру LOGFONT. Поля этой структуры совпадают с аргументами функции CreateFont().
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;
BYTE lfFaceName[LF_FACESIZE];
} LOGFONT;
Рассмотрим назначение полей этой структуры:
lfHeight задает требуемый размер шрифта в точках (пунктах). Если значение положительно, то в высоту включается межстрочный промежуток, а если отрицательно, то модуль указывает высоту символа шрифта. Значение 0 указывает, что используется значение высоты по умолчанию.
lfWidth задает среднюю ширину символов в пунктах. Значение 0 соответствует ширине по умолчанию.
lfEscapement задает наклон базовой линии строки в десятых долях градуса. Для растровых шрифтов игнорируется.
lfOrientation задает ориентацию символа относительно базовой линии в десятых долях градуса. Игнорируется для растровых и TrueType шрифтов, для векторных используются значения: 0, 900, 1800 и 2700
lfWeight задает вес символа (жирность). Соответствует количеству закрашенных пиксел из 1000. Предусмотрены условные обозначения для определения веса, начинающиеся на FW_... Например, нормальный текст (400) соответствует FW_NORMAL (FW_REGULAR), жирный (700) FW_BOLD.
lfItalic ненулевое значение задает наклон символов (начертание slanted). Значение 0 соответствует обычному тексту.
lfUnderline ненулевое значение задает подчеркивание строки текста линией. Значение 0 соответствует обычному тексту.
lfStrikeOut ненулевое значение задает перечеркивание строки текста линией. Значение 0 соответствует обычному тексту.
lfCharSet задает кодовую таблицу, которую должен поддерживать данный шрифт. Используются следующие условные обозначения:
ANSI_CHARSET кодовая таблица ANSI
OEM_CHARSET кодовая таблица OEM
SYMBOL_CHARSET символы
SHIFTJIS_CHARSET японская азбука
DEFAULT_CHARSET любая кодовая таблица.
lfOutPrecision указывает, насколько точно должен соответствовать подбираемый шрифт указанному размеру. (Реально Вы можете заказать растровый шрифт несуществующего размера). См. условные обозначения вида OUT_???_PRECIS в windows.h.
lpClipPrecision указывает, как должен отображаться частично невидимый символ. См. условные обозначения вида CLIP_???_PRECIS в windows.h.
lfQuality указывает качество получаемого шрифта. Обычно используется значение PROOF_QUALITY (или DEFAULT_QUALITY). Если размер растрового шрифта меньше, чем требуется, то Windows может масштабировать шрифт. Однако при этом резко ухудшается качество, поэтому масштабирование можно запретить, используя значение PROOF_QUALITY.
lfPitchAndFamily два младших бита задают тип шрифта - DEFAULT_PITCH (любой тип), VARIABLE_PITCH (пропорциональный) или FIXED_PITCH (моноширинный); старший байт указывает семейство, которое задается одним из следующих символов: FF_DECORATIVE, FF_DONTCARE, FF_MODERN, FF_ROMAN, FF_SCRIPT, FF_SWISS.
lfFaceName массив из LF_FACESIZE символов, содержащий заканчивающееся \0 имя шрифта. Пустое имя соответствует шрифту устройства.
Создание шрифтовых ресурсов
В этом разделе мы будем говорить только о создании собственных растровых шрифтовых ресурсов. Это связано с тем, что стандартные редакторы шрифтовых ресурсов (включаемые в SDK и компиляторы) позволяют создавать только растровые шрифты.
Для начала мы должны нарисовать требуемые нам шрифты с помощью какого-либо редактора ресурсов. Растровые шрифты обычно размещаются в файлах с расширением .FNT (это не шрифтовой файл, а отдельный ресурс, как, скажем, битмап). Нам может понадобится нарисовать несколько шрифтов разного размера, но имеющих общее начертание. Эти шрифты будут сохранены в разных .FNT файлах.
Далее мы должны построить шрифтовой файл .FON, содержащий наши шрифты. Этот файл являться библиотекой ресурсов. Практически он оформлен как обычное Windows приложение, которое содержит только ресурсы. Мы можем описать такое приложение, как приложение вообще не имеющее сегмента кода, или как библиотеку.
Так как наша библиотека должна содержать шрифтовые ресурсы, то мы должны задать файл описания ресурсов .RC, содержащий список нарисованных нами шрифтов, например:
1 FONT fonta.fnt
2 FONT fontb.fnt
3 FONT fontc.fnt
Конечно нам понадобится файл описания приложения .DEF в несколько специфичном виде:
LIBRARY
DESCRIPTION 'FONTRES DISPLAY : 40-char terminal'
EXETYPE WINDOWS
STUB 'WINSTUB.EXE'
DATA NONE
Надо обратить внимание на использование слова LIBRARY, вместо NAME, для указания того, что это не обычное приложение, а библиотека. Далее мы должны указать, что наша библиотека не имеет данных 'DATA NONE' и составить описание нашего шрифта. Для этого мы должны в DESCRIPTION указать строку специального формата:
DESCRIPTION 'FONTRES aspect,logpixelsx,logpixelsy : comment'
DESCRIPTION 'FONTRES DEVICESPECIFIC device : comment'
DESCRIPTION 'FONTRES DISPLAY : comment'
Здесь представлены три разных формата таких описаний. Первый формат указывает характеристики устройства, для которого разработан шрифт; значения характеристик можно найти, получив информацию о контексте устройства, или из таблички со сводкой системных шрифтов, приведенной выше.
Второй формат задает шрифт, созданный для конкретного устройства. Параметр device задает имя устройства, для которого шрифт спроектирован, например: IBM 8514. Третий формат указывает, что шрифт спроектирован для дисплея.
Далее мы должны построить библиотеку ресурсов. Как уже говорилось, это можно сделать двумя способами - создав приложение не имеющее кода или создав приложение, являющееся разделяемой библиотекой.
Если мы хотим описать приложение не имеющее кода, то нам будет удобнее воспользоваться компиляторами Borland, так как построители задач других фирм часто, встретив сегмент нулевой длины, предполагают длину 65536. Для создания модуля не имеющего кода, нам надо написать простейший ассемблерный файл:
_TEXT segment byte public 'CODE'
_TEXT ends
end
Этот файл описывает только один сегмент нулевой длины.
После этого мы можем приступить к построению шрифтового файла. Для этого мы сначала компилируем ассемблерный файл:
tasm file.asm
затем мы должны построить нашу библиотеку и включить в нее спроектированные ресурсы:
tlink file.obj,file.exe,nul,,file.def
rc file.rc
rename file.exe file.fon
Теперь мы располагаем собственным шрифтовым ресурсом, который мы можем применять в нашем приложении. Еще раз надо отметить, что обязательно применение 'Borland TLINK' для построения файла, так как другие сборщики могут построить неверный модуль.
Ранее мы сделали замечание о том, что мы можем строить шрифтовой файл двумя способами - как библиотеку не имеющую кода, или как обычную библиотеку. Если мы хотим строить обычную библиотеку, то вместо ассемблерного файла нам надо написать небольшой файл на C:
#include <windows.h>
extern "C" {
int CALLBACK LibMain(
HANDLE hInstance, WORD wDataSeg, WORD cbHeapSize,LPSTR lpszCmdLine) {
// обычно функция LibMain() разблокирует сегмент данных
// если он имеет динамический heap.
// if ( cbHeapSize ) UnlockData( 0 );
// так как мы вообще не имеем данных (и heap тоже)
// то можем этого не делать.
return 1;}
int CALLBACK WEP( int bSystemExit ) {
return TRUE;}}
Этот файл содержит две процедуры - LibMain() - которая заменяет обычный WinMain() и вызывается при инициализации библиотеки - и процедуру WEP(), которая вызывается при удалении ненужной библиотеки.
В этом файле надо обратить внимание на то, что функция LibMain описана как CALLBACK (PASCAL FAR), в отличие от WinMain. К сожалению, некоторые компиляторы предполагают, что она должна быть NEAR для моделей памяти с одним сегментом кода. В качестве выхода можно изменить имя, например, написать его только большими буквами. Тогда компилятор не распознает эту функцию как стандартную и не сделает ошибки, а сборщик осуществит правильное связывание, так как функция декларирована как CALLBACK (PASCAL FAR).
Вторая особенность - указание, что имена функций не должны кодироваться как C++ имена ( extern "C" ). Это опять–же связано с особенностями некоторых компиляторов, которые не распознают функцию WEP() (Windows Exit Procedure) как стандартную, и осуществляют для нее C++ кодирование имени - при этом сборщик не может правильно построить задачу.
В остальном построение шрифтового файла не отличается от рассмотренного, конечно кроме компиляции исходного текста, которая выполняется как для обычного Windows-приложения:
bcc -ms -W file.c file.def
Настройка приложений
Заканчивая разговор о шрифтах, удобно рассмотреть еще один компонент Windows - средства для настройки приложений. Под настройкой (иногда "профилированием") понимается задание характеристик приложения и их сохранение для использования при следующем запуске.
Обычно такие задачи решаются с помощью создания конфигурационных файлов. Однако конфигурация описывается каждой задачей по-своему, что не всегда удобно. Windows предлагает общий для всех приложений механизм описания их характеристик, с использованием файлов настройки.
Такие файлы (обычно имеющие расширение .INI) являются обычными ASCII–файлами, разделенными на секции, начинающиеся с имени секции, заключенного в квадратные скобки. Далее следует список параметров в виде ‘параметр=значение’, каждый параметр размещается в отдельной строке. В этот файл можно вставлять комментарии - строки начинающиеся с ‘;’.
Пример взят из файла WORKSHOP.INI:
[User Controls]
BorShade=E:\BORLANDC\WORKSHOP\BWCC.DLL
[RWS_Bitmap]
PercentLeft=50
ZoomLeft=1
ZoomRight=1
bVert=0
[RWS_Font]
PercentLeft=50
ZoomLeft=4
ZoomRight=1
bVert=1
Для работы с такими файлами Windows предоставляет набор функций, осуществляющих запись и чтение параметров:
int GetProfileInt(lpszSection, lpszEntry, nDefault);
int GetProfileString(lpszSection, lpszEntry, lpszDefault, lpsBuffer, nMaxBuffer);
BOOL WriteProfileString(lpszSection, lpszEntry, lpszString);
Параметр lpszSection задает имя секции (скобок в имени указывать не надо), lpszEntry - имя параметра. Если мы получаем значение параметра, то можем указать значение по умолчанию, которое возвращается, если данный параметр не найден.
С помощью функции GetProfileString() можно получить список имен всех параметров в секции, указав lpszEntry= NULL. При этом имена параметров секции будут скопированы в буфер последовательно друг за другом, каждое имя будет заканчиваться 0 и после последнего имени будут стоять два 0.
Функция WriteProfileString() позволяет не только записывать параметры, но и удалять, для чего надо указать lpszString=NULL. Можно удалить целиком всю секцию, указав lpszEntry=NULL.
Все три рассмотренных функции используют файл WIN.INI. При этом имя секции часто ассоциируется с именем приложения. (Поэтому в документации имя секции часто называется именем приложения).
Конечно, часто бывает неудобно использовать общий файл настройки для всех существующих приложений (при этом, в частности, трудно организовать удаление приложений). Windows предоставляет возможность использовать собственный файл настройки (и даже несколько). Для работы с собственными файлами настройки предусмотрены еще три функции:
int GetPrivateProfileInt( lpszSection, lpszEntry, nDefault, lpszIniFile );
int GetPrivateProfileString(
lpszSection, lpszEntry, lpszDefault, lpsBuffer, nMaxBuffer, lpszIniFile);
BOOL WritePrivateProfileString(
lpszSection, lpszEntry, lpszString, lpszIniFile);
Последний параметр этих функций задает имя файла настройки. Если Вы не указываете путь к файлу, то он размещается в каталоге Windows.
Работа с принтером
Основы печати
Когда Вы получили контекст устройства для принтера, на самом деле Вы не получили доступа к самому принтеру, а только лишь к промежуточному метафайлу. Пока Вы рисуете на принтере, все нужные команды запоминаются в этом метафайле и только после того, как Вы объявите о завершении работы со страницей, Windows осуществит печать подготовленной страницы из метафайла.
Реальный механизм сложнее, так как спулинг принтера может быть отключен, а, кроме того, Ваше приложение может использовать специальный метод пополосной печати, который заметно усложняет внутреннюю логику печати.
Такой сложный механизм позволяет осуществить спулинг печати и более-менее эффективное разделение принтера между разными приложениями.
Общие правила, с точки зрения приложения, сводятся к следующему:
начиная работу с принтером, Вы должны зарегистрировать собственное задание на печать. Это исключает одновременное печатание данных разными приложениями.
Далее Вы должны заполнить страницу нужными данными и подать команду на печать этой страницы и на переход к следующей.
Когда все страницы напечатаны, Вы должны сказать Windows о завершении печати Вашего задания.
В этой схеме есть несколько недостатков:
Во-первых, процесс печати даже одной страницы может быть весьма продолжительным (до 30-60 минут) - а при такой схеме Вы не можете даже прервать процесс печати;
Во-вторых, графический образ одной страницы на принтере высокого разрешения (особенно цветном) может занимать десятки мегабайт, что достаточно жестко ограничивает возможности печати.
Для того, что бы решить эти проблемы применяют различные приемы. Для того, что бы можно было выполнять какую-либо иную работу одновременно с печатью, и что бы печать можно было прервать в любой момент, добавляют специальную функцию, называемую AbortProc(), которая вызывается автоматически во время печати для обработки сообщений и для того, что бы работу принтера можно было прервать.
В этом случае, помимо регистрации задания, Вы должны установить собственную AbortProc до начала самой печати.
Для решения второй задачи вводят специальный метод пополосной печати (banding). В этом случае Вы запрашиваете у принтера данные о полосе, которую надо отобразить и заполняете эту полосу (или всю страницу - контекст устройства реально соответствует только этой полосе). Тогда максимальный размер временного файла, содержащего образ страницы, ограничен размерами полосы.
В этом случае Вам надо иметь в виду несколько нюансов:
при пополосной печати функция AbortProc() автоматически вызывается слишком редко, поэтому Вы должны сами предусмотреть вызовы этой функции с достаточной частотой.
ориентация полос не обязательно горизонтальна, так как принтер может печатать как в режиме “портрет” (Portrait), так и в режиме “ландшафт” (Landscape).
Полосу для печати у Вас запрашивает Windows. Вы не знаете заранее ни ориентацию этой полосы, ни ее размеры. Windows формирует запрос для печатания полосы до тех пор, пока не будет напечатана вся страница.
Теперь нам надо рассмотреть непосредственно текст программы, осуществляющий печать.
Печатание на принтере
Вообще для управления печатью достаточно только одной процедуры - Escape(), которая использовалась в прежних версиях Windows.
int Escape( hDC, nEscape, cbInput, lpsInData, lpvOutput );
Параметр nEscape указывает код выполняемой операции, параметры cbInput и lpsInData указывают исходные данные для операции, а lpvOutput - результат операции. Применение и форма исходных данных и результатов зависит от операции.
В версии Windows 3.1 к этой процедуре добавлен ряд вспомогательных, которые просто выполняют отдельные операции.
Сейчас мы рассмотрим некоторые основные операции, необходимые для печати. Первая операция регистрирует наше задание на печать:
Escape( hDC, STARTDOC, cbName, lpsName, NULL );
или
StartDoc( hDC, lpDocInfo );
struct DOCINFO {
int cbSize;
LPSTR lpszDocName;
LPSTR lpszOutput;};
Параметры cbName и lpsName определяют название нашего задания. Возвращаемое значение, большее 0, указывает на успешную постановку задания в очередь, а значение 0 и меньше указывает на возникшую ошибку.
Далее, если мы хотим печатать страницу целиком, то после ее заполнения мы должны подать команду на печать страницы:
// заполнить страницу
Escape( hDC, NEWFRAME, 0, NULL, NULL );
или
StartPage( hDC );
// заполнить страницу
EndPage( hDC );
В случае успешного завершения задания мы должны сообщить об этом с помощью операции ENDDOC:
Escape( hDC, ENDDOC, 0, NULL, NULL );
или
EndDoc( hDC );
Однако нам может понадобится прервать печать досрочно. Для этого мы должны разработать и установить нашу собственную процедуру AbortProc. Предположим, что такая функция нами написана и ее имя AbortProc(). Тогда мы можем установить ее следующим способом:
FARPROC lpfnAbortProc= MakeProcInstance( (FARPROC)AbortProc, hInstance );
Escape( hDC, SETABORTPROC, 0, (LPSTR)lpfnAbortProc, NULL );
// печатаем...
FreeProcInstance( lpfnAbortProc );
или
FARPROC lpfnAbortProc= MakeProcInstance( (FARPROC)AbortProc, hInstance );
SetAbortProc( hDC, (ABORTPROC)lpfnAbortProc );
// печатаем...
FreeProcInstance( lpfnAbortProc );
При этом, если наше задание будет досрочно снято с печати, то мы должны сообщить об этом (что бы задание было корректно удалено из очереди):
Escape( hDC, ABORTDOC, 0, NULL, NULL );
или
AbortDoc( hDC );
Небольшой пример (поясняющий правила разработки процедуры AbortProc):
static BOOL bAbort;
BOOL PASCAL FAR _export AbortProc( HDC hdcPRN, short nCode ) {
MSG msg;
while ( !bAbort && PeekMessage( &msg,NULL,0,0,PM_REMOVE ) ) {
TranslateMessage( &msg );
DispatchMessage( &msg );}
return !bAbort;}
static BOOL DoPrint( void ) {
static char szName[]= "Test Printing";
FARPROC lpfnAbort;
BOOL answer= FALSE;
HDC hdcPRN;
hdcPRN= GetPrinterDC(); // получение hdcPRN см. выше
lpfnAbort= MakeProcInstance( (FARPROC)AbortProc, hInstance );
Escape( hdcPRN, SETABORTPROC, 0, (LPSTR)lpfnAbort, NULL );
if ( Escape( hdcPRN, STARTDOC, sizeof(szName)-1, szName, NULL ) > 0 ) {
while ( !bAbort && /* есть что печатать */ ) {
// рисуем на странице
if ( Escape( hdcPRN, NEWFRAME, 0,NULL,NULL ) <= 0 )bAbort= TRUE;}
if ( bAbort ) {
Escape( hdcPRN, ABORTDOC, 0,NULL,NULL );
} else {
Escape( hdcPRN, ENDDOC, 0,NULL,NULL );
answer= TRUE;}}
FreeProcInstance( (FARPROC)lpfnAbort );
DestroyDC( hdcPRN );
return answer;}
В этом примере надо обратить внимание на переменную bAbort. Она может быть установлена в значение TRUE для завершения печати. В данном примере досрочное завершение не предусмотрено, хотя его легко можно сделать, устанавливая эту переменную, например, в ответ на выбор пункта меню, на нажатие кнопки диалога или даже по таймеру.
Если мы будем использовать пополосную печать, то нам надо вместо операции NEWFRAME выполнять серию операций NEXTBAND до тех пор, пока полоса для печати не окажется пустой (что соответствует полностью напечатанной странице); при этом внутренний цикл для печати страниц придется несколько изменить:
...RECT rc;
...while ( !bAbort && /* есть что печатать */ ) {
while ( Escape(hdcPRN, NEXTBAND, 0, NULL, (LPSTR)&rc) > 0 ) {
if ( IsRectEmpty( &rc ) ) goto ok_page;
// заполняем полосу или всю страницу, иногда вызывая AbortProc:
lpfnAbortProc( hdcPRN, 0 );
// Внимание! используется указатель на функцию, а не она сама!}
bAbort= TRUE;
ok_page:;}...
Семейства шрифтов в Windows
По трем из рассмотренных признаков (ширина штриха, ширина символов и наличию засечек) в Windows принято выделять так называемые семейства шрифтов. Для того, что бы пояснить разницу между принятыми семействами, приведем небольшую табличку, содержащую сводку характеристик разных семейств шрифтов в Windows:
Семейство | Ширина штриха | Ширина символов | Наличие засечек | примеры |
MODERN | постоянная | постоянная | — | Courier New |
ROMAN | переменная | переменная | есть | Times New Roman, Antiqua, Garamond, Palatino, Bodoni |
SWISS | переменная | переменная | нет | Arial, Helvetica, Futura, Avantgarde, Optima, Swiss |
SCRIPT | — | — | — | Script, Odessa Script FWF, Decor, Jikharev, Parsek |
DECORATIVE | — | — | — | Windings, MusicalSymbols, Symbol |
DONTCARE | — |
Обычно к семейству MODERN относят все шрифты фиксированной ширины. Название MODERN указывает на сравнительно недавнюю историю этих шрифтов - они получили распространение с развитием печатающей техники и компьютеров, тогда как другие виды шрифтов возникали с XV века, когда заканчивалась эпоха готического шрифта.
В те времена начинал формироваться шрифт, похожий по своему начертанию на один из древнейших шрифтов - римский капитальный. Это был пропорциональный шрифт с засечками и нормальной контрастностью, позже он получил очень широкое распространение в типографском деле и дожил до наших дней. За свою долгую историю он многократно видоизменялся и стал родоначальником большого числа поколений шрифтов. В Windows такие шрифты относятся к семейству ROMAN.
Реально этому семейству соответствует очень большое число разных шрифтов, выделяемых в других классификационных системах. В частности, можно выделить так называемые брусковые шрифты, обычно со слабо выраженным контрастом и засечками, перпендикулярными штрихам и имеющими примерно такую же ширину (пример - Courier); В XX веке возникли шрифты, получившие очень широкое распространение. Наиболее распространенное название для этих шрифтов - рубленые. Эти шрифты не имеют контраста и засечек (sans serif), в Windows им соответствует семейство SWISS. Семейство DONTCARE реально не соответствует никакой группе шрифтов. Оно используется только при указании, из какого семейства надо выбирать шрифт - при этом оно обозначает “любое семейство”.
Стандартные шрифты Windows
В стандартной поставке Windows присутствует небольшой набор шрифтов, представляющий все (определенные в Windows) семейства шрифтов. Этот набор включает в себя растровые, векторные и TrueType шрифты, информация о которых сведена в небольшую таблицу:
Имя шрифта | семейство | кодовая таблица | файл(ы) |
растровые шрифты | |||
System | SWISS | ANSI | xxxSYS.FON |
FixedSys | DONTCARE | ANSI | xxxFIX.FON |
Terminal | MODERN | OEM | xxxOEM.FON |
Courier | MODERN | ANSI | COURy.FON |
MS Sans Serif | SWISS | ANSI | SSERIFy.FON |
MS Serif | ROMAN | ANSI | SERIFy.FON |
Small Fonts | ROMAN | ANSI | SMALLy.FON |
Symbol | DECORATIVE | SYMBOL | SYMBOLy.FON |
векторные шрифты | |||
Modern | MODERN | OEM | MODERN.FON |
Roman | ROMAN | OEM | ROMAN.FON |
Script | SCRIPT | OEM | SCRIPT.FON |
TrueType | |||
Arial | SWISS | ANSI | ARIALzz.TTF(.FOT) |
Courier New | MODERN | ANSI | COURzz.TTF(.FOT) |
Times New Roman | ROMAN | ANSI | TMSRMN.TTF(.FOT) |
Windings | DONTCARE | ANSI | WINDINGS.TTF(.FOT) |
В этой таблице следует внимательно рассмотреть имена файлов. В этих именах маленькими буквами (xxx,y,zz) обозначены изменяющиеся части.
Так xxx обозначает устройство, для которого был спроектирован шрифт. Вместо этой последовательности реально написано CGA, EGA, VGA или 8514.
Символ y обозначает категорию устройств, к которой относится данный шрифт. Возможные значения можно найти в таблице:
Буква | Соотношение сторон | Разрешающая способность X/Y (пиксел/дюйм) | Устройство |
A | 200 | 96/48 | CGA |
B | 133 | 96/72 | EGA |
C | 83 | 60/72 | Okidata printers |
D | 167 | 120/72 | IBM, Epson printers |
E | 100 | 96/96 | VGA |
F | 100 | 120/120 | IBM 8514 |
Последовательность zz указывает, какого типа шрифт описан в этом файле: нормальный (пустая последовательность), жирный BD, наклонный I или жирный наклонный BI.
Получение хендла шрифта
Для того, что бы Вы могли применять шрифт в Вашей программе, Вы должны сначала получить хендл соответствующего шрифта. Шрифт является объектом GDI, поэтому работа с ним похожа на работу с другими объектами GDI. Как правило вы должны выполнить следующие действия:
получить хендл либо стандартного шрифта, либо создав "новый" шрифт
При создании "нового" шрифта создается соответствующий объект GDI, а не новый файл описания шрифта.
выбрать шрифт в контекст устройства
осуществить вывод, используя текущий шрифт
если шрифт был создан, то его надо уничтожить.
Создание шрифтов занимает некоторое время (особенно для отображения TrueType шрифтов - Windows автоматически генерирует промежуточный растровый шрифт, который и используется при выводе). Если надо создавать шрифты, то это удобно делать при создании окна или даже при запуске приложения, а уничтожать - при закрытии окна или при завершении приложения (как и все объекты GDI, созданный шрифт уничтожается с помощью функции DeleteObject).
Если Вам надо получить хендл стандартного шрифта, то Вы можете воспользоваться функцией:
HFONT GetStockObject( nIndex );
Параметр nIndex может быть:
ANSI_FIXED_FONT | соответствует шрифту Courier |
ANSI_VAR_FONT | соответствует шрифту MS Sans Serif |
OEM_FIXED_FONT | соответствует шрифту Terminal |
SYSTEM_FONT | соответствует шрифту System; этот шрифт используется по умолчанию |
SYSTEM_FIXED_FONT | соответствует шрифту FixedSys; До версии Windows 3.0 системный шрифт был фиксированной ширины, он включен в Windows 3.1 для совместимости. |
DEVICE_DEFAULT_FONT | соответствует шрифту, загруженному в устройство; для дисплея не определен |
Если Вам надо создавать собственный шрифт, то Вы можете воспользоваться одной из двух функций:
HFONT CreateFont(
nHeight, nWidth, nEscapement, nOrientation, nWeight,
bItalic, bUnderline, bStrikeOut,
bCharSet, bOutputPrecision, bClipPrecision, bQuality, bPitchAndFamily,
lpszFacename);
или
HFONT CreateFontIndirect( lpLogFont );
Чаще применяется функция CreateFontIndirect(), получающая в качестве параметра указатель на структуру LOGFONT. Поля этой структуры совпадают с аргументами функции CreateFont().
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;
BYTE lfFaceName[LF_FACESIZE];
} LOGFONT;
Рассмотрим назначение полей этой структуры:
lfHeight задает требуемый размер шрифта в точках (пунктах). Если значение положительно, то в высоту включается межстрочный промежуток, а если отрицательно, то модуль указывает высоту символа шрифта. Значение 0 указывает, что используется значение высоты по умолчанию.
lfWidth задает среднюю ширину символов в пунктах. Значение 0 соответствует ширине по умолчанию.
lfEscapement задает наклон базовой линии строки в десятых долях градуса. Для растровых шрифтов игнорируется.
lfOrientation задает ориентацию символа относительно базовой линии в десятых долях градуса. Игнорируется для растровых и TrueType шрифтов, для векторных используются значения: 0, 900, 1800 и 2700
lfWeight задает вес символа (жирность). Соответствует количеству закрашенных пиксел из 1000. Предусмотрены условные обозначения для определения веса, начинающиеся на FW_... Например, нормальный текст (400) соответствует FW_NORMAL (FW_REGULAR), жирный (700) FW_BOLD.
lfItalic ненулевое значение задает наклон символов (начертание slanted). Значение 0 соответствует обычному тексту.
lfUnderline ненулевое значение задает подчеркивание строки текста линией. Значение 0 соответствует обычному тексту.
lfStrikeOut ненулевое значение задает перечеркивание строки текста линией. Значение 0 соответствует обычному тексту.
lfCharSet задает кодовую таблицу, которую должен поддерживать данный шрифт. Используются следующие условные обозначения:
ANSI_CHARSET кодовая таблица ANSI
OEM_CHARSET кодовая таблица OEM
SYMBOL_CHARSET символы
SHIFTJIS_CHARSET японская азбука
DEFAULT_CHARSET любая кодовая таблица.
lfOutPrecision указывает, насколько точно должен соответствовать подбираемый шрифт указанному размеру. (Реально Вы можете заказать растровый шрифт несуществующего размера). См. условные обозначения вида OUT_???_PRECIS в windows.h.
lpClipPrecision указывает, как должен отображаться частично невидимый символ. См. условные обозначения вида CLIP_???_PRECIS в windows.h.
lfQuality указывает качество получаемого шрифта. Обычно используется значение PROOF_QUALITY (или DEFAULT_QUALITY). Если размер растрового шрифта меньше, чем требуется, то Windows может масштабировать шрифт. Однако при этом резко ухудшается качество, поэтому масштабирование можно запретить, используя значение PROOF_QUALITY.
lfPitchAndFamily два младших бита задают тип шрифта - DEFAULT_PITCH (любой тип), VARIABLE_PITCH (пропорциональный) или FIXED_PITCH (моноширинный); старший байт указывает семейство, которое задается одним из следующих символов: FF_DECORATIVE, FF_DONTCARE, FF_MODERN, FF_ROMAN, FF_SCRIPT, FF_SWISS.
lfFaceName массив из LF_FACESIZE символов, содержащий заканчивающееся \0 имя шрифта. Пустое имя соответствует шрифту устройства.
Основы подбора шрифтов в Windows
Когда Вы вызываете функцию, создающую шрифт, Windows перебирает все имеющиеся шрифты, определяя шрифт, точнее всего соответствующий желаемому.
При подборе шрифта используется система “пенальти”: для каждого из шрифтов он вычисляет “пенальти”, соответствующие отличию данного шрифта от желаемого. Шрифт с минимальным пенальти считается наиболее точно соответствующим желаемому.
Пенальти вычисляются следующим образом: для существенных параметров вводятся бальные оценки. Если заказанный параметр соответствует шрифту, пенальти равно 0, а если отличается, то размер пенальти зависит от параметра. Значения пенальти приведены в следующей таблице:
параметр | пенальти |
fCharSet | 4 |
fPitchAndFamily: pitch family | 3 3 |
lfFaceName | 3 |
lfHeight | 2 |
lfWidth | 2 |
lfItalic | 1 |
lfUnderline | 1 |
lfStrikeOut | 1 |
В этой таблице можно заметить несколько интересных особенностей. Например, пенальти за несоответствие кодовой таблице больше, а за не–принадлежность к семейству равна пенальти за несоответствие имени шрифта. Практически, если Вы хотите использовать шрифт с конкретным именем, то Вы должны обязательно указать правильные кодовую страницу и семейство шрифтов, иначе Windows может использовать шрифт с другим именем.
Дата: 2019-07-25, просмотров: 231.