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