Font-formats recognized by the Linux kbd package: CPI fonts

Next Previous Contents

## 3. CPI fonts

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.

## 3.1 Linux use

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.

Next Previous Contents