#!/usr/bin/nickle autoimport ParseArgs import File; typedef struct { int[] bytes; int width; int height; int x_off; int y_off; int advance; int encoding; int location; } glyph_t; typedef struct { glyph_t[...] glyphs; int default_char; int ascent; } font_t; glyph_t read_glyph(file f) { glyph_t glyph = { .encoding = -1, .bytes = (int[...]){}, .width = 0 }; while (!File::end(f)) { string l = fgets(f); string[*] tokens = String::split(l, " "); if (dim(tokens) == 0) continue; switch (tokens[0]) { case "ENCODING": glyph.encoding = atoi(tokens[1]); break; case "DWIDTH": glyph.advance = atoi(tokens[1]); break; case "BBX": glyph.width = atoi(tokens[1]); glyph.height = atoi(tokens[2]); glyph.x_off = atoi(tokens[3]); glyph.y_off = glyph.height + atoi(tokens[4]); break; case "ENDCHAR": return glyph; case "BITMAP": int byte_stride = ceil(glyph.width/8); for (int y = 0; y < glyph.height; y++) { string l = fgets(f); for (int x = 0; x < byte_stride; x++) { string v = String::substr(l, x * 2, 2); glyph.bytes[dim(glyph.bytes)] = atoi(v, 16); } } break; } } return glyph; } font_t read_font(file f) { font_t font = { .glyphs = {}, .default_char = -1 }; bool in_head = true; while (in_head && !File::end(f)) { string l = File::fgets(f); string[*] tokens = String::split(l, " "); switch (tokens[0]) { case "DEFAULT_CHAR": font.default_char = atoi(tokens[1]); break; case "FONT_ASCENT": font.ascent = atoi(tokens[1]); break; case "CHARS": in_head = false; break; } } while (!File::end(f)) { glyph_t glyph = read_glyph(f); if (glyph.encoding == -1) break; font.glyphs[dim(font.glyphs)] = glyph; } return font; } int flip_byte(int x) { int dest = 0; for (int i = 0; i < 8; i++) dest |= ((x >> (7 - i)) & 1) << i; return dest; } void print_font(file out, font_t font, string font_name) { int width = font.glyphs[0].width; int height = font.glyphs[0].height; int max_width = width; int max_height = height; int[128] pos = { 0 ... }; int[...] bytes; bool fixed_size = true; for (int i = 1; i < dim(font.glyphs); i++) { if (font.glyphs[i].width != width || font.glyphs[i].height != height) { fixed_size = false; } max_width = max(max_width, font.glyphs[i].width); max_height = max(max_height, font.glyphs[i].height); } if (font.default_char == -1) font.default_char = font.glyphs[0].encoding; /* build byte array */ for (int i = 0; i < dim(font.glyphs); i++) { if (font.glyphs[i].encoding < dim(pos)) { pos[font.glyphs[i].encoding] = dim(bytes); for (int b = 0; b < dim(font.glyphs[i].bytes); b++) bytes[dim(bytes)] = font.glyphs[i].bytes[b]; } } /* Fill in default glyph */ for (int i = 0; i < dim(pos); i++) if (pos[i] == -1) pos[i] = pos[font.default_char]; fprintf(out, "#include \n"); fprintf(out, "static const uint8_t %s_bytes[%d] = {", font_name, dim(bytes)); for (int b = 0; b < dim(bytes); b++) { if ((b & 15) == 0) fprintf(out, "\n\t"); else fprintf(out, " "); fprintf(out, "0x%02x,", flip_byte(bytes[b])); } fprintf(out, "\n};\n\n"); fprintf(out, "static const uint16_t %s_pos[%d] = {", font_name, dim(pos)); for (int i = 0; i < dim(pos); i++) { if ((i & 7) == 0) fprintf(out, "\n\t"); else fprintf(out, " "); fprintf(out, "%6d,", pos[i]); } fprintf(out, "\n};\n\n"); fprintf(out, "#define GLYPH_WIDTH_MAX %d\n", max_width); fprintf(out, "#define GLYPH_HEIGHT_MAX %d\n", max_height); if (fixed_size) { fprintf(out, "#define GLYPH_WIDTH %d\n", width); fprintf(out, "#define GLYPH_HEIGHT %d\n", height); fprintf(out, "#define GLYPH_ASCENT %d\n", font.ascent); } else { fprintf(out, "static const struct ao_glyph_metrics %s_metrics[%d] = {\n", font_name, dim(pos)); for (int i = 0; i < dim(pos); i++) { int g = 0; for (int j = 0; j < dim(font.glyphs); j++) { if (font.glyphs[j].encoding == i) { g = j; break; } } string c; if (' ' <= i && i <= '~') c = sprintf("%c", i); else c = sprintf("%02x", i); fprintf(out, " { .width = %d, .height = %d, .x_off = %d, .y_off = %d, .advance = %d }, /* %s */\n", font.glyphs[g].width, font.glyphs[g].height, font.glyphs[g].x_off, font.glyphs[g].y_off, font.glyphs[g].advance, c); } fprintf(out, "};\n"); } fprintf(out, "const struct ao_font %s_font = {\n", font_name); fprintf(out, "\t.bytes = %s_bytes,\n", font_name); fprintf(out, "\t.pos = %s_pos,\n", font_name); if (!fixed_size) fprintf(out, "\t.metrics = %s_metrics,\n", font_name); fprintf(out, "\t.max_width = %d,\n", max_width); fprintf(out, "\t.max_height = %d,\n", max_height); fprintf(out, "\t.ascent = %d,\n", font.ascent); fprintf(out, "};\n"); int max_stride = (max_width + 31) >> 5; fprintf(out, "__asm__(\".balign 4\\n.comm ao_glyph_temp 0x%x\");\n", max_stride * max_height * 4); } string to_c(string a) { string r = ""; for (int i = String::rindex(a, "/") + 1; i < String::length(a); i++) { int c = a[i]; if (c == '.') break; if (!(Ctype::isalnum(c) || c == '_')) c = '_'; r += String::new(c); } if (!Ctype::isalpha(r[0])) r = "_" + r; return r; } void do_font(string name, file out) { twixt (file f = File::open(name, "r"); File::close(f)) { font_t font = read_font(f); print_font(out, font, to_c(name)); } } argdesc argd = { args = { { .var = (arg_var.arg_string) &(string output), .name = "output", .abbr = 'o', .expr_name = "file", .desc = "Output file name" }, }, posn_args = { { var = (arg_var.arg_string) &(string input), name = "input", } } }; parseargs(&argd, &argv) if (is_uninit(&output)) do_font(input, stdout); else { twixt(file f = File::open(output, "w"); File::close(f)) do_font(input, f); }