Jump to content
Search In
  • More options...
Find results that contain...
Find results in...
Sign in to follow this  

New Font System

Recommended Posts

I've completed addition of a new EDF font engine for Eternity. There are two types of fonts that can be specified: patch fonts, which work like the traditional type of DOOM font, where each letter has its own patch graphic; and block fonts, where the entire font is stored in a single graphic and is blitted to the screen with a very fast masked block drawing routine.

The full EDF specification:

font <mnemonic>
   id             = <number>  
   start          = <number> OR <char>
   end            = <number> OR <char>
   linesize       = <number>
   spacesize      = <number>
   widthdelta     = <number>
   tallestchar    = <number>
   centerwidth    = <number>
   colorable      = <boolean>
   uppercase      = <boolean>
   blockcentered  = <boolean>
   patchnumoffset = <number>

   linearformat = <string>
   linearlump   = <string>

   // One or more of the following are required:
      // A filter must either specify THESE:
      start = <number> OR <char>
      end   = <number> OR <char>

      // OR this:
      chars = { <number> OR <char>, ... }

      // Mask is required and can contain one and only
      // one optional C printf integer or character
      // formatting sequence
      mask = <string>
Explanation of the fields:
  • id : This is the numeric ID of the font, which is useful in Small scripting for setting the font to HUD widgets.
  • start : The first character in the font, in ASCII order.
  • end : The last character in the font, in ASCII order.
All of the following fields are only relevant to patch fonts:
  • linesize : The amount to step in y coords when a line break is hit.
  • spacesize : Width of a blank space (including characters with no patch specified).
  • widthdelta : An amount subtracted from the width of a character when stepping in x coords to the next character. "1" is a typical value, used by the main Heretic font for example.
  • tallestchar : The absolute tallest character in the font. Needed for situations when text is drawn relative to the bottom of a clipping rectangle (such as the screen buffer).
  • centerwidth : Used for block-centered fonts only. Characters are centered within squares of this size. Hard to understand, and rarely needed.
  • colorable : This font does/doesn't support text color translations.
  • uppercase : This font does/doesn't treat text as all-caps.
  • blockcentered : Centers each character within uniformly spaced blocks of "centerwidth" size; really a special hack for the Heretic intermission screen...
  • patchnumoffset : An amount to subtract from the character amount when calculating the number that is part of a patch font's lump names. For Heretic FONTA and all gamemodes' FONTBs, this value is 32.
To make a block font is drastically more simple. You only have to specify one field if the lump format is linear (raw, headerless 8-bit like Heretic's title screen, or a flat); this increases to two fields if the format is a patch (which is converted into a linear block of data at runtime):
  • linearlump : The lump to use for the entire block font.
  • linearformat : Either "linear" or "patch" currently. Linear is the default value.
When a font is determined to be linear, all of the other fields in the font are given forced default values that work with the linear font blitting code.


To make a patch font, it is necessary to identify to the game engine what patches make up the font. This can either be very simple or very complicated depending on how wisely you name your font's graphic lumps. When loading each font glyph, the filters are applied in the order in which they are specified in the font object. Each filter can specify a range of characters to which it applies, or a list of one or more explicit character values.

The filter is used by taking the character's ASCII value, subtracting the optional "patchnumoffset" value from it, and formatting that number into the "mask" string, like in this example:

filter { start = '!'; end = 0x7f; mask = "STCFN%.3d" }

For ASCII character 'A', this would load STCFN065 because the ASCII value of 'A' is 65. This does require some basic knowledge of C printf format specifiers, but this required knowledge never exceeds how to print a width-limited integer.

(For those alarmist types, this mask formatting string is stringently analyzed for attempts to exploit printf vulnerabilities. Only one specifier is allowed, and it must be of types c, i, d, x, X, o, or u when it exists.)

Since all of this is overwhelming, here are some real-world examples from the new fonts.edf files in Eternity's base directory:
// Console font
font ee_consolefont
   id = 4;
   linearlump = "CONCHARS";

// Small font - this is used by the HUD, finale, and V-system by default
font ee_smallfont
   id          = 0;
   start       = '!';
   end         = 0x7f;
   linesize    = 8;
   spacesize   = 4;
   tallestchar = 8;
   colorable   = true;
   uppercase   = true;
   filter { start = '!'; end = 0x7f; mask = "STCFN%.3d" }

// And this is just to show that it can be unnecessarily
// complicated if you make your lump naming scheme completely
// illogical (Thanks BOOM team...):

// Hud overlay font - used by BOOM-style HUD
font ee_hudfont
   id          = 1;
   start       = '!';
   end         = 0x7f;
   linesize    = 8;
   spacesize   = 4;
   tallestchar = 8; 
   colorable   = true;
   uppercase   = true;
   filter { start = '0'; end = '9';         mask = "DIG%c"  }
   filter { start = 'A'; end = 'Z';         mask = "DIG%c"  }
   filter { chars = { 45, 47, 58, 91, 93 }; mask = "DIG%i"  }
   filter { start = 123; end = 127;         mask = "STBR%i" }
   filter { chars = { '_' };                mask = "DIG45"  }
   filter { chars = { '(' };                mask = "DIG91"  }
   filter { chars = { ')' };                mask = "DIG93"  }

Share this post

Link to post

BTW I forgot to explain the fact that you can override the default fonts used by all native subsystems, including the HUD, HUD overlay, menus, finale, and intermission via a series of new global EDF variables :)

It works like this example:

hu_overlayfont = MyNewOverlayFont;

Share this post

Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this