Module: qfont.cpp

class QFont
.h

constructorQFont(cstring _fname,int _size,int _slant,int _weight,int _width)
destructor~QFont()

Flat functions
 

perrstatic void perr(cstring s)

class QFont
.h

Createbool Create()

Actually create the font

SetResXvoid SetResX(int n)
SetResYvoid SetResY(int n)
GetListBaseint GetListBase()
GetXFontFont GetXFont()
GetAscentint GetAscent()
GetDescentint GetDescent()
GetHeightint GetHeight(cstring s)
GetWidthint GetWidth(ubyte c)
GetWidthint GetWidth(cstring s,int len)

Returns width of string (upto 'len' chars)
This takes into account any overhanging of the LAST char
BUG: doesn't take into account overhanging of FIRST char
If 'len'==-1, the entire string is measured (upto chr(0))

GetLeftBearingint GetLeftBearing(ubyte c)
GetRightBearingint GetRightBearing(ubyte c)

Flat functions
 

AddStringstatic bool AddString(char **list,string s)

Adds string to list 'list'; makes sure 's' is unique


class QFontList
.h

constructorQFontList()
destructor~QFontList()
GetFamilycstring GetFamily(int n)
GetFoundrycstring GetFoundry(int n)


/*
 * QFont - font selection
 * 10-10-96: Created!
 * BUGS:
 * - Win32: hDC's are plucked from QSHELL, instead of from the current QGLContext
 * - Win32: GetWidth() should perhaps use GetABCCharWidths() instead of GetCharWidth32()
 * (no leading/ending spacing added in the other cases)
 * - Left/Right bearing and such nyi
 * FUTURE:
 * - All parameters editable; QFont ctor, QFont::Create() order!
 * - resX/resY should be editable (hor/vert scaling)
 * NOTES:
 * - Depending on ? (the monitor?) fonts are selected differently,
 * so we much try to be as explicit as possible
 * (C) MarketGraph/RVG
 */

#include <stdio.h>
#include <qlib/font.h>
#include <qlib/app.h>
#include <qlib/display.h>
#include <qlib/debug.h>
DEBUG_ENABLE

#include <X11/Xlib.h>
#ifdef WIN32
#include <windows.h>
#endif
#include <GL/gl.h>
#include <GL/glx.h>

struct QFontInternal
{ XFontStruct *fi;
};

#undef  DBG_CLASS
#define DBG_CLASS "QFont"

// c/dtor

QFont::QFont(cstring _fname,int _size,int _slant,int _weight,int _width)
{ 
  fname=qstrdup(_fname);
  size=_size;
  slant=_slant;
  weight=_weight;
  width=_width;
  resX=100;
  resY=100;
  listBase=0;
#ifdef WIN32
  hFont=0;
#endif

  internal=(QFontInternal*)qcalloc(sizeof(*internal));
  internal->fi=0;
}
QFont::~QFont()
{
  if(fname)qfree(fname);
#ifdef WIN32
  if(hFont)DeleteObject(hFont);
#else
  // Free used display lists (important!)
  if(listBase)
  { int first,last;
    first=internal->fi->min_char_or_byte2;
    last =internal->fi->max_char_or_byte2;
    glDeleteLists(listBase,last-first+1);
  }
  //XUnloadFont(dsp->GetX11Display(),fi->fid);
  if(internal)qfree(internal);
#endif
}

#ifdef WIN32
static void perr(cstring s)
{
  LPVOID lpMsgBuf;
 
  FormatMessage( 
    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
    NULL,
    GetLastError(),
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
    (LPTSTR) &lpMsgBuf,
    0,
    NULL 
  );

  // Display the string.
  qdbg("Error (%s): %s\n",s,lpMsgBuf);

  // Free the buffer.
  LocalFree( lpMsgBuf );
}
#endif

