1e6232409Smrg/*
26a45684fSmrg
3e6232409SmrgCopyright 1989, 1998  The Open Group
4e6232409Smrg
5e6232409SmrgPermission to use, copy, modify, distribute, and sell this software and its
6e6232409Smrgdocumentation for any purpose is hereby granted without fee, provided that
7e6232409Smrgthe above copyright notice appear in all copies and that both that
8e6232409Smrgcopyright notice and this permission notice appear in supporting
9e6232409Smrgdocumentation.
10e6232409Smrg
11e6232409SmrgThe above copyright notice and this permission notice shall be included in
12e6232409Smrgall copies or substantial portions of the Software.
13e6232409Smrg
14e6232409SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15e6232409SmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16e6232409SmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17e6232409SmrgOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18e6232409SmrgAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19e6232409SmrgCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20e6232409Smrg
21e6232409SmrgExcept as contained in this notice, the name of The Open Group shall not be
22e6232409Smrgused in advertising or otherwise to promote the sale, use or other dealings
23e6232409Smrgin this Software without prior written authorization from The Open Group.
246a45684fSmrg
256a45684fSmrg */
266a45684fSmrg
276a45684fSmrg#ifdef HAVE_CONFIG_H
286a45684fSmrg# include "config.h"
296a45684fSmrg#endif
30e6232409Smrg
31e6232409Smrg#include <X11/Xlib.h>
32e6232409Smrg#include <X11/Xutil.h>
33e6232409Smrg#include <X11/Xos.h>
34e6232409Smrg#include <stdio.h>
35e6232409Smrg#include <stdlib.h>
36e6232409Smrg#include <limits.h>
37e6232409Smrg#include "dsimple.h"
38e6232409Smrg
39e2515bd3Smrg#ifdef HAVE_BSD_STDLIB_H
40e2515bd3Smrg#include <bsd/stdlib.h>
41e2515bd3Smrg#endif
42e2515bd3Smrg
43e2515bd3Smrg#ifndef HAVE_REALLOCARRAY
44e2515bd3Smrg#define reallocarray(old, num, size) realloc(old, (num) * (size))
45e2515bd3Smrg#endif
46e2515bd3Smrg
478ae5c7d9Smrg#define N_START INT_MAX         /* Maximum # of fonts to start with (should
488ae5c7d9Smrg                                 * always be be > 10000 as modern OSes like
498ae5c7d9Smrg                                 * Solaris 8 already have more than 9000 XLFD
508ae5c7d9Smrg                                 * fonts available) */
51e6232409Smrg
52e6232409Smrg#define L_SHORT    0
53e6232409Smrg#define L_MEDIUM   1
54e6232409Smrg#define L_LONG     2
55e6232409Smrg#define L_VERYLONG 3
56e6232409Smrg
57e6232409Smrgstatic int  max_output_line_width     = 79;
58e6232409Smrgstatic int  output_line_padding       = 3;
59e6232409Smrgstatic int  columns                   = 0;
60e6232409Smrg
61e6232409Smrgstatic Bool sort_output               = True;
62e6232409Smrgstatic Bool open_instead_of_list      = False;
63e6232409Smrgstatic int  long_list                 = L_SHORT;
64e6232409Smrgstatic int  nnames                    = N_START;
65e6232409Smrgstatic int  font_cnt                  = 0;
66e6232409Smrgstatic int  min_max;
67e6232409Smrg
68e6232409Smrgtypedef struct {
69e2515bd3Smrg    const char *name;
708ae5c7d9Smrg    XFontStruct *info;
71e6232409Smrg} FontList;
72e6232409Smrg
73e6232409Smrgstatic FontList *font_list = NULL;
74e6232409Smrg
75e6232409Smrg/* Local prototypes */
7694a251fdSmrgstatic void get_list(const char *pattern);
77e6232409Smrgstatic int  compare(const void *arg1, const void *arg2);
78e6232409Smrgstatic void show_fonts(void);
798ae5c7d9Smrgstatic void copy_number(char **pp1, char **pp2, int n1, int n2);
80e6232409Smrgstatic int  IgnoreError(Display *disp, XErrorEvent *event);
81e6232409Smrgstatic void PrintProperty(XFontProp *prop);
82e6232409Smrgstatic void ComputeFontType(XFontStruct *fs);
83e6232409Smrgstatic void print_character_metrics(register XFontStruct *info);
84e2515bd3Smrgstatic void do_query_font(Display *dpy, const char *name);
85e6232409Smrg
868ae5c7d9Smrgvoid
878ae5c7d9Smrgusage(const char *errmsg)
88e6232409Smrg{
896a45684fSmrg    if (errmsg != NULL)
908ae5c7d9Smrg        fprintf(stderr, "%s: %s\n\n", program_name, errmsg);
918ae5c7d9Smrg
928ae5c7d9Smrg    fprintf(stderr, "usage:  %s [-options] [-fn pattern]\n%s", program_name,
938ae5c7d9Smrg            "where options include:\n"
948ae5c7d9Smrg            "    -l[l[l]]                 give long info about each font\n"
958ae5c7d9Smrg            "    -m                       give character min and max bounds\n"
968ae5c7d9Smrg            "    -C                       force columns\n"
978ae5c7d9Smrg            "    -1                       force single column\n"
988ae5c7d9Smrg            "    -u                       keep output unsorted\n"
998ae5c7d9Smrg            "    -o                       use OpenFont/QueryFont instead of ListFonts\n"
1008ae5c7d9Smrg            "    -w width                 maximum width for multiple columns\n"
1018ae5c7d9Smrg            "    -n columns               number of columns if multi column\n"
1028ae5c7d9Smrg            "    -display displayname     X server to contact\n"
1038ae5c7d9Smrg            "    -d displayname           (alias for -display displayname)\n"
1048ae5c7d9Smrg            "    -v                       print program version\n"
1058ae5c7d9Smrg            "\n");
106e6232409Smrg    Close_Display();
107e6232409Smrg    exit(EXIT_FAILURE);
108e6232409Smrg}
109e6232409Smrg
1108ae5c7d9Smrgint
1118ae5c7d9Smrgmain(int argc, char **argv)
112e6232409Smrg{
113e6232409Smrg    int argcnt = 0, i;
114e6232409Smrg
115e6232409Smrg    INIT_NAME;
116e6232409Smrg
117e6232409Smrg    /* Handle command line arguments, open display */
118e6232409Smrg    Setup_Display_And_Screen(&argc, argv);
119e6232409Smrg
120e6232409Smrg    for (argv++, argc--; argc; argv++, argc--) {
121e6232409Smrg        if (argv[0][0] == '-') {
1226a45684fSmrg            if (argcnt > 0)
1238ae5c7d9Smrg                usage("options may not be specified after font names");
1248ae5c7d9Smrg            for (i = 1; argv[0][i]; i++)
1258ae5c7d9Smrg                switch (argv[0][i]) {
126e6232409Smrg                case 'l':
127e6232409Smrg                    long_list++;
128e6232409Smrg                    break;
129e6232409Smrg                case 'm':
130e6232409Smrg                    min_max++;
131e6232409Smrg                    break;
132e6232409Smrg                case 'C':
133e6232409Smrg                    columns = 0;
134e6232409Smrg                    break;
135e6232409Smrg                case '1':
136e6232409Smrg                    columns = 1;
137e6232409Smrg                    break;
1388ae5c7d9Smrg                case 'f':      /* "-fn" */
1398ae5c7d9Smrg                    if (argv[0][i + 1] != 'n') {
1408ae5c7d9Smrg                        fprintf(stderr, "%s: unrecognized argument %s\n\n",
1418ae5c7d9Smrg                                program_name, argv[0]);
1426a45684fSmrg                        usage(NULL);
1436a45684fSmrg                    }
1448ae5c7d9Smrg                    if (--argc <= 0)
1458ae5c7d9Smrg                        usage("-fn requires an argument");
146e6232409Smrg                    argcnt++;
147e6232409Smrg                    argv++;
148e6232409Smrg                    get_list(argv[0]);
149e6232409Smrg                    goto next;
150e6232409Smrg                case 'w':
1518ae5c7d9Smrg                    if (--argc <= 0)
1528ae5c7d9Smrg                        usage("-w requires an argument");
153e6232409Smrg                    argv++;
154e6232409Smrg                    max_output_line_width = atoi(argv[0]);
155e6232409Smrg                    goto next;
156e6232409Smrg                case 'n':
1578ae5c7d9Smrg                    if (--argc <= 0)
1588ae5c7d9Smrg                        usage("-n requires an argument");
159e6232409Smrg                    argv++;
160e6232409Smrg                    columns = atoi(argv[0]);
161e6232409Smrg                    goto next;
162e6232409Smrg                case 'o':
163e6232409Smrg                    open_instead_of_list = True;
164e6232409Smrg                    break;
165e6232409Smrg                case 'u':
166e6232409Smrg                    sort_output = False;
167e6232409Smrg                    break;
1686a45684fSmrg                case 'v':
1696a45684fSmrg                    puts(PACKAGE_STRING);
1706a45684fSmrg                    exit(0);
171e6232409Smrg                default:
1728ae5c7d9Smrg                    fprintf(stderr, "%s: unrecognized argument -%c\n\n",
1738ae5c7d9Smrg                            program_name, argv[0][i]);
1746a45684fSmrg                    usage(NULL);
175e6232409Smrg                    break;
176e6232409Smrg                }
1776a45684fSmrg            if (i == 1) {
1788ae5c7d9Smrg                fprintf(stderr, "%s: unrecognized argument %s\n\n",
1798ae5c7d9Smrg                        program_name, argv[0]);
1806a45684fSmrg                usage(NULL);
1816a45684fSmrg            }
1828ae5c7d9Smrg        }
1838ae5c7d9Smrg        else {
184e6232409Smrg            argcnt++;
185e6232409Smrg            get_list(argv[0]);
186e6232409Smrg        }
1878ae5c7d9Smrg next: ;
188e6232409Smrg    }
189e6232409Smrg
190e6232409Smrg    if (argcnt == 0)
191e6232409Smrg        get_list("*");
192e6232409Smrg
193e6232409Smrg    show_fonts();
1948ae5c7d9Smrg
195e6232409Smrg    Close_Display();
196e6232409Smrg    return EXIT_SUCCESS;
197e6232409Smrg}
198e6232409Smrg
1998ae5c7d9Smrgstatic void
2008ae5c7d9Smrgget_list(const char *pattern)
201e6232409Smrg{
2028ae5c7d9Smrg    XFontStruct *info;
203e6232409Smrg
204e2515bd3Smrg    if (open_instead_of_list) {
205e2515bd3Smrg        info = XLoadQueryFont(dpy, pattern);
206e6232409Smrg
207e2515bd3Smrg        if (info == NULL) {
208e2515bd3Smrg            fprintf(stderr, "%s: pattern \"%s\" unmatched\n",
209e2515bd3Smrg                    program_name, pattern);
210e2515bd3Smrg            return;
211e6232409Smrg        }
2128ae5c7d9Smrg
213e2515bd3Smrg        font_list = reallocarray(font_list, (font_cnt + 1), sizeof(FontList));
214e2515bd3Smrg        if (font_list == NULL)
215e2515bd3Smrg            Fatal_Error("Out of memory!");
216e2515bd3Smrg        font_list[font_cnt].name = pattern;
217e2515bd3Smrg        if (long_list == L_MEDIUM) {
218e2515bd3Smrg            font_list[font_cnt].info = info;
219e2515bd3Smrg            XUnloadFont(dpy, info->fid);
220e2515bd3Smrg        }
221e2515bd3Smrg        else {
222e6232409Smrg            font_list[font_cnt].info = NULL;
223e2515bd3Smrg            XFreeFont(dpy, info);
224e2515bd3Smrg        }
225e6232409Smrg        font_cnt++;
226e6232409Smrg    }
227e2515bd3Smrg    else {
228e2515bd3Smrg        /* Get list of fonts matching pattern */
229e2515bd3Smrg        int available = nnames + 1;
230e2515bd3Smrg        char **fonts;
231e2515bd3Smrg
232e2515bd3Smrg        for (;;) {
233e2515bd3Smrg            if (long_list == L_MEDIUM)
234e2515bd3Smrg                fonts = XListFontsWithInfo(dpy, pattern, nnames, &available,
235e2515bd3Smrg                                           &info);
236e2515bd3Smrg            else
237e2515bd3Smrg                fonts = XListFonts(dpy, pattern, nnames, &available);
238e2515bd3Smrg            if (fonts == NULL) {
239e2515bd3Smrg                fprintf(stderr, "%s: pattern \"%s\" unmatched\n",
240e2515bd3Smrg                        program_name, pattern);
241e2515bd3Smrg                return;
242e2515bd3Smrg            }
243e2515bd3Smrg            if (available < nnames)
244e2515bd3Smrg                break;
245e2515bd3Smrg            if (long_list == L_MEDIUM)
246e2515bd3Smrg                XFreeFontInfo(fonts, info, available);
247e2515bd3Smrg            else
248e2515bd3Smrg                XFreeFontNames(fonts);
249e2515bd3Smrg            nnames = available * 2;
250e2515bd3Smrg        }
251e2515bd3Smrg
252e2515bd3Smrg        font_list = reallocarray(font_list,
253e2515bd3Smrg                                 (font_cnt + available), sizeof(FontList));
254e2515bd3Smrg        if (font_list == NULL)
255e2515bd3Smrg            Fatal_Error("Out of memory!");
256e2515bd3Smrg        for (int i = 0; i < available; i++) {
257e2515bd3Smrg            font_list[font_cnt].name = fonts[i];
258e2515bd3Smrg            if (long_list == L_MEDIUM)
259e2515bd3Smrg                font_list[font_cnt].info = info + i;
260e2515bd3Smrg            else
261e2515bd3Smrg                font_list[font_cnt].info = NULL;
262e2515bd3Smrg
263e2515bd3Smrg            font_cnt++;
264e2515bd3Smrg        }
265e2515bd3Smrg    }
266e6232409Smrg}
267e6232409Smrg
2688ae5c7d9Smrgstatic int
2698ae5c7d9Smrgcompare(const void *arg1, const void *arg2)
270e6232409Smrg{
271e6232409Smrg    const FontList *f1 = arg1;
272e6232409Smrg    const FontList *f2 = arg2;
273e6232409Smrg    const char *p1 = f1->name;
274e6232409Smrg    const char *p2 = f2->name;
275e6232409Smrg
276e6232409Smrg    while (*p1 && *p2 && *p1 == *p2)
2778ae5c7d9Smrg        p1++, p2++;
2788ae5c7d9Smrg    return (*p1 - *p2);
279e6232409Smrg}
280e6232409Smrg
2818ae5c7d9Smrgstatic void
2828ae5c7d9Smrgshow_fonts(void)
283e6232409Smrg{
284e6232409Smrg    int i;
285e6232409Smrg
286e6232409Smrg    if (font_cnt == 0)
287e6232409Smrg        return;
288e6232409Smrg
289e6232409Smrg    /* first sort the output */
2908ae5c7d9Smrg    if (sort_output)
2918ae5c7d9Smrg        qsort(font_list, font_cnt, sizeof(FontList), compare);
292e6232409Smrg
293e6232409Smrg    if (long_list > L_MEDIUM) {
294e6232409Smrg        for (i = 0; i < font_cnt; i++) {
2958ae5c7d9Smrg            do_query_font(dpy, font_list[i].name);
296e6232409Smrg        }
297e6232409Smrg        return;
298e6232409Smrg    }
299e6232409Smrg
300e6232409Smrg    if (long_list == L_MEDIUM) {
301e6232409Smrg        XFontStruct *pfi;
3028ae5c7d9Smrg
3038ae5c7d9Smrg        const char *string;
304e6232409Smrg
305e6232409Smrg        printf("DIR  ");
306e6232409Smrg        printf("MIN  ");
307e6232409Smrg        printf("MAX ");
308e6232409Smrg        printf("EXIST ");
309e6232409Smrg        printf("DFLT ");
310e6232409Smrg        printf("PROP ");
311e6232409Smrg        printf("ASC ");
312e6232409Smrg        printf("DESC ");
313e6232409Smrg        printf("NAME");
314e6232409Smrg        printf("\n");
3158ae5c7d9Smrg        for (i = 0; i < font_cnt; i++) {
316e6232409Smrg            pfi = font_list[i].info;
317e6232409Smrg            if (!pfi) {
318e6232409Smrg                fprintf(stderr, "%s:  no font information for font \"%s\".\n",
3198ae5c7d9Smrg                        program_name,
3208ae5c7d9Smrg                        font_list[i].name ? font_list[i].name : "");
321e6232409Smrg                continue;
322e6232409Smrg            }
3238ae5c7d9Smrg            switch (pfi->direction) {
3248ae5c7d9Smrg            case FontLeftToRight:
3258ae5c7d9Smrg                string = "-->";
3268ae5c7d9Smrg                break;
3278ae5c7d9Smrg            case FontRightToLeft:
3288ae5c7d9Smrg                string = "<--";
3298ae5c7d9Smrg                break;
3308ae5c7d9Smrg            default:
3318ae5c7d9Smrg                string = "???";
3328ae5c7d9Smrg                break;
333e6232409Smrg            }
334e6232409Smrg            printf("%-4s", string);
3358ae5c7d9Smrg            if (pfi->min_byte1 == 0 && pfi->max_byte1 == 0) {
336e6232409Smrg                printf(" %3d ", pfi->min_char_or_byte2);
337e6232409Smrg                printf(" %3d ", pfi->max_char_or_byte2);
3388ae5c7d9Smrg            }
3398ae5c7d9Smrg            else {
340e6232409Smrg                printf("*%3d ", pfi->min_byte1);
341e6232409Smrg                printf("*%3d ", pfi->max_byte1);
342e6232409Smrg            }
343e6232409Smrg            printf("%5s ", pfi->all_chars_exist ? "all" : "some");
344e6232409Smrg            printf("%4d ", pfi->default_char);
345e6232409Smrg            printf("%4d ", pfi->n_properties);
346e6232409Smrg            printf("%3d ", pfi->ascent);
347e6232409Smrg            printf("%4d ", pfi->descent);
348e6232409Smrg            printf("%s\n", font_list[i].name);
349e6232409Smrg            if (min_max) {
3508ae5c7d9Smrg                char min[BUFSIZ], max[BUFSIZ];
3518ae5c7d9Smrg
3528ae5c7d9Smrg                char *pmax = max, *pmin = min;
353e6232409Smrg
354e6232409Smrg                strcpy(pmin, "     min(l,r,w,a,d) = (");
355e6232409Smrg                strcpy(pmax, "     max(l,r,w,a,d) = (");
356e6232409Smrg                pmin += strlen(pmin);
357e6232409Smrg                pmax += strlen(pmax);
358e6232409Smrg
359e6232409Smrg                copy_number(&pmin, &pmax,
360e6232409Smrg                            pfi->min_bounds.lbearing,
361e6232409Smrg                            pfi->max_bounds.lbearing);
362e6232409Smrg                *pmin++ = *pmax++ = ',';
363e6232409Smrg                copy_number(&pmin, &pmax,
364e6232409Smrg                            pfi->min_bounds.rbearing,
365e6232409Smrg                            pfi->max_bounds.rbearing);
366e6232409Smrg                *pmin++ = *pmax++ = ',';
367e6232409Smrg                copy_number(&pmin, &pmax,
368e6232409Smrg                            pfi->min_bounds.width,
369e6232409Smrg                            pfi->max_bounds.width);
370e6232409Smrg                *pmin++ = *pmax++ = ',';
371e6232409Smrg                copy_number(&pmin, &pmax,
372e6232409Smrg                            pfi->min_bounds.ascent,
373e6232409Smrg                            pfi->max_bounds.ascent);
374e6232409Smrg                *pmin++ = *pmax++ = ',';
375e6232409Smrg                copy_number(&pmin, &pmax,
376e6232409Smrg                            pfi->min_bounds.descent,
377e6232409Smrg                            pfi->max_bounds.descent);
378e6232409Smrg                *pmin++ = *pmax++ = ')';
379e6232409Smrg                *pmin = *pmax = '\0';
380e6232409Smrg                printf("%s\n", min);
381e6232409Smrg                printf("%s\n", max);
382e6232409Smrg            }
383e6232409Smrg        }
384e6232409Smrg        return;
385e6232409Smrg    }
386e6232409Smrg
387e6232409Smrg    if ((columns == 0 && isatty(1)) || columns > 1) {
3888ae5c7d9Smrg        int width, max_width = 0, lines_per_column, j, index;
389e6232409Smrg
3908ae5c7d9Smrg        for (i = 0; i < font_cnt; i++) {
391e6232409Smrg            width = strlen(font_list[i].name);
392e6232409Smrg            if (width > max_width)
393e6232409Smrg                max_width = width;
394e6232409Smrg        }
395e6232409Smrg        if (max_width == 0)
396e6232409Smrg            Fatal_Error("all %d fontnames listed are zero length", font_cnt);
397e6232409Smrg
398e6232409Smrg        if (columns == 0) {
399e6232409Smrg            if ((max_width * 2) + output_line_padding >
400e6232409Smrg                max_output_line_width) {
401e6232409Smrg                columns = 1;
4028ae5c7d9Smrg            }
4038ae5c7d9Smrg            else {
404e6232409Smrg                max_width += output_line_padding;
405e6232409Smrg                columns = ((max_output_line_width +
406e6232409Smrg                            output_line_padding) / max_width);
407e6232409Smrg            }
4088ae5c7d9Smrg        }
4098ae5c7d9Smrg        else {
410e6232409Smrg            max_width += output_line_padding;
411e6232409Smrg        }
4128ae5c7d9Smrg        if (columns <= 1)
4138ae5c7d9Smrg            goto single_column;
414e6232409Smrg
415e6232409Smrg        if (font_cnt < columns)
416e6232409Smrg            columns = font_cnt;
417e6232409Smrg        lines_per_column = (font_cnt + columns - 1) / columns;
418e6232409Smrg
4198ae5c7d9Smrg        for (i = 0; i < lines_per_column; i++) {
4208ae5c7d9Smrg            for (j = 0; j < columns; j++) {
421e6232409Smrg                index = j * lines_per_column + i;
422e6232409Smrg                if (index >= font_cnt)
423e6232409Smrg                    break;
4248ae5c7d9Smrg                if (j + 1 == columns)
4258ae5c7d9Smrg                    printf("%s", font_list[index].name);
426e6232409Smrg                else
4278ae5c7d9Smrg                    printf("%-*s", max_width, font_list[index].name);
428e6232409Smrg            }
429e6232409Smrg            printf("\n");
430e6232409Smrg        }
431e6232409Smrg        return;
432e6232409Smrg    }
433e6232409Smrg
4348ae5c7d9Smrg single_column:
4358ae5c7d9Smrg    for (i = 0; i < font_cnt; i++)
436e6232409Smrg        printf("%s\n", font_list[i].name);
437e6232409Smrg}
438e6232409Smrg
4398ae5c7d9Smrgstatic void
4408ae5c7d9Smrgcopy_number(char **pp1, char **pp2, int n1, int n2)
441e6232409Smrg{
442e6232409Smrg    char *p1 = *pp1;
443e6232409Smrg    char *p2 = *pp2;
4448ae5c7d9Smrg    int w;
445e6232409Smrg
446e6232409Smrg    sprintf(p1, "%d", n1);
447e6232409Smrg    sprintf(p2, "%d", n2);
448e6232409Smrg    w = MAX(strlen(p1), strlen(p2));
449e6232409Smrg    sprintf(p1, "%*d", w, n1);
450e6232409Smrg    sprintf(p2, "%*d", w, n2);
451e6232409Smrg    p1 += strlen(p1);
452e6232409Smrg    p2 += strlen(p2);
453e6232409Smrg    *pp1 = p1;
454e6232409Smrg    *pp2 = p2;
455e6232409Smrg}
456e6232409Smrg
457e6232409Smrg/* ARGSUSED */
4588ae5c7d9Smrgstatic int
4598ae5c7d9SmrgIgnoreError(Display * disp, XErrorEvent *event)
460e6232409Smrg{
461e6232409Smrg    return 0;
462e6232409Smrg}
463e6232409Smrg
46494a251fdSmrgstatic const char *bounds_metrics_title =
4658ae5c7d9Smrg    "width left  right  asc  desc   attr   keysym\n";
466e6232409Smrg
467e6232409Smrg#define PrintBounds(_what,_ptr) \
468e6232409Smrg{   register XCharStruct *p = (_ptr); \
4698fff3f40Smrg    printf ("\t%3s\t\t%4d  %4d  %4d  %4d  %4d  0x%04x\n", \
4708fff3f40Smrg          (_what), p->width, p->lbearing, \
471e6232409Smrg          p->rbearing, p->ascent, p->descent, p->attributes); }
472e6232409Smrg
4738ae5c7d9Smrgstatic const char *stringValued[] = {   /* values are atoms */
474e6232409Smrg    /* font name components (see section 3.2 of the XLFD) */
475e6232409Smrg    "FOUNDRY",
476e6232409Smrg    "FAMILY_NAME",
477e6232409Smrg    "WEIGHT_NAME",
478e6232409Smrg    "SLANT",
479e6232409Smrg    "SETWIDTH_NAME",
480e6232409Smrg    "ADD_STYLE_NAME",
481e6232409Smrg    "SPACING",
482e6232409Smrg    "CHARSET_REGISTRY",
483e6232409Smrg    "CHARSET_ENCODING",
484e6232409Smrg
485e6232409Smrg    /* other standard X font properties (see section 3.2 of the XLFD) */
486e6232409Smrg    "FONT",
487e6232409Smrg    "FACE_NAME",
4888ae5c7d9Smrg    "FULL_NAME",                /* deprecated */
489e6232409Smrg    "COPYRIGHT",
490e6232409Smrg    "NOTICE",
491e6232409Smrg    "FONT_TYPE",
492e6232409Smrg    "FONT_VERSION",
493e6232409Smrg    "RASTERIZER_NAME",
494e6232409Smrg    "RASTERIZER_VERSION",
495e6232409Smrg
496e6232409Smrg    /* other registered font properties (see the X.org Registry, sec. 15) */
497e6232409Smrg    "_ADOBE_POSTSCRIPT_FONTNAME",
4988ae5c7d9Smrg
499e6232409Smrg    /* unregistered font properties */
500e6232409Smrg    "CHARSET_COLLECTIONS",
501e6232409Smrg    "CLASSIFICATION",
502e6232409Smrg    "DEVICE_FONT_NAME",
503e6232409Smrg    "FONTNAME_REGISTRY",
504e6232409Smrg    "MONOSPACED",
505e6232409Smrg    "QUALITY",
506e6232409Smrg    "RELATIVE_SET",
507e6232409Smrg    "STYLE",
5088ae5c7d9Smrg    NULL
5098ae5c7d9Smrg};
510e6232409Smrg
5118ae5c7d9Smrgstatic void
5128ae5c7d9SmrgPrintProperty(XFontProp * prop)
513e6232409Smrg{
514e6232409Smrg    char *atom, *value;
515e6232409Smrg    char nosuch[40];
516e6232409Smrg    int i;
517e6232409Smrg    XErrorHandler oldhandler = XSetErrorHandler(IgnoreError);
518e6232409Smrg
519e6232409Smrg    atom = XGetAtomName(dpy, prop->name);
520e6232409Smrg    if (!atom) {
5218ae5c7d9Smrg        snprintf(nosuch, sizeof(nosuch), "No such atom = %ld", prop->name);
522e6232409Smrg        atom = nosuch;
523e6232409Smrg    }
5248ae5c7d9Smrg    printf("      %s", atom);
525e6232409Smrg
526e6232409Smrg    /* Pad out to a column width of 22, but ensure there is always at
527e6232409Smrg       least one space between property name & value. */
5288ae5c7d9Smrg    for (i = strlen(atom); i < 21; i++)
5298ae5c7d9Smrg        putchar(' ');
530e6232409Smrg    putchar(' ');
531e6232409Smrg
5328ae5c7d9Smrg    for (i = 0;; i++) {
533e6232409Smrg        if (stringValued[i] == NULL) {
5348ae5c7d9Smrg            printf("%ld\n", prop->card32);
535e6232409Smrg            break;
536e6232409Smrg        }
537e6232409Smrg        if (strcmp(stringValued[i], atom) == 0) {
538e6232409Smrg            value = XGetAtomName(dpy, prop->card32);
539e6232409Smrg            if (value == NULL)
5408ae5c7d9Smrg                printf("%ld (expected string value)\n", prop->card32);
541e6232409Smrg            else {
5428ae5c7d9Smrg                printf("%s\n", value);
5438ae5c7d9Smrg                XFree(value);
544e6232409Smrg            }
545e6232409Smrg            break;
546e6232409Smrg        }
5478ae5c7d9Smrg    }
5488ae5c7d9Smrg    if (atom != nosuch)
5498ae5c7d9Smrg        XFree(atom);
5508ae5c7d9Smrg    XSetErrorHandler(oldhandler);
551e6232409Smrg}
552e6232409Smrg
553e6232409Smrgstatic void
554e6232409SmrgComputeFontType(XFontStruct *fs)
555e6232409Smrg{
556e6232409Smrg    Bool char_cell = True;
55794a251fdSmrg    const char *reason = NULL;
558e6232409Smrg    XCharStruct *cs;
5598ae5c7d9Smrg    Atom awatom = XInternAtom(dpy, "AVERAGE_WIDTH", False);
560e6232409Smrg
5618ae5c7d9Smrg    printf("  font type:\t\t");
562e6232409Smrg    if (fs->min_bounds.width != fs->max_bounds.width) {
5638ae5c7d9Smrg        printf("Proportional (min and max widths not equal)\n");
564e6232409Smrg        return;
565e6232409Smrg    }
566e6232409Smrg
567e6232409Smrg    if (awatom) {
5688ae5c7d9Smrg        for (int i = 0; i < fs->n_properties; i++) {
569e6232409Smrg            if (fs->properties[i].name == awatom &&
570e6232409Smrg                (fs->max_bounds.width * 10) != fs->properties[i].card32) {
571e6232409Smrg                char_cell = False;
572e6232409Smrg                reason = "font width not equal to AVERAGE_WIDTH";
573e6232409Smrg                break;
574e6232409Smrg            }
575e6232409Smrg        }
576e6232409Smrg    }
577e6232409Smrg
578e6232409Smrg    if (fs->per_char) {
5798ae5c7d9Smrg        unsigned int i;
580e6232409Smrg        for (i = fs->min_char_or_byte2, cs = fs->per_char;
581e6232409Smrg             i <= fs->max_char_or_byte2; i++, cs++) {
5828ae5c7d9Smrg            if (cs->width == 0)
5838ae5c7d9Smrg                continue;
584e6232409Smrg            if (cs->width != fs->max_bounds.width) {
585e6232409Smrg                /* this shouldn't happen since we checked above */
5868ae5c7d9Smrg                printf("Proportional (characters not all the same width)\n");
587e6232409Smrg                return;
588e6232409Smrg            }
589e6232409Smrg            if (char_cell) {
590e6232409Smrg                if (cs->width < 0) {
591e6232409Smrg                    if (!(cs->width <= cs->lbearing &&
592e6232409Smrg                          cs->lbearing <= cs->rbearing &&
593e6232409Smrg                          cs->rbearing <= 0)) {
594e6232409Smrg                        char_cell = False;
595e6232409Smrg                        reason = "ink outside bounding box";
596e6232409Smrg                    }
5978ae5c7d9Smrg                }
5988ae5c7d9Smrg                else {
599e6232409Smrg                    if (!(0 <= cs->lbearing &&
600e6232409Smrg                          cs->lbearing <= cs->rbearing &&
601e6232409Smrg                          cs->rbearing <= cs->width)) {
602e6232409Smrg                        char_cell = False;
603e6232409Smrg                        reason = "ink outside bounding box";
604e6232409Smrg                    }
605e6232409Smrg                }
606e6232409Smrg                if (!(cs->ascent <= fs->ascent &&
607e6232409Smrg                      cs->descent <= fs->descent)) {
6088ae5c7d9Smrg                    char_cell = False;
609e6232409Smrg                    reason = "characters not all same ascent or descent";
610e6232409Smrg                }
611e6232409Smrg            }
612e6232409Smrg        }
613e6232409Smrg    }
614e6232409Smrg
6158ae5c7d9Smrg    printf("%s", char_cell ? "Character Cell" : "Monospaced");
6168ae5c7d9Smrg    if (reason)
6178ae5c7d9Smrg        printf(" (%s)", reason);
6188ae5c7d9Smrg    printf("\n");
6198ae5c7d9Smrg
620e6232409Smrg    return;
621e6232409Smrg}
622e6232409Smrg
623e6232409Smrgstatic void
624e6232409Smrgprint_character_metrics(register XFontStruct *info)
625e6232409Smrg{
626e6232409Smrg    register XCharStruct *pc = info->per_char;
6278ae5c7d9Smrg    unsigned int i, j;
628e6232409Smrg    unsigned n, saven;
629e6232409Smrg
6308ae5c7d9Smrg    printf("  character metrics:\n");
631e6232409Smrg    saven = ((info->min_byte1 << 8) | info->min_char_or_byte2);
632e6232409Smrg    for (j = info->min_byte1; j <= info->max_byte1; j++) {
633e6232409Smrg        n = saven;
634e6232409Smrg        for (i = info->min_char_or_byte2; i <= info->max_char_or_byte2; i++) {
6358ae5c7d9Smrg            char *s = XKeysymToString((KeySym) n);
6368ae5c7d9Smrg
6378ae5c7d9Smrg            printf("\t0x%02x%02x (%u)\t%4d  %4d  %4d  %4d  %4d  0x%04x  %s\n",
6388ae5c7d9Smrg                   j, i, n, pc->width, pc->lbearing,
6398ae5c7d9Smrg                   pc->rbearing, pc->ascent, pc->descent, pc->attributes,
6408ae5c7d9Smrg                   s ? s : ".");
641e6232409Smrg            pc++;
642e6232409Smrg            n++;
643e6232409Smrg        }
644e6232409Smrg        saven += 256;
645e6232409Smrg    }
646e6232409Smrg}
647e6232409Smrg
6488ae5c7d9Smrgstatic void
649e2515bd3Smrgdo_query_font(Display *display, const char *name)
650e6232409Smrg{
651e6232409Smrg    register int i;
6528ae5c7d9Smrg    register XFontStruct *info = XLoadQueryFont(display, name);
653e6232409Smrg
654e6232409Smrg    if (!info) {
6558ae5c7d9Smrg        fprintf(stderr, "%s:  unable to get info about font \"%s\"\n",
6568ae5c7d9Smrg                program_name, name);
657e6232409Smrg        return;
658e6232409Smrg    }
6598ae5c7d9Smrg    printf("name:  %s\n", name ? name : "(nil)");
6608ae5c7d9Smrg    printf("  direction:\t\t%s\n", ((info->direction == FontLeftToRight)
6618ae5c7d9Smrg                                    ? "left to right" : "right to left"));
6628ae5c7d9Smrg    printf("  indexing:\t\t%s\n",
6638ae5c7d9Smrg           ((info->min_byte1 == 0 && info->max_byte1 == 0)
6648ae5c7d9Smrg            ? "linear" : "matrix"));
6658ae5c7d9Smrg    printf("  rows:\t\t\t0x%02x thru 0x%02x (%d thru %d)\n",
6668ae5c7d9Smrg           info->min_byte1, info->max_byte1,
6678ae5c7d9Smrg           info->min_byte1, info->max_byte1);
6688ae5c7d9Smrg    printf("  columns:\t\t0x%02x thru 0x%02x (%d thru %d)\n",
6698ae5c7d9Smrg           info->min_char_or_byte2, info->max_char_or_byte2,
6708ae5c7d9Smrg           info->min_char_or_byte2, info->max_char_or_byte2);
6718ae5c7d9Smrg    printf("  all chars exist:\t%s\n",
6728ae5c7d9Smrg           (info->all_chars_exist) ? "yes" : "no");
6738ae5c7d9Smrg    printf("  default char:\t\t0x%04x (%d)\n",
6748ae5c7d9Smrg           info->default_char, info->default_char);
6758ae5c7d9Smrg    printf("  ascent:\t\t%d\n", info->ascent);
6768ae5c7d9Smrg    printf("  descent:\t\t%d\n", info->descent);
6778ae5c7d9Smrg    ComputeFontType(info);
6788ae5c7d9Smrg    printf("  bounds:\t\t%s", bounds_metrics_title);
6798ae5c7d9Smrg    PrintBounds("min", &info->min_bounds);
6808ae5c7d9Smrg    PrintBounds("max", &info->max_bounds);
6818ae5c7d9Smrg    if (info->per_char && long_list >= L_VERYLONG)
6828ae5c7d9Smrg        print_character_metrics(info);
6838ae5c7d9Smrg    printf("  properties:\t\t%d\n", info->n_properties);
684e6232409Smrg    for (i = 0; i < info->n_properties; i++)
6858ae5c7d9Smrg        PrintProperty(&info->properties[i]);
6868ae5c7d9Smrg    printf("\n");
687e6232409Smrg
6888ae5c7d9Smrg    XFreeFontInfo(NULL, info, 1);
689e6232409Smrg}
690