QB CULT MAGAZINE
Issue #6 - November 2000

Fonts in QuickBasic

by Sam Thursfield <sam.thursfield@btinternet.com>

Storing Fonts

Maybe you want to jump straight into making the greatest font suite ever, but first you're going to need a decent file format to store the fonts in. There are quite a few ways, my favourite is the binary way, which I used for vFont 3 files.

The Binary Way

This is a very common way. It's used by all the BIOS fonts, by vFont 3, DOS and many others. Basically, it's a string with one character for each line. It's stored in a common and effective way, the way I use for vFont3; it's quite hard to work out by hand, though. First you need the font data. It's got to be eight lines across (if you want less, leave the rest blank), but it can be any length down. Try this 8*16 character...

00101000 
00111000 
00101000 
00000000 
00111000 
00110000 
00111000 
00000000 
00100000 
00100000 
00111000 
00000000 
00111000 
00111000 
00100000 
00000000 

You may have worked out that it says help...Anyway, to convert each line to the right number, you check which numbers are used, and add them together. For the first line it's:

Position1286432168421
Value 0 0 1 0 1000

The only numbers used are 32 and 8, so we add them together to get 40. Therefore the first line of the character would be 40. Each line is called a bitpattern, because technically that's what it is.

The Byte-per-Pixel Way

This is also a popular way. It takes up much more space, but it is much easier to use and more flexible. You can store coloured fonts in this way by simply having the colour number instead of just a 1 or 0. This is a very simple format, check out JFont! to see how simple it is. The character in the example above would be stored like this:

0,0,1,0,1,0,0,0 
0,0,1,1,1,0,0,0 
0,0,1,0,1,0,0,0 
0,0,0,0,0,0,0,0 
0,0,1,1,1,0,0,0 
0,0,1,1,0,0,0,0 
0,0,1,1,1,0,0,0 
0,0,0,0,0,0,0,0 
0,0,1,0,0,0,0,0 
0,0,1,0,0,0,0,0 
0,0,1,1,1,0,0,0 
0,0,0,0,0,0,0,0 
0,0,1,1,1,0,0,0 
0,0,1,1,1,0,0,0 
0,0,1,0,0,0,0,0 
0,0,0,0,0,0,0,0 

It's that difficult!

The Vector Way

Vectors fonts are very hard to draw and exceedingly difficult to code. Truetype fonts are stored as vectors, because they're very flexible and resizable. You can stretch them every which way without them ever looking blocky, although they might look out of proportion.

For those who don't know, vector graphics is a common way of storing things like clipart. A vector file is just stored as a list of shapes, their sizes, colours, fill styles, thicknesses and various other properties. CorelDRAW! uses vectors, have a play with that sometime.

Textmode Fonts

You can change the font used in text mode (Screen 0), and I'm going to show you how to, but not until I've shown you a quick tip on fast textmode printing. You can use print, but if you're doing something like animation, or a textmode GUI (VBDOS style) or something else then it's faster to poke directly to the textmode video memory. No...don't DEF SEG straight to &ha000, because nothing will happen. When you're in textmode then all the action happens at &hb000 if you're on a monochrome monitor, or &hb800 if you're blessed with the gift of a colour monitor (these days, most people are ;^). Anyway, the screen is stored with the character first, the colour second. If you don't understand, try this little code snippit:

SCREEN 0 
CLS 

DEF SEG = &HB800 

    POKE 0, 254 
    POKE 1, 15 

DEF SEG 

What this does is put a bright white square (ascii 254, colour 15) in the top left of the monitor. If it doesn't work and you've got a black and white monitor, buy a new one. In the mean time, change &hb800 to &hb000. I just thought I'd share that tip with you.

Right...now we can get stuck in to Fonts in Textmode!

There are quite a few DOS font editors, my personal favourite is the one by Davey W. Taylor. You can get it from somewhere on the ABC website: www.basicguru.com/abc. It's got a font to com converter, a decent editor, and some other stuff.

<- This is the main screen of Davey W. Taylor's font editor. It's not brilliantly pretty or user-friendly, but I managed to use it well enough, all those years ago. It's also got programs to change the dos font and it can even convert a DOS font to a COM file, which changes the font each time it is run. The font inside is a font I made. It's quite crap, but I spent AGES perfecting it, trying to use QB in it, failing, etc.

So, you want to customise the fonts yourself, eh? Well, it goes like this: You have to use interrupts. The call is:

INT 10h 

AX = 1100h 
BX = Length of font data (16 for one character, 256*16 for the entire character set). 
CX = Number of characters to change 
DX = The character number to start the change at 
ES = Segment of font data 
BP = Offset of font data 

The fonts are stored in a string with the binary method (see Storing Fonts). It's pretty self-explanitary, so I won't go into detail more.

Graphical Fonts