bool QFont::Create()
// Actually create the font
{
  int first,last;

  QASSERT_F(slant>=0&&slant<=QFONT_SLANT_OBLIQUE);
  QASSERT_F(width>=0&&width<=QFONT_WIDTH_CONDENSED);
  QASSERT_F(weight>=0&&weight<=QFONT_WEIGHT_REGULAR);

#ifdef WIN32
  int weightVal[]={ 0,FW_MEDIUM,FW_BOLD,FW_NORMAL,FW_REGULAR };

  // Create OS font
  hFont=CreateFont(size,0,0,0,weightVal[weight],0,0,0,0,0,0,0,0,"arial");
  if(!hFont)return FALSE;

  // Generate display lists for the glyphs
  first=32;
  last =127;
  listBase=glGenLists((GLuint)last+1);
  if(!listBase)
  { qerr("QFont::Create: out of display lists (%d)\n",last+1);
    return FALSE;
  } else
  { // Generate lists (with glBitmap() calls)
    HDC hDC=QSHELL->GetQXWindow()->GetHDC();
    HFONT oldFont;
    QSHELL->GetCanvas()->Select();
    oldFont=(HFONT)SelectObject(hDC,hFont);	// VC6 quirk
//qdbg("hdc=%p, listbase=%d, oldfont=%p",hDC,listBase,oldFont);
    if(!wglUseFontBitmaps(hDC,first,last-first+1,listBase+first))
    { qerr("QFont: can't create wgl display lists for %s/%d",fname,size);
      perr("wglUseFontBitmaps");
      return FALSE;
    }
#ifdef ND_TEST
    GLYPHMETRICS gm;
  //MAT2 m2;
  //m2.eM11=(FIXED)1; m2.eM12=(FIXED)0; m2.eM21=(FIXED)0; m2.eM22=(FIXED)1;
  if(GetGlyphOutline(hDC,'3',GGO_METRICS,&gm,sizeof(gm),0,0)==GDI_ERROR)
  {
    qwarn("QFont:Create(); error in GetGlyphOutline()");
  } else qlog(QLOG_INFO,"width of '3'=%d",gm.gmBlackBoxX);
#endif

    // Get font info
    GetTextMetrics(hDC,&tm);
    SelectObject(hDC,oldFont);
  }
  return TRUE;
#else
  char buf[100];
  char *slantName[] ={ "*","r","i","o" };
  char *weightName[]={ "*","medium","bold","demi","regular" };
  char *widthName[] ={ "*","normal","narrow","condensed" };

  //listBase=0;

  //internal=(QFontInternal*)qcalloc(sizeof(*internal));
  //internal->fi=0;

  sprintf(buf,"-*-%s-%s-%s-%s-*-%d-*-%d-%d-*-*-*-*",fname,weightName[weight],
    slantName[slant],widthName[width],size,
    resX,resY);
  //DEBUG_F(qdbg("QFont '%s'\n",buf));
  internal->fi=XLoadQueryFont(app->GetDisplay()->GetXDisplay(),buf);
  if(!internal->fi)
  { qerr("QFont::Create: can't create font '%s'\n",buf);
    return FALSE;
  }
  // Generate display lists for the glyphs
  first=internal->fi->min_char_or_byte2;
  last =internal->fi->max_char_or_byte2;
  listBase=glGenLists((GLuint)last+1);
  if(!listBase)
  { qerr("QFont::Create: out of display lists (%d)\n",last+1);
    return FALSE;
  } else
  { // Generate lists (with glBitmap() calls)
    glXUseXFont(internal->fi->fid,first,last-first+1,listBase+first);
  }
  return TRUE;
#endif
}

// Win32 needs font support still

// Set attributes
void QFont::SetResX(int n)
{
  if(internal->fi)
  { qerr("QFont::SetResX(); can't modify after Create()");
    return;
  }
  resX=n;
}
void QFont::SetResY(int n)
{
  if(internal->fi)
  { qerr("QFont::SetResY(); can't modify after Create()");
    return;
  }
  resY=n;
}

int QFont::GetListBase()
{ return listBase;
}

// Information
Font QFont::GetXFont()
{
#ifdef WIN32
  QASSERT_0(hFont);
  // No X Font under Win32
  //qerr("QFont:GetXFont() nyi/win32");
  return 0;
#else
  QASSERT_0(internal->fi);		// Create() first
  if(internal->fi)
    return internal->fi->fid;
  return 0;
#endif
}
int QFont::GetAscent()
{
#ifdef WIN32
  return tm.tmAscent;
#else
  QASSERT_VALID()
  DBG_C("GetAscent")
  QASSERT_0(internal->fi);		// Create() first
  return internal->fi->ascent;
#endif
}
int QFont::GetDescent()
{
#ifdef WIN32
  return tm.tmDescent;
#else
  QASSERT_0(internal->fi);		// Create() first
  return internal->fi->descent;
#endif
}
int QFont::GetHeight(cstring s)
{
#ifdef WIN32
  return tm.tmHeight;
#else
  QASSERT_0(internal->fi);		// Create() first
  /*printf("QFont::GetHgt: log=%d+%d, max=%d+%d\n",
    fi->ascent,fi->descent,fi->max_bounds.ascent,fi->max_bounds.descent);*/
  return internal->fi->max_bounds.ascent+internal->fi->max_bounds.descent;
#endif
}

