Radek Chalupa   vývoj software, konzultace a školení programování
 DomůČlánkyUkázky kóduKonzultace a školeníVývoj softwareFreewareKontakt

Nastavení písma. Fonty.  18.1.2002

V tomto pokračování se seznámíme s dalším objektem GDI, kterým bude font (handle typu HFONT). Ve všech dosavadních ukázkách jsme volbu písma nechávali na systému, který nám při výpisu textu do kontextu zařízení vybral příslušný systémový font. Jediné, co jsme mohli ovlivnit, byla barva písma a pozadí. A to proto, jak jsme si řekli, že tyto atributy jsou vlastnostmi (nikoli objekty), které jsme nastavili kontextu zařízení (HDC). Fonty (HFONT) jsou však objekty obdobné perům (HPEN) a štětcům (HBRUSH), se kterými jsme se již seznámili. Znamená to tedy, že musíme nějakým způsobem vytvořit (nebo získat již existující) handle objektu HFONT, přiřadit ho do kontextu zařízení funkcí SelectObject a poté veškeré následné výstupy textu budou používat tento font. Ukážeme si jak dosáhnout třeba takovýto výsledek:

win-api-18-1

Vytvoření fontu

Pro přímé vytvoření fontu můžeme použít funkci CreateFont:

HFONT CreateFont(
  int nHeight,               // height of font
  int nWidth,                // average character width
  int nEscapement,           // angle of escapement
  int nOrientation,          // base-line orientation angle
  int fnWeight,              // font weight
  DWORD fdwItalic,           // italic attribute option
  DWORD fdwUnderline,        // underline attribute option
  DWORD fdwStrikeOut,        // strikeout attribute option
  DWORD fdwCharSet,          // character set identifier
  DWORD fdwOutputPrecision,  // output precision
  DWORD fdwClipPrecision,    // clipping precision
  DWORD fdwQuality,          // output quality
  DWORD fdwPitchAndFamily,   // pitch and family
  LPCTSTR lpszFace           // typeface name
);

Jak je vidět, je třeba zadat různé parametry písma. Jejich přesný význam a definované konstanty pro jednotlivé hodnoty naleznete v nápovědě a nebudu je zde rozepisovat. Řekněme si však o další, někdy šikovnější možnosti vytvoření fontu. Je jí funkce CreateFontIndirect:

HFONT CreateFontIndirect(
  CONST LOGFONT* lplf   // vlastnosti písma
);

Když se však podíváte na strukturu, LOGFONT, zjistíte, že její prvky odpovídají parametrům funkce CreateFont. V čem je tedy výhoda? Spočívá v tom, že si můžeme nejprve nechat "předvyplnit" strukturu LOGFONT parametry nějakého systémového písma, a poté pouze změnit ty vlastnosti, které požadujeme. K tomu použijeme již zmíněnou funkci GetObject:

int GetObject(
  HGDIOBJ hgdiobj,  // handle grafického objektu
  int cbBuffer,     // velikost bufferu
  LPVOID lpvObject  // buffer pro informace o objektu
);

Handle na některý systémový font můžeme získat pomocí funkce GetStockObject:

HGDIOBJ GetStockObject(
  int fnObject   // typ objektu
);

Pokud chceme získat výchozí font, který systém Windows používá pro grafické objekty, použijeme hodnotu DEFAULT_GUI_FONT.

Použití fontu v kontextu zařízení

Ukažme si nyní na konkrétním příkladě aplikaci těchto poznatků. Nejdříve se podívejte na kompletní výpis handleru zprávy WM_PAINT, který realizuje to, co vidíte na úvodním screen-shotu:

void OnPaint(HDC hdc)
{
  TCHAR chText[100];
  LOGFONT logFont;
  HFONT hFont;
  GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(logFont), &logFont);
  logFont.lfItalic = TRUE;
  
  hFont = CreateFontIndirect(&logFont);
  SelectObject(hdc, hFont);
  lstrcpy(chText, TEXT("Výchozí font s přidaným sklonem písma"));
  TextOut(hdc, 40, 10, chText, lstrlen(chText));
  DeleteObject(hFont);
  
  logFont.lfHeight = -18;
  logFont.lfItalic = FALSE;
  lstrcpy(logFont.lfFaceName, TEXT("Comic Sans MS"));
  hFont = CreateFontIndirect(&logFont);
  SelectObject(hdc, hFont);
  SetTextColor(hdc, 0x00D00000);
  lstrcpy(chText, TEXT("Zvětšíme a změníme písmo"));
  TextOut(hdc, 40, 30, chText, lstrlen(chText));
  DeleteObject(hFont);

  logFont.lfEscapement = 1800;
  lstrcpy(logFont.lfFaceName, TEXT("Comic Sans MS"));
  hFont = CreateFontIndirect(&logFont);
  SelectObject(hdc, hFont);
  SetTextColor(hdc, 0x000000D0);
  lstrcpy(chText, TEXT("Nyní píšeme vzhůru nohama"));
  SIZE size;
  GetTextExtentPoint32(hdc, chText, lstrlen(chText), &size);
  TextOut(hdc, 40 + size.cx, 90, chText, lstrlen(chText));
  DeleteObject(hFont);

  logFont.lfEscapement = 900;
  logFont.lfHeight = -22;
  lstrcpy(logFont.lfFaceName, TEXT("Times New Roman"));
  hFont = CreateFontIndirect(&logFont);
  SelectObject(hdc, hFont);
  SetTextColor(hdc, 0x0008000);
  lstrcpy(chText, TEXT("Svislý text "));
  GetTextExtentPoint32(hdc, chText, lstrlen(chText), &size);
  TextOut(hdc, 10, 10 + size.cx, chText, lstrlen(chText));
  DeleteObject(hFont);
}

