1 /*
2  *  This file is part of the XForms library package.
3  *
4  *  XForms is free software; you can redistribute it and/or modify it
5  *  under the terms of the GNU Lesser General Public License as
6  *  published by the Free Software Foundation; either version 2.1, or
7  *  (at your option) any later version.
8  *
9  *  XForms is distributed in the hope that it will be useful, but
10  *  WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public License
15  *  along with XForms.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 
19 /**
20  * \file fonts.c
21  *
22  *  This file is part of the XForms library package.
23  *  Copyright (c) 1996-2002  T.C. Zhao and Mark Overmars
24  *  All rights reserved.
25  *
26  * All font and string size query routines. There are rooms for speed ups.
27  * For one, font switching can be reduced somewhat.
28  *
29  */
30 
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34 
35 #include "include/forms.h"
36 #include "flinternal.h"
37 #include "private/flvasprintf.h"
38 #include <string.h>
39 #include <ctype.h>
40 
41 static XFontStruct * defaultfs;
42 
43 static XFontStruct * try_get_font_struct( int,
44                                           int,
45                                           int );
46 static char * get_fname( const char *,
47                          int );
48 
49 
50 /*
51  * Question marks indicate the sizes in tenth of a point. It will be
52  * replaced on the fly by the font requesting routines. Depending on
53  * the availability of the fonts and capabilities of the server, the
54  * font may or may not be scalable.
55  *
56  * Resolution field is left blank on purpose as the resolution reported
57  * by the server is not reliable thus the program can't force the
58  * resolution. This way the admins can set the proper font path.
59  *
60  * Order is important as it has to agree with FL_TIMES_STYLE etc
61  * defined in Basic.h
62  *
63  */
64 
65 /* These default fonts may not be the most beuatiful X11 fonts available
66    on the system but they are part of X11 distributions since at least
67    20 years, so we can be rather sure that they're available everywhere.
68    (And, remember, these fonts must be available on the machine where the
69    X server is running on, which is not necessarily the machine where the
70    program using XForms is executed.) */
71 
72 static const char *default_fonts[ ] =
73 {
74     "-*-helvetica-medium-r-*-*-*-?-*-*-p-*-*-*",
75     "-*-helvetica-bold-r-*-*-*-?-*-*-p-*-*-*",
76     "-*-helvetica-medium-o-*-*-*-?-*-*-p-*-*-*",
77     "-*-helvetica-bold-o-*-*-*-?-*-*-p-*-*-*",
78 
79     "-*-courier-medium-r-*-*-*-?-*-*-*-*-*-*",
80     "-*-courier-bold-r-*-*-*-?-*-*-*-*-*-*",
81     "-*-courier-medium-o-*-*-*-?-*-*-*-*-*-*",
82     "-*-courier-bold-o-*-*-*-?-*-*-*-*-*-*",
83 
84     "-*-times-medium-r-*-*-*-?-*-*-p-*-*-*",
85     "-*-times-bold-r-*-*-*-?-*-*-p-*-*-*",
86     "-*-times-medium-i-*-*-*-?-*-*-p-*-*-*",
87     "-*-times-bold-i-*-*-*-?-*-*-p-*-*-*",
88 
89     "-*-charter-medium-r-*-*-*-?-*-*-*-*-*-*",
90     "-*-charter-bold-r-*-*-*-?-*-*-*-*-*-*",
91     "-*-charter-medium-i-*-*-*-?-*-*-*-*-*-*",
92     "-*-charter-bold-i-*-*-*-?-*-*-*-*-*-*",
93 
94     NULL
95 };
96 
97 static FL_FONT fl_fonts[ FL_MAXFONTS ];
98 
99 static const char *cv_fname( const char * );
100 
101 #define DEFAULTF1  "fixed"
102 #define DEFAULTF2  "6x13"
103 
104 
105 /***************************************
106  * Global initialization routine. Must be called before any font
107  * routine can be used. We can place fli_init_font in all font switching
108  * and string size query routines so a seperate initialization is not
109  * needed, and it has the added bonus that startup *may* be faster,
110  * but we pay a function call overhead in every call to any of these
111  * font related routines.
112  ***************************************/
113 
114 void
fli_init_font(void)115 fli_init_font( void )
116 {
117     FL_FONT *flf;
118     const char *const *f = default_fonts;
119     static int initialized;
120 
121     if ( initialized )
122         return;
123 
124     initialized = 1;
125 
126     /* If fl_set_font_name() has been called before fl_initialize() we need
127        to keep the changes */
128 
129     for ( flf = fl_fonts, f = default_fonts; *f; f++, flf++ )
130         if ( ! *flf->fname )
131             strcpy( flf->fname, *f );
132 
133     /* Load a default font */
134 
135     if (    ! defaultfs
136          && ! ( defaultfs = XLoadQueryFont( flx->display, DEFAULTF1 ) ) )
137         defaultfs = XLoadQueryFont( flx->display, DEFAULTF2 );
138 
139     /* Load a couple of fonts at normal size to prevent the caching code from
140        using bad looking replacement if strange sizes are requested */
141 
142     fl_get_font_struct( FL_NORMAL_STYLE, FL_DEFAULT_SIZE );
143     fl_get_font_struct( FL_BOLD_STYLE,   FL_DEFAULT_SIZE );
144     fl_get_font_struct( FL_FIXED_STYLE,  FL_DEFAULT_SIZE );
145 }
146 
147 
148 /***************************************
149  * In addition to get the font handle, we also make the font current
150  * in default GC
151  ***************************************/
152 
153 void
fl_set_font(int numb,int size)154 fl_set_font( int numb,
155              int size )
156 {
157     int dh;
158     XCharStruct overall;
159     XFontStruct *fs;
160 
161     fs = fl_get_font_struct( numb, size );
162 
163     /* cur_font is always the one in current GC */
164 
165     if ( fl_state[ fl_vmode ].cur_fnt == fs )
166     {
167 #if FL_DEBUG >= ML_DEBUG
168         M_debug( "fl_set_font", "current", fli_curfnt );
169 #endif
170         return;
171     }
172 
173     fl_state[ fl_vmode ].cur_fnt = flx->fs = fs;
174 
175     /* Basic font info (no need to send a string, we just want the maximum
176        ascent and descent) */
177 
178     XTextExtents( flx->fs, "", 0, &dh, &flx->fasc, &flx->fdesc, &overall );
179     flx->fheight = flx->fasc + flx->fdesc;
180 
181     XSetFont( flx->display, flx->textgc, flx->fs->fid );
182 
183     if ( fli_cntl.debug > 1 )
184     {
185         unsigned long res = 0;
186 
187         if ( XGetFontProperty( flx->fs, XA_RESOLUTION, &res ) )
188             M_info2( "fl_set_font", "FontResolution: %lu", res );
189     }
190 }
191 
192 
193 /***************************************
194  * Add a new font (indexed by n) or change an existing font.
195  * Preferably the font name constains a '?' in the size
196  * position so different sizes can be used.
197  ***************************************/
198 
199 int
fl_set_font_name(int n,const char * name)200 fl_set_font_name( int          n,
201                   const char * name )
202 {
203     FL_FONT *flf;
204 
205     if ( n < 0 || n >= FL_MAXFONTS )
206     {
207         M_warn( "fl_set_font_name", "Bad font number (%d)", n );
208         return -1;
209     }
210 
211     if ( ! name || ! *name )
212     {
213         M_warn( "fl_set_font_name", "Bad font name" );
214         return -1;
215     }
216 
217     if ( strlen( name ) > FL_MAX_FONTNAME_LENGTH )
218     {
219         M_warn( "fl_set_font_name", "Font name too long" );
220         return -1;
221     }
222 
223     flf = fl_fonts + n;
224 
225     if ( *flf->fname )
226     {
227         int i;
228 
229         for ( i = 0; i < flf->nsize; i++ )
230             if ( flf->size[ i ] > 0 )
231                 XFreeFont( flx->display, flf->fs[ i ] );
232         *flf->fname = '\0';
233     }
234 
235     flf->nsize = 0;
236     strcpy( flf->fname, name );
237 
238     if ( ! flx || ! flx->display )
239         return 1;
240 
241     return try_get_font_struct( n, FL_DEFAULT_SIZE, 1 ) ? 0 : -1;
242 }
243 
244 
245 /***************************************
246  * Add a new font (indexed by n) or change an existing font.
247  ***************************************/
248 
249 int
fl_set_font_name_f(int n,const char * fmt,...)250 fl_set_font_name_f( int          n,
251                     const char * fmt,
252                     ... )
253 {
254     char *buf;
255     int ret;
256 
257     EXPAND_FORMAT_STRING( buf, fmt );
258     ret = fl_set_font_name( n, buf );
259     fl_free( buf );
260     return ret;
261 }
262 
263 
264 /***************************************
265  * Returns the name of the indexed font
266  ***************************************/
267 
268 const char *
fl_get_font_name(int n)269 fl_get_font_name( int n )
270 {
271     if ( n < 0 || n >= FL_MAXFONTS )
272         return NULL;
273 
274     return fl_fonts[ n ].fname;
275 }
276 
277 
278 /***************************************
279  * List built-in fonts
280  ***************************************/
281 
282 int
fl_enumerate_fonts(void (* output)(const char * s),int shortform)283 fl_enumerate_fonts( void ( * output )( const char *s ),
284                     int  shortform )
285 {
286     FL_FONT *flf = fl_fonts,
287             *fe  = flf + FL_MAXFONTS;
288     int n = 0;
289 
290     for ( ; output && flf < fe; flf++ )
291         if ( *flf->fname )
292         {
293             output( shortform ? cv_fname( flf->fname ) : flf->fname );
294             n++;
295         }
296 
297     return n;
298 }
299 
300 
301 /***************************************
302  * All font changes go through this routine. If with_fail is false,
303  * this routine will not fail even if requested font can't be loaded.
304  * A substitution will be made.
305  ***************************************/
306 
307 static XFontStruct *
try_get_font_struct(int numb,int size,int with_fail)308 try_get_font_struct( int numb,
309                      int size,
310                      int with_fail )
311 {
312     FL_FONT *flf = fl_fonts;
313     XFontStruct *fs = NULL;
314     int i,
315         is_subst = 0;
316 
317     if ( special_style( numb ) )
318         numb %= FL_SHADOW_STYLE;
319 
320     /* Avoid trying to use negative or zero font size */
321 
322     if ( size <= 0 )
323     {
324         M_info( "try_get_font_struct",
325                 "Bad font size requested (%d), using %d istead",
326                 size, size < 0 ? -size : 1 );
327         size = size < 0 ? -size : 1;
328     }
329 
330     flf = fl_fonts + numb;
331 
332     if ( numb < 0 || numb >= FL_MAXFONTS || ! *flf->fname )
333     {
334         if ( ! fli_no_connection ) {
335 
336             /* This function is typically used to test whether a font is
337                loadable or not, so it's not a fatal error if it fails. Issue
338                a message for information therefore. */
339 
340             M_info( "try_get_font_struct", "Bad FontStyle requested: %d: %s",
341                     numb, flf->fname );
342         }
343 
344         if ( ! fl_state[ fl_vmode ].cur_fnt )
345             M_warn( "try_get_font_struct", "bad font returned" );
346 
347         return fl_state[ fl_vmode ].cur_fnt;
348     }
349 
350     strcpy( fli_curfnt, get_fname( flf->fname, size ) );
351 
352     /* Search for requested size in the cached fonts - fonts with "negative
353        sizes" are replacement fonts found before */
354 
355     for ( fs = NULL, i = 0; ! fs && i < flf->nsize; i++ )
356         if ( size == abs( flf->size[ i ] ) )
357             fs = flf->fs[ i ];
358 
359     /* Return it if font has already been loaded (i.e. is in the cache) */
360 
361     if ( fs )
362         return fs;
363 
364     /* Try to load the font */
365 
366     fs = XLoadQueryFont( flx->display, fli_curfnt );
367 
368     /* If that didn't work try to find a replacement font, i.e. an already
369        loaded font with the nearest size or, if there's none, the very most
370        basic font. */
371 
372     if ( ! fs )
373     {
374        int mdiff = INT_MAX,
375            k;
376 
377         if ( with_fail )
378             return NULL;
379 
380         M_warn( "try_get_font_struct", "Can't load %s, using subsitute",
381                 fli_curfnt );
382 
383         /* Search for a replacement with the nearest size */
384 
385         for ( k = -1, i = 0; i < flf->nsize; i++ )
386         {
387             if ( mdiff > FL_abs( size - flf->size[ i ] ) )
388             {
389                 mdiff = FL_abs( size - flf->size[ i ] );
390                 k = i;
391             }
392         }
393 
394         if ( k != -1 )
395             fs = flf->fs[ k ];
396         else
397             fs = flx->fs ? flx->fs : defaultfs;
398 
399         is_subst = 1;
400     }
401 
402     /* If cache is full make space at the end */
403 
404     if ( flf->nsize == FL_MAX_FONTSIZES )
405     {
406         if ( flf->size[ FL_MAX_FONTSIZES - 1 ] > 0 )
407             XFreeFont( flx->display, flf->fs[ FL_MAX_FONTSIZES - 1 ] );
408         flf->nsize--;
409     }
410 
411     flf->fs[ flf->nsize ] = fs;
412     flf->size[ flf->nsize++ ] = is_subst ? - size : size;
413 
414     /* Here we are guranteed a valid font handle although there is no
415        gurantee the font handle corresponds to the font requested */
416 
417     return fs;
418 }
419 
420 
421 /***************************************
422  ***************************************/
423 
424 XFontStruct *
fl_get_font_struct(int style,int size)425 fl_get_font_struct( int style,
426                     int size )
427 {
428     return try_get_font_struct( style, size, 0 );
429 }
430 
431 
432 /***************************************
433  * Similar to fl_get_string_xxxGC except that there is no side effects.
434  * Must not free the fontstruct as structure FL_FONT caches the
435  * structure for possible future use.
436  ***************************************/
437 
438 int
fl_get_string_width(int style,int size,const char * s,int len)439 fl_get_string_width( int          style,
440                      int          size,
441                      const char * s,
442                      int          len )
443 {
444     XFontStruct *fs = fl_get_font_struct( style, size );
445 
446     return fli_no_connection ? ( len * size ) : XTextWidth( fs, s, len );
447 }
448 
449 
450 /***************************************
451  ***************************************/
452 
453 int
fli_get_string_widthTABfs(XFontStruct * fs,const char * s,int len)454 fli_get_string_widthTABfs( XFontStruct * fs,
455                            const char *  s,
456                            int           len )
457 {
458     int w,
459         tab;
460     const char *p,
461                *q;
462 
463     if ( fli_no_connection )
464         return 12 * len;
465 
466     tab = fli_get_tabpixels( fs );
467 
468     for ( w = 0, q = s; *q && ( p = strchr( q, '\t' ) ) && ( p - s ) < len;
469           q = p + 1 )
470     {
471         w += XTextWidth( fs, q, p - q );
472         w = ( ( w / tab ) + 1 ) * tab;
473     }
474 
475     return w += XTextWidth( fs, q, len - ( q - s ) );
476 }
477 
478 
479 /***************************************
480  ***************************************/
481 
482 int
fl_get_string_widthTAB(int style,int size,const char * s,int len)483 fl_get_string_widthTAB( int          style,
484                         int          size,
485                         const char * s,
486                         int          len )
487 {
488     XFontStruct *fs = fl_get_font_struct( style, size );
489 
490     return fli_get_string_widthTABfs( fs, s, len );
491 }
492 
493 
494 /***************************************
495  * Function returns the height of the string, calculated from adding the
496  * largest ascent and descent of all its characters in the string, via 'asc'
497  * and 'desc' (but which both can be NULL pointers), the maximum ascent
498  * and descent.
499  ***************************************/
500 
501 int
fl_get_string_height(int style,int size,const char * s,int len,int * asc,int * desc)502 fl_get_string_height( int          style,
503                       int          size,
504                       const char * s,
505                       int          len,
506                       int *        asc,
507                       int *        desc )
508 {
509     int a, d;
510 
511     if ( fli_no_connection )
512         a = d = size / 2;
513     else
514     {
515         XFontStruct *fs = fl_get_font_struct( style, size );
516         XCharStruct overall;
517         int dh;
518 
519         XTextExtents( fs, s, len, &dh, &a, &d, &overall );
520     }
521 
522     if ( asc )
523         *asc = a;
524     if ( desc )
525         *desc = d;
526 
527     return a + d;
528 }
529 
530 
531 /***************************************
532  * Returns font height and, via 'asc' and 'desc' (but which both can be NULL
533  * pointers), the fonts ascent and descent.
534  ***************************************/
535 
536 int
fl_get_char_height(int style,int size,int * asc,int * desc)537 fl_get_char_height( int   style,
538                     int   size,
539                     int * asc,
540                     int * desc )
541 {
542     int a, d;
543 
544     if ( fli_no_connection )
545         a = d = size / 2;
546     else
547     {
548         XFontStruct *fs = fl_get_font_struct( style, size );
549 
550         a = fs->ascent;
551         d = fs->descent;
552 
553         if ( asc )
554             *asc = a;
555         if ( desc )
556             *desc = d;
557     }
558 
559     return a + d;
560 }
561 
562 
563 /***************************************
564  * Function returns the width of the widest character in the requested font
565  ***************************************/
566 
567 int
fl_get_char_width(int style,int size)568 fl_get_char_width( int style,
569                    int size )
570 {
571     XFontStruct *fs = fl_get_font_struct( style, size );
572 
573     return fs->max_bounds.width;
574 }
575 
576 
577 /***************************************
578  ***************************************/
579 
580 void
fl_get_string_dimension(int fntstyle,int fntsize,const char * s,int len,int * width,int * height)581 fl_get_string_dimension( int          fntstyle,
582                          int          fntsize,
583                          const char * s,
584                          int          len,
585                          int *        width,
586                          int *        height )
587 {
588     const char *p,
589                *q;
590     int h,
591         maxw = 0,
592         maxh = 0;
593 
594     h = fl_get_char_height( fntstyle, fntsize, NULL, NULL );
595 
596     for ( q = s; *q && ( p = strchr( q, '\n' ) ); q = p + 1 )
597     {
598         maxw = FL_max( maxw,
599                        fl_get_string_width( fntstyle, fntsize, q, p - q ) );
600         maxh += h;
601     }
602 
603     maxw = FL_max( maxw, fl_get_string_width( fntstyle, fntsize,
604                                               q, len - ( q - s ) ) );
605     maxh += h;
606 
607     *width  = maxw;
608     *height = maxh;
609 }
610 
611 
612 /*
613  * Tab handling. Currently only one tab
614  */
615 
616 #define  MaxTabs   5
617 
618 static char *tabstop[ MaxTabs ] = { "aaaaaaaa", 0 };
619 static int tabstopNchar[ MaxTabs ] = { 7 };
620 
621 
622 /***************************************
623  ***************************************/
624 
625 void
fl_set_tabstop(const char * s)626 fl_set_tabstop( const char *s )
627 {
628     static int set;
629 
630     if ( s )
631     {
632         if ( set )
633             fl_free( *tabstop );
634         *tabstop = fl_strdup( s );
635         *tabstopNchar = strlen( *tabstop );
636         set = 1;
637     }
638 }
639 
640 
641 /***************************************
642  ***************************************/
643 
644 int
fli_get_tabpixels(XFontStruct * fs)645 fli_get_tabpixels( XFontStruct * fs )
646 {
647     return   XTextWidth( fs, *tabstop, *tabstopNchar )
648            + XTextWidth( fs, " ", 1 );
649 }
650 
651 
652 /***************************************
653  * Convert X font names to more conventional names by stripping the
654  * auxiliary info.
655  ***************************************/
656 
657 static const char *
cv_fname(const char * f)658 cv_fname( const char *f )
659 {
660     static char fname[ FL_MAX_FONTNAME_LENGTH + 1 ];
661     char *q,
662          *p;
663 
664     /* Remove all the garbages from head */
665 
666     for ( q = strcpy( fname, f ); *q && ! isalnum( ( unsigned char ) *q ); q++ )
667         /* empty */ ;
668 
669     /* Remove all the garbage from the end, starting from '?' */
670 
671     if ( ( p = strchr( fname, '?' ) ) )
672         *--p = '\0';
673 
674     /* Remove all remaining garbages */
675 
676     for ( p = fname + strlen( fname ) - 1;
677           p > q && ! isalnum( ( unsigned char ) *p ); p-- )
678         /* empty */ ;
679 
680     *++p = '\0';
681 
682     return q;
683 }
684 
685 
686 /***************************************
687  * Given a font name and a size (in points), assemble the complete name
688  ***************************************/
689 
690 static char *
get_fname(const char * str,int size)691 get_fname( const char * str,
692            int          size )
693 {
694     static char fname[ sizeof fli_curfnt ];
695     char len_str[ 50 ];    /* should be enough for all ints */
696     char *p;
697 
698     /* If necessary truncate font names that are too long, the caller
699        expects a real string */
700 
701     strncpy( fname, str, sizeof fname - 1 );
702     fname[ sizeof fname - 1 ] = '\0';
703 
704     if ( ( p = strchr( fname, '?' ) ) )
705     {
706         int len = sprintf( len_str, "%d0", size );
707 
708         if ( len + strlen( str ) <= sizeof fname - 1 )
709         {
710             memmove( p + len, p + 1, strlen( p ) );
711             strncpy( p, len_str, len );
712         }
713     }
714 
715     return fname;
716 }
717 
718 
719 /***************************************
720  * Some compatibility stuff, i.e. functions that were never documented
721  * and were removed from V0.89, but apparently this broke some applications
722  * that were using them. Put back in 10/22/00.
723  ***************************************/
724 
725 int
fl_fdesc_(void)726 fl_fdesc_( void )
727 {
728     return flx->fdesc;
729 }
730 
731 
732 /***************************************
733  ***************************************/
734 
735 int
fl_fheight_(void)736 fl_fheight_( void )
737 {
738     return flx->fheight;
739 }
740 
741 
742 /***************************************
743  ***************************************/
744 
745 GC
fl_gc_(void)746 fl_gc_( void )
747 {
748     return flx->gc;
749 }
750 
751 
752 /***************************************
753  ***************************************/
754 
755 GC
fl_textgc_(void)756 fl_textgc_( void )
757 {
758     return flx->textgc;
759 }
760 
761 
762 /***************************************
763  ***************************************/
764 
765 Window
fl_cur_win_(void)766 fl_cur_win_( void )
767 {
768     return flx->win;
769 }
770 
771 
772 /***************************************
773  ***************************************/
774 
775 XFontStruct *
fl_cur_fs_(void)776 fl_cur_fs_( void )
777 {
778     return flx->fs;
779 }
780 
781 
782 /***************************************
783  ***************************************/
784 
785 Display *
fl_display_(void)786 fl_display_( void )
787 {
788     return flx->display;
789 }
790 
791 
792 /*
793  * Local variables:
794  * tab-width: 4
795  * indent-tabs-mode: nil
796  * End:
797  */
798