FontEdit: The FYSOS Font Creator

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 char for you.
This was because the video hardware (and firmware) included the necassary function to take this 16-bit value and convert it to a bitmap of the 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.
The difficult task here is creating that bitmap.

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



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


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. Please note that the values included here are used for my font engine. It is up to you to create an engine to display the font using some or all of these members.

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.
  struct FONT {
    bit8u  sig[4];       // 'FONT'
    bit8u  height;       // height of char set (in bits)
    bit8u  max_width;    // width of widest char in set (in bits)
    bit16u start;        // starting asciiz value (first entry in font == this ascii value)
    bit16u count;        // count of chars in set ( 0 < count <= 256 )
    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  version;      // version of font file (4-bit major : 4-bit minor) (0x10 = 1.0)
    bit8u  type;         // type of font included in this file (0 = bitmap)
    bit8u  type_vers;    // version of font type used (4-bit major : 4-bit minor) (0x10 = 1.0)
    bit8u  resv[11];     // reserved
    char   name[16];     // 15 chars, 1 null
    bit8u  resv1[44];    // reserved (Specific to my engine)
    //struct FONT_INFO info[];  // char info
    //bit8u data[];     // char data
  };  

The members are byte aligned, using no padding.

The structure above is the first 96 bytes of the file. Here is a dump of an example file:

    00000000  46 4F 4E 54 0A 08 20 00-5F 00 B6 03 00 00 EE 06     FONT...._.......
    00000010  00 00 01 00 00 00 10 00-10 00 00 00 00 00 00 00     ................
    00000020  00 00 00 00 43 6F 75 72-69 65 72 20 4E 65 77 00     ....Courier.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 4F 4E 54    // sig = 'FONT'
              0A             // height = 10 bits
              08             // width of widest char = 8 bits
              20 00          // starting ascii char = 32 = space character
              5F 00          // count of chars in set = 95
              B6 03 00 00    // bytes in data section (950 bytes)
              EE 06 00 00    // bytes in whole file (1,774 bytes)
              01 00 00 00    // flags: bit 0 set = fixed width font
              10             // file version = 1.0
              00             // font type = 0
              10             // font type version = 1.0
              00 00 00 00 00 00 00 00 00 00 00 
              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
  

Note that the reserved areas are specific to my Font Engine. I use these areas for non-static data while processing a font.
For the actual font file, these should be zero when creating a new file, and preserved when updating an existing file. These fields may be used in a later version of this font file.

Following that 96 bytes is a count of 8-byte structures, one for each character in the set:

    struct FONT_INFO {
      bit16u index;   // index of this character in data section
      bit8u  width;   // Width of character
      bit8u  flags;   // reserved (used only for my engine)
      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;    // reserved
    };
Here is a dump of the first 2 character entries: (no bitmaps. the bitmap follows this set of 8-byte entries)

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

  00000068  0A 00     // offset is 10.  Ten bytes from beginning of data stream
            08        // 8 bits wide
            00        // ignored by your engine
            00        // signed delta 'x'
            00        // signed delta 'y' 
            00        // signed delta 'w'
            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 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:


contact: fys [at] fysnet [dot] net
Download: http://www.fysnet.net/fontedit/fontedit.zip (218k)
Contents: fontedit.exe (for 32-bit, Windows XP prefered) and fontedit64.exe (for 64-bit Windows, Windows 10 prefered)
Current version (01.14.53) is dated: 2020 May 1
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
wopr.fnt 32 127 8 9 "Shall we play a game!" FYSOS


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