[ contenidos | #winprog ]

Texto y Fuentes

Ejemplo: font_one

[images/font_one.gif]

Carga de Fuentes

La GDI Win32 tiene algunas capacidades admirables para trabajar con diferentes tipos de estilos de presentaci�n, lenguajes, conjuntos de caracteres etc... Uno de los inconvenientes de esto es que trabajar con fuentes luce algo intimidatorio para el que reci�n se inicia. 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( ).

Fuentes por Default

Cuando llamamos por primera vez a 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.

Mostrando Texto

Ahora que tenemos una "linda" fuente, �como hacemos para mostrar alg�n texto en la pantalla? Esto es, asumiendo que no queremos usar un control Edit o un control Static.

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).

Redibujar el Cliente

S�lo una nota sobre el programa ejemplo... cuando 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.

Elecci�n de Fuentes

En general cualquier programa que utiliza fuentes le permite al usuario escoger su propia fuente, como tambi�n el color y los atributos de estilo a usar.

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.

Elecci�n de colores

Para permitirle al usuario cambiar el color de la fuente, existe el di�logo com�n 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 COLORREFs 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.

Cambio de la Fuente en los Controles

Algo que podemos hacer en alg�n punto es cambiar la fuente de los controles en los di�logos o ventanas. Generalmente este es el caso cuando usamos 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.


Copyright © 1998-2003, Brook Miles (theForger). All rights reserved.

Versi�n en Espa�ol: FedericoPizarro - 2003