/* Dan Heller <argv@sun.com>, based on a design by Tim O'Reilly
 *
 * xshowfonts.c -
 *   Displays a set of fonts specified on the command line, from
 * a pipe, or typed into stdin.  Fonts can be specified as specific
 * or wildcard character strings.  A pixmap is created to
 * display all the fonts.  This is done by using the pixmap as the
 * pixmap image for a label widget.  Each font prints its own name
 * in its own font style -- the -phrase option prints the phrase 
 * instead.
 *
 * All fonts are loaded first and scanned to determine the total
 * width and height of the pixmap first.  Then the fonts are 
 * reopened again to actually render the fonts into the pixmap.  
 * All this could be avoided by using XListFontsWithInfo() 
 * rather than XListFonts() but since the list is potentially 
 * very large, I didn't want to overload the server and client 
 * with all those fonts + a very large pixmap.
 *
 * Usage: xshowfonts
 *   -s sorts the fonts in alphabetical order before displaying 
        them.
 *   -v verbose mode for when input is redirected to stdin.
 *   -w width of viewport window
 *   -h height of viewport window
 *   -fg foreground_color
 *   -bg background_color
 *   -phrase "text string" (otherwise, name of font is used)
 *   -indicates to read from stdin.  Piping doesn't require 
      the '-' argument.  With no arguments, xshowfonts reads 
 *    from stdin anyway.
 *
 * Neat ways to use the program:
 *  xshowfonts -fg green -bg black "*adobe*"
 *  xshowfonts -sort "*"
 *  xshowfonts -phrase "The quick brown fox jumps over the lazy 
 *              dog" "*times*"
 *  xlsfonts | xshowfonts -sort
 *  xshowfonts "*helvetica*"
 *
 * compile: (triple click and paste next line)
     cc -O -s xshowfonts.c -lXaw -lXt -lXmu -lX11 -o xshowfonts
 */

#include <stdio.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Viewport.h>

struct _resrcs {
    int sort;
    int verbose;
    Pixel fg, bg;
    char *phrase;
    int view_width, view_height;
} Resrcs;

static XtResource resources[] = {
    { "sort", "Sort", XtRBoolean, sizeof (int),
	XtOffsetOf(struct _resrcs,sort), XtRImmediate, 
        False },
    { "verbose", "Verbose", XtRBoolean, sizeof (int),
	XtOffsetOf(struct _resrcs,verbose), XtRImmediate, 
        False },
    { "foreground", "Foreground", XtRPixel, sizeof (Pixel),
	XtOffsetOf(struct _resrcs,fg), XtRString, 
        XtDefaultForeground },
    { "background", "Background", XtRPixel, sizeof (Pixel),
	XtOffsetOf(struct _resrcs,bg), XtRString, 
        XtDefaultBackground },
    { "phrase", "Phrase", XtRString, sizeof (String),
	XtOffsetOf(struct _resrcs,phrase), XtRImmediate, NULL },
    { "view-width", "View-width", XtRInt, sizeof (int),
	XtOffsetOf(struct _resrcs,view_width), XtRImmediate, 
        (char *)500 },
    { "view-height", "View-height", XtRInt, sizeof (int),
	XtOffsetOf(struct _resrcs,view_height), XtRImmediate, 
        (char *)300 },
};

static XrmOptionDescRec options[] = {
    { "-sort", "sort", XrmoptionNoArg, "True" },
    { "-v", "verbose", XrmoptionNoArg, "True" },
    { "-fg", "foreground", XrmoptionSepArg, NULL },
    { "-bg", "background", XrmoptionSepArg, NULL },
    { "-phrase", "phrase", XrmoptionSepArg, NULL },
    { "-w", "view-width", XrmoptionSepArg, NULL },
    { "-h", "view-height", XrmoptionSepArg, NULL },
};

/* sort font according to these parameters.
 * font specs we're interested in:
 *    -fndry-fmly-wght-slant-*swdth-*adstyl-*pxlsz-ptsz- ....
 * foundry -- sort by foundry first; similar ones are always 
 *            grouped together
 * weight -- medium, demi-bold, bold
 * slant -- roman, italic/oblique, reverse italic/oblique 
 *          (i or o, r, ri, ro)
 * ptsize -- increase numerical order
 */
font_cmp(f1, f2)
char **f1, **f2;
{
    char fndry1[16], fmly1[64], wght1[32], slant1[3];
    char fndry2[16], fmly2[64], wght2[32], slant2[3];
    int n, m, ptsize1, ptsize2;
    char *font_fmt_str = "-%[^-]-%[^-]-%[^-]-%[^-]-%*[^0-9]%*d-%d-";
    n = sscanf(*f1, font_fmt_str, fndry1, fmly1, wght1, slant1, 
               &ptsize1);
    m = sscanf(*f2, font_fmt_str, fndry2, fmly2, wght2, slant2, 
               &ptsize2);
    if (m < 5 || n < 5)
	/* font not in correct format -- just return font names 
	 * in order */
	return strcmp(*f1, *f2);
    if (n = strcmp(fndry1, fndry2))
	return n; /* different foundries -- return alphabetical 
                   * order */
    if (n = strcmp(fmly1, fmly2))
	return n; /* different families -- return alphabetical 
                   * order */
    if (n = strcmp(wght1, wght2))
	return -n; /* weight happens to be correct in reverse 
                    * alpha order */
    if (n = strcmp(slant1, slant2))
	return n; /* slants happen to be correct in alphabetical 
                   * order */
    /* sort according to point size */
    return ptsize1 - ptsize2;
}

