diff options
author | Steffen Winterfeldt <wfeldt@opensuse.org> | 2004-03-15 08:51:06 +0000 |
---|---|---|
committer | Steffen Winterfeldt <wfeldt@opensuse.org> | 2004-03-15 08:51:06 +0000 |
commit | 0de4ac28074290e470c2b55e26a0f4cdf232db5c (patch) | |
tree | bfecc1aabb297695e8377451f0944c43d45fa6c3 /getx11font.c |
- created repository
Diffstat (limited to 'getx11font.c')
-rw-r--r-- | getx11font.c | 712 |
1 files changed, 712 insertions, 0 deletions
diff --git a/getx11font.c b/getx11font.c new file mode 100644 index 0000000..b0d4116 --- /dev/null +++ b/getx11font.c @@ -0,0 +1,712 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <getopt.h> +#include <iconv.h> +#include <errno.h> +#include <inttypes.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <X11/X.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +#define MAGIC 0xd2828e07 + +struct option options[] = { + { "verbose", 0, NULL, 'v' }, + { "font", 1, NULL, 'f' }, + { "add", 1, NULL, 'a' }, + { "add-text", 1, NULL, 't' }, + { "add-charset", 1, NULL, 'c' }, + { "line-height", 1, NULL, 'l' }, + { "prop", 1, NULL, 'p' }, + { "test", 0, NULL, 999 }, + { } +}; + +typedef struct { + unsigned size; + unsigned char *data; + unsigned real_size; +} file_data_t; + +typedef struct { + uint32_t magic; + uint16_t entries; + uint8_t height; + uint8_t line_height; +} font_header_t; + +typedef struct { + uint16_t ofs; + uint16_t c; + uint32_t size; +} char_header_t; + +typedef struct { + char *name; + XFontStruct *x; + unsigned used:1; /* font actually used */ +} font_t; + +typedef struct char_data_s { + struct char_data_s* next; + unsigned ok:1; /* char exists */ + int c; /* char (utf32) */ + int index; /* array index for font */ + font_t *font; /* pointer to font */ + int width; /* char width */ + int height; /* char (actually font) height */ + unsigned char *bitmap; /* char bitmap, width x height */ + int x_ofs; + int y_ofs; + int real_width; + int real_height; + char_header_t head; +} char_data_t; + +int opt_verbose = 0; +char *opt_file; +int opt_test = 0; +int opt_line_height = 0; +int opt_prop = 0; +int opt_spacing = 0; +int opt_space_width = 0; + +file_data_t font = {}; + +font_t font_list[16]; +int fonts; + +char_data_t *char_list; + +static char_data_t *add_char(int c); +static char_data_t *find_char(int c); +static void dump_char(char_data_t *cd); +static void dump_char_list(void); +static void sort_char_list(void); +static int char_sort(const void *a, const void *b); +static void locate_char(char_data_t *cd); +static int char_index(XFontStruct *xfont, int c); +static int empty_row(char_data_t *cd, int row); +static int empty_column(char_data_t *cd, int column); +static void add_bbox(char_data_t *cd); +static void make_prop(char_data_t *cd); +static void encode_chars(font_header_t *fh); +static void add_data(file_data_t *d, void *buffer, unsigned size); +static void write_data(char *name); + +int main(int argc, char **argv) +{ + Display *display; + XGCValues gcv; + GC gc1, gc2; + Pixmap pixmap; + XImage *xi; + XChar2b xc; + int i, j, k, font_width, font_height; + char *str, *str1; + char_data_t *cd; + iconv_t ic = (iconv_t) -1, ic2; + char obuf[4], ibuf[6]; + char obuf2[4*0x100], ibuf2[0x100]; + char *obuf_ptr, *ibuf_ptr; + size_t obuf_left, ibuf_left; + FILE *f; + font_header_t fh; + unsigned char uc; + + opterr = 0; + + while((i = getopt_long(argc, argv, "a:f:c:l:p:t:v", options, NULL)) != -1) { + switch(i) { + case 'f': + if(fonts < sizeof font_list / sizeof *font_list) { + font_list[fonts++].name = optarg; + } + break; + + case 'a': + str = optarg; + if(sscanf(str, "%i - %i%n", &i, &j, &k) == 2 && k == strlen(str)) { + if(i < 0 || j < 0 || j < i || j - i >= 0x10000) { + fprintf(stderr, "invalid char range spec: %s\n", str); + return 1; + } + while(i <= j) add_char(i++); + } + else { + i = strtol(str, &str1, 0); + if(*str1 || i < 0) { + fprintf(stderr, "invalid char number: %s\n", str); + return 1; + } + add_char(i); + } + break; + + case 'l': + str = optarg; + i = strtol(str, &str1, 0); + if(*str1 || i < 0) { + fprintf(stderr, "invalid line height: %s\n", str); + return 1; + } + opt_line_height = i; + break; + + case 'p': + str = optarg; + if(sscanf(str, "%i , %i%n", &i, &j, &k) == 2 && k == strlen(str)) { + opt_prop = 1; + opt_spacing = i; + opt_space_width = j; + } + else { + fprintf(stderr, "invalid spec: %s\n", str); + return 1; + } + break; + + case 'c': + ic2 = iconv_open("utf32le", optarg); + if(ic2 == (iconv_t) -1) { + fprintf(stderr, "don't know char set %s\ntry 'iconv --list'\n", optarg); + return 1; + } + ibuf_ptr = ibuf2; + ibuf_left = sizeof ibuf2; + obuf_ptr = obuf2; + obuf_left = sizeof obuf2; + for(j = 0; j < sizeof ibuf2; j++) ibuf2[j] = j; + iconv(ic2, &ibuf_ptr, &ibuf_left, &obuf_ptr, &obuf_left); + for(str = obuf2; str < obuf_ptr; str += 4) { + i = *(int *) str; + if(i >= 0x20) add_char(i); + } + iconv_close(ic2); + break; + + case 't': + if(ic == (iconv_t) -1) { + ic = iconv_open("utf32le", "utf8"); + if(ic == (iconv_t) -1) { + fprintf(stderr, "can't convert utf8 data\n"); + return 1; + } + } + if((f = fopen(optarg, "r"))) { + int ok; + + ibuf_left = 0; + while((i = fread(ibuf + ibuf_left, 1, sizeof ibuf - ibuf_left, f)) > 0) { + // fprintf(stderr, "ibuf_left = %d, fread = %d\n", ibuf_left, i); + ibuf_ptr = ibuf; + ibuf_left += i; + do { + obuf_ptr = obuf; + obuf_left = sizeof obuf; + k = iconv(ic, &ibuf_ptr, &ibuf_left, &obuf_ptr, &obuf_left); + // fprintf(stderr, "k = %d, errno = %d, ibuf_left = %d, obuf_left = %d\n", k, errno, ibuf_left, obuf_left); + if(k >= 0 || (k == -1 && errno == E2BIG)) { + ok = 1; + if(!obuf_left) { + i = *(int *) obuf; + if(i >= 0x20) { + // fprintf(stderr, "add char 0x%x\n", i); + add_char(i); + } + } + } + else { + ok = 0; + } + } + while(ok && ibuf_left); + if(k == -1 && errno == EILSEQ) { + perror("iconv"); + return 1; + } + if(ibuf_left) { + memcpy(ibuf, ibuf + sizeof ibuf - ibuf_left, ibuf_left); + } + } + fclose(f); + } + else { + perror(optarg); + return 1; + } + break; + + case 'v': + opt_verbose++; + break; + + case 999: + opt_test++; + break; + } + } + + argc -= optind; argv += optind; + + if(argc != 1) { + fprintf(stderr, + "Usage: getx11font [options] fontfile\n" + "Build font for boot loader using X11 fonts.\n" + " -a, --add=first[-last]\n\tAdd chars from this range.\n" + " -c, --add-charset=charset\n\tAdd all chars from this charset.\n" + " -f, --font=X11_font_spec\n\tUse this font.\n" + " -h, --help\n\tShow this help text.\n" + " -l, --line-height=n\n\tSet line height (default: font height).\n" + " -p, --prop=n1,n2\n\tFake proportionally spaced Font.\n\tn1: spacing between chars; n2: space (char U+0020) width\n" + " -t, --add-text=samplefile\n\tAdd all chars used in this file. File must be UTF-8 encoded.\n" + " -v, --verbose\n\tDump font info.\n" + ); + return 1; + } + + opt_file = argv[0]; + + if(ic != (iconv_t) -1) iconv_close(ic); + + /* use default char list */ + if(!char_list) for(i = 0x20; i <= 0x7f; i++) add_char(i); + + /* default font */ + if(!fonts) font_list[fonts++].name = "fixed"; + + sort_char_list(); + + if(!(display = XOpenDisplay(getenv("DISPLAY")))) { + return fprintf(stderr, "unable to open display\n"), 2; + } + + /* open all fonts */ + for(i = 0; i < fonts; i++) { + if(!(font_list[i].x = XLoadQueryFont(display, font_list[i].name))) { + fprintf(stderr, "Warning: no such font: %s\n", font_list[i].name); + } + } + + /* look for chars in fonts */ + for(cd = char_list; cd; cd = cd->next) locate_char(cd); + + /* get font heigth */ + for(font_height = 0, i = 0; i < fonts; i++) { + if(font_list[i].used) { + j = font_list[i].x->max_bounds.ascent + font_list[i].x->max_bounds.descent; + if(j > font_height) font_height = j; + } + } + + /* get font width */ + for(font_width = 0, cd = char_list; cd; cd = cd->next) { + if(cd->width > font_width) font_width = cd->width; + /* char height = font height */ + cd->height = font_height; + } + + printf("Font Size: %d x %d", font_width, font_height); + if(opt_line_height && opt_line_height != font_height) { + printf(", Line Height: %d", opt_line_height); + } + printf("\n"); + + if(font_width > 32 || font_height > 32) { + fprintf(stderr, "Font size too large (max 32 x 32).\n"); + return 8; + } + + if(!font_width || !font_height) { + fprintf(stderr, "Strange font.\n"); + return 8; + } + + /* now, render all chars */ + + pixmap = XCreatePixmap(display, DefaultRootWindow(display), font_width, font_height, 1); + + gcv.background = gcv.foreground = 0; + gcv.fill_style = FillSolid; + + gc1 = XCreateGC(display, pixmap, GCForeground | GCBackground | GCFillStyle, &gcv); + gcv.foreground = 1; + gc2 = XCreateGC(display, pixmap, GCForeground | GCBackground | GCFillStyle, &gcv); + + for(cd = char_list; cd; cd = cd->next) { + if(!cd->ok) continue; + xc.byte1 = cd->c >> 8; + xc.byte2 = cd->c; + + XSetFont(display, gc2, cd->font->x->fid); + + XFillRectangle(display, pixmap, gc1, 0, 0, font_width, font_height); + XDrawImageString16(display, pixmap, gc2, 0, cd->font->x->max_bounds.ascent, &xc, 1); + + xi = XGetImage(display, pixmap, 0, 0, font_width, font_height, 1, XYPixmap); + + cd->bitmap = calloc(cd->height * cd->width, 1); + for(k = 0, j = 0; j < cd->height; j++) { + for(i = 0; i < cd->width; i++) { + cd->bitmap[k++] = XGetPixel(xi, i, j); + } + } + + XDestroyImage(xi); + } + + XFreePixmap(display, pixmap); + XFreeGC(display, gc2); + XFreeGC(display, gc1); + + XCloseDisplay(display); + + for(cd = char_list; cd; cd = cd->next) add_bbox(cd); + + if(opt_prop) { + for(cd = char_list; cd; cd = cd->next) make_prop(cd); + } + + encode_chars(&fh); + + for(i = j = 0, cd = char_list; cd; cd = cd->next) { + if(!cd->ok) { + printf(i ? ", " : "Missing Chars: "); + printf("0x%04x", cd->c); + i = 1; + } + } + if(i) printf("\n"); + + add_data(&font, &fh, sizeof fh); + for(cd = char_list; cd; cd = cd->next) { + if(!cd->ok) continue; + add_data(&font, &cd->head, sizeof cd->head); + } + + i = 0; + for(cd = char_list; cd; cd = cd->next) { + if(!cd->ok) continue; + k = cd->real_width * cd->real_height; + for(uc = 0, i = j = 0; i < k; i++, j++) { + if(j == 8) { + add_data(&font, &uc, 1); + uc = 0; + j = 0; + } + if(cd->bitmap[i]) uc += (1 << j); + } + if(j) add_data(&font, &uc, 1); + } + + if(opt_verbose) dump_char_list(); + + write_data(opt_file); + + return 0; +} + + +char_data_t *add_char(int c) +{ + char_data_t *cd; + + if((cd = find_char(c))) return cd; + + cd = calloc(1, sizeof *cd); + cd->c = c; + cd->next = char_list; + + return char_list = cd; +} + + +char_data_t *find_char(int c) +{ + char_data_t *cd; + + for(cd = char_list; cd; cd = cd->next) { + if(cd->c == c) return cd; + } + + return NULL; +} + + +void dump_char(char_data_t *cd) +{ + int i, j; + unsigned char *p; + + if(!cd || !cd->ok) return; + + printf( + "Char 0x%04x\n Font: %s\n Size: %d x %d\n", + cd->c, cd->font->name, cd->width, cd->height + ); + + if(cd->bitmap) { + printf( + " Bitmap: %d x %d\n Offset: %d x %d\n", + cd->real_width, cd->real_height, cd->x_ofs, cd->y_ofs + ); + p = cd->bitmap; + for(j = 0; j < cd->height; j++) { + printf(" |"); + if(j < cd->y_ofs || j >= cd->y_ofs + cd->real_height) { + for(i = 0; i < cd->width; i++) printf("."); + } + else { + for(i = 0; i < cd->width; i++) { + if(i < cd->x_ofs || i >= cd->x_ofs + cd->real_width) { + printf("."); + } + else { + printf("%c", *p++ ? '#' : ' '); + } + } + } + printf("|\n"); + } + } + +} + + +void dump_char_list() +{ + char_data_t *cd; + + for(cd = char_list; cd; cd = cd->next) { + dump_char(cd); + } +} + + +void sort_char_list() +{ + char_data_t *cd; + unsigned u, len; + char_data_t **c_list; + + for(len = 0, cd = char_list; cd; cd = cd->next) len++; + + if(!len) return; + + c_list = calloc(len + 1, sizeof *c_list); + + for(u = 0, cd = char_list; cd; cd = cd->next, u++) c_list[u] = cd; + + qsort(c_list, len, sizeof *c_list, char_sort); + + for(u = 0; u < len; u++) { + c_list[u]->next = c_list[u + 1]; + } + + char_list = *c_list; + + free(c_list); +} + + +int char_sort(const void *a, const void *b) +{ + return (*(char_data_t **) a)->c - (*(char_data_t **) b)->c; +} + + +void locate_char(char_data_t *cd) +{ + int i, j; + XCharStruct *xc; + + for(i = 0; i < fonts; i++) { + if((j = char_index(font_list[i].x, cd->c)) >= 0) { + xc = font_list[i].x->per_char ? font_list[i].x->per_char + j : &(font_list[i].x->max_bounds); + if(xc && xc->width) { + cd->index = j; + cd->font = font_list + i; + cd->width = xc->width; + cd->ok = 1; + font_list[i].used = 1; + break; + } + } + } +} + + +int char_index(XFontStruct *xfont, int c) +{ + int i; + + if(!xfont || (c & ~0xffff)) return -1; + + if(!xfont->min_byte1 && !xfont->max_byte1) { + i = c - xfont->min_char_or_byte2; + if(i > (int) xfont->max_char_or_byte2) i = -1; + } + else { + i = ((c >> 8) - xfont->min_byte1) + * (xfont->max_char_or_byte2 - xfont->min_char_or_byte2 + 1) + + (c & 0xff) - xfont->min_char_or_byte2; + } + + return i; +} + + +int empty_row(char_data_t *cd, int row) +{ + unsigned char *p1, *p2; + + p2 = (p1 = cd->bitmap + row * cd->real_width) + cd->real_width; + while(p1 < p2) if(*p1++) return 0; + + return 1; +} + + +int empty_column(char_data_t *cd, int col) +{ + int i; + unsigned char *p; + + for(p = cd->bitmap + col, i = 0; i < cd->real_height; i++, p += cd->real_width) { + if(*p) return 0; + } + + return 1; +} + + +void add_bbox(char_data_t *cd) +{ + int i; + unsigned char *p1, *p2; + + if(!cd->ok) return; + + cd->real_width = cd->width; + cd->real_height = cd->height; + + if(opt_test) return; + + while(cd->real_height && empty_row(cd, cd->real_height - 1)) cd->real_height--; + + while(cd->real_height && empty_row(cd, 0)) { + cd->real_height--; + cd->y_ofs++; + memcpy(cd->bitmap, cd->bitmap + cd->real_width, cd->real_width * cd->real_height); + } + + while(cd->real_width && empty_column(cd, cd->real_width - 1)) { + cd->real_width--; + p2 = (p1 = cd->bitmap + cd->real_width) + 1; + for(i = 1; i < cd->real_height; i++, p1 += cd->real_width, p2 += cd->real_width + 1) { + memcpy(p1, p2, cd->real_width); + } + } + + while(cd->real_width && empty_column(cd, 0)) { + cd->real_width--; + cd->x_ofs++; + p2 = (p1 = cd->bitmap) + 1; + for(i = 0; i < cd->real_height; i++, p1 += cd->real_width, p2 += cd->real_width + 1) { + memcpy(p1, p2, cd->real_width); + } + } +} + + +/* + * Fake proprtionally spaced font from fixed size font. + */ +void make_prop(char_data_t *cd) +{ + if(!cd->ok) return; + + cd->x_ofs = opt_spacing; + cd->width = cd->x_ofs + (cd->real_width ?: opt_space_width); +} + + +void encode_chars(font_header_t *fh) +{ + int ofs; + char_data_t *cd; + + memset(fh, 0, sizeof *fh); + fh->magic = MAGIC; + fh->height = char_list->height; + fh->line_height = opt_line_height ?: fh->height; + + ofs = sizeof *fh; + + for(cd = char_list; cd; cd = cd->next) if(cd->ok) fh->entries++; + + ofs += fh->entries * sizeof cd->head; + + for(cd = char_list; cd; cd = cd->next) { + if(!cd->ok) continue; + memset(&cd->head, 0, sizeof cd->head); + cd->head.ofs = ofs; + cd->head.c = cd->c; + cd->head.size = + (((cd->c >> 16) & 0x1f) << 0) + + ((cd->x_ofs & 0x1f) << 5) + + ((cd->y_ofs & 0x1f) << 10) + + ((cd->real_width & 0x1f) << 15) + + ((cd->real_height & 0x1f) << 20) + + ((cd->width & 0x1f) << 25); + ofs += (cd->real_width * cd->real_height + 7) >> 3; + } +} + + +void add_data(file_data_t *d, void *buffer, unsigned size) +{ + if(!size || !d || !buffer) return; + + if(d->size + size > d->real_size) { + d->real_size = d->size + size + 0x1000; + d->data = realloc(d->data, d->real_size); + if(!d->data) d->real_size = 0; + } + + if(d->size + size <= d->real_size) { + memcpy(d->data + d->size, buffer, size); + d->size += size; + } + else { + fprintf(stderr, "Oops, out of memory? Aborted.\n"); + exit(10); + } +} + + +void write_data(char *name) +{ + FILE *f; + + f = strcmp(name, "-") ? fopen(name, "w") : stdout; + + if(!f) { + perror(name); + return; + } + + if(fwrite(font.data, font.size, 1, f) != 1) { + perror(name); exit(3); + } + + fclose(f); +} + + |