Продолжение. Начало см. в # 5, 6, 7/97.

Занятие четвертое

Дмитрий Рамодин

Шрифты и пути
Использование функции EnumFontFamilies()
Структура LOGFONT
Функции GDI, создающие пути

Шрифты и пути

Итак, мы снова встретились с вами, чтобы продолжить изучение таинственных дебрей Windows API. На этом занятии вы научитесь узнавать шрифты, установленные в вашей операционной системе, и управлять их появлением на экране. Вы также узнаете, как создаются пути - еще один интересный графический объект ОС Windows.

Прежде всего выясним, что же такое шрифт. Это набор символов одного дизайна, отображающих текстовую и прочую символьную информацию на экране дисплея или на принтере. Первое, с чем вы встретитесь, это название шрифта (typeface). Многие наверняка используют в своих документах шрифты с названиями Times New Roman или Arial. Важной характеристикой шрифта является стиль (style):

нормальный (normal);
жирный (bold);
курсивный (italic);
подчеркнутый (underline);
перечеркнутый (strikeout).

Допускается комбинирование стилей. Так, можно выбрать, скажем, жирный курсив.

Шрифты, обладающие общими принципами начертания, принадлежат одному из семейств (Family). Всего таких семейств шесть (см. табл. 1).

Семейство шрифтов
Система константа Windows
Описание
Don't care
FF_DONTCARE
Общее семейство шрифтов. Используется, когда о шрифте нет информации или не важно, к какому семейству принадлежит шрифт
Roman
FF_ROMAN
Пропорциональные шрифты с засечками (серифами), например Times New Roman
Swiss
FF_SWISS
Пропорциональные шрифты без засечек, например Arial
Modern
FF_MODERN
Равноширинные шрифты без засечек, например Courier New
Script
FF_SCRIPT
Семейство "рукописных" шрифтов, например Script
Decorative
FF_DECORATIVE
Декоративные шрифты, например Old English
Таблица 1.

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

И последняя характеристика, которая важна при использовании шрифтов, это кодировка. Ею определяется порядок расположения символов и их количество. Так, например, шрифтами с кириллической кодировкой KOI-8 нельзя показать текст, созданный в текстовом процессоре, который использует кириллическую кодировку ANSI 1251. Об этом следует помнить всегда.

Вы можете просмотреть все шрифты, установленные в вашей системе (см. врезку "Использование функции EnumFontFamilies()").

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

Для создания шрифтов используются всего лишь три функции:

CreateFont() - создает шрифт по заданным в ней параметрам;

CreateFontIndirect() - создает шрифт в соответствии со структурой LOGFONT, переданной функции;

ChooseFont() - выводит стандартную диалоговую панель Font и возвращает выбор пользователя.

Принципиального различия между CreateFont() и CreateFontIndirect() не существует. Просто в первом случае используется набор параметров, а во втором они собраны в виде структуры LOGFONT (см. врезку "Структура LOGFONT"), на базе которой создаются все логические шрифты. Что касается функции ChooseFont(), то она сама не создает никаких шрифтов, но помогает организовать диалог с пользователем, запросив у него, какой шрифт выбрать. Еще эта функция удобна тем, что она возвращает ссылку на структуру LOGFONT, уже заполненную данными на основе опроса пользователя. Нам остается лишь передать эту ссылку в качестве параметра функции CreateFontIndirect(), которая создаст требуемый шрифт. К сожалению, ChooseFont() можно применить лишь в тех программах, которые подразумевают общение с пользователем.

После того как шрифт создан, не забудьте выделить его в контексте устройства командой SelectObject(). С этого момента любой вывод текста будет производиться шрифтом, который вы создали.

Родственным векторным шрифтам понятием являются пути (paths) - кривые, описывающие контур сложных графических объектов. Пути очень распространены в программах работы с графикой и САПР. При этом создание путей - дело достаточно простое. Для этого нужно задать ограничители пути. Ограничители пути представляют собой вызовы пары базовых функций BeginPath() и EndPath(). Внутри этих ограничителей вы можете вставить практически любую функцию рисования GDI (см. табл. 2).

