#include #include #include #include #include #include FT_FREETYPE_H #include #include #include #define N_CHARS 128 #define round_up_26_6(val) (((val) + 63) >> 6u) int max_advance; int max_top = -9999; int min_bottom = 9999; int line_height; int baseline_offset; typedef struct { int width; int height; int top; int left; uint8_t * bitmap; } char_info_t; static char_info_t char_infos[N_CHARS]; static void load_char(FT_Face face, int char_code) { if (FT_Load_Char(face, char_code, FT_LOAD_RENDER) != 0) { return; } int advance = round_up_26_6(face->glyph->advance.x); if (advance > max_advance) { max_advance = advance; } if ((face->glyph->bitmap.width == 0) || (face->glyph->bitmap.rows == 0)) { return; } char_infos[char_code].width = face->glyph->bitmap.width; char_infos[char_code].height = face->glyph->bitmap.rows; char_infos[char_code].top = face->glyph->bitmap_top; if (char_infos[char_code].top > max_top) { max_top = char_infos[char_code].top; } int bottom = char_infos[char_code].top - char_infos[char_code].height; if (bottom < min_bottom) { min_bottom = bottom; } char_infos[char_code].left = face->glyph->bitmap_left; char_infos[char_code].bitmap = malloc(char_infos[char_code].width * char_infos[char_code].height); memcpy(char_infos[char_code].bitmap, face->glyph->bitmap.buffer, char_infos[char_code].width * char_infos[char_code].height); } static void generate_bytes(FILE * file, const uint8_t * bytes, int count) { for (int i = 0; i < count; i++) { if (i % 8 == 0) { fprintf(file, " "); } fprintf(file, "0x%02xu,", bytes[i]); if ((i + 1) % 8 == 0) { fprintf(file, "\n"); } else if (i < (count - 1)) { fprintf(file, " "); } } if (count % 8 != 0) { fprintf(file, "\n"); } } static bool generate(const char * d_file_name) { FILE * fh = fopen(d_file_name, "wb"); fprintf(fh, "module hulk.kfont;\n"); fprintf(fh, "struct CharInfo {\n"); fprintf(fh, " /** Glyph bitmap width. */\n"); fprintf(fh, " ubyte width;\n"); fprintf(fh, " /** Glyph bitmap height. */\n"); fprintf(fh, " ubyte height;\n"); fprintf(fh, " /** Glyph bitmap top offset (descent from top of text row). */\n"); fprintf(fh, " ubyte top;\n"); fprintf(fh, " /** Glyph bitmap left offset. */\n"); fprintf(fh, " ubyte left;\n"); fprintf(fh, " /** Glyph bitmap. */\n"); fprintf(fh, " const ubyte[] bitmap;\n"); fprintf(fh, "};\n"); fprintf(fh, "struct FontInfo {\n"); fprintf(fh, " /** Font line height. */\n"); fprintf(fh, " ubyte line_height;\n"); fprintf(fh, " /** Font advance. */\n"); fprintf(fh, " ubyte advance;\n"); fprintf(fh, " /** Characters. */\n"); fprintf(fh, " const CharInfo[] chars;\n"); fprintf(fh, "};\n"); fprintf(fh, "/** Kernel font data. */\n"); fprintf(fh, "__gshared const FontInfo Kfont = {\n"); fprintf(fh, " %du,\n", line_height); fprintf(fh, " %du,\n", max_advance); fprintf(fh, " [\n"); for (int i = 0; i < N_CHARS; i++) { fprintf(fh, " /* %d */", i); fprintf(fh, " CharInfo(\n"); fprintf(fh, " %du /* width */,\n", char_infos[i].width); fprintf(fh, " %du /* height */,\n", char_infos[i].height); fprintf(fh, " %du /* top */,\n", char_infos[i].top); fprintf(fh, " %du /* left */,\n", char_infos[i].left); if (char_infos[i].width > 0) { fprintf(fh, " [\n"); generate_bytes(fh, char_infos[i].bitmap, char_infos[i].width * char_infos[i].height); fprintf(fh, " ]\n"); } else { fprintf(fh, " []\n"); } fprintf(fh, " ),\n"); } fprintf(fh, " ],\n"); fprintf(fh, "};\n"); fclose(fh); return true; } int main(int argc, char * argv[]) { /* Expect: font file, size, out file */ if (argc != 4) { fprintf(stderr, "Incorrect arguments\n"); return 1; } const char * font_file = argv[1]; int size = atoi(argv[2]); const char * out_file = argv[3]; FT_Library ft_library; if (FT_Init_FreeType(&ft_library) != 0) { fprintf(stderr, "Could not initialize freetype\n"); return 2; } FT_Face face; if (FT_New_Face(ft_library, font_file, 0, &face) != 0) { fprintf(stderr, "Could not open %s\n", font_file); return 3; } FT_Set_Pixel_Sizes(face, 0, size); for (int i = 0; i < N_CHARS; i++) { load_char(face, i); } line_height = round_up_26_6(face->size->metrics.height); baseline_offset = (line_height - (max_top - min_bottom)) / 2 - min_bottom; if ((line_height < 1) || (line_height > 0xFF)) { fprintf(stderr, "Error: invalid line_height %d\n", line_height); return false; } if ((max_advance < 1) || (max_advance > 0xFF)) { fprintf(stderr, "Error: invalid max_advance %d\n", max_advance); return false; } for (int i = 0; i < N_CHARS; i++) { if ((char_infos[i].width < 0) || (char_infos[i].width > max_advance)) { fprintf(stderr, "Error: invalid character %d width: %d\n", i, char_infos[i].width); return false; } if ((char_infos[i].height < 0) || (char_infos[i].height > line_height)) { fprintf(stderr, "Error: invalid character %d height: %d\n", i, char_infos[i].height); return false; } if ((char_infos[i].left < 0) || (char_infos[i].left >= max_advance)) { fprintf(stderr, "Error: invalid character %d left: %d\n", i, char_infos[i].left); return false; } /* Adjust character top to be index downward from top of font box. */ char_infos[i].top = line_height - baseline_offset - char_infos[i].top; if ((char_infos[i].top < 0) || (char_infos[i].top >= line_height)) { fprintf(stderr, "Error: invalid character %d top: %d\n", i, char_infos[i].top); return false; } if ((char_infos[i].left + char_infos[i].width) > max_advance) { fprintf(stderr, "Error: character %d left (%d) + width (%d) > advance (%d)\n", i, char_infos[i].left, char_infos[i].width, max_advance); return false; } if ((char_infos[i].top + char_infos[i].height) > line_height) { fprintf(stderr, "Error: character %d top (%d) + height (%d) > line height (%d)\n", i, char_infos[i].top, char_infos[i].height, line_height); return false; } } if (!generate(out_file)) { return 1; } return 0; }