Game Development Community

TGE uses different fonts than the ones specified (win32)

by Florian Ross · in Torque Game Engine · 01/18/2008 (2:05 pm) · 5 replies

If windows doesn't find the font specified it will just use a different font with the same characteristics. Unfortunately TGE (and TGEA where we discovered this) doesnt report these circumstances but silently accepts this. We only saw this when a font (Arial Bold) didnt render at all at certain sizes but only rendered as Arial at other sizes.

After a few hours of digging through the font code i saw that in file platformWin32 in function PlatformFont::CharInfo &WinFont::getCharInfo(const UTF16 ch) const the Arial Bold i specified earlier was rendered as MS Sans Serif. MS Sans Serif on my computer is a Bitmap font and as such cant be used with GetGlyphOutline. The If statement fails and it silently renders each character as a black square.

So i added a console warning if it cant render normally printable characters

change
if(GetGlyphOutline(
            fontHDC,	// handle of device context 
            ch,	// character to query 
            GGO_GRAY8_BITMAP,	// format of data to return 
            &metrics,	// address of structure for metrics 
            sizeof(scratchPad),	// size of buffer for data 
            scratchPad,	// address of buffer for data 
            &matrix 	// address of transformation matrix structure  
            ) != GDI_ERROR)
    {
        [b]...[/b]
    }
    else
    {    
      SIZE size;
      GetTextExtentPoint32W(fontHDC, &ch, 1, &size);
      if(size.cx)
      {
          c.xIncrement = size.cx;
          c.bitmapIndex = 0;
      }
    }
to
if(GetGlyphOutline(
            fontHDC,	// handle of device context 
            ch,	// character to query 
            GGO_GRAY8_BITMAP,	// format of data to return 
            &metrics,	// address of structure for metrics 
            sizeof(scratchPad),	// size of buffer for data 
            scratchPad,	// address of buffer for data 
            &matrix 	// address of transformation matrix structure  
            ) != GDI_ERROR)
    {
        ...
    }
    else
    {
      [b]// printable character should be renderable except space
      if (dIsprint(ch) && !dIsspace(ch))
      {
         char fontName[512];
         UTF16 fontName16[512];
         GetTextFace(fontHDC, sizeof(fontName16),fontName16);
         convertUTF16toUTF8(fontName16, (UTF8*)&fontName, sizeof(fontName));
         Con::warnf("Couldn't create a bitmap for character #%d of font '%s %d'.", ch, fontName, mTextMetric.tmHeight);
      }[/b]
      
      SIZE size;
      GetTextExtentPoint32W(fontHDC, &ch, 1, &size);
      if(size.cx)
      {
          c.xIncrement = size.cx;
          c.bitmapIndex = 0;
      }
    }
add extern bool dIsprint(const char c); to platform.h in engine/platform
extern bool dIsdigit(const char c);
extern bool dIsspace(const char c);
[b]extern bool dIsprint(const char c);[/b]
//------------------------------------------------------------------------------
// Unicode string conversions
add a function dIsprint to winStrings.cpp
bool dIsspace(const char c)
{
   return isspace(c)!=0;
}

[b]bool dIsprint(const char c)
{
   return isprint(c)!=0;
}[/b]

bool dIsdigit(const char c)
{
   return isdigit(c)!=0;
}

#1
01/18/2008 (2:14 pm)
To make windows stop using Bitmap fonts you have to edit the CreateFont function in bool WinFont::create(const char *name, U32 size, U32 charset)
change the parameter OUT_TT_PRECIS to OUT_TT_ONLY_PRECIS so that windows will only use TrueType fonts. This will howewer make our Arial Bold render as just Arial. To make it look like it is Arial Bold we have to detect if we wanted Bold initially and add it as a parameter to CreateFont

this code does exactly that. Replace bool WinFont::create(conts char *name, U32 size, U32 charset) {...} with:

