FontEdit: The FYSOS Font Creator

Update: 16 Apr 2022: New version allows opening/saving in four formats. My FNT format described below, Linux's PSFv1 and PSFv2 formats, and the Grub PFF2 format. See the 'Other Functions' section below for more information.
Update: 30 Jan 2022: New version allows opening/saving in three formats. My FNT format described below, and Linux's PSFv1 and PSFv2 formats.
Update: 29 Jan 2022: New version now allowing for 32-bit Unicode values instead of only 8-bit ASCII values.
(See the bottom of this page for instructions on converting your existing 'FONT' files to this new 'Font' file.)

Reasoning:

If you are like me, you have a hobby of creating your own operating system.

Back in the day, you could simply send a 16-bit attribute/character pair to the screen hardware and it would display the character for you.
This was due to the video hardware (and firmware) included in the machine, having the necessary function to take this 16-bit value and convert it to a bitmap of the character, pixel by pixel.

This is now called the Legacy firmware and hardware.

However, lately, more and more systems are starting to use a new firmware called UEFI and no longer includes the legacy hardware function to convert that 16-bit value to a bitmap. Therefore, it is up to you, the developer, to display your own font to the screen.

The easiest way is to have an already created, hardcoded if you will, bitmap in memory and simply display a pixel if the bit is set, or bring through the background if the bit is clear.

The difficult and tedious task here is creating that bitmap.

This is why I created this utility, to simply allow me to create a font, saved as some meta-data and a stream of bits. Here is an example:

lucida_c.png

The app allows you to click on a box to "set" the bit or "clear" the bit saving each character of the font.

To Create a new Font

Click on File, New
new_font.png

Creating a Character

Again, the three Delta fields have nothing to do with the font file. The character is independent of these fields. These fields are used by your Font Engine. See the format of the font file below for more information.

Other Functions

The All-Important File Format:

The file format is explained below. This is the format of the file you need to read from disk, or hard-code some or all of it into your kernel file.

The font file has three sections.
  1) a 'struct FONT' header
  2) a count of 'struct FONT_INFO' blocks, one for each character in the file.
  3) the bit stream. This bit stream is a set of bits for each character.

The Font Header.
  #define MAX_NAME_LEN  32
  
  struct FONT {
    bit8u  sig[4];       // 'Font'
    bit8u  height;       // height of char set
    bit8u  max_width;    // width of widest char in set
    bit16u info_start;   // zero based offset to the first FONT_INFO block
    bit32s start;        // starting value (first entry in font == this value) (*** Signed ***)
    bit32s count;        // count of chars in set ( 0 < count <= 0x10FFFF ) (*** Signed ***)
    bit32u datalen;      // len of the data section in bytes
    bit32u total_size;   // total size of this file in bytes
    bit32u flags;        // bit 0 = fixed width font, remaining bits are reserved
    bit8u  name[MAX_NAME_LEN]; // utf-8 null terminated
    bit8u  resv[36];     // reserved and preserved
  };

