/*
* This file is part of the XForms library package.
*
* XForms is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1, or
* (at your option) any later version.
*
* XForms is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with XForms. If not, see .
*/
/**
* \file fonts.c
*
* This file is part of the XForms library package.
* Copyright (c) 1996-2002 T.C. Zhao and Mark Overmars
* All rights reserved.
*
* All font and string size query routines. There are rooms for speed ups.
* For one, font switching can be reduced somewhat.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "include/forms.h"
#include "flinternal.h"
#include "private/flvasprintf.h"
#include
#include
static XFontStruct * defaultfs;
static XFontStruct * try_get_font_struct( int,
int,
int );
static char * get_fname( const char *,
int );
/*
* Question marks indicate the sizes in tenth of a point. It will be
* replaced on the fly by the font requesting routines. Depending on
* the availability of the fonts and capabilities of the server, the
* font may or may not be scalable.
*
* Resolution field is left blank on purpose as the resolution reported
* by the server is not reliable thus the program can't force the
* resolution. This way the admins can set the proper font path.
*
* Order is important as it has to agree with FL_TIMES_STYLE etc
* defined in Basic.h
*
*/
static const char *default_fonts[ ] =
{
"-*-helvetica-medium-r-*-*-*-?-*-*-p-*-*-*",
"-*-helvetica-bold-r-*-*-*-?-*-*-p-*-*-*",
"-*-helvetica-medium-o-*-*-*-?-*-*-p-*-*-*",
"-*-helvetica-bold-o-*-*-*-?-*-*-p-*-*-*",
"-*-courier-medium-r-*-*-*-?-*-*-*-*-*-*",
"-*-courier-bold-r-*-*-*-?-*-*-*-*-*-*",
"-*-courier-medium-o-*-*-*-?-*-*-*-*-*-*",
"-*-courier-bold-o-*-*-*-?-*-*-*-*-*-*",
"-*-times-medium-r-*-*-*-?-*-*-p-*-*-*",
"-*-times-bold-r-*-*-*-?-*-*-p-*-*-*",
"-*-times-medium-i-*-*-*-?-*-*-p-*-*-*",
"-*-times-bold-i-*-*-*-?-*-*-p-*-*-*",
"-*-charter-medium-r-*-*-*-?-*-*-*-*-*-*",
"-*-charter-bold-r-*-*-*-?-*-*-*-*-*-*",
"-*-charter-medium-i-*-*-*-?-*-*-*-*-*-*",
"-*-charter-bold-i-*-*-*-?-*-*-*-*-*-*",
NULL
};
static FL_FONT fl_fonts[ FL_MAXFONTS ];
static const char *cv_fname( const char * );
#define DEFAULTF1 "fixed"
#define DEFAULTF2 "6x13"
/***************************************
* Global initialization routine. Must be called before any font
* routine can be used. We can place fli_init_font in all font switching
* and string size query routines so a seperate initialization is not
* needed, and it has the added bonus that startup *may* be faster,
* but we pay a function call overhead in every call to any of these
* font related routines.
***************************************/
void
fli_init_font( void )
{
FL_FONT *flf;
const char *const *f = default_fonts;
static int initialized;
if ( initialized )
return;
initialized = 1;
/* If fl_set_font_name() has been called before fl_initialize() we need
to keep the changes */
for ( flf = fl_fonts, f = default_fonts; *f; f++, flf++ )
if ( ! *flf->fname )
strcpy( flf->fname, *f );
/* Load a default font */
if ( ! defaultfs
&& ! ( defaultfs = XLoadQueryFont( flx->display, DEFAULTF1 ) ) )
defaultfs = XLoadQueryFont( flx->display, DEFAULTF2 );
/* Load a couple of fonts at normal size to prevent the caching code from
using bad looking replacement if strange sizes are requested */
fl_get_font_struct( FL_NORMAL_STYLE, FL_DEFAULT_SIZE );
fl_get_font_struct( FL_BOLD_STYLE, FL_DEFAULT_SIZE );
fl_get_font_struct( FL_FIXED_STYLE, FL_DEFAULT_SIZE );
}
/***************************************
* In addition to get the font handle, we also make the font current
* in default GC
***************************************/
void
fl_set_font( int numb,
int size )
{
int dh;
XCharStruct overall;
XFontStruct *fs;
fs = fl_get_font_struct( numb, size );
/* cur_font is always the one in current GC */
if ( fl_state[ fl_vmode ].cur_fnt == fs )
{
#if FL_DEBUG >= ML_DEBUG
M_debug( "fl_set_font", "current", fli_curfnt );
#endif
return;
}
fl_state[ fl_vmode ].cur_fnt = flx->fs = fs;
/* Basic font info (no need to send a string, we just want the maximum
ascent and descent) */
XTextExtents( flx->fs, "", 0, &dh, &flx->fasc, &flx->fdesc, &overall );
flx->fheight = flx->fasc + flx->fdesc;
XSetFont( flx->display, flx->textgc, flx->fs->fid );
if ( fli_cntl.debug > 1 )
{
unsigned long res = 0;
if ( XGetFontProperty( flx->fs, XA_RESOLUTION, &res ) )
M_info2( "fl_set_font", "FontResolution: %lu", res );
}
}
/***************************************
* Add a new font (indexed by n) or change an existing font.
* Preferably the font name constains a '?' in the size
* position so different sizes can be used.
***************************************/
int
fl_set_font_name( int n,
const char * name )
{
FL_FONT *flf;
if ( n < 0 || n >= FL_MAXFONTS )
{
M_warn( "fl_set_font_name", "Bad font number (%d)", n );
return -1;
}
if ( ! name || ! *name )
{
M_warn( "fl_set_font_name", "Bad font name" );
return -1;
}
if ( strlen( name ) > FL_MAX_FONTNAME_LENGTH )
{
M_warn( "fl_set_font_name", "Font name too long" );
return -1;
}
flf = fl_fonts + n;
if ( *flf->fname )
{
int i;
for ( i = 0; i < flf->nsize; i++ )
if ( flf->size[ i ] > 0 )
XFreeFont( flx->display, flf->fs[ i ] );
*flf->fname = '\0';
}
flf->nsize = 0;
strcpy( flf->fname, name );
if ( ! flx || ! flx->display )
return 1;
return try_get_font_struct( n, FL_DEFAULT_SIZE, 1 ) ? 0 : -1;
}
/***************************************
* Add a new font (indexed by n) or change an existing font.
***************************************/
int
fl_set_font_name_f( int n,
const char * fmt,
... )
{
char *buf;
int ret;
EXPAND_FORMAT_STRING( buf, fmt );
ret = fl_set_font_name( n, buf );
fl_free( buf );
return ret;
}
/***************************************
* Returns the name of the indexed font
***************************************/
const char *
fl_get_font_name( int n )
{
if ( n < 0 || n >= FL_MAXFONTS )
return NULL;
return fl_fonts[ n ].fname;
}
/***************************************
* List built-in fonts
***************************************/
int
fl_enumerate_fonts( void ( * output )( const char *s ),
int shortform )
{
FL_FONT *flf = fl_fonts,
*fe = flf + FL_MAXFONTS;
int n = 0;
for ( ; output && flf < fe; flf++ )
if ( *flf->fname )
{
output( shortform ? cv_fname( flf->fname ) : flf->fname );
n++;
}
return n;
}
/***************************************
* All font changes go through this routine. If with_fail is false,
* this routine will not fail even if requested font can't be loaded.
* A substitution will be made.
***************************************/
static XFontStruct *
try_get_font_struct( int numb,
int size,
int with_fail )
{
FL_FONT *flf = fl_fonts;
XFontStruct *fs = NULL;
int n = 0,
i,
is_subst = 0;
if ( special_style( numb ) )
numb %= FL_SHADOW_STYLE;
/* Avoid trying to use negative or zero font size */
if ( size <= 0 )
{
M_info( "try_get_font_struct",
"Bad font size requested (%d), using %d istead",
size, size < 0 ? -size : 1 );
size = size < 0 ? -size : 1;
}
flf = fl_fonts + numb;
if ( numb < 0 || numb >= FL_MAXFONTS || ! *flf->fname )
{
if ( ! fli_no_connection ) {
/* This function is typically used to test whether a font is
loadable or not, so it's not a fatal error if it fails. Issue
a message for information therefore. */
M_info( "try_get_font_struct", "Bad FontStyle requested: %d: %s",
numb, flf->fname );
}
if ( ! fl_state[ fl_vmode ].cur_fnt )
M_warn( "try_get_font_struct", "bad font returned" );
return fl_state[ fl_vmode ].cur_fnt;
}
strcpy( fli_curfnt, get_fname( flf->fname, size ) );
/* Search for requested size in the cached fonts - fonts with "negative
sizes" are replacement fonts found before */
for ( fs = NULL, i = 0; ! fs && i < flf->nsize; i++ )
if ( size == abs( flf->size[ i ] ) )
fs = flf->fs[ i ];
/* Return it if font has already been loaded (i.e. is in the cache) */
if ( fs )
return fs;
/* Try to load the font */
fs = XLoadQueryFont( flx->display, fli_curfnt );
/* If that didn't work try to find a replacement font, i.e. an already
loaded font with the nearest size or, if there's none, the very most
basic font. */
if ( ! fs )
{
int mdiff = INT_MAX,
k;
if ( with_fail )
return NULL;
M_warn( "try_get_font_struct", "Can't load %s, using subsitute",
fli_curfnt );
/* Search for a replacement with the nearest size */
for ( k = -1, i = 0; i < flf->nsize; i++ )
{
if ( mdiff > FL_abs( size - flf->size[ i ] ) )
{
mdiff = FL_abs( size - flf->size[ i ] );
k = i;
}
}
if ( k != -1 )
fs = flf->fs[ k ];
else
fs = flx->fs ? flx->fs : defaultfs;
is_subst = 1;
}
/* If cache is full make space at the end */
if ( flf->nsize == FL_MAX_FONTSIZES )
{
if ( flf->size[ FL_MAX_FONTSIZES - 1 ] > 0 )
XFreeFont( flx->display, flf->fs[ FL_MAX_FONTSIZES - 1 ] );
flf->nsize--;
}
flf->fs[ flf->nsize ] = fs;
flf->size[ flf->nsize++ ] = is_subst ? - size : size;
/* Here we are guranteed a valid font handle although there is no
gurantee the font handle corresponds to the font requested */
return fs;
}
/***************************************
***************************************/
XFontStruct *
fl_get_font_struct( int style,
int size )
{
return try_get_font_struct( style, size, 0 );
}
/***************************************
* Similar to fl_get_string_xxxGC except that there is no side effects.
* Must not free the fontstruct as structure FL_FONT caches the
* structure for possible future use.
***************************************/
int
fl_get_string_width( int style,
int size,
const char * s,
int len )
{
XFontStruct *fs = fl_get_font_struct( style, size );
return fli_no_connection ? ( len * size ) : XTextWidth( fs, s, len );
}
/***************************************
***************************************/
int
fli_get_string_widthTABfs( XFontStruct * fs,
const char * s,
int len )
{
int w,
tab;
const char *p,
*q;
if ( fli_no_connection )
return 12 * len;
tab = fli_get_tabpixels( fs );
for ( w = 0, q = s; *q && ( p = strchr( q, '\t' ) ) && ( p - s ) < len;
q = p + 1 )
{
w += XTextWidth( fs, q, p - q );
w = ( ( w / tab ) + 1 ) * tab;
}
return w += XTextWidth( fs, q, len - ( q - s ) );
}
/***************************************
***************************************/
int
fl_get_string_widthTAB( int style,
int size,
const char * s,
int len )
{
XFontStruct *fs = fl_get_font_struct( style, size );
return fli_get_string_widthTABfs( fs, s, len );
}
/***************************************
* Function returns the height of the string, calculated from adding the
* largest ascent and descent of all its characters in the string, via 'asc'
* and 'desc' (but which both can be NULL pointers), the maximum ascent
* and descent.
***************************************/
int
fl_get_string_height( int style,
int size,
const char * s,
int len,
int * asc,
int * desc )
{
int a, d;
if ( fli_no_connection )
a = d = size / 2;
else
{
XFontStruct *fs = fl_get_font_struct( style, size );
XCharStruct overall;
int dh;
XTextExtents( fs, s, len, &dh, &a, &d, &overall );
}
if ( asc )
*asc = a;
if ( desc )
*desc = d;
return a + d;
}
/***************************************
* Returns font height and, via 'asc' and 'desc' (but which both can be NULL
* pointers), the fonts ascent and descent.
***************************************/
int
fl_get_char_height( int style,
int size,
int * asc,
int * desc )
{
int a, d;
if ( fli_no_connection )
a = d = size / 2;
else
{
XFontStruct *fs = fl_get_font_struct( style, size );
a = fs->ascent;
d = fs->descent;
if ( asc )
*asc = a;
if ( desc )
*desc = d;
}
return a + d;
}
/***************************************
* Function returns the width of the widest character in the requested font
***************************************/
int
fl_get_char_width( int style,
int size )
{
XFontStruct *fs = fl_get_font_struct( style, size );
return fs->max_bounds.width;
}
/***************************************
***************************************/
void
fl_get_string_dimension( int fntstyle,
int fntsize,
const char * s,
int len,
int * width,
int * height )
{
const char *p,
*q;
int h,
maxw = 0,
maxh = 0;
h = fl_get_char_height( fntstyle, fntsize, NULL, NULL );
for ( q = s; *q && ( p = strchr( q, '\n' ) ); q = p + 1 )
{
maxw = FL_max( maxw,
fl_get_string_width( fntstyle, fntsize, q, p - q ) );
maxh += h;
}
maxw = FL_max( maxw, fl_get_string_width( fntstyle, fntsize,
q, len - ( q - s ) ) );
maxh += h;
*width = maxw;
*height = maxh;
}
/*
* Tab handling. Currently only one tab
*/
#define MaxTabs 5
static char *tabstop[ MaxTabs ] = { "aaaaaaaa", 0 };
static int tabstopNchar[ MaxTabs ] = { 7 };
/***************************************
***************************************/
void
fl_set_tabstop( const char *s )
{
static int set;
if ( s )
{
if ( set )
fl_free( *tabstop );
*tabstop = fl_strdup( s );
*tabstopNchar = strlen( *tabstop );
set = 1;
}
}
/***************************************
***************************************/
int
fli_get_tabpixels( XFontStruct * fs )
{
return XTextWidth( fs, *tabstop, *tabstopNchar )
+ XTextWidth( fs, " ", 1 );
}
/***************************************
* Convert X font names to more conventional names by stripping the
* auxiliary info.
***************************************/
static const char *
cv_fname( const char *f )
{
static char fname[ FL_MAX_FONTNAME_LENGTH + 1 ];
char *q,
*p;
/* Remove all the garbages from head */
for ( q = strcpy( fname, f ); *q && ! isalnum( ( unsigned char ) *q ); q++ )
/* empty */ ;
/* Remove all the garbage from the end, starting from '?' */
if ( ( p = strchr( fname, '?' ) ) )
*--p = '\0';
/* Remove all remaining garbages */
for ( p = fname + strlen( fname ) - 1;
p > q && ! isalnum( ( unsigned char ) *p ); p-- )
/* empty */ ;
*++p = '\0';
return q;
}
/***************************************
* Given a font name and a size (in points), assemble the complete name
***************************************/
static char *
get_fname( const char * str,
int size )
{
static char fname[ sizeof fli_curfnt ];
char len_str[ 50 ]; /* should be enough for all ints */
char *p;
/* If necessary truncate font names that are too long, the caller
expects a real string */
strncpy( fname, str, sizeof fname - 1 );
fname[ sizeof fname - 1 ] = '\0';
if ( ( p = strchr( fname, '?' ) ) )
{
int len = sprintf( len_str, "%d0", size );
if ( len + strlen( str ) <= sizeof fname - 1 )
{
memmove( p + len, p + 1, strlen( p ) );
strncpy( p, len_str, len );
}
}
return fname;
}
/***************************************
* Some compatibility stuff, i.e. functions that were never documented
* and were removed from V0.89, but apparently this broke some applications
* that were using them. Put back in 10/22/00.
***************************************/
int
fl_fdesc_( void )
{
return flx->fdesc;
}
/***************************************
***************************************/
int
fl_fheight_( void )
{
return flx->fheight;
}
/***************************************
***************************************/
GC
fl_gc_( void )
{
return flx->gc;
}
/***************************************
***************************************/
GC
fl_textgc_( void )
{
return flx->textgc;
}
/***************************************
***************************************/
Window
fl_cur_win_( void )
{
return flx->win;
}
/***************************************
***************************************/
XFontStruct *
fl_cur_fs_( void )
{
return flx->fs;
}
/***************************************
***************************************/
Display *
fl_display_( void )
{
return flx->display;
}
/*
* Local variables:
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/