Jump to content
  • Advertisement
Sign in to follow this  
simotix

Font kerning information with Font rendering

This topic is 2756 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am generating a font sheet where I know the X and Y location of the characters on it, along with other information pertaining to the font name and font size. However, I am having a problem with piecing this information together to make something renderable. To obtain the left/right bearing information and regular text metrics (for the height), I use GetCharABCWidthsFloat and GetTextMetricsW.

Does anyone have any suggestions on how to using the font information to piece together a renderable representation of fonts? A good example of this is the sequences "ft" and "nj" in Times New Roman. Inthe sequence "ft", the 'f' will seem like it is coupling the 't' and with "nj" it will appear that the 'j' is hooking under the 'n'.

Share this post


Link to post
Share on other sites
Advertisement
I use freetype2 for font rendering. It provides and uses kerning information, too.

I guess a simple way would be to maintain a list of offsets depending on the character to the left, i.e. for the j keep the standard offset as well as another offset for the n (and possibly other characters as well, maybe in that case the default would be a smaller offset and a bigger one for characters like j,g,y, etc.).

That won't always work for different fonts, however (like arial n and times j), but I assume that's not an issue right now.

Share this post


Link to post
Share on other sites
I able to get kerning information through the gdi dll's, so I don't need to use freefont. I am sure there is an algorithm out there somewhere, I have just not come across it yet.

Share this post


Link to post
Share on other sites
I call "GetCharABCWidthsFloatW" to get the width information for a character. With this I will get the leftside bearing, right side bearing and the advanced width.

For positioning each characters, I will start with a "xPlacement" variables that will begin at zero. I will first adjust the xPlacement variable by subtracting the "left side bearing". After the character is drawn, I will then advance it by the width of the character( I will show the calculation for this later). I will then move the xPlacement variable by adding the "right side bearing" information from the current "xPlacement".

This, in my opinion is all that should be code for character placement, correct?

An important thing is to correct the width of the characters. The width will be calculated by taking the advancedWidth, plus the POSITIVE version of the left side bearing and the POSITIVE version of the right side bearing. I will convert this values to positive, if they are negative so I can have the total width of the character.

Here is some pseudo code about how it is generated.


float xPlacement = 0.0f;
for(int i = 0; i < strlen(text); ++i)
{
char charValue = text;
GetCharWidthABC(.., .., charInfo);

float posLeft = charInfo.leftSideBearing;
if(charInfo.leftSideBearing < 0)
posLeft = -charInfo.leftSideBearing;

float posRight = charInfo.rightSideBearing;
if(posRight < 0)
posRight = -charInfo.rightSideBearing;

float posWidth = posRight + posRight + charInfo.advancedWidth;

float letterWidth = posWidth;

xPlacement -= charInfo.leftSideBearing;

(.... generated some vertex coordinates, using the xPlacement variable and letterWidth ...)

xPlacement += letterWidth;
xPlacement += charInfo.rightSideBearing
}

Does this appear to be the correct way to do this?

Share this post


Link to post
Share on other sites
You never say what exactly the problem is... However I think you're confused about what GetCharABCWidths returns. A and C are the left & right side bearings and B is the black width, not the advance width.

Assuming I'm reading your code right, you want to set posWidth equal to charinfo.abcB and xPlacement += abcA + abcB + abcC. Most of your logic looks unecessary unless you need it for some reason other than letter placement.

If you actually need kerning info, the simplest way to get it is via GetKerningPairs.

Share this post


Link to post
Share on other sites
Quote:
Original post by Anon Mike
You never say what exactly the problem is...


Sorry about that.

I am trying to render the ASCII characters 0-256 to a bitmap image (which I can do). The problem I am having is taking each individual characters and adjusting them for letter placement.

Quote:
Original post by Anon Mike
I think you're confused about what GetCharABCWidths returns. A and C are the left & right side bearings and B is the black width, not the advance width.


What is the point of left and right bearings if not for kerning?

Quote:
Original post by Anon Mike
If you actually need kerning info, the simplest way to get it is via GetKerningPairs.


I went ahead and tried this, however, I believe that I am doing it wrong as it is not adjust the characters correctly (I scaled down the values since they were enormous). Do you have any suggestions to what I may be doing wrong?


int size = (16 * 12) / 4;

HDC dc = CreateCompatibleDC(0);
GLYPHMETRICS gm;
MAT2 mat;
mat.eM11.fract = 0; mat.eM11.value = 1; mat.eM21.fract = 0; mat.eM21.value = 0;
mat.eM12.fract = 0; mat.eM12.value = 0; mat.eM22.fract = 0; mat.eM22.value = 1;

HFONT fnt = CreateFont(size,0,0,0,FW_NORMAL,0,0,0,ANSI_CHARSET,OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,PROOF_QUALITY,FF_DONTCARE,"Arial");
SelectObject (dc, fnt);

int nKerningPairs = GetKerningPairs(dc, 0, 0);
KERNINGPAIR * kerningpairs = new KERNINGPAIR[nKerningPairs];
GetKerningPairs(dc, nKerningPairs, kerningpairs);