int QFont::GetWidth(ubyte c)
{
#ifdef WIN32
  HDC hDC=QSHELL->GetQXWindow()->GetHDC();
  HFONT oldFont;
  int   w;

  oldFont=(HFONT)SelectObject(hDC,hFont);
  // Get width of a single char
  // Note that GetCharWidth32() is only available on WinNT (!)
  if(!GetCharWidth(hDC,c,c,&w))
  {
    qwarn("QFont:GetWidth(); error in GetCharWidth32()");
  }
  SelectObject(hDC,oldFont);
//qlog(QLOG_INFO,"Metrics width of '%c' = %d",c,w);
  return w;
#else
  QASSERT_0(internal->fi);		// Create() first
  // Check if in font
  if(c<internal->fi->min_char_or_byte2||c>internal->fi->max_char_or_byte2)
    return 0;
  return internal->fi->per_char[c-internal->fi->min_char_or_byte2].width;
#endif
}
int QFont::GetWidth(cstring s,int len)
// Returns width of string (upto 'len' chars)
// This takes into account any overhanging of the LAST char
// BUG: doesn't take into account overhanging of FIRST char
// If 'len'==-1, the entire string is measured (upto chr(0))
{
  int wid=0;
  char lastC;
  int  cwid,rb;

#ifdef WIN32
  QASSERT_0(hFont);
#else
  QASSERT_0(internal->fi);		// Create() first
#endif

  if(len==-1)
  {
    // Take care of right hanging of last char
    if(s!=0&&*s!=0)
    { lastC=s[strlen(s)-1];
      cwid=GetWidth(lastC);
      rb=GetRightBearing(lastC);
      if(rb>cwid)wid=rb-cwid;
    }
    for(;*s;s++)
    { wid+=GetWidth(*s);
    }
  } else
  {
    // Take care of right hanging of last char
    if(s!=0&&len>0)
    { lastC=s[len-1];
      cwid=GetWidth(lastC);
      rb=GetRightBearing(lastC);
      if(rb>cwid)wid=rb-cwid;
    }
    for(;len>0;len--)
    { wid+=GetWidth(*s);
      s++;
    }
  }
  return wid;
}
int QFont::GetLeftBearing(ubyte c)
{
#ifdef WIN32
  QASSERT_0(hFont);
  //qerr("QFont:GetLeftBearing() nyi/win32");
  return 0;
#else
  QASSERT_0(internal->fi);		// Create() first
  // Check if in font
  if(c<internal->fi->min_char_or_byte2||c>internal->fi->max_char_or_byte2)
    return 0;
  return internal->fi->per_char[c-internal->fi->min_char_or_byte2].lbearing;
#endif
}
int QFont::GetRightBearing(ubyte c)
{
#ifdef WIN32
  //qerr("QFont:GetRightBearing() nyi/win32");
  return 0;
#else
  QASSERT_0(internal->fi);		// Create() first
  // Check if in font
  if(c<internal->fi->min_char_or_byte2||c>internal->fi->max_char_or_byte2)
    return 0;
  return internal->fi->per_char[c-internal->fi->min_char_or_byte2].rbearing;
#endif
}

/***************
* FONT LISTING *
***************/
static bool AddString(char **list,string s)
// Adds string to list 'list'; makes sure 's' is unique
{
  int i;
  for(i=0;;i++)
  { if(list[i]==0)
    { list[i]=qstrdup(s);
      qdbg("AddString(%s); list[%d] set (%p)\n",s,i,list[i]);
      return TRUE;
    } else
    { 
      //qdbg("  cmp(%s,%s)\n",list[i],s);
      if(strcmp(list[i],s)==0)return FALSE;
    }
  }
  //return FALSE;
}

QFontList::QFontList()
{
#ifdef WIN32
  qerr("QFontList ctor nyi/win32");
#else
  char *s;
  int n;
  qdbg("QFontList ctor\n");
  xlist=XListFonts(app->GetDisplay()->GetXDisplay(),"*",9999,&count);
  qdbg("  count=%d\n",count);
  for(int i=0;i<count;i++)
  { //qdbg("  '%s'\n",xlist[i]);
  }
  // Collect family names
  familyCount=count/2;
  family=(char**)qcalloc(sizeof(char*)*familyCount);
  char buf[150];
  for(i=n=0;i<count;i++)
  { if(xlist[i][0]!='-')continue;
    strcpy(buf,xlist[i]);
    // Find 2nd field awk { print $2; }
    s=strtok(buf,"-");
    if(s)s=strtok(0,"-");
    if(s)
    { if(AddString(family,s))n++;
    }
  }
  familyCount=n;

  // Foundry names
  foundryCount=0;
  foundry=0;
#endif
}

QFontList::~QFontList()
{
#ifndef WIN32
  if(xlist)
    XFreeFontNames(xlist);
#endif
}

cstring QFontList::GetFamily(int n)
{
  if(n<0||n>=familyCount)return 0;
  return family[n];
}

cstring QFontList::GetFoundry(int n)
{
  if(n<0||n>=foundryCount)return 0;
  return foundry[n];
}