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