fsxNet Wiki

BBS Development & Resources

User Tools

Site Tools


resources:misc:tdf

TheDraw File File Format

Disclaimer

The following document is from http://www.roysac.com/blog/2014/04/thedraw-fonts-file-tdf-specifications/ RoySac's Website. All credits goes to him.

Note that this is not an official specifications document, I reverse engineered it by testing, so there might be cases I am unaware of, although I am pretty confident that I covered everything about it.

“TheDraw” Fonts Files have the extension “.TDF” (which is short for “TheDraw Font” duh :)).

One .TDF Fonts file is by default designed as a “collection” of multiple fonts, even if it only holds a single font (like after exporting a single font to an extra file). It can hold up to 34 fonts. More are not possible, if used with TheDraw itself or with it’s external TDFONTS.EXE Fonts Editor utility.

An empty fonts file without any font in it yet is always 232 bytes in size. The actual character data are always stored after those 232 bytes and can vary in size, depending on the size, type and complexity of the font characters. Every additional font added to the collection is 212 bytes long if without any character data.

Font Header

OffsetLengthDescriptionSample Value
0 1 Character 19 (13h) 13
1 18 Fix String TheDraw FONTS file
19 1 Character 26 (1Ah) 1A
20 4 Fix 4 Bytes Sequence Indicating the Start of a new Font Definition 55 AA 00 FF
24 1 Byte: Length of the Font Name (max. is 12 characters) 0B
25 12 Font Name (String). Only use the number of bytes specified in byte 24 and ignore any after (it might be 00 values or maybe garbage) MY NEW FONT
37 4 unused as far I know. Typically all 0 bytes 00 00 00 00
41 1 Font Type (byte): 00 = Outline, 01 = Block, 02 = Color 02
42 1 Letter Spacing: 01h – 29h, representing the range 0-40 as decimal values 01 … 29
43 2 Block Size (Word, Little Endian) Size of character data after main font definition block, including terminating 0 if followed by another font (last font in collection is not Null terminated) 7F 04 (= 1,151)
45 188 2 bytes (Word, Little Endian) for each character from ASC(33) (“!”) to ASC(126) (“~”) (94 characters total) with the offset (starting at 0) after the font header definition where the character data start (e.g. 0 would mean that character data start at byte 233 [for the first font in the file only]) FF FF = char not defined
233 BlockSize Character Detail Data

The Characters

  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO
  PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

Offset Addresses

Font 1 Header 20
Font 1 Data 233
Font 2 Header 232 + Font 1 Block Size + 1
Font 2 Data Font 2 Header + 212 + 1
Font 3 Header Font 2 Data + Font 2 Block Size + 1
Font 3 Data Font 3 Header + 212 + 1
Font X …

Font Character

Offset Length Description Sample Value
0 1 Max Width of Character ( 1 ⇐ W ⇐ 30 (1Eh) ) 0A
1 1

Byte 1 (All Font Types)

The individual characters for Fonts of type “Block” or “Outline” are single bytes only where “Block” using the actual ASCII character code. For Fonts of type “Color“, each character takes up 2 bytes. The first byte is the character itself and the 2nd byte is to specify the background and foreground colors used for the character. “Outline” Font Types store Letters, which are used as character reference. For visualization purposes you might want to use the following mapping.

Letter Chr Dec/Hex Description Out-/Inside
A 205 / CD
B 196 / C4
C 179 / B3
D 186 / BA
E 213 / D5 Upper-left outer corner Up-to-Right outer corner outside inside
F 187 / BB Upper-right outer corner Right-to-Down outer corner outside inside
G 214 / D6 Up-to-Right inner corner Upper-left inner corner outside inside
H 191 / BF Right-to-Down inner corner Upper-right inner corner outside inside
I 200 / C8 Lower-left inner corner inside
J 190 / BE Lower-right inner corner inside
K 192 / C0 Lower-left outer corner outside
L 189 / BD Lower-right outer corner outside
M 181 / B5
N 199 / C7
O 247 / F7 Hard space for all spaces inside a character
@ @ 064 / 40 Filler for all leading spaces
& & 038 / 26 Descender mark

Byte 2 (“Color” Font Type Only)

00 - FF, the first part (or Floor(Value / 16)) holds the background color (0,1,2,3 .. max. 7) and the second part (or Value mod 16) holds the foreground color (0,1,2,3 .. max. F). See the Color Table below for the available colors.

The end of each line in the character is terminated by the Carriage Return character (ASC(13) or 0Dh)

A character value = 0 (Null) indicates the end of the current character (Note: that a color value of “00” for fonts of type “Color” is perfectly legal and should not be interpreted as Null termination).

