/*****************************************************************************
load BDF bitmap font into memory
These fonts used to be used (still are?) by X windows,
and are usually freely available.
xxx - bdfLoadFont() always allocates space for 256 characters
*****************************************************************************/
#include <string.h> /* strlen(), memcmp(), memset() */
#include <stdlib.h> /* atoi() */
/* FILE, printf(), sscanf(), fopen(), fgets(), fclose() */
#include <stdio.h>
#include <gui/font.h>
#include <os/stream.h>
#include <os/os.h>
class BfdFont : public IFont
{
protected:
int Wd, Ht;
int First, Last;
byte *Spacings, *Bitmaps;
int loadHelp(IStream *Infile, char *Line);
public:
BfdFont();
virtual ~BfdFont();
int load(const wchar_t* FileName);
STDMETHOD(DrawText)(ISurface* pSurf, int x, int y, const wchar_t* String,
pixel_t pixColour);
STDMETHOD(GetTextExtent)(const wchar_t* String, point_t* size);
};
IFont* CreateFontBfd(const wchar_t* lpszFileName)
{
BfdFont* pFont = new BfdFont;
if (pFont && pFont->load(lpszFileName) == 0)
return pFont;
else
{
delete pFont;
return NULL;
}
}
#define MAX_LINE_LEN 256
#define MAX_ENC 2048
#define MAX_WD 5
#define MAX_HT 40
/*****************************************************************************
*****************************************************************************/
BfdFont::BfdFont()
{
Wd=0;
Ht=0;
First=0;
Last=0;
Spacings=NULL;
Bitmaps=NULL;
}
/*****************************************************************************
*****************************************************************************/
BfdFont::~BfdFont(void)
{
Wd=0;
Ht=0;
First=0;
Last=0;
if (Spacings != NULL)
{
delete[] Spacings;
Spacings=NULL;
}
if (Bitmaps != NULL)
{
delete[] Bitmaps;
Bitmaps=NULL;
}
}
char* fgets(char* buf, int max, IStream* strm)
{
char ch, *start = buf;
while (true)
{
if (FAILED(IStream_Read(strm, &ch, 1)))
return NULL;
if (ch != '\n')
*buf++ = ch;
else
break;
}
return start;
}
void fclose(IStream* strm)
{
IUnknown_Release(strm);
}
IStream *_wfopen(const wchar_t *filename, const wchar_t *mode)
{
IUnknown* ptr = sysOpen(filename);
IStream* ret;
if (ptr)
{
ret = NULL;
IUnknown_QueryInterface(ptr, IID_IStream, (void**) &ret);
IUnknown_Release(ptr);
return ret;
}
else
return NULL;
}
/*****************************************************************************
*****************************************************************************/
static int _expect(IStream* Infile, char *Line, char *Target, int Quiet)
{ unsigned Len;
Len = strlen(Target);
do
{
if (fgets(Line, MAX_LINE_LEN, Infile) == NULL)
{
fclose(Infile);
if(!Quiet)
wprintf(L"did not find %s line\n", Target);
return(-1);
}
} while(memcmp(Line, Target, Len) != 0);
return 0;
}
/*****************************************************************************
*****************************************************************************/
int Encoding=-1;
int FontYOff;
int BfdFont::loadHelp(IStream *Infile, char *Line)
{
int CharWd, CharHt, CharXOff, CharYOff;//, Encoding;
// Encoding=atoi(Line + 9);
Encoding++;
if(Encoding > MAX_ENC)
{
wprintf(L"bad ENCODING (%u)\n", Encoding);
return(-1);
}
if(Encoding < First)
First=Encoding;
if(Encoding > Last)
Last=Encoding;
/* look for BBX, which gives the proportional spacing value (char width) */
if(_expect(Infile, Line, "BBX", 0) != 0)
return(-1);
sscanf(Line + 4, "%d %d %d %d",
&CharWd, &CharHt, &CharXOff, &CharYOff);
Spacings[Encoding]=CharWd;
/* look for BITMAP */
if(_expect(Infile, Line, "BITMAP", 0) != 0)
return(-1);
/* start reading the actual raster data */
{
byte CharData[MAX_HT][MAX_WD], *Ptr;
int Row, Col, YPos;
memset(CharData, 0, sizeof(CharData));
for(Row=0; Row < MAX_HT; Row++)
{
memset(Line, 0, MAX_LINE_LEN);
if(fgets(Line, MAX_LINE_LEN, Infile) == NULL)
return(-1);
/* exit this loop when ENDCHAR seen */
if(memcmp(Line, "ENDCHAR", 7) == 0)
break;
/* read char from top to bottom */
YPos=Ht - CharHt +
FontYOff - CharYOff + Row;
if(YPos < 0 || YPos >= MAX_HT)
{
wprintf(L"warning: top or bottom edge "
L"of char %d might be clipped (YPos "
L"== %d)\n", Encoding, YPos);
continue;
}
for(Col=0; Col < MAX_WD; Col++)
{
byte Temp;
if(sscanf(Line + Col * 2, "%2x", &Temp) != 1)
break;
CharData[YPos][Col]=Temp; }}
/* store at Bitmaps */
Ptr=Bitmaps + Wd * Ht * Encoding;
for(Row=0; Row < Ht; Row++)
{ for(Col=0; Col < Wd; Col++)
Ptr[Col]=CharData[Row][Col];
Ptr += Wd; }}
return(0);
}
/*****************************************************************************
*****************************************************************************/
int BfdFont::load(const wchar_t* FileName)
{
char Line[MAX_LINE_LEN];
IStream* Infile;
// int FontYOff;
wprintf(L"bdfLoadFont '%s': ", FileName);
/* open file */
Infile=_wfopen(FileName, L"r");
if(Infile == NULL)
{ wprintf(L"can't open file\n");
return(-1); }
/* look for STARTFONT */
if(_expect(Infile, Line, "STARTFONT", 0) != 0)
{ fclose(Infile);
return(-1); }
/* OK, it's a BDF font file: look for FONTBOUNDINGBOX */
if(_expect(Infile, Line, "FONTBOUNDINGBOX", 0) != 0)
{ fclose(Infile);
return(-1); }
{ int FontWd, FontXOff;
sscanf(Line + 16, "%d %d %d %d",
&FontWd, &Ht, &FontXOff, &FontYOff);
/* use FONTBOUNDINGBOX to set Wd (in range 1-MAX_WD bytes),
Ht (in range 1-MAX_HT rows), and FontYOff */
Wd=(FontWd + 7) >> 3;
if(Wd < 1 || Wd > MAX_WD)
{ wprintf(L"bad Wd (%u)\n", Wd);
fclose(Infile);
return(-1); }
if(Ht < 1 || Ht > MAX_HT)
{ wprintf(L"bad Ht (%u)\n", Ht);
fclose(Infile);
return(-1); }
wprintf(L"Wd=%u, Ht=%u, ",
Wd, Ht);
}
/* look for "CHARS ". You need the space,
or it will trip on CHARSET_REGISTRY, etc. */
if(_expect(Infile, Line, "CHARS ", 0) != 0)
{ fclose(Infile);
return(-1); }
/* CHARS sets size of Spacings and Bitmaps arrays */
{ unsigned NumChars;
NumChars=MAX_ENC + 1;//atoi(Line + 6); xxx
Spacings=(byte *)malloc(NumChars);
if(Spacings == NULL)
ERR_MEM: { wprintf(L"out of memory\n");
fclose(Infile);
return(-1); }
Bitmaps=(byte *)malloc(NumChars * Ht * Wd);
if(Bitmaps == NULL)
{ free(Spacings);
goto ERR_MEM; }
wprintf(L"%u characters, ", NumChars); }
/* got all the info we need, move on to bitmap data */
First=MAX_ENC;/* xxx */
Last=0; /* xxx */
/* look for ENCODING */
{ int Quiet=0, Err=-1;
Encoding=31;
while(_expect(Infile, Line, "ENCODING", Quiet) == 0)
{ Err=loadHelp(Infile, Line);
if(Err != 0)
break;
Quiet=1; }
First=0; /* xxx */
fclose(Infile);
if(Err != 0)
{ free(Spacings);
free(Bitmaps); }
else wprintf(L"OK\n");
return(Err); }}
HRESULT BfdFont::DrawText(ISurface* pSurf, int x, int y, const wchar_t* String, pixel_t pixColour)
{
bmp1 SrcBmp;
rect Clip;
char Char;
for(Char=*String++; Char != '\0'; Char=*String++)
/* valid Char? */
{
if(Char < TheFont.First || Char > TheFont.Last)
continue;
Char -= TheFont.First;
if(TheFont.Spacings == NULL)
/* monospaced font: make each char 8 pixels * character cell width */
Clip.Wd=TheFont.Wd * 8;
else
Clip.Wd=TheFont.Spacings[Char];
/* clip it */
Clip.Ht=TheFont.Ht;
Clip.DstX=this->CsrX;
Clip.DstY=this->CsrY;
if(!taliWinClip(&(Clip.SrcX), &(Clip.SrcY),
&(Clip.DstX), &(Clip.DstY),
&(Clip.Wd), &(Clip.Ht), this->Wd, this->Ht))
continue;
/* create monochrome source bitmap */
SrcBmp.Wd=TheFont.Wd * 8;
SrcBmp.Ht=Clip.Ht;
SrcBmp.Raster=TheFont.Bitmaps + TheFont.Wd *
TheFont.Ht * Char;
/* blit it */
this->Bitmap->blit1(Clip, SrcBmp);
/* advance cursor. ONE pixel between chars */
this->CsrX=this->CsrX + Clip.Wd + 1;
}
/* prevent delete[]/free() by SrcBmp destructor */
SrcBmp.Raster=NULL;
}
/*****************************************************************************
*****************************************************************************/
HRESULT BfdFont::GetTextExtent(const wchar_t *String, point_t* size)
{
unsigned RetVal;
/* monospaced font */
if(Spacings == NULL)
return Point(strlen(String) * Wd * 8, Ht);
/* proportional-spaced font */
for(RetVal=0; *String != '\0'; String++)
{
if(*String < First || *String > Last)
continue; /* undefined chars have zero width */
RetVal += Spacings[*String - First] + 1;
}
size->x = RetVal;
size->y = Ht;
return S_OK;
}