main(argc, argv)
int argc;
char *argv[];
{
    Widget topLevel, vp;
    char **list = (char **)NULL, **tmp;
    char buf[128];
    extern char **XListFonts();
    extern int strcmp();
    XFontStruct *font;
    Pixmap pixmap;
    GC gc;
    Display *dpy;
    int istty = isatty(0), redirect = !istty, i, j, total = 0;
    unsigned int w, width = 0, height = 0;

    topLevel = XtInitialize(argv[0], argv[0], options, 
        XtNumber(options), &argc, argv);
    dpy = XtDisplay(topLevel);

    XtGetApplicationResources(topLevel, &Resrcs,
	resources, XtNumber(resources), NULL, 0);

    if (!argv[1] || !strcmp(argv[1], "-")) {
	printf("Loading fonts from input. ");
	if (istty) {
	    puts("End with EOF or .");
	    redirect++;
	} else
	    puts("Use -v to view font names being loaded.");
    } else if (!istty && strcmp(argv[1], "-"))
	printf("%s: either use pipes or specify font names -- not both.\n",
	    argv[0]), exit(1);
    while (*++argv || redirect) {
	if (!redirect)
	    if (!strcmp(*argv, "-"))
		redirect++;
	    else
		strcpy(buf, *argv);
	if (redirect) {
	    if (istty)
		printf("Fontname: "), fflush(stdout);
	    if (!fgets(buf, sizeof buf, stdin) || 
                !strcmp(buf, ".\n"))
		break;
	    buf[strlen(buf)-1] = 0;
	}
	if (!buf[0])
	    continue;
	if (istty || Resrcs.verbose)
	    printf("Loading \"%s\"...", buf), fflush(stdout);
	tmp = XListFonts(dpy, buf, 32767, &i);
	if (i == 0) {
	    printf("couldn't load font ");
	    if (!istty && !Resrcs.verbose)
		printf("\"%s\"", buf);
	    putchar('\n');
	    continue;
	}
	if (istty || Resrcs.verbose)
	    printf("%d font%s\n", i, i == 1? "" : "s");
	if (!list) {
	    list = tmp;
	    total = i;
	} else {
	    i += total;
	    if (!(list = (char **)XtRealloc(list, i * 
                     sizeof (char *))))
		XtError("Not enough memory for font names");
	    for (j = 0; total < i; j++, total++)
		list[total] = tmp[j];
	}
    }
    if (total == 0)
	puts("No fonts?!"), exit(1);
    printf("Total fonts loaded: %d\n", total);
    if (Resrcs.sort) {
	printf("Sorting fonts..."), fflush(stdout);
	qsort(list, total, sizeof (char *), font_cmp);
	putchar('\n');
    }
    /* calculate size for pixmap by getting the dimensions 
     * of each font */
    puts("Calculating sizes for pixmap.");
    for (i = 0; i < total; i++) {
	if (!(font = XLoadQueryFont(dpy, list[i]))) {
	    printf("Can't load font: %s\n", list[i]);
	    continue;
	}
	if ((w = XTextWidth(font, list[i], 
	     strlen(list[i]))) > width)
	    width = w;
	height += font->ascent + font->descent;
	XFreeFont(dpy, font);
    }
    width += 6;
    height += 6;
    /* Create pixmap + GC */
    printf("Creating pixmap of size %dx%d\n", width, height);
    if (!(pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy),
	width, height, DefaultDepth(dpy, DefaultScreen(dpy)))))
	XtError("Can't Create pixmap");
    if (!(gc = XCreateGC(dpy, pixmap, NULL, 0)))
	XtError("Can't create gc");
    XSetForeground(dpy, gc, Resrcs.bg);
    XFillRectangle(dpy, pixmap, gc, 0, 0, width, height);
    XSetForeground(dpy, gc, Resrcs.fg);
    XSetBackground(dpy, gc, Resrcs.bg);
    height = 0;
    for (i = 0; i < total; i++) {
	if (!(font = XLoadQueryFont(dpy, list[i])))
	    continue; /* it's already been reported */
	XSetFont(dpy, gc, font->fid);
	height += font->ascent;
	if (Resrcs.phrase)
	    XDrawString(dpy, pixmap, gc, 0, height,
		Resrcs.phrase, strlen(Resrcs.phrase));
	else
	    XDrawString(dpy, pixmap, gc, 5, height, list[i], 
                   strlen(list[i]));
	height += font->descent;
	XFreeFont(dpy, font);
    }
    vp = XtVaCreateManagedWidget("viewport", viewportWidgetClass, 
         topLevel,
	XtNallowHoriz,	True,
	XtNallowVert,	True,
	XtNwidth,	Resrcs.view_width,
	XtNheight,	Resrcs.view_height,
	NULL);
    XtVaCreateManagedWidget("_foo", labelWidgetClass, vp,
	XtNbitmap,	pixmap,
	NULL);

    if (!redirect)
	XFreeFontNames(list);

    XtRealizeWidget(topLevel);
    XtMainLoop();
}
