In the DOS world screen or printer fonts come in
Code Page Information format. There are two versions:
the MS-DOS/PC-DOS "FONT" format, and the DR-DOS/Novell DOS
"DRFONT" compressed format.
These *.CPI files may contain fonts for several
code pages, and, given a code page, for several point sizes.
MS-DOS files usually have 16x8, 14x8, 8x8 fonts, while
DR-DOS files often have 6x8, 8x8, 14x8, 16x8 fonts (in this order).
Printer .CPI files have only one font.
(One sometimes encounters entirely different .cpi files,
namely Unix .cpio files moved to a DOS machine with 8.3 filenames.
Such files are archives.)
The files in "FONT" format have the following layout.
First a 23-byte header.
struct {
char id0; /* 0: 0xff */
char id[7]; /* 1-7: "FONT " */
char reserved[8]; /* 8-15: 0 */
short pnum; /* 16-17: number of pointers: 1 */
char ptyp; /* 18: type of pointers: 1 */
long fih_offset; /* 19-22: file offset of FontInfoHeader: 0x17 */
} FontFileHeader; /* 0-22 */
(Files in "DRFONT compressed format" have 0x7f "DRFONT " in bytes 0-7.
After this FontFileHeader they have an extended header
struct {
char num_fonts_per_codepage; /* 23: N=4 */
char font_height[N]; /* 24-27: height of each font */
long dfd_offset[N]; /* 28-43: file offsets of DisplayFontData */
} DRDOSExtendedFontFileHeader;
and consequently fih_offset will be 44 (0x2c) instead of 23 (0x17).)
Next a 2-byte header that tells how many code pages this file contains.
struct {
short num_codepages;
} FontInfoHeader; /* 23-24 */
Next the indicated number of code pages.
Each code page has a header and font data. In some files all headers
come first and then all font data. In other files headers and font data
alternate, that is, data for one code page is kept together.
The code page header (the offsets given are for the first occurrence):
struct {
short cpeh_size; /* 25-26: size of this header: 28 */
long next_cpeh_offset; /* 27-30: offset of next header; 0 or -1 for last */
short device_type; /* 31-32: 1: screen, 2: printer */
char device_name[8]; /* 33-40: e.g. "EGA " */
short codepage; /* 41-42: 0, 437, 737, 85[0257], 86[013569], ... */
char reserved[6]; /* 43-48: 0 */
long cpih_offset; /* 49-52: pointer to CPInfoHeader or 0 */
} CPEntryHeader; /* 25-52 */
MS-DOS and PC-DOS sometimes have 26 instead of 28 for the cpeh_size
field in printer font files; one even meets both 26 and 28 in the same file.
Probably there is confusion over whether cpih_offset
is short or long.
When headers and fonts are interspersed, next_cpeh_offset
will point past the font, regardless of whether more entries follow.
When first all headers are given, next_cpeh_offset is zero
in the last header.
It happens that next_cpeh_offset does not point to the
next header, but to the one after that, or that it is zero while
still one header follows. In such cases num_codepages
gives the correct number of headers. (In the cases where it is
zero while still one header follows, the last header is a dummy
one, for codepage 0 and with no associated font.)
Early DR-DOS printer font files have 1 instead of 2 in device_type.
Device names include "EGA ", "LCD " for screen, and
"4201 ", "4208 ", "5202 ", "1050 ", "EPS ", "PPDS "
for printer devices.
A code page font starts with a general header:
struct {
short version; /* 53-54: 1: FONT, 2: DRFONT */
short num_fonts; /* 55-56 */
short size; /* 57-58: length of font data for each font */
} CPInfoHeader; /* 53-58 */
(For printer fonts num_fonts is 1 or 2, while only a single
font follows. For screen fonts num_fonts is 1, 3 or 4.
DRFONT files have in the size field the size (24) of the
DRFONT header without the index table.)
And then for each font a header followed by the actual data.
For screen fonts
struct {
char height; /* 59: one of 6, 8, 14, 16 */
char width; /* 60: 8 */
short reserved; /* 61-62: 0 */
short num_chars; /* 63-64: 256 */
} ScreenFontHeader; /* 59-64 */
and for printer fonts
struct {
short printer_type; /* 59-60: 1=4201/1050/EPS, 2=5202/4208/PPDS */
short seqlength; /* 61-62: length of escape sequences */
} PrinterFontHeader; /* 59-62 */
followed by two escape sequences of the indicated length
to select the hardware codepage or the downloaded codepage.
However, in DRFONT files the CPInfoHeader is followed by
the DRFONT header, consisting first of 4 ScreenFontHeaders,
one for each point size, and then an index indicating where
in the font bitmap the corresponding characters can be found.
struct {
struct ScreenFontHeader sfh[4];
short FontIndex[256];
} DRFONTheader;
The font index is some integer, in the range 0-400 or so,
indicating where in the font this code position can be found.
In this way the font data is separated from the code used.
Linux does not accept .CPI files, but the codepage
utility from the kbd package is willing to read .CPI files
of "FONT" type, and output .cp files suitable for setfont.
For example, the call codepage -a iso.cpi will create
ten font files 850.cp, 437.cp, ..., 869.cp
each containing a single font of pointsize 16, and
codepage -a ega2.cpi six font files
850.cp, ..., 737.cp each containing three fonts
of pointsizes 8, 14, 16.