Well now, graphical fonts can be stored any way you like. If you want to write your own font routine, you can use any of the routines listed in Storing Fonts or invent one of your own.

Firstly, you'll need to make a font to use. You can either make a font editor - which isn't as difficult as it seems - or just nick a font from the BIOS. The BIOS fonts are stored with the binary method (look at Storing Fonts again), and you can just read them straight from memory. They start at FFA6:000E and are stored sequentially, with 8 bytes per character (one for each line). If you want 8*16 fonts, they're at 045A:0006, and of course they're 16 bytes per character.

Well, once you've got your font file, be it made in an editor, freshly picked from the BIOS or pinched from somewhere else, you're going to need to display it. If you want to see mine, download my own vFont3 system, which has a fairly basic editor but is a good general font routine. You can get from my website, address at the end.

In the way of parameters, your font routine's going to need the x and y position, the colour and the string to print. That's all the important ones, but you may need more: styles, font to use, etc.

You've got to work through the string one at a time, so have a for loop counting the current character you're on in the string. Then work out the position of the character; an array offset or memory address or something else. Work down line by line on the character, and draw each line. Bold is simple: draw two pixels across for every one. That'll make a cheap bold effect. Shadowed is even simpler, all you have to do is call the routine again, adding 1 to the x and y positions, and a different colour. And of course without the shadow perameter.. For italic, add the line number subtracted from the height of the fonts to each line. Look, I find it hard to explain things. How about an annotated version of some of my own font, vFont3:

These constants are for the Style parameter. They're bits to set. Testing for them is done with AND, so you can have, say, shaded and italic, just by supplying 3, or Shaded + Italic.

CONST Shaded = 1 
CONST Italic = 2 
CONST Shadowed = 4 
CONST Underlined = 8 

'This can be supplied as the x parameter, which prints the font in the centre of the screen. Why are there two definitions?
'Because I spell it the British way (being English) but most people don't. 

CONST Centred = -32767 
CONST Centered = -32767 

'Because I made this for use in any screen mode (I've used it in most modes from SCREEN 1 on my Amstrad to 1024x768
'with Future.library) it can't automatically determine the screen mode, so you must set this to be able to centre text. 

DIM SHARED ScreenWidth AS INTEGER 

'This holds the font data. In the 1st dimension it's 0-255 for the characters, in the 2nd element 0 is the width of the character
'while 1-16 are the lines (stored with the binary method). To put them into files, I just BSAVEd the whole array. 

DIM SHARED Font(255, 16) AS INTEGER 

'This is the heart of the system. Let me explain the parameters for you: 
'    xPos and yPos are self-explanitory, the pixel positions of the text. 
'    Text$ is also pretty obvious, being the text to be printed. 
'    Col isn't hard to work out either, it's the colour of the text. 
'    bCol is the shadow colour, not used to anything else. 
'    Style holds all the required Style constants added together. 
'    FontArray() has the actual font used. 

SUB vFont (xPos, yPos, Text$, Col, bCol, Style, FontArray()) 

'This code checks if centring is required. If it does, it uses the standard centring formula. vLen is a function listed next. 

IF xPos = -32767 THEN xPos = ScreenWidth \ 2 - vLen(Text$, FontArray()) \ 2 

'For some reason that I can't explain, vFont always draws the characters backwards, starting with the right-most line and
'ending on the left. That means I must advance the position first. xx and yy are the current text position. The - (5 * (Style AND
'2)) the sort of thing I like. I hate having hundreds of IF...ELSEs, so I have formulas. (Style AND 2) returns -1 if italic is used,
'and 0 otherwise. Therefore, 5 * (Style AND 2) will be 0 if italic is off and -5 otherwise. Take it away from 7, it adds 5 to it.
'Get it? I know they make the program hard to read, but they make it smaller and more effecient. 

xx = xPos + 7 - (5 * (Style AND 2)) 
yy = yPos 

'Ok...this needed an IF. If it's shadowed then we call this sub again, but with bCol as colour, Style with the Shadowed attribute
'removed, and 1 taken away from xPos and yPos (another symbol of vFont's backwardsness). You'll notice vFont doesn't actually need
'the style constants to run, they're just provided to make it easier for the user. 

IF Style AND 4 THEN 
    VFont Xpos - 1, YPos - 1, Text$, BCol, 0, Style - 4, FontArray() 
END IF 

'Right, this is the main loop. Chr goes through each letter in the string to be printed. Fairly obvious, huh? 

