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