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
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;
}
}toif(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/platformextern bool dIsdigit(const char c); extern bool dIsspace(const char c); [b]extern bool dIsprint(const char c);[/b] //------------------------------------------------------------------------------ // Unicode string conversionsadd 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;
}About the author
#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
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?
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
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.
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
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
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
Torque 3D Owner Florian Ross
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; }