FOR Chr = 1 TO LEN(Text$) 

        'MemPtr is a remnent from vFont 2...in the old version, it read the font straight from the BIOS...this has the pointer, if you 
        'ever wanted it. 

    'MemPtr = 8 * ASC(MID$(Text$, Chr, 1)) + &HE 

        'l is the current line in the present character. It does do all sixteen lines, in an 8*8 font the rest just have a bitpattern of 0. 

    FOR l = 0 TO 15 

                'Bitpattern has the current line data in it. The reason it gets it from l + 1 in the array is that element 0 has the width of 
                'the character. 

        BitPattern = FontArray(ASC(MID$(Text$, Chr, 1)), l + 1) 

                'I thought it would get a bit (more) complex if I used a formula for italic as well, so I went for readability rather than 
                'size here. 

        IF Style AND 2 THEN 

                        'Let me explain the drawing routine..this just draws one line of the character. It's for the italic text, so xx has to 
                        'be made to 'lean'. This is done by adding half the line number subtracted from 15, to reverse it. I ain't got a clue 
                        'why it needs to be halved, but it does. yy obviously needs the line number added to it. Usually, it just uses Col 
                        'as the colour, but if shaded is used then this formula will stop being zero:  - (15 - L) * (Style AND 1). 
                        'You know QB does multiplication before addition, so it only does this if Style has one, meaning the user wants 
                        'fancy shaded text. It then does the 15-l on the colour, so it is shaded from Col down 15 colours. It's not 
                        'brilliantly flexible, but it works. 

            IF (BitPattern AND 1) THEN PSET (xx + (15 - (l \ 2)), yy + l), Col - (15 - l) * (Style AND 1) 
            IF (BitPattern AND 2) THEN PSET (xx + (15 - (l \ 2)) - 1, yy + l), Col - (15 - l) * (Style AND 1) 
            IF (BitPattern AND 4) THEN PSET (xx + (15 - (l \ 2)) - 2, yy + l), Col - (15 - l) * (Style AND 1) 
            IF (BitPattern AND 8) THEN PSET (xx + (15 - (l \ 2)) - 3, yy + l), Col - (15 - l) * (Style AND 1) 
            IF (BitPattern AND 16) THEN PSET (xx + (15 - (l \ 2)) - 4, yy + l), Col - (15 - l) * (Style AND 1) 
            IF (BitPattern AND 32) THEN PSET (xx + (15 - (l \ 2)) - 5, yy + l), Col - (15 - l) * (Style AND 1) 
            IF (BitPattern AND 64) THEN PSET (xx + (15 - (l \ 2)) - 6, yy + l), Col - (15 - l) * (Style AND 1) 
            IF (BitPattern AND 128) THEN PSET (xx + (15 - (l \ 2)) - 7, yy + l), Col - (15 - l) * (Style AND 1) 

        ELSE 

                        'This is the same as above, except xx isn't fixed to make it italic. 

            IF (BitPattern AND 1) THEN PSET (xx, yy + l), Col - (15 - L) * (Style AND 1) 
            IF (BitPattern AND 2) THEN PSET (xx - 1, yy + l), Col - (15 - l) * (Style AND 1) 
            IF (BitPattern AND 4) THEN PSET (xx - 2, yy + l), Col - (15 - l) * (Style AND 1) 
            IF (BitPattern AND 8) THEN PSET (xx - 3, yy + l), Col - (15 - l) * (Style AND 1) 
            IF (BitPattern AND 16) THEN PSET (xx - 4, yy + l), Col - (15 - l) * (Style AND 1) 
            IF (BitPattern AND 32) THEN PSET (xx - 5, yy + l), Col - (15 - l) * (Style AND 1) 
            IF (BitPattern AND 64) THEN PSET (xx - 6, yy + l), Col - (15 - l) * (Style AND 1) 
            IF (BitPattern AND 128) THEN PSET (xx - 7, yy + l), Col - (15 - l) * (Style AND 1) 

        END IF 

    NEXT 

        'This increases xx by the width of the current character. 

    xx = xx + FontArray(ASC(MID$(Text$, Chr, 1)), 0) 

NEXT 

END SUB 

'This returns the pixel length of Text$ in font FontArray(). 

FUNCTION vLen (Text$, FontArray()) 

'It's quite simple, it just adds all the character widths together. 

TempLen = 0 
FOR I = 1 TO LEN(Text$) 
    TempLen = TempLen + FontArray(ASC(MID$(Text$, I, 1)), 0) 
NEXT I 
VLen = TempLen 

END FUNCTION 

And there you have it...hopefully you can do something with it. You can email me at <sam.thursfield@btinternet.com>, visit my (rubbish) site at eps.50megs.com, download vFont3, look out for Aradina (my QuickBASIC RPG), Canal Company (my DJGPP simulation game), Mood 3D (my DJGPP 3d engine) and Ardos (my Inform text adventure) when they're finished. If that's not a great plug, what is? Also, I learned all this from many other fonts, including Phillip Jay Cohen's Outlined Font, Peter Cooper's FontPut, Leandro Pardini's MSFont, and M/K's MKFont.