General Notes

  • If the current line is being terminated before the specified “Max. Width” is reached, everything after the break will be treated as non-existent or transparent space. I tried to illustrate this behavior in the following drawing.
  • If the characters of your “Color” font are too large, TheDraw might run into a problem because the space needed exceed the maximum possible offset value in the font base definition. Keep in mind that only 2 bytes are reserved, providing a max. offset of 65534 (=FEh, remember that FFh = no character defined). Taking a color font using 2 bytes per char and maximum dimensions per character of 30×12 + up-to 11 line-breaks and 1 null termination for a total of 94 characters, you are running out of address space much earlier than the end of character 93.
  • Remember that the “Outline” font type has restrictions regarding the use of characters. Those rules are enforced by the font-editor tool though and not by the font file format, so be careful, if you are going to create new .TDF font files yourself.
  • If a character is created using the “Copy” character feature by the TDFonts.exe font editor, the target character will point to the same offset position for the character data as the source character. Only if the target character is changed afterwards will the character data be stored at a different offset location. This reduces space and can be solution for the address space issue mentioned at the first bullet point.

From the TheDraw Documentation

Outline Fonts

    ╒═════════════╗
    │ ╓─────────┐ ║     ╒══╗         Horizontal Beam
    │ ╚═══╗ ╒═══╛ ║     │  ║           ╒═════════╗
    └───┐ ║ │ ╓───╜     │  ║           └─────────╜
        │ ║ │ ║         │  ║
        │ ║ │ ║         │  ║ <── Vertical Beam
        │ ╚═╛ ║         └──╜
        └─────╜

Pay special attention to how the top/right of the outside border, and the bottom/left of the inside border were done. Of particular importance are the corners. The following characters must be used in the described locations.

For outside borders:
    ╒     Upper–left outer corner
    ╗     Upper–right outer corner
    └     Lower–left outer corner
    ╜     Lower–right outer corner
    ╓     Up–to–Right inner corner
    ┐     Right–to–Down inner corner

For inside borders:
    ╓     Upper–left inner corner
    ┐     Upper–right inner corner
    ╚     Lower–left inner corner
    ╛     Lower–right inner corner
    ╒     Up–to–Right outer corner
    ╗     Right–to–Down outer corner

In general the drawing rules are:

  • Unordered List ItemDouble lines are always the rightmost side of a column.
  • Unordered List ItemDouble lines are always the topmost side of a beam.

RESERVED CHARACTERS

The reserved characters are for solid spaces and descender line markers. Typically solid spaces are used to fill the inside of columns and beams. These are drawn with “≈” characters. ie:

                  ╒════════╗  ╒════════╗
                  │≈≈≈≈≈≈≈≈║  │≈≈≈≈≈≈≈≈║      Note that solid
                  │≈≈╓──┐≈≈║  │≈≈╔══╕≈≈║      spaces are always
                  │≈≈╚══╛≈≈║  │≈≈║  │≈≈║      visible in TDFonts
                  │≈≈≈≈≈≈≈≈║  │≈≈╙──┘≈≈║        editor verses the
                  │≈≈╓──┐≈≈║  │≈≈≈≈≈≈≈≈║      TheDraw editor).
                  └──╜  └──╜  └────┐≈≈╓╜
                                   └──╜&

TheDraw specially tracks descenders so to align symbols and handle carriage returns properly. A carriage return should move the same number of lines for either A’s or Q’s for instance. If decenders were not marked, following kind of alignment error occurs:

                       ▄▄▄▄▄   █▀▀▀█
                       █   █   █   █
                       █▀▀▀█   ▀▀▀█▀

Symbols demanding descenders should be marked as shown in the above “Q” symbol. Lines marked with ampersands are not counted when TheDraw aligns the symbols. Font symbols typically needing the descender marks are:

                    $ , ; _ Q g j p q y

Others symbols may require them also, depending on how your font is designed or implemented.

Wrapping Up

That’s all actually, there is nothing more to it I believe. I am pretty sure that you will hear more of this from me in the future, probably some kind of tools, which I already mentioned briefly at the beginning of this post.

I hope that you find this information useful. Let me know, if you decide to do something yourself with this information and write some tool of your own etc. Just contact me 19:).

Cheers! Carsten aka Roy/SAC20

Source Code

MPL

This is a simple MPL Script to use TDF files in Mystic BBS v1.12A29+, written by xqtr.

tdf.mps
Uses cfg;
 
Const 
  HeaderOffset = 233;
 
Type 
  TTDFont = Record
    A         : char;
    typo      : Array[1..18] Of char;
    B         : char;
    fs        : Array[1..4] Of Byte;
    NameLen   : Byte;
    FontName  : Array[1..12] Of char;
    nouse     : Array[1..4] Of Byte;
    FontType  : Byte;
    Spacing   : Byte;
    BlockSize : word;
    CharAddr  : Array[1..94] Of word;
  End;
 
Type 
  TFontChar = Record
    width  : Byte;
    height : Byte;
  End;
 
Var 
  font     : TTDFont;
  Datapath : String;
  FontFile : String;
 
