====== 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 ==== ^Offset^Length^Description^Sample 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 |Max Height of Character** ( 1<= H <= 12 (0Ch) ) |05| ** The Max Height Value is not reliable. If a line ends with a &, this line is not counted in the Max Height. For Example, if a character is 6 lines high and line 5 and line 4 end with a & character, the value for Max Height will be 4 and not 6. The Max Height value can only be used to determine the “Line Height” to determine where to position the cursor after a line-break. To read the full character data, always read to the Null (Chr(0)) terminator. === 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 || {{ http://www.roysac.com/blogimages/TDFOutlineChars.png }} {{ http://www.roysac.com/blogimages/TDFOutlineStyleTypes.png }} === 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. {{ http://www.roysac.com/images/Colors_Of_ANSI.gif }} 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. 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** * http://www.roysac.com/blog/2014/04/thedraw-fonts-file-tdf-specifications/