Získání handle kontextu zařízení v proceduře okna by jste již měli znát z minulých dílů, pro úplnost ho zde uvedu:

case WM_PAINT:
  hdc = BeginPaint(hWnd, &ps);
  OnPaint(hdc);
  EndPaint(hWnd, &ps);
  break;

Jak je vidět z výpisu, jde vždy o modifikaci požadované vlastnosti písma ve struktuře LOGFONT, použití funkce CreateFontIndirect a vybrání fontu do kontextu zařízení. Samozřejmě nesmíme zapomenout vytvořený font po použití vždy zrušit funkcí DeleteObject, a pak teprve toto handle použít pro vytvoření dalšího fontu. V ukázce vidíte také, jak vypsat písmo pod libovolným úhlem. V tom nejjednodušším případě (výchozím grafickém módu) nastavíme prvek lfEscapement na hodnotu vyjadřující sklon základny textu vůči ose x v desetinách stupně. Použitá hodnota 90 tedy představuje pravý úhel a 180 písmo otočené "vzhůru nohama".

Nastavení písma oknu (prvku na dialogu)

Nyní si ještě ukážeme, jak nastavit vlastní font některému prvku Windows. Mohl by jím být edit, button apod. Ukážeme si to u dialogu "about.box", kde máme prvek typu "STATIC". V editoru zdrojů musíme pouze změnit identifikátor static prvku na hodnotu jinou než je IDC_STATIC. Vše ostatní zařídíme v proceduře dialogu, kde při inicializaci (handler zprávy WM_INITDIALOG) vytvoříme vlastní font a nastavíme jej prvku pomocí zprávy WM_SETFONT. Před ukončením dialogu font opět zrušíme.

HFONT hfDialog;
LOGFONT logFont;

LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch (message)
  {
  case WM_INITDIALOG:
    GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(logFont), &logFont);
    logFont.lfHeight = -22;
    logFont.lfItalic = TRUE;
    logFont.lfUnderline = TRUE;
    lstrcpy(logFont.lfFaceName, TEXT("Times New Roman"));
    hfDialog = CreateFontIndirect(&logFont);
    SendDlgItemMessage(hDlg, IDC_FONT, WM_SETFONT, (WPARAM)hfDialog, (LPARAM)TRUE);
    return TRUE;

  case WM_COMMAND:
    if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) 
    {
      DeleteObject(hfDialog);
      EndDialog(hDlg, LOWORD(wParam));
      return TRUE;
    }
    break;
  }
  return FALSE;
}

Výsledek vidíte na obrázku:

win-api-18-2

Ještě poznámku ke kódu. Jak si můžete všimnout, pro poslání zprávy požadovanému prvku na dialogu, u něhož známe identifikátor ale nikoli handle, můžeme použít místo funkce SendMessage funkci SendDlgItemMessage:

LRESULT SendDlgItemMessage(
  HWND hDlg,      // handle dialogu
  int nIDDlgItem, // identifikátor prvku
  UINT Msg,       // zpráva
  WPARAM wParam,  // 1. parametr zprávy
  LPARAM lParam   // 2. parametr zprávy
);

kde zadáváme handle dialogu a identifikátor prvku. Pokud bychom opravdu potřebovali znát handle prvku, samozřejmě můžeme ho zjistit pomocí funkce GetDlgItem:

HWND GetDlgItem(
  HWND hDlg,       // handle dialogu
  int nIDDlgItem   // identifikátor prvku
);

která nám vrátí handle, na základě handle rodičovského dialogu a identifikátoru prvku.

Doprovodný projekt je ke stažení zde: win_api_18.zip


Komentář k článku

Jméno:


Email:


Zde můžeš napsat komentář k tomuto článku: (max. 1000 znaků)



Copyright © 2019 - Radek Chalupa