Font File format
Typically, fonts are saved with “.fnt” as the file extension. AFAIK, there are no actual rules as to what you save a font file as. The font file format is not limited to fonts. It is for any monochrome bitmap sequence. Since it is unlikely that you would want a monochrome bitmap for anything other than a font (although bitmasking is a possibility), i'll differentiate them from the 2 bit bitmap sequence files by just calling them font files.
Structure of the file
A font file is divided into two parts, the font header and the character records.
[FONTHDR] 4 BYTES //font header [CHRRECS] ? BYTES //character records
i'm going to give a section to each part of the file format
The font header(FNTHDR)
The font header contains information about the font, including how many characters are in the font as well as how wide the font is.
[FONTHDR] BEGIN [FILEID] 1 BYTE VALUE=0x01 //0x01 as the first byte of the header indicates a monochrome bmp seq [CHRCNT] 1 BYTE VALUE=? //number of character records that can be found in the bmp seq [MAXWID] 1 BYTE VALUE=? //largest width found in the bmp seq [MAXHGT] 1 BYTE VALUE=? //largest height found in the bmp seq END
MAXWID and MAXHGT are measured in pixels.
Character records(CHRRECS)
Within the character records portion of the file, a number (corresponding to CHRCNT from FONTHDR) of variably sized character records are stored.
[CHRRECS] BEGIN [CHRREC0] ? BYTES //first character record [CHRREC1] ? BYTES //second character record [CHRREC2] ? BYTES //third character record . . . [CHRRECn] ? BYTES //last character record (n=CHRCNT-1) END
Individual Characters(CHRRECx)
A single character record contains binary data about the character, as well as a header containing the extent of that binary data.
[CHRRECx] //x is any record number BEGIN [CHRHDR] 4 BYTES //character record header [CHRDAT] ? BYTES //binary data for bitmap END
Character Header(CHRHDR)
The character header contains information about an individual character's extent.
[CHRHDR] BEGIN [CHRX] 1 BYTE //x offset(in pixels) [CHRY] 1 BYTE //y offset(in pixels) [CHRW] 1 BYTE //width of bitmap data(in pixels) [CHRH] 1 BYTE //height of bitmap data(in pixels) END
This describes a rectangle that must lie within the rectangle specified by the font header.
CHRX must be in the range [0,MAXWID)*
CHRY must be in the range [0,MAXHGT)*
CHRX+CHRW must not exceed MAXWID and must be at least 1*
CHRY+CHRH must not exceed MAXHGT and must be at least 1*
The size of CHRDAT can be determined based on CHRW and CHRH
(size of CHRDAT)=(CHRH)*(((CHRW)+7)/8)
Character Data(CHRDAT)
The character data contains a number of variably sized scan lines corresponding to CHRH.
[CHRDAT] BEGIN [SCANLINE0] ? BYTES //first scan line [SCANLINE1] ? BYTES //second scan line [SCANLINE2] ? BYTES //third scan line . . . [SCANLINEn] ? BYTES //last scan line(n=CHRH-1) END
Scan lines(SCANLINEx)
Individual scan lines contain bit values for each horizontal row in the bmp. It contains one byte for every eight pixels in the bmp, or any fraction thereof.
If CHRW is [1,8], there will be 1 BYTE in the scan line if
CHRW is [9,16], there will be 2 BYTES in the scan line If
CHRW is [17,24] there will be 3 BYTES in the scan line (etc)
Each byte contains information for up to eight pixels. The first pixel is represented by bit7, then bit6, and so on.
For example, the following line of pixels (1=black, 0=white):
010110
would have the following bit values and would fit into a single byte:
b7: 0 b6: 1 b5: 0 b4: 1 b3: 1 b2: 0 (b1 and b0 will have a value of 0, although they will be ignored) for a byte value of 0x58
For another example, the following line of pixels:
01011000101
would require 2 bytes, since there are 11 pixels, and the bit values would look like:
BYTE0: b7: 0 b6: 1 b5: 0 b4: 1 b3: 1 b2: 0 b1: 0 b0: 0 BYTE1: b7: 1 b6: 0 b5: 1 (b4 through b0 are ignored and have a value of 0)
and so, the bytes would be 0x58 and 0xA0
Encoding Letters
Here are two examples of encoding a monochrome bitmap into a character record.
The letter A
Bit pattern:
01234567 0 00110000 1 01111000 2 11001100 3 11001100 4 11111100 5 11001100 6 11001100 7 00000000
(indices for x and y position are above and to left of bit pattern)
Determine CHRX and CHRY
The first column with a 1 in it is the first column(index 0), so CHRX will be 0 The first row with a 1 in it is the first row(index 0), so CHRY will be 0
Determine CHRW and CHRH
The two rightmost columns are not used, the first unused column having an index of 6, so CHRW=6-CHRX=6-0=6. The bottom most row is not used, the first unused row has an index of 7, so CHRH=7-CHRY=7-0=7
so, the CHRHDR will look like:
00 00 06 07
We strip away the excess columns and rows from the bit pattern:
012345 0 001100 1 011110 2 110011 3 110011 4 111111 5 110011 6 110011
And we encode each scan line, winding up with CHRDAT looking like:
30 78 CC CC FC CC CC
The entire character record looks like:
00 00 06 07 30 78 CC CC FC CC CC
The Comma (,)
The bit pattern:
01234567 0 00000000 1 00000000 2 00000000 3 00000000 4 00000000 5 00011000 6 00011000 7 00110000
Determine CHRX and CHRY
the first column with a 1 in it has an index of 2, so CHRX=2 the first row with a 1 in it has an index of 5, so CHRY=5
Determine CHRW and CHRH
the rightmost three columns are unused. the first of these has and index of 5, so CHRW=5-CHRX=5-2=3 the bottommost row is used, so the first unused row would have an index of 8, so CHRH=8-CHRY=8-5=3
The CHRHDR for this character would look like:
02 05 03 03
Remove rows and columns from the bit pattern
012 0 011 1 011 2 110
(note: i renumbered the indices)
now, encode each of the scan lines into a single byte (since there are only 3 bits each)
60 60 C0
and the entire character record would look like:
02 05 03 03 60 60 C0
Final Words
One thing remains to say. The first bitmap in the font file [CHRREC0] corresponds to character 0x20, which is more commonly known as just “the space”.
i've had difficulty using 2pic.exe to make font files. i guess the command line for it is just too long.