/*---------------------------------------------------------------------------- Code to dump or display characters in .FON bitmap font file Chris Giese http://SisAndHappy.com/ChrisGiese I, the copyright holder of this work, hereby release it into the public domain. This applies worldwide. If this is not legally possible: I grant any entity the right to use this work for any purpose, without any conditions, unless such conditions are required by law. 28 Jan, 2022: extensively redone; separate "dump font", "load font", and "demo font" routines 24 Feb, 2012: read_le[16|32]() redone 7 Apr, 2005: initial release Sources: - A file from www.wotsit.org which actually contained a description of .FNT files. Since .FNT files are embedded in .FON files as Win16 resources, this was indeed helpful. The text file contained these notes at the top: Windows 3 Developer's Notes summary ENDUSER This article can be found in the Software/Data Library by searching on the keyword FONTFMT or S12687. - Ralf Brown's Interrupt List contains information about the DOS .EXE header, Win16 .EXE header, and Win16 resource table .FON files are actually Win16 DLLs. They typically contain: 1. a DOS .EXE stub which, if you attempt to "run" the DLL from plain DOS, displays a message like This program cannot be run in DOS mode 2. a Win16 New Executable (NE) header 3. a Win16 resource table 4. a FONTDIR resource 5. one or more FONT resources 6. a VERSION resource The FONT resources contain embeded .FNT files. Note that the bitmaps are stored in column-major order, i.e. for the following bitmap: ........ .... 00 00 .....**. .... 06 00 ....*..* .... 09 00 ...*.... *... 10 80 ..*..... .*.. 20 40 ..*..... .*.. 20 40 ..*..... .*.. 20 40 ..****** **.. 3F C0 ..*..... .*.. 20 40 ..*..... .*.. 20 40 ..*..... .*.. 20 40 ........ .... 00 00 the data is stored in the .FON file as 00 06 09 10 20 20 20 3F 20 20 20 00 00 00 00 80 40 40 40 C0 40 40 40 00 Proportional spacing data is stored in a separate "character table" ----------------------------------------------------------------------------*/ #include /* malloc(), free(), atexit() */ #include /* memset() */ /* EOF, SEEK_..., FILE, printf(), putchar() */ #include /* fopen(), fseek(), ftell(), fread(), fclose() */ #include /* union REGS, int86() */ #if 0 #include #else typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef signed short int16_t; typedef unsigned long uint32_t; #endif #if defined(__TURBOC__) #define HUGE huge #define set_graphics_mode(X) { union REGS regs; regs.x.ax = (X); int86(0x10, ®s, ®s); } #elif defined(__WATCOMC__)&&!defined(__386__) #define HUGE huge #define set_graphics_mode(X) { union REGS regs; regs.x.ax = (X); int86(0x10, ®s, ®s); } #define _argv __argv #elif defined(__WATCOMC__)&&defined(__386__) #define HUGE /* nothing */ #define set_graphics_mode(X) { union REGS regs; regs.w.ax = (X); int386(0x10, ®s, ®s); } #undef MK_FP #define MK_FP(S,O) (void *)((S) * 16uL + (O)) #define _argv __argv #elif defined(__DJGPP__) #include #include #define HUGE /* nothing */ #define set_graphics_mode(X) { union REGS regs; regs.w.ax = (X); int86(0x10, ®s, ®s); } #define MK_FP(S,O) (void *)((S) * 16uL + (O) + __djgpp_conventional_base) #define _argv __crt0_argv #else #error Sorry, unsupported compiler #endif typedef struct { unsigned bold : 1; unsigned italic : 1; unsigned strikeout : 1; unsigned underline : 1; unsigned points; uint32_t lowest_char, highest_char; uint8_t wd, ht; /* 'wd' is in BYTES; not pixels */ uint8_t *bitmaps; uint8_t *spacings; /* optional proportional spacing bytes */ // uint16_t *encodings; } font_t; typedef struct { uint16_t wd, ht; uint32_t bytes_per_row; uint8_t HUGE *raster; } img_t; typedef struct { int16_t src_x, src_y, dst_x, dst_y; uint16_t wd, ht; } clip_t; img_t g_fb; /****************************************************************************/ static unsigned read_pixel1(const img_t *img, unsigned x, unsigned y) { uint8_t HUGE *ptr, shift; ptr = &img->raster[img->bytes_per_row * y + x / 8]; shift = 7 - (x & 7); return (ptr[0] >> shift) & 0x01; } /****************************************************************************/ static void write_pixel1(img_t *img, unsigned x, unsigned y, unsigned c) { uint8_t HUGE *ptr, mask, val; ptr = &img->raster[img->bytes_per_row * y + x / 8]; mask = 0x80 >> (x & 7); val = (c & 0x01) * 0xFF; ptr[0] = (ptr[0] & ~mask) | (val & mask); } /****************************************************************************/ static void blit1(img_t *dst, const img_t *src, const clip_t *clip) { unsigned y, x, c; for(y = 0; y < clip->ht; y++) for(x = 0; x < clip->wd; x++) { c = read_pixel1(src, x + clip->src_x, y + clip->src_y); c &= 0x01; write_pixel1(dst, x + clip->dst_x, y + clip->dst_y, c); } } /****************************************************************************/ static void bputs(const font_t *font, const char *s) { static unsigned csr_x, csr_y; /**/ unsigned c; clip_t clip; img_t src; clip.ht = src.ht = font->ht; /* char height */ src.bytes_per_row = font->wd; /* char width, in bytes */ clip.src_x = 0; clip.src_y = 0; clip.dst_x = csr_x; clip.dst_y = csr_y; for(; *s != '\0'; s++) { c = *s - font->lowest_char; /* get char width and point to bitmap */ src.raster = &font->bitmaps[font->wd * font->ht * c]; src.wd = clip.wd = (font->spacings == NULL) ? font->wd * 8 : font->spacings[c]; // xxx - do clipping here blit1(&g_fb, &src, &clip); /* advance cursor */ clip.dst_x += clip.wd; } /* leave csr_x = 0 */ csr_y += font->ht; } /****************************************************************************/ static void set_text_mode(void) { set_graphics_mode(3); } /****************************************************************************/ static void demo_font(const font_t *font) { if(g_fb.raster == NULL) { #if defined(__DJGPP__) /* turn off data segment limit, for nearptr access */ if(!(_crt0_startup_flags & _CRT0_FLAG_NEARPTR)) { if(!__djgpp_nearptr_enable()) { printf("Error: can't enable near pointer " "access (WinNT/2k/XP?)\n"); return; } } #endif g_fb.wd = 640; g_fb.ht = 480; g_fb.bytes_per_row = 80; g_fb.raster = MK_FP(0xA000, 0); /* set VGA graphics mode 0x12: 640x480 pixels; four monochrome planes (16 colors; but we'll use only black and white) */ set_graphics_mode(0x12); atexit(set_text_mode); } // bputs(font, "Hello"); bputs(font, _argv[1]); } /****************************************************************************/ static unsigned read_le16(const void *buf0) { const uint8_t *buf = buf0; unsigned rv; rv = buf[1]; /* LSB */ rv <<= 8; rv |= buf[0]; /* MSB */ return rv; } /****************************************************************************/ static uint32_t read_le32(const void *buf0) { const uint8_t *buf = buf0; uint32_t rv; rv = buf[3]; /* MSB */ rv <<= 8; rv |= buf[2]; rv <<= 8; rv |= buf[1]; rv <<= 8; rv |= buf[0]; /* LSB */ return rv; } /****************************************************************************/ static int load_font(font_t *font, FILE *f, uint32_t res_offset) { unsigned num_chars, j, x, y; uint8_t hdr[118], *buf, *p; int i; memset(font, 0, sizeof(font_t)); fseek(f, res_offset, SEEK_SET); if(fread(hdr, 1, sizeof(hdr), f) != sizeof(hdr)) return -1; if(hdr[80] & 0x01) font->italic = 1; if(hdr[81] & 0x01) font->underline = 1; if(hdr[82] & 0x01) font->strikeout = 1; font->points = read_le16(hdr); font->lowest_char = hdr[95]; font->highest_char = hdr[96]; num_chars = hdr[96] - hdr[95] + 1; font->wd = (read_le16(&hdr[93]) + 7) / 8; font->ht = read_le16(&hdr[88]); // xxx - *4 assumes version 0x200 font if((buf = malloc(num_chars * 4)) == NULL) MEM: { printf("Error in load_font(): out of memory\n"); return -1; } if(fread(buf, 1, num_chars * 4, f) != num_chars * 4) ERR: { free(buf); printf("Error in load_font(): unexpected end of input\n"); return -1; } if((font->bitmaps = malloc(num_chars * font->ht * font->wd)) == NULL) { free(buf); goto MEM; } if((font->spacings = malloc(num_chars)) == NULL) { free(font->bitmaps); free(buf); goto MEM; } for(j = 0; j < num_chars; j++) { fseek(f, res_offset + read_le16(&buf[j * 4 + 2]), SEEK_SET); p = &font->bitmaps[j * font->ht * font->wd]; for(x = 0; x < font->wd; x++) { for(y = 0; y < font->ht; y++) { if((i = fgetc(f)) == EOF) { free(font->spacings); free(font->bitmaps); goto ERR; } p[font->wd * y + x] = i; } } font->spacings[j] = buf[j * 4]; } free(buf); return 0; } /***************************************************************************** display info about font resource at file offset 'res_off' in Win16 .EXE file 'in' .FNT file format, as encapsulated (as a Win16 resource) inside a .FON file: offset size description ------ ---- ----------- 0 2 version (00 02 for Win2.x, 00 03 for Win3.x) (MODERN.FON is 00 01 ???) 2 4 resource/file size 6 60 copyright string 66 2 font type (b0=vector font) 68 2 points 70 2 vertical resolution (in dots-per-inch, or dpi) 72 2 horizontal resolution (in dots-per-inch, or dpi) 74 2 ascent (distance from top of character cell to baseline) 76 2 internal leading (lines of pixheight used for accent marks) 78 2 external leading (additional lines requested for accents) 80 1 b0=italic 81 1 b0=underline 82 1 b0=strikeout 83 2 weight (400="regular") 85 1 charset (details?) 86 2 pixwidth (non-zero for monospaced fonts only) 88 2 pixheight (total height of character cell) 90 1 pitch and family b0=variable pitch (same as proportional spacing?) b7-b4=family: b0-b4 family description ----- ------ ------------------- 0 DONTCARE (or don't know) 1 ROMAN proportional spaced 2 SWISS proportional spaced 3 MODERN fixed-pitch 4 SCRIPT - 5 DECORATIVE - 91 2 average width (same as pixwidth for fixed-pitch fonts, else width of "X") 93 2 max width (same as pixwidth for fixed-pitch fonts) 95 1 first char in font (usually 32 for space) 96 1 last char in font (usually 255) 97 1 default char in font (used to display invalid chars) 98 1 break char (for word breaks) 99 2 width of font bitmap; in bytes (always even) 101 4 offset of string containing device name (0=none) 105 4 offset of string containing face name 109 4 (?) 113 4 offset of font bitmap 117 1 (reserved) (font version 0x200) 118 - character table (4 bytes per entry) (font version 0x300) 118 4 flags: b0=fixed-pitch font, b1=proportional-spaced font, b2=ABC fixed font, b3=ABC proportional font, b4=monochrome font, b5=16-color font, b6=256-color font, b7=RGB font 122 2 A-space (?) 124 2 B-space (?) 126 2 C-space (?) 128 4 offset of color table (color fonts only) 132 16 (reserved) 148 - character table (6 bytes per entry) *****************************************************************************/ static int dump_font(FILE *f, uint32_t res_offset) { uint8_t buf[118]; fseek(f, res_offset, SEEK_SET); if(fread(buf, 1, sizeof(buf), f) != sizeof(buf)) return -1; printf("ver=0x%3X", read_le16(buf)); printf(", %2u pt", read_le16(&buf[68])); // xxx - bold? if(buf[80] & 0x01) printf(", italic"); if(buf[81] & 0x01) printf(", underline"); if(buf[82] & 0x01) printf(", strikeout"); printf(", wt %u", read_le16(&buf[83])); printf(", %u x %u pixels", read_le16(&buf[93]), read_le16(&buf[88])); /* buf[22] & 3 = font pitch #define DEFAULT_PITCH 0 #define FIXED_PITCH 1 #define VARIABLE_PITCH 2 buf[22] >> 4 = font family #define FF_DONTCARE 0 #define FF_ROMAN 1 #define FF_SWISS 2 #define FF_MODERN 3 #define FF_SCRIPT 4 #define FF_DECORATIVE 5 */ printf(", font family=%u", buf[90] >> 4); printf(", chars %u-%u", buf[95], buf[96]); /* name */ fseek(f, read_le32(&buf[105]) - sizeof(buf) , SEEK_CUR); (void)fgets(buf, sizeof(buf), f); printf("\t%s\n", buf); return 0; } /****************************************************************************/ #include /* getch() */ int main(int arg_c, char *arg_v[]) { uint32_t new_exe_offset, shift, res_offset, save; unsigned j, res_type, res_count; uint8_t buf[64]; char *in_name; FILE *f; #if 0 /* sserife.fon sserifee.fon sserifeg.fon sserifer.fon sserifet.fon sseriff.fon sseriffe.fon sseriffg.fon sseriffr.fon sserifft.fon */ in_name = "/win/fonts/sserife.fon"; #else /* check args */ if(arg_c != 2) { printf("Dumps/displays .FON files\n"); return 1; } in_name = arg_v[1]; #endif /* open input file */ if((f = fopen(in_name, "rb")) == NULL) { printf("Error: can't open input file '%s'\n", in_name); return 2; } /* validate DOS .EXE header */ if(fread(buf, 1, 64, f) != 64 || buf[0] != 'M' || buf[1] != 'Z') NOT: { printf("Error: file '%s' is not a .FON file\n", in_name); fclose(f); return 3; } /* validate Win16 .EXE header */ j = read_le16(&buf[8]); /* DOS header size, in 16-byte paragraphs */ new_exe_offset = read_le32(&buf[60]); if(j < 4 || new_exe_offset == 0) goto NOT; fseek(f, new_exe_offset, SEEK_SET); if(fread(buf, 1, 38, f) != 38 || buf[0] != 'N' || buf[1] != 'E') goto NOT; /* find resource table and seek to it */ if((j = read_le16(&buf[36])) == 0) goto NOT; fseek(f, j + new_exe_offset, SEEK_SET); /* read rscAlignShift */ if(fread(buf, 1, 2, f) != 2) ERR: { printf("Error reading file '%s'\n", in_name); fclose(f); return 4; } shift = read_le16(&buf[0]); shift = 1 << shift; while(fread(buf, 1, 8, f) == 8) { res_count = read_le16(&buf[2]); /* resource type: 0=end, 0x8007=FONTDIR, 0x8008=FONT, 0x8010=VERSION */ if((res_type = read_le16(buf)) == 0 || res_type == 0x8008) break; fseek(f, res_count * 12, SEEK_CUR); } if(res_type == 0x8008 && res_count != 0) { printf("%u bitmap fonts in .FON file '%s'\n", res_count, in_name); for(j = 0; j < res_count; j++) { if(fread(buf, 1, 12, f) != 12) goto ERR; res_offset = read_le16(buf) * shift; save = ftell(f); #if 0 /* dump the font */ if(dump_font(f, res_offset) != 0) goto ERR; fseek(f, save, SEEK_SET); } } fclose(f); #else /* load the font(s) and use it to display some text */ { font_t font; if(load_font(&font, f, res_offset) != 0) goto ERR; (void)demo_font(&font); free(font.spacings); free(font.bitmaps); } fseek(f, save, SEEK_SET); } } fclose(f); getch(); #endif return 0; }