#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 const char * bare_header_name(const char * h_file_name) { const char * p = rindex(h_file_name, '/'); if (p == NULL) { p = h_file_name; } else { p++; } return p; } static char * include_guard_name(const char * h_file_name) { const char * p = bare_header_name(h_file_name); char * guard_name = malloc(strlen(p) + 1); strcpy(guard_name, p); char * m = guard_name; while (*m != '\0') { if ('a' <= *m && *m <= 'z') { *m = toupper(*m); } else if (('0' <= *m && *m <= '9') || ('A' <= *m && *m <= 'Z')) { /* no change */ } else { *m = '_'; } m++; } return guard_name; } 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 void generate(const char * c_file_name) { char * h_file_name = malloc(strlen(c_file_name) + 1); strcpy(h_file_name, c_file_name); h_file_name[strlen(h_file_name) - 1] = 'h'; char * guard = include_guard_name(h_file_name); FILE * h_file = fopen(h_file_name, "wb"); fprintf(h_file, "#ifndef %s\n", guard); fprintf(h_file, "#define %s\n\n", guard); fprintf(h_file, "#include \n"); fprintf(h_file, "typedef struct {\n int width;\n int height;\n int top;\n int left;\n const uint8_t * bitmap;\n} fontgen_char_info_t;\n"); fprintf(h_file, "typedef struct {\n int line_height;\n int advance;\n int baseline_offset;\n const fontgen_char_info_t ** char_infos;\n} fontgen_font_t;\n"); fprintf(h_file, "extern const fontgen_font_t kfont;\n"); fprintf(h_file, "#endif\n"); fclose(h_file); FILE * c_file = fopen(c_file_name, "wb"); fprintf(c_file, "#include \"%s\"\n", bare_header_name(h_file_name)); fprintf(c_file, "#include \n"); for (int i = 0; i < N_CHARS; i++) { if (char_infos[i].width > 0) { fprintf(c_file, "static const uint8_t char_bitmap_%d[] = {\n", i); generate_bytes(c_file, char_infos[i].bitmap, char_infos[i].width * char_infos[i].height); fprintf(c_file, "};\n"); } fprintf(c_file, "static const fontgen_char_info_t char_%d = {\n", i); fprintf(c_file, " %d,\n", char_infos[i].width); fprintf(c_file, " %d,\n", char_infos[i].height); fprintf(c_file, " %d,\n", char_infos[i].top); fprintf(c_file, " %d,\n", char_infos[i].left); if (char_infos[i].width > 0) { fprintf(c_file, " char_bitmap_%d,\n", i); } else { fprintf(c_file, " NULL,\n"); } fprintf(c_file, "};\n\n"); } fprintf(c_file, "const fontgen_char_info_t * char_infos[] = {\n"); for (int i = 0; i < N_CHARS; i++) { fprintf(c_file, " &char_%d,\n", i); } fprintf(c_file, "};\n"); fprintf(c_file, "const fontgen_font_t kfont = {\n"); fprintf(c_file, " %d,\n", line_height); fprintf(c_file, " %d,\n", max_advance); fprintf(c_file, " %d,\n", baseline_offset); fprintf(c_file, " char_infos,\n"); fprintf(c_file, "};\n"); fclose(c_file); } 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; generate(out_file); return 0; }