bool WinFont::create(const char *desiredFontName, U32 size, U32 charset /* = TGE_ANSI_CHARSET */)
{
    if(desiredFontName == NULL || size < 1)
        return false;

    if(charset > NUMCHARSETMAP)
       charset = TGE_ANSI_CHARSET;

#ifdef UNICODE
    UTF16 desiredFontName16[512];
    convertUTF8toUTF16((UTF8 *)desiredFontName, desiredFontName16, sizeof(desiredFontName16));
    mFont = CreateFont(size,0,0,0,0,0,0,0,DEFAULT_CHARSET,OUT_TT_ONLY_PRECIS,0,PROOF_QUALITY,0,desiredFontName16);
#else
    mFont = CreateFont(size,0,0,0,0,0,0,0,charsetMap[charset],OUT_TT_PRECIS,0,PROOF_QUALITY,0,desiredFontName);
#endif
    if(mFont == NULL)
      return false;

    // test if the created font is the one we wanted
    SelectObject(fontHDC, mFont);
    char actualFontName[512];
#ifdef UNICODE
    UTF16 actualFontName16[512];
    GetTextFace(fontHDC, sizeof(actualFontName16), actualFontName16);
    convertUTF16toUTF8(actualFontName16, (UTF8*)&actualFontName, sizeof(actualFontName));
#else
    GetTextFace(fontHDC, sizeof(actualFontName), &actualFontName);
#endif

    if (dStrcmp(desiredFontName, actualFontName))
    {
       Con::warnf ("%s %d is not availabe in your System. Used %s %d instead.", desiredFontName, size, actualFontName, size);

       U32 weight = 0;
       bool italic = false;

       if (dStrstr((const char*)desiredFontName, "Bold"))
          if (!dStrstr((const char*)actualFontName, "Bold"))
             weight = FW_BOLD;

       if (dStrstr((const char*)desiredFontName, "Black"))
          if (!dStrstr((const char*)actualFontName, "Black"))
             weight = FW_BLACK;

       if (dStrstr((const char*)desiredFontName, "Italic"))
          if (!dStrstr((const char*)actualFontName, "Italic"))
             italic = true;

       // we need to get rid of the old font so we can get a new one with the features we want
       DeleteObject(mFont);
       mFont = NULL;

       // create a new font with added "bold" and "italic" values
#ifdef UNICODE
      mFont = CreateFont(size,0,0,0,weight,italic,0,0,DEFAULT_CHARSET,OUT_TT_ONLY_PRECIS,0,PROOF_QUALITY,0,actualFontName16);
#else
      mFont = CreateFont(size,0,0,0,weight,italic,0,0,charsetMap[charset],OUT_TT_ONLY_PRECIS,0,PROOF_QUALITY,0,actualFontName);
#endif
       
    }

    SelectObject(fontHDC, fontBMP);
    SelectObject(fontHDC, mFont);
    GetTextMetrics(fontHDC, &mTextMetric);

    return true;
}
#2
01/18/2008 (2:31 pm)
To make sure that your game has the required fonts, include them in your "common/ui/cache" folder
#3
01/31/2008 (7:31 am)
Thanks for this Florian, I've been playing with the font system myself of late.

You mention "GetGlyphOutline". I may be reading too much into the name but do you know if it's possible to use this to "outline" font characters (say black with a white outline) without too much custom coding?
#4
01/31/2008 (1:38 pm)
You mean Outline as in "not filled", right? The easiest way to do that would be to use a font that is just outlined and not filled. From what i could gather on MSDN there is no parameter that will do that for you.

If you want to code something you will probably have to return a bezier curve from getGlyphOutline and scanconvert it yourself. This would however result in major coding tho.
#5
01/31/2008 (1:53 pm)
Well I meant filled (with black) and outlined (with white) or vice-versa.

I was trying to get TGE to outline existing fonts rather than have to create our own and distribute them.

I thought it was more than trivial in effort but hadn't had a chance to really look into it.

Thanks