Current section
Files
Jump to
Current section
Files
c_src/mk_font.c
/* Generates fonts.h from font files using FreeType 2 library
* https://www.freetype.org/ */
#include <ctype.h>
#include <libgen.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <freetype2/ft2build.h>
#include FT_FREETYPE_H
#include "common.h"
#define HEIGHT 52
#define PIXEL_SIZE HEIGHT
#define MAX_WIDTH 100
#define DESCENT 11 /* Found empirically. TODO: how to calculate? */
#define ALPHABET \
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
typedef struct Context {
char name[MAX_NAME + 1];
char *alphabet;
int num_fonts;
int num_chars;
int height;
int baseline;
int font_idx;
int glyph_idx;
char glyph_char;
} Context;
#define BEGIN(_ctx) \
(printf("/* autogenerated by %s */\n#include <stdio.h>\n#include " \
"\"common.h\"\n", \
__FILE__))
#define FONT_START(_ctx) (printf("\n/* %s */\n", (_ctx)->name))
#define GLYPH_START(_ctx) \
(printf("static int8_t font%i_glyph%i[] = {// %c\n ", _ctx->font_idx, \
_ctx->glyph_idx, _ctx->glyph_char))
#define BYTE_OUT(_ctx, _v) (printf("%i,", (_v)))
#define ROW_END(_ctx) (printf("%i,\n ", BC_NEWLINE))
#define GLYPH_END(_ctx) (printf("%i};\n", BC_EOG))
#define FONT_END(_ctx) (font_end(_ctx))
#define END(_ctx) (end(_ctx))
void font_end(Context *ctx) {
printf("static int8_t const *font%i_glyphs[] = {\n ", ctx->font_idx);
for (int i = 0; i < ctx->num_chars; i++) {
printf("font%i_glyph%i,", ctx->font_idx, i);
if (i % 10 == 9)
printf("\n ");
}
printf("NULL};\nstatic Font font%i = {\"%s\", \"%s\", %i, %i, "
"font%i_glyphs};\n",
ctx->font_idx, ctx->name, ctx->alphabet, HEIGHT, ctx->baseline,
ctx->font_idx);
}
void end(Context *ctx) {
printf("\nstatic Font *fonts[] = {\n ");
for (int i = 0; i < ctx->num_fonts; i++) {
/* TODO: we can use struct Font fonts[] and inline
* {name, alphabet, height, baseline, fontN_glyphs} here, but would need
* some extra storage or 2-pass iteration */
printf("&font%i,", i);
}
printf("NULL\n};\n");
}
void draw_bitmap(Context *ctx, FT_Bitmap *bitmap, FT_Int bitmap_top) {
/* Eg, letters 'o' and 'j'
* _____________________________________
* | | y_shift |
* | y_shift ___|___ |
* __|__ O | |
* // \\ || | bitmap_top |
* || || bitmap_top || | | HEIGHT
*_\\_//_____________||__|_ baseline |
* || | |
* \\_// | DESCENT |
* ________________"____|____________|__
*
* For "o" bitmap->rows == bitmap_top
* For "j" bitmap->rows == bitmap_top + descent
*
**/
int y_shift = HEIGHT - (bitmap_top + DESCENT);
GLYPH_START(ctx);
for (int i = 0; i < y_shift; i++)
ROW_END(ctx);
for (size_t row = 0; row < bitmap->rows; row++) {
int num_spaces = 0;
for (size_t col = 0; col < bitmap->width; col++) {
if (row < 0 || col < 0 || col >= MAX_WIDTH || row >= HEIGHT) {
fprintf(stderr,
"WARNING: %s canvas overflow: character '%c' exceeds canvas "
"size (%i, %i) at (%zu, %zu)\n",
ctx->name, ctx->glyph_char, HEIGHT, MAX_WIDTH, row, col);
continue;
}
unsigned char pixel_brightness =
bitmap->buffer[row * bitmap->pitch + col];
if (pixel_brightness > 1) {
if (num_spaces) {
BYTE_OUT(ctx, BC_NUM_SPACES(num_spaces));
num_spaces = 0;
}
BYTE_OUT(ctx, BC_GREY_PIXEL(pixel_brightness));
} else {
num_spaces++;
}
}
ROW_END(ctx);
}
GLYPH_END(ctx);
}
int main(int argc, char **argv) {
FT_Library library;
FT_Face face;
FT_GlyphSlot slot;
FT_Error error;
Context ctx;
char *filepath, *filename;
char *text;
int num_chars;
if (argc < 2) {
fprintf(stderr, "usage: %s font1 font2 ...fontN\n", argv[0]);
exit(1);
}
ctx.alphabet = ALPHABET;
ctx.num_chars = strlen(ctx.alphabet);
ctx.num_fonts = argc - 1;
ctx.height = HEIGHT;
ctx.baseline = HEIGHT - DESCENT;
error = FT_Init_FreeType(&library); /* initialize library */
/* error handling omitted */
BEGIN(ctx);
for (int i = 1; i < argc; i++) {
filepath = argv[i];
/* converting filepath to lowercase file name without extension */
filename = basename(filepath);
strncpy(ctx.name, filename, MAX_NAME);
ctx.name[MAX_NAME + 1] = '\0';
char *ext_pos = strrchr(ctx.name, '.');
if (ext_pos != NULL) {
*ext_pos = '\0';
}
for (char *p = ctx.name; *p; ++p)
*p = tolower(*p);
ctx.font_idx = i - 1;
error = FT_New_Face(library, filepath, 0, &face); /* create face object */
if (error) {
fprintf(stderr, "error opening font %s\n", filepath);
exit(1);
}
error = FT_Set_Pixel_Sizes(face, 0, PIXEL_SIZE);
if (error) {
fprintf(stderr, "error setting pixel size %s\n", filepath);
exit(1);
}
slot = face->glyph;
FONT_START(&ctx);
for (int n = 0; n < ctx.num_chars; n++) {
/* load glyph image into the slot (erase previous one) */
error = FT_Load_Char(face, ctx.alphabet[n], FT_LOAD_RENDER);
if (error) {
fprintf(stderr, "error rendering char '%c' %s\n", ctx.alphabet[n],
filepath);
continue;
}
ctx.glyph_char = ctx.alphabet[n];
ctx.glyph_idx = n;
draw_bitmap(&ctx, &slot->bitmap, slot->bitmap_top);
}
FONT_END(&ctx);
FT_Done_Face(face);
}
END(&ctx);
FT_Done_FreeType(library);
return 0;
}