#define _GNU_SOURCE /* asprintf */ #include #include #include #include #include #include #include #include #include #include #include #include #include FT_FREETYPE_H #include #define MAGIC 0xd2828e06 // 4 bits seem to be enough #define GRAY_BITS 4 // 3 gave smallest file for about 16x16 pixel fonts, but it doesn't really matter much #define GRAY_BIT_COUNT 3 #define MAX_GRAY ((1 << GRAY_BITS) - 3) #define REP_BLACK (MAX_GRAY + 1) #define REP_WHITE (MAX_GRAY + 2) struct option options[] = { {"add" , 1, NULL, 'a'}, {"add-charset" , 1, NULL, 'c'}, {"font" , 1, NULL, 'f'}, {"line-height" , 1, NULL, 'l'}, {"font-path" , 1, NULL, 'p'}, {"show" , 0, NULL, 's'}, {"add-text" , 1, NULL, 't'}, {"verbose" , 0, NULL, 'v'}, {"test" , 0, NULL, 999}, {} }; typedef struct list_any_s { struct list_any_s *next; } list_any_t; typedef struct { void *start; void *end; } list_t; typedef struct n_set_s { struct n_set_s *next; int first, last; } n_set_t; typedef struct { unsigned size; unsigned char *data; unsigned real_size; } file_data_t; typedef struct __attribute((packed)) { uint32_t magic; uint32_t entries; int8_t height; int8_t baseline; int8_t line_height; } font_header_t; typedef struct font_s { struct font_s *next; char *name; char *file_name; FT_Face face; int size; int prop; int space_width; int dy; list_t chars; /* n_set_t */ unsigned used: 1; /* font is actually used */ unsigned ok: 1; unsigned bold: 1; unsigned nobitmap: 1; unsigned autohint: 2; /* 0: auto, 1: off, 2: on */ } font_t; typedef struct char_data_s { struct char_data_s *next; unsigned ok: 1; /* char exists */ int c; /* char (utf32) */ font_t *font; /* pointer to font */ int x_advance; int x_ofs; /* where to draw lower left bitmap corner */ int y_ofs; unsigned char *bitmap; /* char bitmap, width x height */ int bitmap_width; int bitmap_height; unsigned char *data; int data_len; } char_data_t; list_t font_list; /* font_t */ list_t char_list; /* char_data_t */ list_t chars_missing; /* n_set_t */ int font_height; int font_y_ofs; struct { int verbose; int test; int line_height; char *font_path; list_t chars; /* n_set_t */ char *file; unsigned show: 1; } opt; file_data_t *read_file(char *name); void dump_char(char_data_t *cd); void add_data(file_data_t *d, void *buffer, unsigned size); void write_data(file_data_t *d, char *name); int intersect(int first0, int last0, int first1, int last1); void insert_int_list(list_t *list, int first, int last); void *add_list(list_t *list, void *entry); void *new_mem(size_t size); char *new_str(char *str); int parse_int_list(list_t *list, char *str); char *search_font(char *font_path, char *name); void render_char(char_data_t *cd); int empty_row(char_data_t *cd, int row); int empty_column(char_data_t *cd, int column); void add_bbox(char_data_t *cd); void make_prop(char_data_t *cd); char *utf32_to_utf8(int u8); void add_bits(unsigned char *buf, int *buf_ptr, int bits, unsigned data); unsigned read_unsigned_bits(unsigned char *buf, int *buf_ptr, int bits); int read_signed_bits(unsigned char *buf, int *buf_ptr, int bits); int signed_bits(int num); int unsigned_bits(unsigned num); void encode_char(char_data_t *cd); int show_font(char *name); /* * Get next token from string *stringp, where tokens are possibly-empty * strings separated by characters from delim. * * Writes NULs into the string at *stringp to end tokens. * delim need not remain constant from call to call. * On return, *stringp points past the last NUL written (if there might * be further tokens), or is NULL (if there are definitely no more tokens). * * If *stringp is NULL, strsep returns NULL. */ char *strsep(stringp, delim)register char **stringp; register const char *delim; { register char *s; register const char *spanp; register int c, sc; char *tok; if ((s = *stringp) == NULL) return (NULL); for (tok = s;;) { c = *s++; spanp = delim; do { if ((sc = *spanp++) == c) { if (c == 0) s = NULL; else s[ - 1] = 0; *stringp = s; return (tok); } } while (sc != 0); } /* NOTREACHED */ } int main(int argc, char **argv) { int i, j, k, err, ofs; char *str, *str1, *t, *s, *s1, *font_spec; 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_t *font; n_set_t *n; char_data_t *cd; FT_Library ft_lib; font_header_t fh; file_data_t font_file = {}; unsigned char char_ofs[5]; opt.font_path = (char*)malloc(sizeof(char)*(strlen(getenv("WINDIR")) + 7)); strcpy(opt.font_path, getenv("WINDIR")); opt.font_path = strcat(opt.font_path, "\\FONTS"); opterr = 0; while ((i = getopt_long(argc, argv, "a:c:f:l:p:st:v", options, NULL)) != - 1) { switch (i) { case 'a': err = parse_int_list(&opt.chars, optarg); if (err) { fprintf(stderr, "%s: invalid char range spec\n", optarg); return 1; } break; case 'c': ic2 = iconv_open("UTF-32LE", 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) insert_int_list(&opt.chars, i, i); } iconv_close(ic2); break; case 'f': font = add_list(&font_list, new_mem(sizeof *font)); font_spec = new_str(optarg); if ((s = strchr(font_spec, ':'))) { font->name = new_mem(s - font_spec + 1); memcpy(font->name, font_spec, s - font_spec); t = s + 1; err = 0; while (!err && (str = strsep(&t, ":"))) { if ((s = strchr(str, '='))) { *s++ = 0; if (!strcmp(str, "size")) { font->size = strtol(s, &s1, 0); if (*s1) err = 1; } else if (!strcmp(str, "prop")) { font->prop = strtol(s, &s1, 0); if (*s1) err = 1; } else if (!strcmp(str, "space_width")) { font->space_width = strtol(s, &s1, 0); if (*s1) err = 1; } else if (!strcmp(str, "dy")) { font->dy = strtol(s, &s1, 0); if (*s1) err = 1; } else if (!strcmp(str, "bold")) { font->bold = strtol(s, &s1, 0); if (*s1) err = 1; } else if (!strcmp(str, "nobitmap")) { font->nobitmap = strtol(s, &s1, 0); if (*s1) err = 1; } else if (!strcmp(str, "autohint")) { font->autohint = strtol(s, &s1, 0) + 1; if (*s1) err = 1; } else if (!strcmp(str, "c")) { err = parse_int_list(&font->chars, s); } else { err = 1; } } else { if (*str) err = 1; } } free(font_spec); if (err) { fprintf(stderr, "%s: invalid font spec\n", optarg); return 1; } } else { font->name = font_spec; } break; case 'l': str = optarg; i = strtol(str, &str1, 0); if (*str1 || i < 0) { fprintf(stderr, "%s: invalid line height\n", str); return 1; } opt.line_height = i; break; case 'p': opt.font_path = optarg; break; case 's': opt.show = 1; break; case 't': if (ic == (iconv_t) - 1) { ic = iconv_open("UTF-32LE", "UTF-8"); 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, k ? errno : 0, ibuf_left, obuf_left); if (k >= 0 || (k == - 1 && !obuf_left)) { ok = 1; if (!obuf_left) { i = *(int*)obuf; if (i >= 0x20) { insert_int_list(&opt.chars, i, 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; } } if (ic != (iconv_t) - 1) iconv_close(ic); // use default char list if (!opt.chars.start) insert_int_list(&opt.chars, 0x20, 0x7f); argc -= optind; argv += optind; // FreeSans[size=16 prop=2 space_width=4 dy=16 c=0x1200,0x1000-0x2000] if (argc != 1) { fprintf(stderr, "Usage: mkblfont [options] fontfile\n""Build font for boot loader.\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=font_spec\n\tUse this font. Spec format is fontname[option1 option2 ...]\n"" -h, --help\n\tShow this help text.\n"" -l, --line-height=n\n\tSet line height (default: font height).\n"" -p, --font-path=font path\n\tFont path, elements separated by ':'.\n"" -s, --show\n\tShow font info.\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 (opt.show) return show_font(opt.file); if ((err = FT_Init_FreeType(&ft_lib))) { fprintf(stderr, "FreeType init failed (err = %d)\n", err); return 3; } // open all fonts for (font = font_list.start; font; font = font->next) { font->file_name = search_font(opt.font_path, font->name); if (font->file_name) { err = FT_New_Face(ft_lib, font->file_name, 0, &font->face); if (!err) { if (!font->size) { if (font->face->num_fixed_sizes > 0) { font->size = font->face->available_sizes[0].height; } } if (font->size && !FT_Set_Pixel_Sizes(font->face, font->size, 0)) font->ok = 1; } } } // print some info if (opt.verbose) { printf("Font List\n"); for (font = font_list.start; font; font = font->next) { printf(" %s (%s)\n", font->name, font->ok ? "ok" : "not used"); printf(" File %s\n", font->file_name); printf(" Size %d", font->size); if (font->dy) printf(", dY %d", font->dy); if (font->prop) printf(", Prop %d", font->prop); if (font->space_width) printf(", SpaceWidth %d", font->space_width); printf("\n"); if (font->chars.start) { for (n = font->chars.start; n; n = n->next) { printf(" c 0x%04x", n->first); if (n->last != n->first) printf("-0x%04x", n->last); printf("\n"); } } } printf("\n"); } // print even more info if (opt.verbose >= 2) { printf("Requested Char List\n"); for (n = opt.chars.start; n; n = n->next) { printf(" 0x%04x", n->first); if (n->last != n->first) printf("-0x%04x", n->last); printf("\n"); } printf("\n"); } // build char list for (n = opt.chars.start; n; n = n->next) { for (i = n->first; i <= n->last; i++) { cd = add_list(&char_list, new_mem(sizeof *cd)); cd->c = i; } } // just check the list is really sorted for (i = - 1, cd = char_list.start; cd; cd = cd->next) { if (cd->c <= i) { fprintf(stderr, "internal error: char list not sorted\n"); return 4; } i = cd->c; } // render all chars for (cd = char_list.start; cd; cd = cd->next) { render_char(cd); } FT_Done_FreeType(ft_lib); // fix vertical glyph positions for (cd = char_list.start; cd; cd = cd->next) { if (cd->ok) cd->y_ofs += cd->font->dy; } // get font dimensions font_height = font_y_ofs = 0; for (cd = char_list.start; cd; cd = cd->next) { if (cd->y_ofs < font_y_ofs) font_y_ofs = cd->y_ofs; i = cd->bitmap_height + cd->y_ofs; if (i > font_height) font_height = i; } font_height -= font_y_ofs; if (!opt.test) for (cd = char_list.start; cd; cd = cd->next) add_bbox(cd); for (cd = char_list.start; cd; cd = cd->next) make_prop(cd); for (i = j = 0, cd = char_list.start; cd; cd = cd->next) { if (!cd->ok) insert_int_list(&chars_missing, cd->c, cd->c); } for (cd = char_list.start; cd; cd = cd->next) encode_char(cd); memset(&fh, 0, sizeof fh); fh.magic = MAGIC; fh.height = font_height; fh.baseline = - font_y_ofs; fh.line_height = opt.line_height ? : fh.height + 2; fh.entries = 0; for (cd = char_list.start; cd; cd = cd->next) if (cd->ok) fh.entries++; if (opt.verbose) { if (chars_missing.start) { printf("Missing Chars\n"); for (n = chars_missing.start; n; n = n->next) { printf(" 0x%04x", n->first); if (n->last != n->first) printf("-0x%04x", n->last); printf("\n"); } printf("\n"); } printf("Font Size\n Height: %d\n Baseline: %d\n Line Height: %d\n\n", font_height, - font_y_ofs, fh.line_height); for (cd = char_list.start; cd; cd = cd->next) dump_char(cd); } // font_file.size=5; add_data(&font_file, &fh, sizeof fh); ofs = font_file.size + fh.entries * sizeof char_ofs; for (cd = char_list.start; cd; cd = cd->next) { if (!cd->ok) continue; i = 0; char_ofs[0] = cd->c &0xff; char_ofs[1] = (cd->c >> 8) &0x000000ff; char_ofs[2] = (ofs &0x07) << 5; char_ofs[3] = (ofs >> 3) &0x000000ff; char_ofs[4] = (ofs >> 11) &0x000000ff; if (cd->c == 0x3f) { printf("0x%x\n", cd->data_len); } add_data(&font_file, char_ofs, sizeof char_ofs); ofs += cd->data_len; } for (cd = char_list.start; cd; cd = cd->next) { if (!cd->ok) continue; add_data(&font_file, cd->data, cd->data_len); } write_data(&font_file, opt.file); return 0; } file_data_t *read_file(char *name) { file_data_t *fd; FILE *f; fd = new_mem(sizeof *fd); if (!name) return fd; f = fopen(name, "r"); if (!f) { perror(name); return fd; } if (fseek(f, 0, SEEK_END)) { perror(name); exit(30); } fd->size = fd->real_size = ftell(f); if (fseek(f, 0, SEEK_SET)) { perror(name); exit(30); } if (fd->size) { fd->data = new_mem(fd->size); if (!fd->data) { fprintf(stderr, "malloc failed\n"); exit(30); } } if (fread(fd->data, 1, fd->size, f) != fd->size) { perror(name); exit(30); } fclose(f); return fd; } void dump_char(char_data_t *cd) { int i, j, y0, y1, y2, x0, x1, x2, width; unsigned char *p; char c; if (!cd || !cd->ok) return ; printf("Char 0x%04x '%s'\n", cd->c, utf32_to_utf8(cd->c)); if (cd->font) printf(" Font: %s (%d)\n", cd->font->name, cd->font->size); printf(" Bitmap: %d x %d\n Advance: %d\n Offset: %d x %d\n", cd->bitmap_width, cd->bitmap_height, cd->x_advance, cd->x_ofs, cd->y_ofs); if (opt.verbose >= 2 && cd->data) { printf(" Data[%d]:", cd->data_len); for (i = 0; i < cd->data_len; i++) { if (!(i &7)) { printf("\n "); } printf(" %02x", cd->data[i]); } printf("\n"); } if (cd->bitmap) { p = cd->bitmap; y0 = font_height + font_y_ofs; y1 = y0 - cd->bitmap_height - cd->y_ofs; y2 = y1 + cd->bitmap_height; x1 = cd->bitmap_width + cd->x_ofs; if (cd->x_advance > x1) x1 = cd->x_advance; if (cd->x_ofs < 0) { width = x1 - cd->x_ofs; x1 = 0; x0 = - cd->x_ofs; } else { width = x1; x1 = cd->x_ofs; x0 = 0; } x2 = x1 + cd->bitmap_width; // printf("y0 = %d, y1 = %d, y2 = %d\n", y0, y1, y2); // printf("x0 = %d, x1 = %d, x2 = %d, width = %d\n", x0, x1, x2, width); printf(" "); c = ' '; for (i = 0; i < cd->x_advance + x0; i++) { if (i == x0) c = '_'; printf("%c", c); } printf("\n"); for (j = 0; j < font_height; j++) { printf(" %s", j == y0 - 1 ? "->|" : " |"); if (j < y1 || j >= y2) { for (i = 0; i < width; i++) printf("."); } else { for (i = 0; i < width; i++) { if (i < x1 || i >= x2) { printf("."); } else { c = p[(j - y1) *cd->bitmap_width + i - x1]; if (c == 0) { c = ' '; } else if (c >= MAX_GRAY) { c = '#'; } else { c += '0'; if (c > '9') c += 'a' - '9' - 1; } printf("%c", c); } } } printf("|%s\n", j == y0 - 1 ? "<-" : ""); } printf(" "); c = ' '; for (i = 0; i < cd->x_advance + x0; i++) { if (i == x0) c = '-'; printf("%c", c); } printf("\n"); } printf("\n"); } 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(file_data_t *d, char *name) { FILE *f; f = strcmp(name, "-") ? fopen(name, "wb"): stdout; if (!f) { perror(name); return ; } if (fwrite(d->data, d->size, 1, f) != 1) { perror(name); exit(3); } fclose(f); } int intersect(int first0, int last0, int first1, int last1) { return (first1 >= first0 && first1 <= last0 + 1) || (last1 >= first0 - 1 && last1 <= last0) || (first1 < first0 && last1 > last0); } void insert_int_list(list_t *list, int first, int last) { n_set_t *n, *p, *next; for (n = list->start; n; n = n->next) { if (intersect(n->first, n->last, first, last)) { if (first < n->first) n->first = first; if (last > n->last) n->last = last; break; } } if (!n) { /* not joined */ if (!(n = list->start) || first < n->first) { list->start = p = new_mem(sizeof *p); p->next = n; p->first = first; p->last = last; } else { for (n = list->start; n; n = n->next) { if (!n->next || first < n->next->first) { p = new_mem(sizeof *p); p->next = n->next; p->first = first; p->last = last; n->next = p; if (!p->next) list->end = p; break; } } } } for (n = list->start; n; n = next) { if (!(next = n->next)) break; if (intersect(n->first, n->last, next->first, next->last)) { if (next->first < n->first) n->first = next->first; if (next->last > n->last) n->last = next->last; n->next = next->next; if (!n->next) list->end = n; free(next); next = n; } } } void *add_list(list_t *list, void *entry) { if (list->end) { ((list_any_t*)list->end)->next = entry; } list->end = entry; if (!list->start) { list->start = entry; } return entry; } void *new_mem(size_t size) { return calloc(size, 1); } char *new_str(char *str) { return str ? strdup(str): str; } int parse_int_list(list_t *list, char *str) { int err = 0, i, j, k; char *s, *s1, *t; list->start = list->end = NULL; if (!str) return 0; while (isspace(*str)) str++; if (! *str) return 0; t = str = new_str(str); while ((s = strsep(&t, ","))) { if (sscanf(s, "%i - %i%n", &i, &j, &k) == 2 && k == strlen(s)) { insert_int_list(list, i, j); } else { i = strtol(s, &s1, 0); if (*s1) { err = 1; break; } insert_int_list(list, i, i); } } free(str); return err; } char *search_font(char *font_path, char *name) { int i, j; char *font_name = NULL; char *cur_path, *sep, *p; struct stat sbuf; static char *suffix[] = { "", ".ttc", ".ttf", ".pfa", ".pfb", ".pcf.gz" }; if (!font_path || !name) return NULL; while (*font_path) { cur_path = strdup(font_path); if ((sep = strchr(cur_path, ';'))) *sep = 0; for (i = 0; i < sizeof suffix / sizeof * suffix; i++) { // #asprintf(&font_name, "%s/%s%s", cur_path, name, suffix[i]); font_name = (char*)malloc(sizeof(char)*(strlen(cur_path) + strlen(name) + strlen(suffix[i]) + 2)); if (font_name != NULL) { j = 0; p = cur_path; while (*p) font_name[j++] = *p++; font_name[j++] = 0x5c; p = name; while (*p) font_name[j++] = *p++; p = suffix[i]; while (*p) font_name[j++] = *p++; font_name[j] = 0; } if (!stat(font_name, &sbuf) && S_ISREG(sbuf.st_mode)) break; free(font_name); font_name = NULL; } if (i < sizeof suffix / sizeof * suffix) { free(cur_path); break; } if (sep) { font_path += sep - cur_path + 1; } else { font_path = ""; } free(cur_path); } return font_name; } void render_char(char_data_t *cd) { font_t *font; n_set_t *n; int err, glyph_index; FT_GlyphSlot glyph; int i, j; unsigned char uc; for (font = font_list.start; font; font = font->next) { if (!font->ok) continue; if (font->chars.start) { for (n = font->chars.start; n; n = n->next) { if (cd->c >= n->first && cd->c <= n->last) break; } if (!n) continue; } glyph_index = FT_Get_Char_Index(font->face, cd->c); if (!glyph_index) continue; err = FT_Load_Char(font->face, cd->c, FT_LOAD_RENDER | (font->nobitmap ? FT_LOAD_NO_BITMAP : 0) | (font->autohint ? font->autohint == 1 ? FT_LOAD_NO_AUTOHINT : FT_LOAD_FORCE_AUTOHINT: 0)); if (err) continue; cd->ok = 1; cd->font = font; glyph = font->face->glyph; if (cd->font->bold) FT_GlyphSlot_Embolden(glyph); cd->bitmap_width = glyph->bitmap.width; cd->bitmap_height = glyph->bitmap.rows; cd->bitmap = new_mem(cd->bitmap_width *cd->bitmap_height); cd->x_advance = glyph->advance.x / 64.; cd->x_ofs = glyph->bitmap_left; cd->y_ofs = glyph->bitmap_top - glyph->bitmap.rows; for (j = 0; j < cd->bitmap_height; j++) { for (i = 0; i < cd->bitmap_width; i++) { switch (glyph->bitmap.pixel_mode) { case FT_PIXEL_MODE_MONO: uc = ((glyph->bitmap.buffer[i / 8+j * glyph->bitmap.pitch] >> (7-(i &7))) &1) *MAX_GRAY; break; case FT_PIXEL_MODE_GRAY: uc = (glyph->bitmap.buffer[i + j * glyph->bitmap.pitch]*(MAX_GRAY + 1)) / (255+1); break; default: uc = 0; } cd->bitmap[i + j * cd->bitmap_width] = uc; } } #if 0 printf("bitmap: mode %d, %d x %d, + %d x %d, advance %f x %f\n", glyph->bitmap.pixel_mode, glyph->bitmap.width, glyph->bitmap.rows, glyph->bitmap_left, glyph->bitmap_top, glyph->advance.x / 64., glyph->advance.y / 64.); printf("metrics:\n size %f x %f\n bearing %f x %f, advance %f\n", glyph->metrics.width / 64., glyph->metrics.height / 64., glyph->metrics.horiBearingX / 64., glyph->metrics.horiBearingY / 64., glyph->metrics.horiAdvance / 64.); #endif break; } } int empty_row(char_data_t *cd, int row) { unsigned char *p1, *p2; p2 = (p1 = cd->bitmap + row * cd->bitmap_width) + cd->bitmap_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->bitmap_height; i++, p += cd->bitmap_width) { if (*p) return 0; } return 1; } void add_bbox(char_data_t *cd) { int i; unsigned char *bitmap; int width, height, dx, dy; if (!cd->ok) return ; width = cd->bitmap_width; height = cd->bitmap_height; dx = dy = 0; while (height && empty_row(cd, height - 1)) height--; while (width && empty_column(cd, width - 1)) width--; for (dx = 0; dx < width && empty_column(cd, dx); dx++) ; for (dy = 0; dy < height && empty_row(cd, dy); dy++) ; width -= dx; height -= dy; if (width != cd->bitmap_width || height != cd->bitmap_height) { bitmap = new_mem(width *height); for (i = 0; i < height; i++) { memcpy(bitmap + i * width, cd->bitmap + dx + (i + dy) *cd->bitmap_width, width); } free(cd->bitmap); cd->bitmap = bitmap; cd->x_ofs += dx; cd->y_ofs += cd->bitmap_height - height - dy; cd->bitmap_width = width; cd->bitmap_height = height; } } /* * Fake proprtionally spaced font from fixed size font. */ void make_prop(char_data_t *cd) { int width; if (!cd->ok || !cd->font->prop) return ; width = cd->bitmap_width ? cd->bitmap_width + cd->font->prop: cd->font->space_width; cd->x_ofs = cd->font->prop; cd->x_advance = width; } char *utf32_to_utf8(int u8) { static char buf[16]; static iconv_t ic = (iconv_t) - 1; char *ibuf, *obuf; size_t obuf_left, ibuf_left; int i; *buf = 0; if (ic == (iconv_t) - 1) { ic = iconv_open("UTF-8", "UTF-32LE"); if (ic == (iconv_t) - 1) { fprintf(stderr, "Error: can't convert utf8 data.\n"); exit(1); } } ibuf = (char*) &u8; obuf = buf; ibuf_left = 4; obuf_left = sizeof buf - 1; i = iconv(ic, &ibuf, &ibuf_left, &obuf, &obuf_left); if (i >= 0) { i = sizeof buf - 1-obuf_left; buf[i] = 0; } else { fprintf(stderr, "Warning: failed to convert 0x%x to utf8.\n", u8); } return buf; } void add_bits(unsigned char *buf, int *buf_ptr, int bits, unsigned data) { int rem, ptr; while (bits > 0) { ptr = *buf_ptr >> 3; rem = 8-(*buf_ptr &7); if (rem > bits) rem = bits; buf[ptr] = (buf[ptr] &((1 << (*buf_ptr &7)) - 1)) + ((data &((1 << rem) - 1)) << (*buf_ptr &7)); *buf_ptr += rem; bits -= rem; data >>= rem; } } unsigned read_unsigned_bits(unsigned char *buf, int *buf_ptr, int bits) { int rem, ptr; unsigned data = 0, dptr = 0; while (bits > 0) { ptr = *buf_ptr >> 3; rem = 8-(*buf_ptr &7); if (rem > bits) rem = bits; data += ((buf[ptr] >> (*buf_ptr &7)) &((1 << rem) - 1)) << dptr; dptr += rem; *buf_ptr += rem; bits -= rem; } return data; } int read_signed_bits(unsigned char *buf, int *buf_ptr, int bits) { int i; i = read_unsigned_bits(buf, buf_ptr, bits); if (bits == 0) return i; if ((i &(1 << (bits - 1)))) { i += - 1 << bits; } return i; } int signed_bits(int num) { int bits = 32; int val = num &(1 << 31); if (num == 0) return 0; while ((num &(1 << 31)) == val) { bits--; num <<= 1; } return bits + 1; } int unsigned_bits(unsigned num) { int bits = 0; if (num == 0) return 0; while (num) { num >>= 1; bits++; } return bits; } void encode_cnt(unsigned char *buf, int *buf_ptr, int lc, int lc_cnt) { if ((lc_cnt - 2) >= (1 << GRAY_BIT_COUNT)) { fprintf(stderr, "cnt %d too large\n", lc_cnt); exit(1); } if (lc_cnt >= 2) { *buf_ptr -= GRAY_BITS; add_bits(buf, buf_ptr, GRAY_BITS, lc == 0 ? REP_BLACK : REP_WHITE); // printf("(%d)", lc == 0 ? REP_BLACK : REP_WHITE); add_bits(buf, buf_ptr, GRAY_BIT_COUNT, lc_cnt - 2); // printf("(%d)", lc_cnt - 2); } else if (lc_cnt) { add_bits(buf, buf_ptr, GRAY_BITS, lc); // printf("[%d]", lc); } } void encode_char(char_data_t *cd) { int i, j, bits, lc_cnt; unsigned char *buf; int buf_ptr; unsigned type; unsigned char col[MAX_GRAY + 1]; int lc; if (!cd->ok) return ; // just large enough buf = new_mem(cd->bitmap_width *cd->bitmap_height + 5 * 8+1); buf_ptr = 0; memset(col, 0, sizeof col); for (i = 0; i < cd->bitmap_width *cd->bitmap_height; i++) { if (cd->bitmap[i] <= MAX_GRAY) { col[cd->bitmap[i]] = 1; } } type = 0; for (i = 1; i < MAX_GRAY; i++) { if (col[i]) { type = 1; break; } } // type 0: mono, 1: grays add_bits(buf, &buf_ptr, 2, type); bits = unsigned_bits(cd->bitmap_width); j = unsigned_bits(cd->bitmap_height); if (j > bits) bits = j; j = signed_bits(cd->x_advance); if (j > bits) bits = j; j = signed_bits(cd->x_ofs); if (j > bits) bits = j; j = signed_bits(cd->y_ofs); if (j > bits) bits = j; if (!bits) bits = 1; if (bits > 8) { free(buf); cd->ok = 0; return ; } add_bits(buf, &buf_ptr, 3, bits - 1); add_bits(buf, &buf_ptr, bits, cd->bitmap_width); add_bits(buf, &buf_ptr, bits, cd->bitmap_height); add_bits(buf, &buf_ptr, bits, cd->x_ofs); add_bits(buf, &buf_ptr, bits, cd->y_ofs); add_bits(buf, &buf_ptr, bits, cd->x_advance); switch (type) { case 0: for (i = 0; i < cd->bitmap_width *cd->bitmap_height; i++) { add_bits(buf, &buf_ptr, 1, cd->bitmap[i] ? 1 : 0); } break; case 1: lc = - 1; for (i = lc_cnt = 0; i < cd->bitmap_width *cd->bitmap_height; i++) { if (cd->bitmap[i] == lc && (lc == 0 || lc == MAX_GRAY) && lc_cnt < ((1 << GRAY_BIT_COUNT) + 1)) { lc_cnt++; } else { if (lc_cnt) { encode_cnt(buf, &buf_ptr, lc, lc_cnt); lc_cnt = 0; lc = - 1; } add_bits(buf, &buf_ptr, GRAY_BITS, cd->bitmap[i]); // printf("[%d]", cd->bitmap[i]); } lc = cd->bitmap[i]; } if (lc_cnt) { encode_cnt(buf, &buf_ptr, lc, lc_cnt); } break; } cd->data = new_mem(cd->data_len = ((buf_ptr + 7) >> 3)); memcpy(cd->data, buf, cd->data_len); free(buf); } int show_font(char *name) { int i, j, ofs, ofs2, bits, lc, lc_cnt, bitmap_len; file_data_t *font_file; font_header_t fh; unsigned type; char_data_t *cd; opt.verbose++; font_file = read_file(name); if (font_file->size < sizeof fh) return 0; memcpy(&fh, font_file->data, sizeof fh); if (fh.magic != MAGIC) { fprintf(stderr, "%s: wrong file format\n", name); return 1; } if (font_file->size < sizeof fh + fh.entries * 5) { fprintf(stderr, "%s: file too short\n", name); return 2; } font_height = fh.height; font_y_ofs = - fh.baseline; for (i = 0; i < fh.entries; i++) { cd = add_list(&char_list, new_mem(sizeof *cd)); j = 0; cd->c = read_unsigned_bits(font_file->data + sizeof fh + i * 5, &j, 21); ofs = read_unsigned_bits(font_file->data + sizeof fh + i * 5, &j, 19); if (i != fh.entries - 1) { j = 21; ofs2 = read_unsigned_bits(font_file->data + sizeof fh + (i + 1) *5, &j, 19); } else { ofs2 = font_file->size; } if (ofs2 < ofs || ofs2 > font_file->size) { fprintf(stderr, "%s: invalid data for chhar 0x%04x\n", name, cd->c); return 3; } cd->data = new_mem(cd->data_len = ofs2 - ofs); memcpy(cd->data, font_file->data + ofs, cd->data_len); } for (cd = char_list.start; cd; cd = cd->next) { j = 0; type = read_unsigned_bits(cd->data, &j, 2); bits = read_unsigned_bits(cd->data, &j, 3) + 1; if (type > 1) { fprintf(stderr, "%s: unknown type %d for char 0x%04x\n", name, type, cd->c); return 3; } cd->bitmap_width = read_unsigned_bits(cd->data, &j, bits); cd->bitmap_height = read_unsigned_bits(cd->data, &j, bits); cd->x_ofs = read_signed_bits(cd->data, &j, bits); cd->y_ofs = read_signed_bits(cd->data, &j, bits); cd->x_advance = read_signed_bits(cd->data, &j, bits); cd->bitmap = new_mem(bitmap_len = cd->bitmap_width *cd->bitmap_height); switch (type) { case 0: for (i = 0; i < bitmap_len; i++) { cd->bitmap[i] = read_unsigned_bits(cd->data, &j, 1) ? MAX_GRAY : 0; } break; case 1: for (i = 0; i < bitmap_len;) { lc = read_unsigned_bits(cd->data, &j, GRAY_BITS); // printf("(%d)", lc); if (lc <= MAX_GRAY) { cd->bitmap[i++] = lc; continue; } lc = lc == REP_BLACK ? 0 : MAX_GRAY; lc_cnt = read_unsigned_bits(cd->data, &j, GRAY_BIT_COUNT) + 3; // printf("(%d)", lc_cnt); while (i < bitmap_len && lc_cnt--) cd->bitmap[i++] = lc; } break; } cd->ok = 1; } printf("Font Size\n Height: %d\n Baseline: %d\n Line Height: %d\n\n", font_height, - font_y_ofs, fh.line_height); for (cd = char_list.start; cd; cd = cd->next) dump_char(cd); return 0; }