Записанный BeginPath() и EndPath() путь становится активным в контексте устройства. И с того момента с путем можно проделать следующие операции:

  • отрисовать его активным пером (функция StrokePath() );
  • залить нарисованный путь (функция FillPath() );
  • отрисовать и залить путь одновременно (функция StrokeAndFillPath() );
  • отконвертировать путь в путь отсечения (функция SelectClipPath() );
  • отконвертировать путь в графическую область (функция PathToRegion() );
  • выровнять путь, преобразовав все его кривые в серию прямых линий (функция FlattenPath() );
  • затребовать координаты всех линий и кривых в пути (функция GetPath() ).
  • По поводу отрисовки пути позволю себе замечание. Если вы сначала отрисуете путь функцией StrokePath(), то после этого не заливайте его функцией FillPath(), потому что кроме заливки внутренней области, обрамленной путем, будет залит и сам путь. В результате, если вы выполнили путь черным цветом, а залили его красным, то контур также станет красным. Поэтому в таких случаях нужно использовать функцию одновременного рисования и заливки StrokeAndFillPath().

    Теперь проиллюстрируем все сказанное примером программы, выполненной с использованием Borland C++ Builder.

    Запустите Borland C++ Builder, создайте новый проект и перейдите в режим текстового редактирования формы нажатием комбинации клавиш +. Затем наберите следующий текст:

    object Form1: TForm1
      Left = 212
      Top = 198
      Width = ~748
      Height = 300
      Caption = 'Fonts Usage Demo'
      Font.Charset = DEFAULT_CHARSET
      Font.Color = clWindowText
      Font.Height = -13
      Font.Name = 'MS Sans Serif'
      Font.Style = []
      OnCreate = FormCreate
      OnDestroy = FormDestroy
      PixelsPerInch = 120
      TextHeight = 16
      object Bevel1: TBevel
        Left = 476
        Top = 0
        Width = 2
        Height = 261
      end
      object Label1: TLabel
        Left = 10
        Top = 5
        Width = 69
        Height = 16
        Caption = 'Font Family'
      end
      object Label2: TLabel
        Left = 10
        Top = 60
        Width = 73
        Height = 16
        Caption = 'Type Faces'
      end
      object Label3: TLabel
        Left = 370
        Top = 5
        Width = 77
        Height = 16
        Caption = 'Rotate angel'
      end
      object ComboBox1: TComboBox
        Left = 5
        Top = 25
        Width = 196
        Height = 24
        Style = csDropDownList
        ItemHeight = 16
        Items.Strings = (
          'Don'#39't care'
          'Roman'
          'Swiss'
          'Modern'
          'Script'
          'Decorative')
        TabOrder = 0
        OnChange = ComboBox1Change
      end
      object ListBox1: TListBox
        Left = 5
        Top = 80
        Width = 196
        Height = 181
        ItemHeight = 16
        TabOrder = 1
      end
      object GroupBox1: TGroupBox
        Left = 210
        Top = 5
        Width = 136
        Height = 106
        Caption = 'Style'
        TabOrder = 2
        object CheckBox1: TCheckBox
          Left = 20
          Top = 20
          Width = 91
          Height = 17
          Caption = 'Bold'
          TabOrder = 0
        end
        object CheckBox2: TCheckBox
          Left = 20
          Top = 40
          Width = 97
          Height = 17
          Caption = 'Italic'
          TabOrder = 1
        end
        object CheckBox3: TCheckBox
          Left = 20
          Top = 60
          Width = 97
          Height = 17
          Caption = 'Underline'
          TabOrder = 2
        end
        object CheckBox4: TCheckBox
          Left = 20
          Top = 80
          Width = 97
          Height = 17
          Caption = 'Strikeout'
          TabOrder = 3
        end
      end
      object Button1: TButton
        Left = 365
        Top = 110
        Width = 96
        Height = 25
        Caption = 'Show'
        TabOrder = 3
        OnClick = Button1Click
      end
      object UpDown1: TUpDown
        Left = 436
        Top = 40
        Width = 15
        Height = 24
        Associate = Edit1
        Min = 0
        Max = 359
        Position = 0
        TabOrder = 4
        Wrap = False
      end
      object Edit1: TEdit
        Left = 375
        Top = 40
        Width = 61
        Height = 24
        TabOrder = 5
        Text = '0'
      end
      object GroupBox2: TGroupBox
        Left = 210
        Top = 120
        Width = 136
        Height = 141
        Caption = 'Font Color'
        TabOrder = 6
        object Label4: TLabel
          Left = 90
          Top = 30
          Width = 26
          Height = 16
          Caption = 'Red'
          Color = clBtnFace
          Font.Charset = RUSSIAN_CHARSET
          Font.Color = clRed
          Font.Height = -13
          Font.Name = 'MS Sans Serif'
          Font.Style = []
          ParentColor = False
          ParentFont = False
        end
        object Label5: TLabel
          Left = 90
          Top = 70
          Width = 37
          Height = 16
          Caption = 'Green'
          Font.Charset = DEFAULT_CHARSET
          Font.Color = clGreen
          Font.Height = -13
          Font.Name = 'MS Sans Serif'
          Font.Style = []
          ParentFont = False
        end
        object Label6: TLabel
          Left = 90
          Top = 110
          Width = 27
          Height = 16
          Caption = 'Blue'
          Font.Charset = RUSSIAN_CHARSET
          Font.Color = clBlue
          Font.Height = -13
          Font.Name = 'MS Sans Serif'
          Font.Style = []
          ParentFont = False
        end
        object Edit2: TEdit
          Left = 10
          Top = 25
          Width = 51
          Height = 24
          TabOrder = 0
          Text = '0'
        end
        object Edit3: TEdit
          Left = 10
          Top = 65
          Width = 51
          Height = 24
          TabOrder = 1
          Text = '0'
        end
        object Edit4: TEdit
          Left = 10
          Top = 105
          Width = 51
          Height = 24
          TabOrder = 2
          Text = '0'
        end
        object UpDown2: TUpDown
          Left = 61
          Top = 25
          Width = 15
          Height = 24
          Associate = Edit2
          Min = 0
          Max = 255
          Position = 0
          TabOrder = 3
          Wrap = False
        end
        object UpDown3: TUpDown
          Left = 61
          Top = 65
          Width = 15
          Height = 24
          Associate = Edit3
          Min = 0
          Max = 255
          Position = 0
          TabOrder = 4
          Wrap = False
        end
        object UpDown4: TUpDown
          Left = 61
          Top = 105
          Width = 15
          Height = 24
          Associate = Edit4
          Min = 0
          Max = 255
          Position = 0
          TabOrder = 5
          Wrap = False
        end
      end
      object Button2: TButton
        Left = 365
        Top = 170
        Width = 96
        Height = 26
        Caption = 'Make Path'
        TabOrder = 7
        OnClick = Button2Click
      end
      object Button3: TButton
        Left = 365
        Top = 235
        Width = 96
        Height = 26
        Caption = 'Fill Path'
        TabOrder = 8
        OnClick = Button3Click
      end
    end

    Снова нажмите +, чтобы переключиться в режим графического редактирования. Если все сделано правильно, то вы увидите форму.

    Теперь наберем исходный текст самой программы. Сначала создадим заголовочный файл main.h :

    //--------------------------------------
    #ifndef mainH
    #define mainH
    //--------------------------------------
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    //--------------------------------------
    class TForm1 : public TForm
    {
    __published:    // IDE-managed Components
            TBevel *Bevel1;
            TComboBox *ComboBox1;
            TLabel *Label1;
            TLabel *Label2;
            TLabel *Label3;
            TLabel *Label4;
            TLabel *Label5;
            TLabel *Label6;
            TListBox *ListBox1;
            TGroupBox *GroupBox1;
            TGroupBox *GroupBox2;
            TCheckBox *CheckBox1;
            TCheckBox *CheckBox2;
            TCheckBox *CheckBox3;
            TCheckBox *CheckBox4;
            TButton *Button1;
            TButton *Button2;
            TUpDown *UpDown1;
            TUpDown *UpDown2;
            TUpDown *UpDown3;
            TUpDown *UpDown4;
            TEdit *Edit1;
            TEdit *Edit2;
            TEdit *Edit3;
            TEdit *Edit4;
            TButton *Button3;
            void __fastcall FormCreate(TObject *Sender);
            void __fastcall ComboBox1Change(TObject *Sender);
            void __fastcall FormDestroy(TObject *Sender);
            void __fastcall Button1Click(TObject *Sender);
            void __fastcall Button2Click(TObject *Sender);
            void __fastcall Button3Click(TObject *Sender);
    private:        // User declarations
            void __fastcall ClearPicture(void);
            HFONT __fastcall SetupFont
                            (HDC, int, bool, bool, bool, bool, int, LPCTSTR, long);
            void __fastcall RemoveFont(HDC, HFONT);
      HFONT __fastcall SetupChoosenFont(void);
            void __fastcall ShowFonts(int);
            void __fastcall ShowCross(void);
    public:         // User declarations
            __fastcall TForm1(TComponent* Owner);
    };
    //--------------------------------------
    int CALLBACK EnumFontProc
                    (ENUMLOGFONT FAR*, TEXTMETRIC FAR*, int, LPARAM);
    //--------------------------------------
    extern TForm1 *Form1;
    //--------------------------------------
    #endif

    В этом файле описываются сама форма и ссылки на все компоненты, размещенные на ней, а также задаются обработчики событий от компонентов.

    Сама программа представлена файлом main.cpp, исходный текст которого приведен ниже.

    //--------------------------------------
    #include 
    #pragma hdrstop
    #include "main.h"
    //--------------------------------------
    #pragma resource "*.dfm"
    
    TForm1 *Form1;
    HDC dc;
    //--------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
            : TForm(Owner)
    {
    }
    //--------------------------------------
    //      Стирает старый рисунок
    //--------------------------------------
    void __fastcall TForm1::ClearPicture(void)
    {
            InvalidateRect(Handle, NULL, true);
            UpdateWindow(Handle);
    }
    //--------------------------------------
    //      Отмечает крестиком место начала рисования
    //--------------------------------------
    void __fastcall TForm1::ShowCross(void)
    {
            // Передвинуть перо 
            MoveToEx(dc, 537, 100, NULL);
            // Нарисовать линию
            LineTo(dc, 544, 100);
            MoveToEx(dc, 540, 97, NULL);
            LineTo(dc, 540, 104);
    }
    //--------------------------------------
    //      Вспомогательная функция создания шрифта
    //--------------------------------------
    HFONT __fastcall TForm1::SetupFont(
            HDC dc,
            int size,
            bool bold,
            bool italic,
            bool underline,
            bool strikeout,
            int family,
            LPCTSTR typeface,
            long rotation)
    {
            HFONT font = CreateFont(
            // Пересчитывает из логических единиц в типографские
            -MulDiv(size, GetDeviceCaps(dc, LOGPIXELSY), 72),
            0,      // Выбрать ширину автоматически
            rotation*10,    // Угол поворота текста
            rotation*10,    // Угол ориентации базовой линии
            bold, italic, underline, strikeout,     // Стиль создаваемого шрифта
            DEFAULT_CHARSET,                // Символьный набор по умолчанию
            OUT_DEFAULT_PRECIS,
            CLIP_CHARACTER_PRECIS,
            DEFAULT_QUALITY,                // Качество вывода по определению
            family | DEFAULT_PITCH, // Семейство шрифта
            typeface);                      // Название шрифта
            SetTextColor(dc,                // Задать цвет текста
                    RGB(
                    StrToInt(Edit2->Text),
                    StrToInt(Edit3->Text),
                    StrToInt(Edit4->Text)));
            return SelectObject(dc, font);  // Выбрать созданный шрифт
    }
    //--------------------------------------
    //      Вернуть старый шрифт
    //--------------------------------------
    void __fastcall TForm1::RemoveFont(HDC dc, HFONT oldfont)
    {
            HFONT font = SelectObject(dc, oldfont);
            DeleteObject(font);
    }
    //--------------------------------------
    //      Задать шрифт на базе полученных от 
    //      пользователя данных
    //--------------------------------------
    HFONT __fastcall TForm1::SetupChoosenFont(void)
    {
            return SetupFont(
            dc, 30,
            CheckBox1->Checked,
            CheckBox2->Checked,
            CheckBox3->Checked,
            CheckBox4->Checked,
            // Пересчитать индекс из списка в константу FF_
            ComboBox1->ItemIndex << 4,
            (ListBox1->Items)->Strings[ListBox1->ItemIndex].c_str(),
            StrToInt(Edit1->Text));
    }
    //--------------------------------------
    //      Показать все имеющиеся шрифты
    //--------------------------------------
    void __fastcall TForm1::ShowFonts(int Family)
    {
      ListBox1->Clear();
            // Перебрать установленные в системе шрифты
            EnumFontFamilies(dc, NULL, (FONTENUMPROC)EnumFontProc, (LPARAM)Family);
            // Отсортировать шрифты в алфавитном порядке
            ListBox1->Sorted = true;
            // Выделить первый шрифт из списка
            ListBox1->ItemIndex = 0;
    }
    //--------------------------------------
    void __fastcall TForm1::FormCreate(TObject *Sender)
    {
            // Получить контекст устройства
            dc = GetDC(Handle);
            ComboBox1->ItemIndex = 0;
            // Показать все шрифты семейства Don't care
            ShowFonts(FF_DONTCARE);
            // Цвет подложки должен проглядывать
            // Уберите этот оператор и посмотрите, что получится!!!
            SetBkMode(dc, TRANSPARENT);
    }
    //--------------------------------------
    void __fastcall TForm1::FormDestroy(TObject *Sender)
    {
            // Освободить контекст устройства
            ReleaseDC(Handle, dc);
    }
    //--------------------------------------
    //      Пользователь выбрал другое семейство шрифтов
    //      Нужно изменить список шрифтов
    //--------------------------------------
    void __fastcall TForm1::ComboBox1Change(TObject *Sender)
    {
            // Чтобы определить константу семейства шрифтов FF_, 
            // нужно сдвинуть номер выбранного из списка пункта
            // на четыре бита влево
            ShowFonts( (static_cast(Sender)->ItemIndex) << 4 );
    }
    //--------------------------------------
    //      Функция косвенного вызова для перебора шрифтов
    //      В качестве последнего произвольного параметра
    //      ей передается константа FF_ выбранного 
    //      пользователем семейства шрифтов
    //--------------------------------------
    int CALLBACK EnumFontProc(
            ENUMLOGFONT FAR* lf,
            TEXTMETRIC FAR* tm,
            int FontType,
                    LPARAM FamilyRequested)
    {
            BYTE nFamily;
    
            // Выделить из структуры семейство шрифта
            nFamily = tm->tmPitchAndFamily & 0xF0;
            // Нас интересуют только TrueType-шрифты
            if(FontType == TRUETYPE_FONTTYPE)
                    // Входит ли шрифт в семейство,
                    // запрошенное пользователем?
                    if(nFamily == FamilyRequested)
                            // Тогда добавить его в список
                            Form1->ListBox1->Items->Add(
                                    AnsiString((char*)lf->elfFullName));
            return true;    // Продолжаем перебор
    }
    //--------------------------------------
    // Обработчик нажатия кнопки Show
    //--------------------------------------
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
            HFONT old_font;
    
            ClearPicture();
            // Задать шрифт по выбору пользователя
            old_font = SetupChoosenFont();
            // Показать крестиком начало координат рисования
            ShowCross();
            // Показать текст Font
            TextOut(dc, 540, 100, "Font", 4);
            // Вернуть старый шрифт
            RemoveFont(dc, old_font);
    }
    //--------------------------------------
    //      Обработчик нажатия кнопки Make Path
    //--------------------------------------
    void __fastcall TForm1::Button2Click(TObject *Sender)
    {
            HFONT old_font;
            HPEN old_pen;
    
            ClearPicture();
            old_font = SetupChoosenFont();
            old_pen = SelectObject(dc,
            // Задать перо для рисования пути
            CreatePen(PS_SOLID, 1,
                    // Считать цветовые константы
                    // для задания пера
                    RGB(
                    StrToInt(Edit2->Text),
                    StrToInt(Edit3->Text),
                    StrToInt(Edit4->Text))));
            // Создаем путь
            BeginPath(dc);
            ShowCross();
            TextOut(dc, 540, 100, "Font", 4);
            // Заканчиваем создание пути
            EndPath(dc);
            // Показать путь
            StrokePath(dc);
            DeleteObject(SelectObject(dc, old_pen));
            RemoveFont(dc, old_font);
    }
    //--------------------------------------
    //      Обработчик нажатия кнопки Fill Path
    //--------------------------------------
    void __fastcall TForm1::Button3Click(TObject *Sender)
    {
            HFONT old_font;
            HPEN old_pen;
            HBRUSH old_brush;
            ClearPicture();
            old_font = SetupChoosenFont();
            old_pen = SelectObject(dc,
                    CreatePen(PS_SOLID, 1,
                            RGB(
                            StrToInt(Edit2->Text),
                            StrToInt(Edit3->Text),
                            StrToInt(Edit4->Text))));
            old_brush = SelectObject(dc,
                            CreateSolidBrush(
                            // Задаем инверсный цвет заливки
                            RGB(
                            ~StrToInt(Edit2->Text),
                            ~StrToInt(Edit3->Text),
                            ~StrToInt(Edit4->Text))));
            BeginPath(dc);
            ShowCross();
            TextOut(dc, 540, 100, "Font", 4);
            EndPath(dc);
            StrokeAndFillPath(dc);
            DeleteObject(SelectObject(dc, old_brush));
            DeleteObject(SelectObject(dc, old_pen));
            RemoveFont(dc, old_font);
    }
    //--------------------------------------

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

    Если вы проделаете то же самое, но нажмете кнопку Make Path, то надпись будет выполнена с помощью пути.

    Получить залитый инверсным цветом путь можно, нажав кнопку Fill Path.

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


    Использование функции EnumFontFamilies()

    Функция EnumFontFamilies() перебирает шрифты определенного семейства и описывается следующим образом:

    int EnumFontFamilies(
    HDC hdc,
            // ссылка на контекст устройства
    LPCTSTR lpszFamily,
            // указатель на строку с именем семейства шрифта
    FONTENUMPROC lpEnumFontFamProc,
            // указатель на функцию косвенного вызова
    LPARAM lParam 
            // произвольный параметр
    );

    Некоторые параметры рассмотрим детальнее. Самый важный параметр - lpszFamily. Вы должны задать в нем название семейства шрифтов в виде строки, заканчивающейся нулем. Например, Roman. Тогда функция будет перебирать шрифты только этого семейства. Когда этот параметр задан как NULL, то произвольно выбирается по одному шрифту из каждого семейства. Но на практике это не работает, вернее работает совсем не так.

    Внимание! Параметр lpszFamily в документации описан неправильно! Если вы передадите в нем название семейства шрифтов, то ничего не произойдет. Но если передать в нем конкретное название шрифта, например Arial Cyr, то функция найдет все шрифты с таким именем, включая Arial Cyr Bold, Arial Cyr Italic, Arial Cyr Underline и т.д. Если же вы передадите NULL в lpszFamily, то EnumFontFamilies() переберет абсолютно все шрифты, установленные в системе, включая системные, матричные и т. п.

    lpEnumFontFamProc указывает на функцию косвенного вызова, к которой происходит обращение каждый раз, когда EnumFontFamilies() находит очередной шрифт. Описание функции косвенного вызова приводится далее.

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

    Функция EnumFontFamilies() возвращает число, которое в свою очередь было возвращено ей функцией косвенного вызова.

    Теперь рассмотрим, как описывается функция косвенного вызова типа FONTENUMPROC.

    int CALLBACK EnumFontFamProc(
    ENUMLOGFONT FAR *lpelf,
            // указатель на данные логического шрифта
    TEXTMETRIC FAR *lpntm,
            // указатель на данные физического шрифта
    int FontType,
            // тип шрифта
    LPARAM lParam 
            // пользовательский параметр, переданный от EnumFontFamilies()
    );

    Мы не будем рассматривать структуры, адресуемые параметрами lpelf и lpntm, поскольку они очень большие. Отметим лишь отдельные их поля. У структуры ENUMLOGFONT нас интересует поле elfFullName, в которое записывается полное название найденного шрифта. У структуры TEXTMETRIC полезным для нас будет поле tmPitchAndFamily. Оно описывает пропорциональность и семейство шрифта. Обратите внимание, что семейство шрифта задается не строкой, а системной константой (см. таблицу).


    Структура LOGFONT

    Структура LOGFONT описывается в документации следующим образом:

    typedef struct tagLOGFONT { 
    LONG lfHeight; 
       LONG lfWidth; 
       LONG lfEscapement; 
       LONG lfOrientation; 
       LONG lfWeight; 
       BYTE lfItalic; 
       BYTE lfUnderline; 
       BYTE lfStrikeOut; 
       BYTE lfCharSet; 
       BYTE lfOutPrecision; 
       BYTE lfClipPrecision; 
       BYTE lfQuality; 
       BYTE lfPitchAndFamily; 
       TCHAR lfFaceName[LF_FACESIZE]; 
    } LOGFONT;

    Кратко рассмотрим назначение ее полей.

    lfHeight - определяет высоту шрифта в логических единицах. Чтобы получить это число из типографского размера в точках, Microsoft рекомендует пользоваться формулой lfHeight = -MulDiv(PointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72). Правда, это работает лишь в режиме отображения MM_TEXT.

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

    lfEscapement - задает угол поворота шрифта против часовой стрелки в десятых градуса.

    lfOrientation - в Windows 95 должен быть равен значению lfEscapement.

    lfWeight - определяет вес шрифта, т. е. его "жирность" в диапазоне от 0 до 1000. Для нормального шрифта определена константа FW_NORMAL, равная 400, а для жирного шрифта используется константа FW_BOLD, равная 700. Если задан ноль, то выбирается вес шрифта по умолчанию.

    lfItalic - если "истина", то это курсивный (наклонный) шрифт.

    fUnderline - если "истина", то это подчеркнутый шрифт.

    lfStrikeOut - если "истина", то это перечеркнутый шрифт.

    lfCharSet - задает национальные и кодировочные особенности шрифта. Так, например, существует константа RUSSIAN_CHARSET. Чаще всего, правда, используется константа по умолчанию - DEFAULT_CHARSET.

    lfOutPrecision - опция точности вывода шрифта. Используется исключительно в профессиональных системах. Для обычных программ лучше задавать ее как OUT_DEFAULT_PRECIS.

    lfClipPrecision - задает точность отсечения символов, выходящих за пределы области отсечения. В обычных программах лучше просто задать CLIP_DEFAULT_PRECIS.

    lfQuality - определяет качество отображения шрифта. Это означает, что на основе этой константы программа отображения шрифтов определяет, насколько точно должны соблюдаться заданные параметры шрифта при его выборе. Обычно этот параметр задается как DEFAULT_QUALITY. Это означает, что точность неважна. Для чернового вывода можно задать этот параметр как DRAFT_QUALITY. Если же требуется высокая точность, нужно использовать константу PROOF_QUALITY.

    lfPitchAndFamily - задает семейство создаваемого шрифта и дополнительный параметр Pitch. Константы, определяющие семейства шрифтов, вы можете посмотреть в табл. 1. Что касается дополнительного параметра Pitch, то его рекомендуется всегда задавать как DEFAULT_PITCH.

    fFaceName[] - это имя создаваемого шрифта, например Pragmatica, Courier New и т. д.


    Функции GDI, создающие пути

    В Windows NT для создания путей могут быть применены следующие функции

    AngleArc
    LineTo
    Polyline
    Arc
    MoveToEx
    PolylineTo
    ArcTo
    Pie
    PolyPolygon
    Chord
    PolyBezier
    PolyPolyline
    CloseFigure
    PolyBezierTo
    Rectangle
    Ellipse
    PolyDraw
    RoundRect
    ExtTextOut
    Polygon
    TextOut

    Для Windows 95 список допустимых вызовов API меньше

    CloseFigure
    ExtTextOut
    LineTo
    MoveToEx
    PolyBezier
    PolyBezierTo
    Polygon
    Polyline
    PolylineTo
    PolyPolygon
    PolyPolyline
    TextOut