Procedure GetTDFHeader(f:String);
Var 
  fptr : file;
  i : Integer;
Begin
  If Not fileexist(f) Then
    Begin
      Writeln('Font file [ '+f+' ]does not exist');
      Pause;
      Halt;
    End;
  fontfile := f;
  fAssign(fptr,f,66);
  fReset(fptr);
  fRead(fptr,font,sizeof(font));
  fClose(fptr);
End;
 
Procedure TDFWriteCharBL(x,y:Byte;c:char): Byte;
Var 
  fptr  : file;
  i     : Integer;
  FChar : TFontChar;
  tbyte : Array[1..2] Of Byte;
  sx,sy : Byte;
  asc   : Byte;
Begin
  If c=' ' Then
    Begin
      TDFWritecharBL := 1;
      Exit;
    End;
  asc := ord(c)-32;
  fAssign(fptr,fontfile,66);
  fReset(fptr);
  fSeek(fptr,headeroffset+font.charaddr[asc]);
  fRead(fptr,FChar,sizeof(Fchar));
  tbyte[1] := 32;
  tbyte[2] := 32;
  GotoXY(x,y);
  While tbyte[1]<>0 And Not feof(fptr) Do
    Begin
      fRead(fptr,tbyte[1],1);
      If tbyte[1]=13 Then
        Begin
          GotoXY(x,wherey+1);
          If wherey>25 Then Break;
        End;
      Else
        Begin
          fRead(fptr,tbyte[2],1);
          TextColor(tbyte[2] % 16 + tbyte[2] - (tbyte[2] % 16));
          Write(chr(tbyte[1]));
          If wherex>79 Then Break;
        End;
    End;
  fClose(fptr);
  TDFWritecharbl := fchar.width;
End;
 
Procedure TDFWriteCharCL(x,y:Byte;c:char): Byte;
Var 
  fptr  : file;
  i     : Integer;
  FChar : TFontChar;
  tbyte : Array[1..2] Of Byte;
  sx,sy : Byte;
  asc   : Byte;
Begin
  If c=' ' Then
    Begin
      TDFWritecharcl := 1;
      Exit;
    End;
  asc := ord(c)-32;
  fAssign(fptr,fontfile,66);
  fReset(fptr);
  fSeek(fptr,headeroffset+font.charaddr[asc]);
  fRead(fptr,FChar,sizeof(Fchar));
  tbyte[1] := 32;
  GotoXY(x,y);
  While tbyte[1]<>0 And Not feof(fptr) Do
    Begin
      fRead(fptr,tbyte[1],1);
      If tbyte[1]=13 Then
        Begin
          GotoXY(x,wherey+1);
          If wherey>25 Then Break;
        End
      Else
        Begin
          Write(chr(tbyte[1]));
          If wherex>79 Then Break;
        End;
    End;
  fClose(fptr);
  TDFWritecharcl := fchar.width;
End;
 
Procedure TDFWrite(x,y:Byte; s:String);
Var 
  i     : Byte;
  sx,sy : Byte;
Begin
  GotoXY(x,y);
  sx := x;
  sy := y;
  Case font.fonttype Of 
    2:
       Begin
         For i:=1 To Length(s) Do
           Begin
             sx := sx+TDFWritecharBL(sx,y,s[i])+font.spacing;
           End;
       End;
    1:
       Begin
         For i:=1 To Length(s) Do
           Begin
             sx := sx+TDFWritecharCL(sx,y,s[i])+font.spacing;
           End;
       End;
  End;
End;
 
Procedure TDFTypeWrite(x,y:Byte; s:String; d:Integer);
Var 
  i     : Byte;
  sx,sy : Byte;
Begin
  GotoXY(x,y);
  sx := x;
  sy := y;
  Case font.fonttype Of 
    2:
       Begin
         For i:=1 To Length(s) Do
           Begin
             sx := sx+TDFWritecharBL(sx,y,s[i])+font.spacing;
             Delay(d);
           End;
       End;
    1:
       Begin
         For i:=1 To Length(s) Do
           Begin
             sx := sx+TDFWritecharCL(sx,y,s[i])+font.spacing;
             Delay(d);
           End;
       End;
  End;
End;
 
// Main Block
 
Begin
  ClrScr;
  DataPath := JustPath(ProgName);
  gettdfheader(datapath+'drgx.tdf');
  // Init the font to use
  font.spacing := 1;
  TDFWrite(1,2,'Shall we play');
  // Write a String
  TDFWrite(30,6,'a game...');
 
  gettdfheader(datapath+'acidscapesl.tdf');
  font.spacing := 1;
  TDFWrite(3,9,'repeaters');
  TDFWrite(3,15,'Mystic');
  TDFWrite(23,20,'BBS');
  GotoXY(1,25);
  Pause;
 
End;

Links

resources/misc/tdf.txt · Last modified: 2017/03/12 14:28 by xqtr