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