xlsfonts.c revision 8fff3f40
1/*
2 * $Xorg: xlsfonts.c,v 1.4 2001/02/09 02:05:54 xorgcvs Exp $
3 *
4 *
5Copyright 1989, 1998  The Open Group
6
7Permission to use, copy, modify, distribute, and sell this software and its
8documentation for any purpose is hereby granted without fee, provided that
9the above copyright notice appear in all copies and that both that
10copyright notice and this permission notice appear in supporting
11documentation.
12
13The above copyright notice and this permission notice shall be included in
14all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
19OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
20AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23Except as contained in this notice, the name of The Open Group shall not be
24used in advertising or otherwise to promote the sale, use or other dealings
25in this Software without prior written authorization from The Open Group.
26 * */
27/* $XFree86: xc/programs/xlsfonts/xlsfonts.c,v 1.9 2003/09/08 14:25:33 eich Exp $ */
28
29#include <X11/Xlib.h>
30#include <X11/Xutil.h>
31#include <X11/Xos.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <limits.h>
35#include "dsimple.h"
36
37#define N_START INT_MAX  /* Maximum # of fonts to start with (should
38                          * always be be > 10000 as modern OSes like
39                          * Solaris 8 already have more than 9000 XLFD
40                          * fonts available) */
41
42#define L_SHORT    0
43#define L_MEDIUM   1
44#define L_LONG     2
45#define L_VERYLONG 3
46
47static int  max_output_line_width     = 79;
48static int  output_line_padding       = 3;
49static int  columns                   = 0;
50
51static Bool sort_output               = True;
52static Bool open_instead_of_list      = False;
53static int  long_list                 = L_SHORT;
54static int  nnames                    = N_START;
55static int  font_cnt                  = 0;
56static int  min_max;
57
58typedef struct {
59  char           *name;
60  XFontStruct    *info;
61} FontList;
62
63static FontList *font_list = NULL;
64
65/* Local prototypes */
66static void get_list(char *pattern);
67static int  compare(const void *arg1, const void *arg2);
68static void show_fonts(void);
69static void copy_number(char **pp1, char**pp2, int n1, int n2);
70static int  IgnoreError(Display *disp, XErrorEvent *event);
71static void PrintProperty(XFontProp *prop);
72static void ComputeFontType(XFontStruct *fs);
73static void print_character_metrics(register XFontStruct *info);
74static void do_query_font (Display *dpy, char *name);
75
76void usage(void)
77{
78    fprintf (stderr, "usage:  %s [-options] [-fn pattern]\n", program_name);
79    fprintf (stderr, "where options include:\n");
80    fprintf (stderr, "    -l[l[l]]                 give long info about each font\n");
81    fprintf (stderr, "    -m                       give character min and max bounds\n");
82    fprintf (stderr, "    -C                       force columns\n");
83    fprintf (stderr, "    -1                       force single column\n");
84    fprintf (stderr, "    -u                       keep output unsorted\n");
85    fprintf (stderr, "    -o                       use OpenFont/QueryFont instead of ListFonts\n");
86    fprintf (stderr, "    -w width                 maximum width for multiple columns\n");
87    fprintf (stderr, "    -n columns               number of columns if multi column\n");
88    fprintf (stderr, "    -display displayname     X server to contact\n");
89    fprintf (stderr, "    -d displayname           (alias for -display displayname)\n");
90    fprintf (stderr, "\n");
91    Close_Display();
92    exit(EXIT_FAILURE);
93}
94
95int main(int argc, char **argv)
96{
97    int argcnt = 0, i;
98
99    INIT_NAME;
100
101    /* Handle command line arguments, open display */
102    Setup_Display_And_Screen(&argc, argv);
103
104    for (argv++, argc--; argc; argv++, argc--) {
105        if (argv[0][0] == '-') {
106            if (argcnt > 0) usage ();
107            for (i=1; argv[0][i]; i++)
108                switch(argv[0][i]) {
109                case 'l':
110                    long_list++;
111                    break;
112                case 'm':
113                    min_max++;
114                    break;
115                case 'C':
116                    columns = 0;
117                    break;
118                case '1':
119                    columns = 1;
120                    break;
121                case 'f': /* "-fn" */
122                    if (--argc <= 0) usage ();
123                    if (argv[0][i+1] != 'n') usage ();
124                    argcnt++;
125                    argv++;
126                    get_list(argv[0]);
127                    goto next;
128                case 'w':
129                    if (--argc <= 0) usage ();
130                    argv++;
131                    max_output_line_width = atoi(argv[0]);
132                    goto next;
133                case 'n':
134                    if (--argc <= 0) usage ();
135                    argv++;
136                    columns = atoi(argv[0]);
137                    goto next;
138                case 'o':
139                    open_instead_of_list = True;
140                    break;
141                case 'u':
142                    sort_output = False;
143                    break;
144                default:
145                    usage();
146                    break;
147                }
148            if (i == 1)
149                usage();
150        } else {
151            argcnt++;
152            get_list(argv[0]);
153        }
154      next: ;
155    }
156
157    if (argcnt == 0)
158        get_list("*");
159
160    show_fonts();
161
162    Close_Display();
163    return EXIT_SUCCESS;
164}
165
166
167static
168void get_list(char *pattern)
169{
170    int           available = nnames+1,
171                  i;
172    char        **fonts;
173    XFontStruct  *info;
174
175    /* Get list of fonts matching pattern */
176    for (;;) {
177        if (open_instead_of_list) {
178            info = XLoadQueryFont (dpy, pattern);
179
180            if (info) {
181                fonts = &pattern;
182                available = 1;
183                XUnloadFont (dpy, info->fid);
184            } else {
185                fonts = NULL;
186            }
187            break;
188        }
189
190        if (long_list == L_MEDIUM)
191            fonts = XListFontsWithInfo(dpy, pattern, nnames, &available, &info);
192        else
193            fonts = XListFonts(dpy, pattern, nnames, &available);
194        if (fonts == NULL || available < nnames)
195            break;
196        if (long_list == L_MEDIUM)
197            XFreeFontInfo(fonts, info, available);
198        else
199            XFreeFontNames(fonts);
200        nnames = available * 2;
201    }
202
203    if (fonts == NULL) {
204        fprintf(stderr, "%s: pattern \"%s\" unmatched\n",
205                program_name, pattern);
206        return;
207    }
208
209    font_list = (FontList *)Realloc((char *)font_list,
210            (font_cnt + available) * sizeof(FontList));
211    for (i=0; i<available; i++) {
212        font_list[font_cnt].name = fonts[i];
213        if (long_list == L_MEDIUM)
214            font_list[font_cnt].info = info + i;
215        else
216            font_list[font_cnt].info = NULL;
217
218        font_cnt++;
219    }
220}
221
222static
223int compare(const void *arg1, const void *arg2)
224{
225    const FontList *f1 = arg1;
226    const FontList *f2 = arg2;
227    const char *p1 = f1->name;
228    const char *p2 = f2->name;
229
230    while (*p1 && *p2 && *p1 == *p2)
231            p1++, p2++;
232    return(*p1 - *p2);
233}
234
235static
236void show_fonts(void)
237{
238    int i;
239
240    if (font_cnt == 0)
241        return;
242
243    /* first sort the output */
244    if (sort_output) qsort(font_list, font_cnt, sizeof(FontList), compare);
245
246    if (long_list > L_MEDIUM) {
247        for (i = 0; i < font_cnt; i++) {
248            do_query_font (dpy, font_list[i].name);
249        }
250        return;
251    }
252
253    if (long_list == L_MEDIUM) {
254        XFontStruct *pfi;
255        char        *string;
256
257        printf("DIR  ");
258        printf("MIN  ");
259        printf("MAX ");
260        printf("EXIST ");
261        printf("DFLT ");
262        printf("PROP ");
263        printf("ASC ");
264        printf("DESC ");
265        printf("NAME");
266        printf("\n");
267        for (i=0; i<font_cnt; i++) {
268            pfi = font_list[i].info;
269            if (!pfi) {
270                fprintf(stderr, "%s:  no font information for font \"%s\".\n",
271                        program_name,
272                        font_list[i].name ?
273                        font_list[i].name : "");
274                continue;
275            }
276            switch(pfi->direction) {
277                case FontLeftToRight: string = "-->"; break;
278                case FontRightToLeft: string = "<--"; break;
279                default:              string = "???"; break;
280            }
281            printf("%-4s", string);
282            if (pfi->min_byte1 == 0 &&
283                pfi->max_byte1 == 0) {
284                printf(" %3d ", pfi->min_char_or_byte2);
285                printf(" %3d ", pfi->max_char_or_byte2);
286            } else {
287                printf("*%3d ", pfi->min_byte1);
288                printf("*%3d ", pfi->max_byte1);
289            }
290            printf("%5s ", pfi->all_chars_exist ? "all" : "some");
291            printf("%4d ", pfi->default_char);
292            printf("%4d ", pfi->n_properties);
293            printf("%3d ", pfi->ascent);
294            printf("%4d ", pfi->descent);
295            printf("%s\n", font_list[i].name);
296            if (min_max) {
297                char  min[ BUFSIZ ],
298                      max[ BUFSIZ ];
299                char *pmax = max,
300                     *pmin = min;
301
302                strcpy(pmin, "     min(l,r,w,a,d) = (");
303                strcpy(pmax, "     max(l,r,w,a,d) = (");
304                pmin += strlen(pmin);
305                pmax += strlen(pmax);
306
307                copy_number(&pmin, &pmax,
308                            pfi->min_bounds.lbearing,
309                            pfi->max_bounds.lbearing);
310                *pmin++ = *pmax++ = ',';
311                copy_number(&pmin, &pmax,
312                            pfi->min_bounds.rbearing,
313                            pfi->max_bounds.rbearing);
314                *pmin++ = *pmax++ = ',';
315                copy_number(&pmin, &pmax,
316                            pfi->min_bounds.width,
317                            pfi->max_bounds.width);
318                *pmin++ = *pmax++ = ',';
319                copy_number(&pmin, &pmax,
320                            pfi->min_bounds.ascent,
321                            pfi->max_bounds.ascent);
322                *pmin++ = *pmax++ = ',';
323                copy_number(&pmin, &pmax,
324                            pfi->min_bounds.descent,
325                            pfi->max_bounds.descent);
326                *pmin++ = *pmax++ = ')';
327                *pmin = *pmax = '\0';
328                printf("%s\n", min);
329                printf("%s\n", max);
330            }
331        }
332        return;
333    }
334
335    if ((columns == 0 && isatty(1)) || columns > 1) {
336        int width,
337            max_width = 0,
338            lines_per_column,
339            j,
340            index;
341
342        for (i=0; i<font_cnt; i++) {
343            width = strlen(font_list[i].name);
344            if (width > max_width)
345                max_width = width;
346        }
347        if (max_width == 0)
348            Fatal_Error("all %d fontnames listed are zero length", font_cnt);
349
350        if (columns == 0) {
351            if ((max_width * 2) + output_line_padding >
352                max_output_line_width) {
353                columns = 1;
354            } else {
355                max_width += output_line_padding;
356                columns = ((max_output_line_width +
357                            output_line_padding) / max_width);
358            }
359        } else {
360            max_width += output_line_padding;
361        }
362        if (columns <= 1) goto single_column;
363
364        if (font_cnt < columns)
365            columns = font_cnt;
366        lines_per_column = (font_cnt + columns - 1) / columns;
367
368        for (i=0; i<lines_per_column; i++) {
369            for (j=0; j<columns; j++) {
370                index = j * lines_per_column + i;
371                if (index >= font_cnt)
372                    break;
373                if (j+1 == columns)
374                    printf("%s", font_list[ index ].name);
375                else
376                    printf("%-*s",
377                           max_width,
378                           font_list[ index ].name);
379            }
380            printf("\n");
381        }
382        return;
383    }
384
385  single_column:
386    for (i=0; i<font_cnt; i++)
387        printf("%s\n", font_list[i].name);
388}
389
390static
391void copy_number(char **pp1, char**pp2, int n1, int n2)
392{
393    char *p1 = *pp1;
394    char *p2 = *pp2;
395    int   w;
396
397    sprintf(p1, "%d", n1);
398    sprintf(p2, "%d", n2);
399    w = MAX(strlen(p1), strlen(p2));
400    sprintf(p1, "%*d", w, n1);
401    sprintf(p2, "%*d", w, n2);
402    p1 += strlen(p1);
403    p2 += strlen(p2);
404    *pp1 = p1;
405    *pp2 = p2;
406}
407
408
409
410/* ARGSUSED */
411static
412int IgnoreError(Display *disp, XErrorEvent *event)
413{
414    return 0;
415}
416
417static char *bounds_metrics_title =
418                      "width left  right  asc  desc   attr   keysym\n";
419
420#define PrintBounds(_what,_ptr) \
421{   register XCharStruct *p = (_ptr); \
422    printf ("\t%3s\t\t%4d  %4d  %4d  %4d  %4d  0x%04x\n", \
423          (_what), p->width, p->lbearing, \
424          p->rbearing, p->ascent, p->descent, p->attributes); }
425
426
427static char* stringValued [] = { /* values are atoms */
428    /* font name components (see section 3.2 of the XLFD) */
429    "FOUNDRY",
430    "FAMILY_NAME",
431    "WEIGHT_NAME",
432    "SLANT",
433    "SETWIDTH_NAME",
434    "ADD_STYLE_NAME",
435    "SPACING",
436    "CHARSET_REGISTRY",
437    "CHARSET_ENCODING",
438
439    /* other standard X font properties (see section 3.2 of the XLFD) */
440    "FONT",
441    "FACE_NAME",
442    "FULL_NAME",              /* deprecated */
443    "COPYRIGHT",
444    "NOTICE",
445    "FONT_TYPE",
446    "FONT_VERSION",
447    "RASTERIZER_NAME",
448    "RASTERIZER_VERSION",
449
450    /* other registered font properties (see the X.org Registry, sec. 15) */
451    "_ADOBE_POSTSCRIPT_FONTNAME",
452
453    /* unregistered font properties */
454    "CHARSET_COLLECTIONS",
455    "CLASSIFICATION",
456    "DEVICE_FONT_NAME",
457    "FONTNAME_REGISTRY",
458    "MONOSPACED",
459    "QUALITY",
460    "RELATIVE_SET",
461    "STYLE",
462     NULL
463    };
464
465static
466void PrintProperty(XFontProp *prop)
467{
468    char *atom, *value;
469    char nosuch[40];
470    int i;
471    XErrorHandler oldhandler = XSetErrorHandler(IgnoreError);
472
473    atom = XGetAtomName(dpy, prop->name);
474    if (!atom) {
475        atom = nosuch;
476        nosuch[0] = '\0';
477        (void)sprintf (atom, "No such atom = %ld", prop->name);
478    }
479    printf ("      %s", atom);
480
481    /* Pad out to a column width of 22, but ensure there is always at
482       least one space between property name & value. */
483    for (i = strlen(atom); i < 21; i++) putchar (' ');
484    putchar(' ');
485
486    for (i = 0; ; i++) {
487        if (stringValued[i] == NULL) {
488            printf ("%ld\n", prop->card32);
489            break;
490        }
491        if (strcmp(stringValued[i], atom) == 0) {
492            value = XGetAtomName(dpy, prop->card32);
493            if (value == NULL)
494                printf ("%ld (expected string value)\n", prop->card32);
495            else {
496                printf ("%s\n", value);
497                XFree (value);
498            }
499            break;
500        }
501    }
502    if (atom != nosuch) XFree (atom);
503    XSetErrorHandler (oldhandler);
504}
505
506
507static void
508ComputeFontType(XFontStruct *fs)
509{
510    int i;
511    Bool char_cell = True;
512    char *reason = NULL;
513    XCharStruct *cs;
514    Atom awatom = XInternAtom (dpy, "AVERAGE_WIDTH", False);
515
516    printf ("  font type:\t\t");
517    if (fs->min_bounds.width != fs->max_bounds.width) {
518        printf ("Proportional (min and max widths not equal)\n");
519        return;
520    }
521
522    if (awatom) {
523        for (i = 0; i < fs->n_properties; i++) {
524            if (fs->properties[i].name == awatom &&
525                (fs->max_bounds.width * 10) != fs->properties[i].card32) {
526                char_cell = False;
527                reason = "font width not equal to AVERAGE_WIDTH";
528                break;
529            }
530        }
531    }
532
533    if (fs->per_char) {
534        for (i = fs->min_char_or_byte2, cs = fs->per_char;
535             i <= fs->max_char_or_byte2; i++, cs++) {
536            if (cs->width == 0) continue;
537            if (cs->width != fs->max_bounds.width) {
538                /* this shouldn't happen since we checked above */
539                printf ("Proportional (characters not all the same width)\n");
540                return;
541            }
542            if (char_cell) {
543                if (cs->width < 0) {
544                    if (!(cs->width <= cs->lbearing &&
545                          cs->lbearing <= cs->rbearing &&
546                          cs->rbearing <= 0)) {
547                        char_cell = False;
548                        reason = "ink outside bounding box";
549                    }
550                } else {
551                    if (!(0 <= cs->lbearing &&
552                          cs->lbearing <= cs->rbearing &&
553                          cs->rbearing <= cs->width)) {
554                        char_cell = False;
555                        reason = "ink outside bounding box";
556                    }
557                }
558                if (!(cs->ascent <= fs->ascent &&
559                      cs->descent <= fs->descent)) {
560                    char_cell  = False;
561                    reason = "characters not all same ascent or descent";
562                }
563            }
564        }
565    }
566
567    printf ("%s", char_cell ? "Character Cell" : "Monospaced");
568    if (reason) printf (" (%s)", reason);
569    printf ("\n");
570
571    return;
572}
573
574
575static void
576print_character_metrics(register XFontStruct *info)
577{
578    register XCharStruct *pc = info->per_char;
579    register int i, j;
580    unsigned n, saven;
581
582    printf ("  character metrics:\n");
583    saven = ((info->min_byte1 << 8) | info->min_char_or_byte2);
584    for (j = info->min_byte1; j <= info->max_byte1; j++) {
585        n = saven;
586        for (i = info->min_char_or_byte2; i <= info->max_char_or_byte2; i++) {
587            char *s = XKeysymToString ((KeySym) n);
588            printf ("\t0x%02x%02x (%u)\t%4d  %4d  %4d  %4d  %4d  0x%04x  %s\n",
589                    j, i, n, pc->width, pc->lbearing,
590                    pc->rbearing, pc->ascent, pc->descent, pc->attributes,
591                    s ? s : ".");
592            pc++;
593            n++;
594        }
595        saven += 256;
596    }
597}
598
599static
600void do_query_font (Display *dpy, char *name)
601{
602    register int i;
603    register XFontStruct *info = XLoadQueryFont (dpy, name);
604
605    if (!info) {
606        fprintf (stderr, "%s:  unable to get info about font \"%s\"\n",
607                 program_name, name);
608        return;
609    }
610    printf ("name:  %s\n", name ? name : "(nil)");
611    printf ("  direction:\t\t%s\n", ((info->direction == FontLeftToRight)
612                                     ? "left to right" : "right to left"));
613    printf ("  indexing:\t\t%s\n",
614            ((info->min_byte1 == 0 && info->max_byte1 == 0) ? "linear" :
615             "matrix"));
616    printf ("  rows:\t\t\t0x%02x thru 0x%02x (%d thru %d)\n",
617            info->min_byte1, info->max_byte1,
618            info->min_byte1, info->max_byte1);
619    printf ("  columns:\t\t0x%02x thru 0x%02x (%d thru %d)\n",
620            info->min_char_or_byte2, info->max_char_or_byte2,
621            info->min_char_or_byte2, info->max_char_or_byte2);
622    printf ("  all chars exist:\t%s\n",
623        (info->all_chars_exist) ? "yes" : "no");
624    printf ("  default char:\t\t0x%04x (%d)\n",
625            info->default_char, info->default_char);
626    printf ("  ascent:\t\t%d\n", info->ascent);
627    printf ("  descent:\t\t%d\n", info->descent);
628    ComputeFontType (info);
629    printf ("  bounds:\t\t%s", bounds_metrics_title);
630    PrintBounds ("min", &info->min_bounds);
631    PrintBounds ("max", &info->max_bounds);
632    if (info->per_char && long_list >= L_VERYLONG)
633        print_character_metrics (info);
634    printf ("  properties:\t\t%d\n", info->n_properties);
635    for (i = 0; i < info->n_properties; i++)
636        PrintProperty (&info->properties[i]);
637    printf ("\n");
638
639    XFreeFontInfo (NULL, info, 1);
640}
641
642
643