#include #include #include "bdf_font.h" void bf_Error(bf_t *bf, char *fmt, ...) { va_list va; va_start(va, fmt); vprintf(fmt, va); printf("\n"); va_end(va); } void bf_Log(bf_t *bf, char *fmt, ...) { va_list va; va_start(va, fmt); if ( bf->is_verbose != 0 ) { vprintf(fmt, va); printf("\n"); } va_end(va); } /* bf_Open(0, BDF_BBX_MODE_MINIMAL) */ bf_t *bf_Open(int is_verbose, int bbx_mode) { bf_t *bf; bf = (bf_t *)malloc(sizeof(bf_t)); if ( bf != NULL ) { bf->is_verbose = is_verbose; bf->glyph_list = NULL; bf->glyph_cnt = 0; bf->glyph_max = 0; bf->str_font = NULL; /* argument for FONT in bdf file */ bf->str_copyright = NULL; /* argument for COPYRIGHT in bdf file */ bf->target_data = NULL; bf->target_max = 0; bf->target_cnt = 0; bf->selected_glyphs = 0; bf->enc_w = 0; bf->enc_h = 0; bf->enc_x = 0; bf->enc_y = 0; bf->bbx_mode = bbx_mode; bf->tile_h_size = 1; bf->tile_v_size = 1; return bf; } return NULL; } void bf_Clear(bf_t *bf) { int i; for( i = 0; i < bf->glyph_cnt; i++ ) { free( bf->glyph_list[i] ); } bf->glyph_cnt = 0; if ( bf->str_font != NULL ) free(bf->str_font); bf->str_font = NULL; if ( bf->str_copyright != NULL ) free(bf->str_copyright); bf->str_copyright = NULL; } void bf_Close(bf_t *bf) { bf_Clear(bf); if ( bf->glyph_list != NULL ) free(bf->glyph_list); if ( bf->target_data != NULL ) free(bf->target_data); bf->glyph_list = NULL; bf->glyph_max = 0; free(bf); } static int bf_extend(bf_t *bf) { int extend = 16; void *ptr; if ( bf->glyph_list == NULL ) { ptr = malloc(extend*sizeof(bg_t *)); bf->glyph_max = 0; } else { ptr = realloc(bf->glyph_list, (bf->glyph_max + extend)*sizeof(bg_t *)); } if ( ptr == NULL ) return 0; bf->glyph_max += extend; bf->glyph_list = (bg_t **)ptr; return 1; } /* returns glyph position or -1 */ int bf_AddGlyph(bf_t *bf) { while( bf->glyph_max <= bf->glyph_cnt ) if ( bf_extend(bf) == 0 ) return -1; bf->glyph_list[bf->glyph_cnt] = bg_Open(); if ( bf->glyph_list[bf->glyph_cnt] == NULL ) return -1; bf->glyph_list[bf->glyph_cnt]->bf = bf; bf->glyph_cnt++; return bf->glyph_cnt-1; } static int bf_extend_target_data(bf_t *bf) { int extend = 16; int i; void *ptr; if ( bf->target_data == NULL ) { ptr = malloc(extend*sizeof(uint8_t)); bf->target_max = 0; } else { ptr = realloc(bf->target_data, (bf->target_max + extend)*sizeof(uint8_t)); } if ( ptr == NULL ) return 0; bf->target_data = (uint8_t *)ptr; for( i = bf->target_max; i < bf->target_max + extend; i++ ) bf->target_data[i] = 0; bf->target_max += extend; return 1; } int bf_AddTargetData(bf_t *bf, uint8_t data) { while( bf->target_max <= bf->target_cnt ) if ( bf_extend_target_data(bf) == 0 ) return -1; bf->target_data[bf->target_cnt] = data; bf->target_cnt++; return bf->target_cnt-1; } void bf_ClearTargetData(bf_t *bf) { int i; for( i = 0; i < bf->target_max; i++ ) bf->target_data[i] = 0; bf->target_cnt = 0; } void bf_CalculateSelectedNumberOfGlyphs(bf_t *bf) { int i; bg_t *bg; bf->selected_glyphs = 0; for( i = 0; i < bf->glyph_cnt; i++ ) { bg = bf->glyph_list[i]; if ( bg->map_to >= 0 ) { bf->selected_glyphs++; } } } void bf_ReduceAllGlyph(bf_t *bf) { int i; bg_t *bg; int red_x, red_y; bf_Log(bf, "Reduce: Start"); for( i = 0; i < bf->glyph_cnt; i++ ) { bg = bf->glyph_list[i]; if ( bg->map_to >= 0 ) { bg_ReduceGlyph(bg); //bg_ShowBitmap(bg, &(bg->bbx)); red_x = bg->bitmap_width - bg->bbx.w; red_y = bg->bitmap_height - bg->bbx.h; if ( red_x > 0 || red_y > 0 ) { //bf_Log(bf, "Reduce: Encoding %ld, x by %d, y by %d", bg->encoding, red_x, red_y); } } } bf_Log(bf, "Reduce: End"); } void bf_ShowAllGlyphs(bf_t *bf, bbx_t *bbx) { int i; bg_t *bg; for( i = 0; i < bf->glyph_cnt; i++ ) { bg = bf->glyph_list[i]; if ( bg->map_to >= 0 ) { bg_ShowBitmap(bg, bbx); } } } int bf_GetIndexByEncoding(bf_t *bf, long encoding) { int i; bg_t *bg; for( i = 0; i < bf->glyph_cnt; i++ ) { bg = bf->glyph_list[i]; if ( bg->encoding == encoding ) return i; } return -1; } void bf_CalculateMaxBBX(bf_t *bf) { int i; int is_first = 1; int enc_idx; bg_t *bg; for( i = 0; i < bf->glyph_cnt; i++ ) { bg = bf->glyph_list[i]; if ( bg->map_to >= 0 ) { if ( is_first != 0 ) { bf->max = bg->bbx; bf->enc_x = bg->encoding; bf->enc_y = bg->encoding; bf->enc_w = bg->encoding; bf->enc_h = bg->encoding; is_first = 0; } else { enc_idx = bg_Max(bg, &(bf->max)); switch(enc_idx) { case 1: bf->enc_w = bg->encoding; break; case 2: bf->enc_h = bg->encoding; break; case 3: bf->enc_x = bg->encoding; break; case 4: bf->enc_y = bg->encoding; break; } } } } if ( bf->bbx_mode == BDF_BBX_MODE_M8 ) { /* old bf->max.w = ( bf->max.w + 7 ) & ~7; bf->max.h = ( bf->max.h + 7 ) & ~7; */ bf->max.w = 8; bf->max.h = 7; } bf_Log(bf, "CalculateMaxBBX: x=%ld, y=%ld, w=%ld, h=%ld", bf->max.x, bf->max.y, bf->max.w, bf->max.h); bf_Log(bf, "CalculateMaxBBX: Encodings x=%ld, y=%ld, w=%ld, h=%ld", bf->enc_x, bf->enc_y, bf->enc_w, bf->enc_h); } void bf_CalculateMinMaxDWidth(bf_t *bf) { int i; bg_t *bg; bf->dx_min = 0x07fff; bf->dx_max = -0x07fff; bf->x_min = 0x07fff; bf->x_max = -0x07fff; for( i = 0; i < bf->glyph_cnt; i++ ) { bg = bf->glyph_list[i]; if ( bg->map_to >= 0 ) { if ( bf->dx_min > bg->dwidth_x ) bf->dx_min = bg->dwidth_x; if ( bf->dx_max < bg->dwidth_x ) bf->dx_max = bg->dwidth_x; if ( bf->x_min > bg->bbx.x ) bf->x_min = bg->bbx.x; if ( bf->x_max < bg->bbx.x ) bf->x_max = bg->bbx.x; } } bf_Log(bf, "bf_CalculateMinMaxDWidth: dx_min=%ld, dx_max=%ld", bf->dx_min, bf->dx_max); bf_Log(bf, "bf_CalculateMinMaxDWidth: x_min=%ld, x_max=%ld", bf->x_min, bf->x_max); if ( bf->dx_min == bf->dx_max && bf->x_min >= 0 ) { bf_Log(bf, "bf_CalculateMinMaxDWidth: Monospaced font."); /* not sufficient: also bbx.x must be >= 0 for all glyphs */ /* for a monospaced font, dx must be identical to bbx.w */ } } int get_unsigned_bit_size(unsigned long v) { int i = 0; while( v != 0 ) { v = v / 2; i++; } return i; } int get_signed_bit_size(long v) { if ( v < 0 ) return get_unsigned_bit_size(-v-1) + 1; return get_unsigned_bit_size(v) + 1; } /* #define BDF_BBX_MODE_MINIMAL 0 #define BDF_BBX_MODE_HEIGHT 1 #define BDF_BBX_MODE_MAX 2 #define BDF_BBX_MODE_M8 3 */ void bf_copy_bbx_and_update_shift(bf_t *bf, bbx_t *target_bbx, bg_t *bg) { /* modifing the following code requires update ind bdf_rle.c also */ if ( bf->bbx_mode == BDF_BBX_MODE_MINIMAL ) // mode 0 { *target_bbx = bg->bbx; } else if ( bf->bbx_mode == BDF_BBX_MODE_MAX ) // mode 2 (monospace) { *target_bbx = bf->max; target_bbx->x = 0; if ( bg->bbx.x < 0 ) bg->shift_x = bg->bbx.x; if ( target_bbx->w < bg->dwidth_x ) target_bbx->w = bg->dwidth_x; } else if ( bf->bbx_mode == BDF_BBX_MODE_5X7 ) // mode 4 (5x7 displays) { *target_bbx = bf->max; target_bbx->x = 0; if ( bg->bbx.x < 0 ) bg->shift_x = bg->bbx.x; target_bbx->w = 5; } else if ( bf->bbx_mode == BDF_BBX_MODE_M8 ) { /* old target_bbx->w = bf->max.w; if ( target_bbx->w < bg->dwidth_x ) target_bbx->w = bg->dwidth_x; target_bbx->w = (target_bbx->w+7) & ~7; target_bbx->h = (bf->max.h+7) & ~7; */ target_bbx->w = 8; target_bbx->h = 8; target_bbx->x = bf->max.x; target_bbx->y = bf->max.y; target_bbx->x = 0; if ( bg->bbx.x < 0 ) bg->shift_x = bg->bbx.x; } else { *target_bbx = bf->max; target_bbx->w = bg->bbx.w; target_bbx->x = bg->bbx.x; target_bbx->x = 0; if ( bg->bbx.x < 0 ) { /* e.g. "j" */ target_bbx->w -= bg->bbx.x; bg->shift_x = bg->bbx.x; } else { /* e.g. "B" */ target_bbx->w += bg->bbx.x; //bg->shift_x = bg->bbx.x; } if ( target_bbx->w < bg->dwidth_x ) target_bbx->w = bg->dwidth_x; } bg->width_deviation = target_bbx->w - bg->bbx.w; } void bf_CalculateMaxBitFieldSize(bf_t *bf) { int i; bg_t *bg; int bs; bbx_t local_bbx; bf->bbx_x_max_bit_size = 0; bf->bbx_y_max_bit_size = 0; bf->bbx_w_max_bit_size = 0; bf->bbx_h_max_bit_size = 0; bf->dx_max_bit_size = 0; for( i = 0; i < bf->glyph_cnt; i++ ) { bg = bf->glyph_list[i]; if ( bg->map_to >= 0 ) { bf_copy_bbx_and_update_shift(bf, &local_bbx, bg); #ifdef OLD_CLODE /* modifing the following code requires update ind bdf_rle.c also */ if ( bf->bbx_mode == BDF_BBX_MODE_MINIMAL ) { local_bbx = bg->bbx; } else if ( bf->bbx_mode == BDF_BBX_MODE_MAX ) { local_bbx = bf->max; local_bbx.x = 0; if ( bg->bbx.x < 0 ) bg->shift_x = bg->bbx.x; if ( local_bbx.w < bg->dwidth_x ) local_bbx.w = bg->dwidth_x; } else if ( bf->bbx_mode == BDF_BBX_MODE_M8 ) { local_bbx.w = bf->max.w; if ( local_bbx.w < bg->dwidth_x ) local_bbx.w = bg->dwidth_x; local_bbx.w = (local_bbx.w+7) & ~7; local_bbx.h = (bf->max.h+7) & ~7; local_bbx.x = bf->max.x; local_bbx.y = bf->max.y; local_bbx.x = 0; if ( bg->bbx.x < 0 ) bg->shift_x = bg->bbx.x; } else { local_bbx = bf->max; local_bbx.w = bg->bbx.w; local_bbx.x = bg->bbx.x; local_bbx.x = 0; if ( bg->bbx.x < 0 ) { /* e.g. "j" */ local_bbx.w -= bg->bbx.x; bg->shift_x = bg->bbx.x; } else { /* e.g. "B" */ local_bbx.w += bg->bbx.x; //bg->shift_x = bg->bbx.x; } if ( local_bbx.w < bg->dwidth_x ) local_bbx.w = bg->dwidth_x; } #endif bs = get_unsigned_bit_size(local_bbx.w); if ( bf->bbx_w_max_bit_size < bs ) bf->bbx_w_max_bit_size = bs; bs = get_unsigned_bit_size(local_bbx.h); if ( bf->bbx_h_max_bit_size < bs ) bf->bbx_h_max_bit_size = bs; //printf("%ld ", local_bbx.x); bs = get_signed_bit_size(local_bbx.x); if ( bf->bbx_x_max_bit_size < bs ) bf->bbx_x_max_bit_size = bs; //printf("%d:%d ",(int)local_bbx->x, (int)bs); bs = get_signed_bit_size(local_bbx.y); if ( bf->bbx_y_max_bit_size < bs ) bf->bbx_y_max_bit_size = bs; if ( bf->bbx_mode == BDF_BBX_MODE_MINIMAL ) { bs = get_signed_bit_size(bg->dwidth_x); } else if ( bf->bbx_mode == BDF_BBX_MODE_MAX ) { bs = get_signed_bit_size(local_bbx.w); } else { bs = get_signed_bit_size(local_bbx.w); } if ( bf->dx_max_bit_size < bs ) bf->dx_max_bit_size = bs; } } bf_Log(bf, "bf_CalculateMaxBitFieldSize: bbx.x=%d, bbx.y=%d, bbx.w=%d, bbx.h=%d, dwidth=%d", bf->bbx_x_max_bit_size, bf->bbx_y_max_bit_size, bf->bbx_w_max_bit_size, bf->bbx_h_max_bit_size, bf->dx_max_bit_size); } void bf_ShowMonospaceStatistics(bf_t *bf) { int i; bg_t *bg; long cnt = 0; long max = 0; long sum = 0; for( i = 0; i < bf->glyph_cnt; i++ ) { bg = bf->glyph_list[i]; if ( bg->map_to >= 0 ) { if ( max < bg->width_deviation ) max = bg->width_deviation; sum += bg->width_deviation; cnt++; } } if ( cnt == 0 ) cnt++; bf_Log(bf, "Monospace Statistics: Max width extention %ld, average width extention %ld.%ld", max, sum/cnt, (sum*10/cnt) % 10 ); /* Monospaced font: average width extention does not differ much between -b 1 and --b 2 Variable width font: big difference for the average width extention between modes 1 and 2. Examples: ./bdfconv -b 1 -v ../bdf/profont12.bdf Monospace Statistics: Max width extention 6, average width extention 1.7 ./bdfconv -b 2 -v ../bdf/profont12.bdf Monospace Statistics: Max width extention 6, average width extention 1.7 --> profont12.bdf is a monospaced font ./bdfconv -b 1 -v ../bdf/helvR12.bdf Monospace Statistics: Max width extention 6, average width extention 1.9 ./bdfconv -b 2 -v ../bdf/helvR12.bdf Monospace Statistics: Max width extention 15, average width extention 8.0 --> helvR12.bdf is not a monospaced font */ } int bf_WriteUCGCByFP(bf_t *bf, FILE *out_fp, const char *fontname, const char *indent) { int i; int bytes_per_line = 16; fprintf(out_fp, "/*\n"); fprintf(out_fp, " Fontname: %s\n", bf->str_font); fprintf(out_fp, " Copyright: %s\n", bf->str_copyright); fprintf(out_fp, " Glyphs: %d/%d\n", (int)bf->selected_glyphs, (int)bf->glyph_cnt ); fprintf(out_fp, " BBX Build Mode: %d\n", (int)bf->bbx_mode); fprintf(out_fp, "*/\n"); fprintf(out_fp, "#include \"ucg.h\"\n"); fprintf(out_fp, "const ucg_fntpgm_uint8_t %s[%d] UCG_FONT_SECTION(\"%s\") = {\n", fontname, bf->target_cnt, fontname); fprintf(out_fp, " "); for( i = 0; i < bf->target_cnt; i++ ) { fprintf(out_fp, "%d", bf->target_data[i]); if ( i+1 != bf->target_cnt ) fprintf(out_fp, ","); if ( (i+1) % bytes_per_line == 0 ) fprintf(out_fp, "\n%s", indent); } fprintf(out_fp, "};\n"); return 1; } int bf_WriteU8G2CByFP(bf_t *bf, FILE *out_fp, const char *fontname, const char *indent) { int i; int bytes_per_line = 32; int extra1; fprintf(out_fp, "/*\n"); fprintf(out_fp, " Fontname: %s\n", bf->str_font); fprintf(out_fp, " Copyright: %s\n", bf->str_copyright); fprintf(out_fp, " Glyphs: %d/%d\n", (int)bf->selected_glyphs, (int)bf->glyph_cnt ); fprintf(out_fp, " BBX Build Mode: %d\n", (int)bf->bbx_mode); fprintf(out_fp, "*/\n"); if ( bf->target_data[bf->target_cnt-1] == 0 ) extra1 = 0; else extra1 = 1; if ( bf->target_cnt-1+extra1 > 32760 ) { fprintf(out_fp, "#ifdef U8G2_USE_LARGE_FONTS\n"); } if ( bf->bbx_mode == 3 ) // maybe better check for the font_format { //fprintf(out_fp, "#include \"u8x8.h\"\n"); fprintf(out_fp, "const uint8_t %s[%d] U8X8_FONT_SECTION(\"%s\") = \n", fontname, bf->target_cnt+extra1, fontname); } else { //fprintf(out_fp, "#include \"u8g2.h\"\n"); fprintf(out_fp, "const uint8_t %s[%d] U8G2_FONT_SECTION(\"%s\") = \n", fontname, bf->target_cnt+extra1, fontname); } fprintf(out_fp, "%s\"", indent); for( i = 0; i < bf->target_cnt-1+extra1; i++ ) { if ( bf->target_data[i] < 32 || bf->target_data[i] == '\"' || bf->target_data[i] == '\\' || bf->target_data[i] == '?' || ( bf->target_data[i] >= '0' && bf->target_data[i] <= '9' )) { fprintf(out_fp, "\\%o", bf->target_data[i]); //fprintf(out_fp, "\\x%02x", bf->target_data[i]); } else if ( bf->target_data[i] < 127 ) /* issue 482, do not output ASCII char 127, instead use octal code for 127 */ { fprintf(out_fp, "%c", bf->target_data[i]); } else { fprintf(out_fp, "\\%o", bf->target_data[i]); } if ( (i+1) % bytes_per_line == 0 ) fprintf(out_fp, "\"\n%s\"", indent); } fprintf(out_fp, "\";\n"); if ( bf->target_cnt-1+extra1 > 32760 ) { fprintf(out_fp, "#endif /* U8G2_USE_LARGE_FONTS */\n"); } return 1; } int bf_WriteUCGCByFilename(bf_t *bf, const char *filename, const char *fontname, const char *indent) { FILE *fp; fp = fopen(filename, "wb"); if ( fp == NULL ) { bf_Log(bf, "bf_WriteUCGCByFilename: Open error '%s'", filename); return 0; } bf_WriteUCGCByFP(bf, fp, fontname, indent); bf_Log(bf, "bf_WriteUCGCByFilename: Write file '%s'", filename); fclose(fp); return 1; } /* called from main() */ int bf_WriteU8G2CByFilename(bf_t *bf, const char *filename, const char *fontname, const char *indent) { FILE *fp; fp = fopen(filename, "wb"); if ( fp == NULL ) { bf_Log(bf, "bf_WriteU8G2CByFilename: Open error '%s'", filename); return 0; } bf_WriteU8G2CByFP(bf, fp, fontname, indent); bf_Log(bf, "bf_WriteU8G2CByFilename: Write file '%s'", filename); fclose(fp); return 1; } /* xo, yo: offset for 8x8 fonts (font_format==2) called from main() */ bf_t *bf_OpenFromFile(const char *bdf_filename, int is_verbose, int bbx_mode, const char *map_str, const char *map_file_name, int font_format, int xo, int yo, int th, int tv) { bf_t *bf; bf = bf_Open(is_verbose, bbx_mode); if ( bf != NULL ) { bf->tile_h_size = th; bf->tile_v_size = tv; if ( bf_ParseFile(bf, bdf_filename) != 0 ) { if ( map_file_name[0] != '\0' ) { bf_MapFile(bf, map_file_name); } else { bf_Map(bf, map_str); } bf_CalculateSelectedNumberOfGlyphs(bf); bf_ReduceAllGlyph(bf); bf_CalculateMaxBBX(bf); //bf_ShowAllGlyphs(bf, &(bf->max)); bf_CalculateMinMaxDWidth(bf); /* issue 669 */ if ( bf->bbx_mode == BDF_BBX_MODE_MAX ) if ( bf->max.w < bf->dx_max ) bf->max.w = bf->dx_max; bf_CalculateMaxBitFieldSize(bf); if ( font_format == 0 || font_format == 1 ) { bf_RLECompressAllGlyphs(bf); } else { bf_Generate8x8Font(bf, xo, yo); /* bdf_8x8.c */ } if ( bf->bbx_mode != BDF_BBX_MODE_MINIMAL ) bf_ShowMonospaceStatistics(bf); /* Show stats only for none minimal mode. For minimal mode it will always be zero */ return bf; } bf_Close(bf); } return NULL; }