for(U32 c = 0; c < numberOfCharacters; ++c)
{
fontResource->GetCharcterInfo(m_Text[c], charInfo);


float posLeft = charInfo.leftSideBearing;
if(charInfo.leftSideBearing < 0)
posLeft = -charInfo.leftSideBearing;

float posRight = charInfo.rightSideBearing;
if(posRight < 0)
posRight = -charInfo.rightSideBearing;

float posWidth = posRight + posRight + charInfo.advancedWidth;

letterWidth = (posWidth * 0.01f);

if(c != 0)
{

DWORD BytesReq = GetGlyphOutlineW(dc, m_Text[c], GGO_GRAY8_BITMAP, &gm, 0, 0, &mat);
U8 * glyphImg= new U8[BytesReq];
DWORD r = GetGlyphOutlineW(dc, m_Text[c], GGO_GRAY8_BITMAP, &gm, BytesReq, glyphImg, &mat);

for (int k=0; k<nKerningPairs; k++)
{
if ((kerningpairs[k].wFirst == previousCharIndex) && (kerningpairs[k].wSecond == m_Text[c])) {
letterBottomLeftX+= (kerningpairs[k].iKernAmount* 0.01f);
break;
}
}
letterBottomLeftX+= (gm.gmCellIncX * 0.01f);
}

letterBottomLeftX += letterWidth;
previousCharIndex = m_Text[c];
}

Share this post


Link to post
Share on other sites
Quote:
Original post by simotix
Quote:
Original post by Anon Mike
You never say what exactly the problem is...

I am trying to render the ASCII characters 0-256 to a bitmap image (which I can do). The problem I am having is taking each individual characters and adjusting them for letter placement.

What does "adjusting them for letter placement" mean? I'm *guessing* that you are seeing bad letter spacing and this is what you're trying to fix.

Quote:
What is the point of left and right bearings if not for kerning?

The side bearings and kerning both tweak letter spacing, but they are different concepts. Negative side bearings are used to account for the fact that advance width may be less than black width, typically because a portion of the glyph overhangs into the space where the adjacent glyphs would be. In practice you see this a lot with italic fonts and or decorative fonts with fancy swashes & such.

Kerning on the other hand exists because specific sets of glyphs sometimes look strange if they're rendered with the default letter spacing. Putting 'A' and 'V' next to each other is the canonical example. The tend to look to far apart so kerning is used to push them together a bit. In practice very few apps bother with kerning, on Windows at least. It's very unlikely your problem has anything to do with kerning.

Quote:
I went ahead and tried this, however, I believe that I am doing it wrong as it is not adjust the characters correctly (I scaled down the values since they were enormous). Do you have any suggestions to what I may be doing wrong?

I still suspect you are confused about what the 'B' width returned by GetCharABCWidths means. It is not the advance width. The advance width is the sum of A+B+C. I also suspect that whatever font "GetCharacterInfo" is working with is different than the HFONT you create a few lines above. The fact that you say you have to manually scale the results you get from GetKerningPairs tends to support this.

Share this post


Link to post
Share on other sites
Quote:
Original post by Anon Mike
Quote:
Original post by simotix
Quote:
Original post by Anon Mike
You never say what exactly the problem is...

I am trying to render the ASCII characters 0-256 to a bitmap image (which I can do). The problem I am having is taking each individual characters and adjusting them for letter placement.

What does "adjusting them for letter placement" mean? I'm *guessing* that you are seeing bad letter spacing and this is what you're trying to fix.


When I say adjusting for letter placement, I mean for coupling together. For example, if I have the letters "n" and "j" in the font "Times New Roman", the hook of the j should go under then.

To account for this, I would need to where the "j" will be rendered. For example, with the letters "n" and "j", and they are built with rectangles with the width of 1, I would render "n" at the position (0, 0, 0). However, since the width of the rectangle "n" will be one, I would then render "j" at (1, 0, 0). What I need to calculate is two things.

1) I would need to adjust the "j" so that way it takes in to account whatever information it needs so the "j" would hook under "n" in a font like Times New Roman.

2) The actual width of the rectangle for the character. For example, the width of the rectangle for "@" should not be the same as "i" for most fonts.

Share this post


Link to post
Share on other sites
Since you cannot rely on the Right Way To Do It (asking a competent implementation like GDI or Freetype to lay out and render a whole string, with hinting and subpixel positioning, taking care for you not only of kerning but also of OpenType ligatures and other features) you should query the font for the full lists of metrics and kerning pairs, which are very simple tables within the TrueType/OpenType font, and place them in the metadata that go with your spritesheet.

Then you know everything you need to implement variable spacing and kerning: the distance between the left side of the "box" of consecutive characters is the previous character's advance width plus (or minus) the kerning adjustment if there is a kerning pair for those characters.

Share this post


Link to post
Share on other sites
Quote:
Original post by LorenzoGatti
Since you cannot rely on the Right Way To Do It (asking a competent implementation like GDI or Freetype to lay out and render a whole string, with hinting and subpixel positioning, taking care for you not only of kerning but also of OpenType ligatures and other features) you should query the font for the full lists of metrics and kerning pairs, which are very simple tables within the TrueType/OpenType font, and place them in the metadata that go with your spritesheet.

Then you know everything you need to implement variable spacing and kerning: the distance between the left side of the "box" of consecutive characters is the previous character's advance width plus (or minus) the kerning adjustment if there is a kerning pair for those characters.


The problem is, I do not know what to query and then what the algorithm is to apply it.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!