The members are byte aligned, using no padding.
Notes:
The structure above is the first 96 bytes of the file. Here is a dump of an example file:

    00000000  46 6F 6E 74 0A 08 60 00-20 00 00 00 5F 00 00 00     Font..`....._...
    00000010  B6 03 00 00 8A 08 00 00-01 00 00 00 43 6F 75 72     ............Cour
    00000020  69 65 72 20 4E 65 77 00-00 00 00 00 00 00 00 00     ier.New.........
    00000030  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00     ................
    00000040  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00     ................
    00000050  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00     ................
    
    00000000  46 6F 6E 74    // sig = 'Font'
              0A             // height = 10 bits
              08             // width of widest char = 8 bits
              60 00          // zero based offset to the first FONT_INFO block
              20 00 00 00    // starting ascii char = 32 = space character
              5F 00 00 00    // count of chars in set = 95
              B6 03 00 00    // bytes in data section (950 bytes)
              8A 08 00 00    // bytes in whole file (2,186 bytes)
              01 00 00 00    // flags: bit 0 set = fixed width font

              43 6F 75 72 69 65 72 20 4E 65 77 00 00 00 00 00 // Name: "Courier New" + ASCIIZ
              00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00

              00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
              00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
              00 00 00 00

All reserved fields should be zero when creating a new file and preserved when updating an existing file.

Following that 96 bytes is a count of 12-byte structures, one for each character in the set:
Note that the info_start field in the header indicates where this block set starts, though is usually 96.
Also note that at this time, if this value is larger than 96, any data between the header and this block set is undefined. This optional space is available for future use.

    struct FONT_INFO {
      bit32u index;   // index of this character in data section
      bit8u  width;   // Width of character
      char   deltax;  // +/- offset to print char 
      char   deltay;  // +/- offset to print char (allows for drop chars, etc)
      char   deltaw;  // +/- offset to combine with width above when moving to the next char
      bit8u  resv[4]; // reserved
    };
Here is a dump of the first 2 character entries: (no bitmaps. the bitmap follows this set of 12-byte entries)

  00000060  00 00 00 00 08 00 00 00-00 00 00 00
            0A 00 00 00 08 00 00 00 00 00 00 00
  
  00000060  00 00 00 00  // offset is zero.  First character in the data stream
            08           // 8 bits wide
            00           // signed delta 'x'
            00           // signed delta 'y' 
            00           // signed delta 'w'
            00 00 00 00  // reserved

  0000006C  0A 00 00 00  // offset is 10.  Ten bytes from beginning of data stream
            08           // 8 bits wide
            00           // signed delta 'x'
            00           // signed delta 'y' 
            00           // signed delta 'w'
            00 00 00 00  // reserved
  


The bitmap data stream follows the blocks above.
It is a bit stream of each character.
The first bit is the upper left pixel of the character, the next bit being the next in the row and so on. (The first bit is bit 7, the next is bit 6, etc.)
The bit stream is NOT right-side padded. i.e.: If the character is 5 bits in width, the first byte of the bit stream holds all of the first row and 3 bits of the second row.
The bit stream IS end padded. i.e.: The next character's bit stream will start on a byte boundary.

For example:
- On an eight bit width character, first byte of the bit stream, bit 7 is the upper left pixel and bit 0 is the upper right pixel.
- On a nine bit width character, first byte of the bit stream, bit 7 is the upper left pixel and bit 7 of the next byte is the upper right pixel on the first row.

You can dump the pixel data to a file for debugging purposes, and more importantly, to hard code into your kernel. Here is an example of a dump file's semi-colon entry:

   /* character 27 */
   0x00, 0x24, 0x02, 0x50, 0x00,
   /* Bitmap:
    ...
    ...
    ...
    .1.
    .1.
    ...
    ...
    .1.
    .1.
    1..
    ...
    ...
    */
  


Please note the following items:

If you have existing FONT files and wish to update to this new format, I have included the source code to a console app that will make the conversion for you. See the .zip file for 'conv.c' and 'conv.h'.


Contact: fys [at] fysnet [dot] net
Download: https://www.fysnet.net/fontedit/fontedit.zip (229k)
Contents: fontedit.exe (for 32-bit, Windows XP preferred) and fontedit64.exe (for 64-bit Windows, Windows 10 preferred)
Current version (01.54.10) is dated: 2022 Feb 5
Win10 x64 version may require updated DLLs.

Source code is available at my book's source code github page.


Here is a list of available fonts:
Font name Start End Width Height      Description/Comments Author
Arial.fnt 32 126 variable 12 Standard Arial Font FYSOS
Couriernew.fnt 32 126 8 10 Standard Courier New Font FYSOS
ImprintShadow.fnt 32 127 variable 21 Imprint Shadow FYSOS
LucidaC.fnt 32 127 variable 19 Lucida Calligraphy FYSOS
OCRAExtended.fnt 32 255 10 14 OCR A Extended FYSOS
SansSerf.fnt 0 255 variable 12 Sans Serif Font FYSOS
Simple.fnt 32 126 variable 12 Standard Simple Font FYSOS
System128.fnt 0 127 8 14 Standard System Font (0 -> 127) FYSOS
System256.fnt 0 255 8 14 Standard System Font (0 -> 255) FYSOS
SystemCaps.fnt 0 127 8 14 Similar to System128 with small caps for the lower-case characters FYSOS
WindBlown.fnt 32 127 8 14 Characters are blown to the right (missing a couple of characters) FYSOS
wopr.fnt 32 127 8 9 "Shall we play a game!" FYSOS
Your font could be listed here ? ? ? ? Name of your font You


See my other pages on OS Development:
- https://www.fysnet.net/osdesign_book_series.htm (My book series)
- https://www.fysnet.net/fysos.htm (My OS Project)
- https://www.fysnet.net/ultimate/index.htm
- https://forum.osdev.org/ (OS Dev Forum)