Ejemplo: font_one
CreateFont()
, la principal API para trabajar con fuentes tiene 14 parámetros, los cuales especifican el alto, estilo, ancho, familia y otros varios atributos.
Afortunadamente no es tan difícil como parece y una gran parte del trabajo es realizada con valores por default. Todos menos dos de los valores de CreateFont()
pueden ser puestos en 0
o en NULL
para que el sistema use los valores por default dando como resultado una fuente plana y común.
CreateFont( )
crea un HFONT
, un handle a la Fuente Lógica en memoria. Los datos referenciados por este handle pueden ser recuperados dentro de una estrucura LOGFONT
usando la operación GetObject( )
, de la misma manera que lo hacíamos con los bitmaps donde la estructura BITMAP
era rellenada a partir de un HBITMAP
.
Los miembros de LOGFONT
son idénticos a los parámetros de CreateFont()
y por conveniencia podemos crear directamente una fuente apartir una estructura LOGFONT
ya existente, usando CreateFontIndirect( )
. Esto es muy útil debido a que hace mas fácil la tarea de crear una nueva fuente a partir de un handle cuando solo queremos alterar ciertos aspectos de ésta. Para rellenar los campos de la estructura LOGFONT
usamos GetObject( )
, alteramos los campos que deseamos y creamos una nueva fuente usando CreateFontIndirect( )
.
HFONT hf; HDC hdc; long lfHeight; hdc = GetDC(NULL); lfHeight = -MulDiv(12, GetDeviceCaps(hdc, LOGPIXELSY), 72); ReleaseDC(NULL, hdc); hf = CreateFont(lfHeight, 0, 0, 0, 0, TRUE, 0, 0, 0, 0, 0, 0, 0, "Times New Roman"); if(hf) { DeleteObject(g_hfFont); g_hfFont = hf; } else { MessageBox(hwnd, "Font creation failed!", "Error", MB_OK | MB_ICONEXCLAMATION); }
Este es el código usado para crear la fuente en la imagen ejemplo. La fuente es Times New Roman de 12 puntos con el estilo Itálica El flag para obtener la fuente en itálica es el sexto parámetro de CreateFont( )
, el cual se puede apreciar que está puesto en TRUE
. El último parámetro indica el nombre de la fuente que queremos usar.
La única parte tramposa de este código es el valor usado para el tamaño de la fuente, el parámetro lfHeight
de CreateFont( )
. Generalmente las personas, cuando trabajan con fuentes, están acostumbradas a trabajar con tamaños especificados en Puntos, Tamaño 10, Tamaño 12, etc... Sin embargo, CreateFont( )
no acepta tamaños en puntos,solo acepta Unidades Lógicas, las cuales son diferentes en nuestra pantalla que en nuestra impresora y aún entre impresoras y pantallas.
La razón de que exista esta situación es porque la resolución de los diferentes dispositivos es bastante diferente... las impresoras pueden imprimir de 600 a 1200 pixeles por pulgada, mientras que una pantalla con suerte llega a mostrar unos 200. Si usamos el mismo tamaño para la pantalla que para la impresora, quizás no podamos ver algunos caracteres en particular.
Todo lo que tenemos que hacer es convertir el tamaño punto al tamaño lógico para un dispositivo determinado. En este caso el dispositivo es la pantalla, por lo tanto obtenemos el HDC de la pantalla y obtenemos el número de pixeles lógicos por pulgada usando GetDeviceCaps()
y metemos esto dentro de la fórmula provista tan generosamene en MSDN la cual usa MulDiv( )
para convertir nuestro tamaño punto, en este caso 12, al tamaño lógico que CreateFont( )
espera. Luego almacenamos este en lfHeight
y lo pasamos como primer parámetro de CreateFont( )
.
GetDC( )
para obtener el HDC
de nuestra ventana, la fuente por default que es seleccionada es System, la cual para ser honesto, no es muy atractiva. La forma más simple de obtener una fuente mejor sin tener que pasar por CreateFont()
es llamar a GetStockObject()
y preguntar por la DEFAULT_GUI_FONT
.
Este es un objeto del sistema y podemos usarlo todas las veces que deseamos sin necesidad retener memoria, podemos llamar a DeleteObject()
para borrarlo pero esto no causará efecto, lo cual es una buena idea porque no tendremos que averiguar si dicha fuente fué creada con CreateFont( )
o con GetStockObject( )
antes de intentar eliminarla.
Las opciones básicas son TextOut( )
y DrawText( )
. TextOut()
es mas simple pero tiene menos opciones y no hace cubrimientos de palabras o alineación por nosotros.
char szSize[100]; char szTitle[] = "These are the dimensions of your client area:"; HFONT hfOld = SelectObject(hdc, hf); SetBkColor(hdc, g_rgbBackground); SetTextColor(hdc, g_rgbText); if(g_bOpaque) { SetBkMode(hdc, OPAQUE); } else { SetBkMode(hdc, TRANSPARENT); } DrawText(hdc, szTitle, -1, prc, DT_WORDBREAK); wsprintf(szSize, "{%d, %d, %d, %d}", prc->left, prc->top, prc->right, prc->bottom); DrawText(hdc, szSize, -1, prc, DT_SINGLELINE | DT_CENTER | DT_VCENTER); SelectObject(hdc, hfOld);
La primer cosa que hacemos es usar SelectObject( )
para obtener la fuente que queremos usar dentro de nuestro HDC
y para prepararnos para dibujar. Todas las operaciones de texto usarán, de aquí en mas, esta fuente hasta que se seleccione alguna otra.
Ahora vamos a definir los colores del texto y del fondo. Cuando fijamos el color de fondo no cambiamos todo el fondo a un determinado color, solo afecta a ciertas operaciones que dibujan texto usando el color de fondo. Esto también depende del Modo de Fondo (o Background Mode) que esté seleccionado actualmente. Si está definido como OPAQUE
(el default) entonces cada vez que se escriba texto, el color de fondo del texto será el que esté seleccionado como background color. Si está definido como TRANSPARENT
, el texto es dibujado sin color de fondo y quedará como fondo del texto lo que sea que encuentre dibujado debajo de éste.
Ahora realmente dibujamos el texto usando DrawText( )
, pasamos el HDC
a usar y el string a dibujar. El tercer parámetro es la longitud del string, pero hemos pasado -1 debido a que DrawText( )
es lo suficientemente astuto para darse cuenta cual es la longitud del texto. En el cuarto parámetro pasamos prc
, el puntero al cliente RECT
. DrawText( )
dibujará dentro de este rectángulo basándose en los otros flags que le pasamos.
En la primer llamada especificamos DT_WORDBREAK
, el cual alinea por default el texto a la izquierda y acomodará el texto automáticamente a los límites del rectángulo... muy útil.
Para la segunda llamada solo imprimimos una sola línea sin ajustar el texto al rectángulo y la centramos horizontalmente y verticalmente (lo cual hará DrawText()
cuando dibujemos una sola línea).
WNDCLASS
es registrada he puesto los estilos CS_VREDRAW
y CS_HREDRAW
. Esto provoca que el área cliente entera sea sea redibujada si la ventana cambia su tamaño, mientras que por default solo se dibujan las partes que han cambiado. Esto luce algo malo debido a que el texto centrado es movido cuando la ventana cambia su tamaño pero no se actualiza como esperamos.
Al igual que los diálogos comunes para abrir y guardar archivos, hay un diálogo común para escoger una fuente. Este es llamado ChooseFont( )
y funciona junto con la estructura CHOOSEFONT
.
HFONT g_hfFont = GetStockObject(DEFAULT_GUI_FONT); COLORREF g_rgbText = RGB(0, 0, 0);
void DoSelectFont(HWND hwnd) { CHOOSEFONT cf = {sizeof(CHOOSEFONT)}; LOGFONT lf; GetObject(g_hfFont, sizeof(LOGFONT), &lf); cf.Flags = CF_EFFECTS | CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS; cf.hwndOwner = hwnd; cf.lpLogFont = &lf; cf.rgbColors = g_rgbText; if(ChooseFont(&cf)) { HFONT hf = CreateFontIndirect(&lf); if(hf) { g_hfFont = hf; } else { MessageBox(hwnd, "Font creation failed!", "Error", MB_OK | MB_ICONEXCLAMATION); } g_rgbText = cf.rgbColors; } }
El hwnd
en esta llamada es simplemente la ventana que vamos a usar como padre del diálogo.
La forma más fácil de usar este diálogo es junto con una estructura LOGFONT
, la cual es bastante parecida a la estructura HFONT
que veníamos usando. Hacemos que el campo lpLogFont
apunte a LOGFONT
, que acabamos de llenar con información y también agregamos el flag CF_INITTOLOGFONTSTRUCT
para que ChooseFont( )
use este campo. El flag CF_EFFECTS
le dice a ChooseFont( )
que permita al usuario seleccionar un color como así también efectos de subrayado y tachado.
Los estilos Negrita e Itálica no se cuentan como efectos, son considerados partes de la fuente y de hecho algunas fuentes solo vienen en Negrita o en Itálica. Si queremos chequear o prevenir al usuario de seleccionar una fuente negrita o itálica, podemos chequear los campos lfWeight
y lfItalic
, respectivamente, de la estructura LOGFONT
después de que el usuario ha hecho su elección. Luego se le puede preguntar al usuario si desea hacer otra selección o algún cambio a los campos antes de llamar a CreateFontIndirect()
.
El color de una fuente no está asociado con un HFONT
y por lo tanto debe ser almacenado por separado. EL campo rgbColors
de la estructura CHOOSEFONT
es usado para pasar el color inicial y para recuperar luego el nuevo color.
CF_SCREENFONTS
indica que queremos fuentes diseñadas para funcionar en la pantalla y no fuentes diseñadas para funcionar en una impresora. Algunos soportan ambas, otros soportan una u otra. Dependiendo de para que vas a usar la fuente, este y muchos otros flags pueden ser encontrados en MSDN para definir exactamente que fuentes deseamos que el usuario sea capaz de seleccionar.
ChooseColor( )
. Este es el código usado para permitirle al usuario seleccionar el color de fondo en programa ejemplo.
COLORREF g_rgbBackground = RGB(255, 255, 255); COLORREF g_rgbCustom[16] = {0};
void DoSelectColour(HWND hwnd) { CHOOSECOLOR cc = {sizeof(CHOOSECOLOR)}; cc.Flags = CC_RGBINIT | CC_FULLOPEN | CC_ANYCOLOR; cc.hwndOwner = hwnd; cc.rgbResult = g_rgbBackground; cc.lpCustColors = g_rgbCustom; if(ChooseColor(&cc)) { g_rgbBackground = cc.rgbResult; } }
Esto es algo complicado, nuevamente estamos usando el parámetro hwnd
como padre del diálogo. El parámetro CC_RGBINIT
indica que debemos comenzar con el color que pasamos en el campo rgbResult
, el cual también es donde obtenemos el color que el usuario ha seleccionado cuando el diálogo se cierra.
El arreglo g_rgbCustom
de 16 COLORREF
s es necesario para almacenar cualquier valor que el usuario decide poner dentro de la tabla de colores personalizados incluída en el diálogo. Podemos potencialmente almacenar estos valores en otro lugar que no sea el registro, porque de no ser así dichos valores se perderán al cerrar el programa. Este parámetro no es opcional.
CreateWindow( )
para crear controles como lo hemos hecho en los ejemplos anteriores. Los controles al igual que las ventanas usan la fuente System por default, por lo tanto usamos WM_SETFONT
para definir un nuevo handle a la fuente (de GetStockObject()
) para que use el control. Podemos usar este método con fuentes que creamos usando CreateFont()
. Simplemente pasamos el handle a la fuente como wParam
y ponemos lParam
en TRUE
para hacer que el control se redibuje.
He realizado esto en los ejemplos anteriores, pero tiene sentido mencionarlo aquí debido a que es relevante y muy breve:
SendDlgItemMessage(hwnd, IDC_OF_YOUR_CONTROL, WM_SETFONT, (WPARAM)hfFont, TRUE);
Donde hfFont
es, por su puesto, el HFONT
que queremos usar y IDC_OF_YOUR_CONTROL
es el ID del control al cual le queremos cambiar la fuente.
Versión en Español: FedericoPizarro - 2003