Font kerning information with Font rendering

Started by
26 comments, last by simotix 13 years, 3 months ago
There's nothing you need to query other than what is already returned to you by GetCharABCWidths. 'j' in Times New Roman 12pt has an 'A' width of -1 for example.
-Mike
Advertisement
Quote:Original post by Anon Mike
There's nothing you need to query other than what is already returned to you by GetCharABCWidths. 'j' in Times New Roman 12pt has an 'A' width of -1 for example.


Interesting, perhaps I was trying to make it more difficult than it really was.

How would you recommend calculating the width? I was going to just use the "B" width and scale.
Quote:Original post by simotix
How would you recommend calculating the width? I was going to just use the "B" width and scale.


Freetype has the FT_Glyph_Metrics struct containing the advance width, bearings etc. and the FT_Get_Kerning() function to report kerning pairs.

Omae Wa Mou Shindeiru

I was hoping to avoid using FreeType or other libraries as this functionality should work with windows functions.

I am extremely close, although I do not see any adjustment for kerning information. For example, in Times New Roman it seems like "nj" would have kerning information, however, I am getting that there is no kerning amount. The only thing that is adjust for a combination like "nj" is GLYPHMETRICS.gmCellIncX .

Here is what I am doing, is there any suggestions?

	int previousCharIndex = -1;	int size = 48;	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,"Times New Roman");	SelectObject (dc, fnt);	int nKerningPairs = GetKerningPairs(dc, 0, 0);	KERNINGPAIR * kerningpairs = new KERNINGPAIR[nKerningPairs];	GetKerningPairs(dc, nKerningPairs, kerningpairs);	float letterBottomLeftX = 0.0f;	float letterHeight = 1.0f;	float letterWidth = 1.0f;	float previousRightSideBearing = 0.0f;	float scale = 0.05f;	for(U32 c = 0; c < numberOfCharacters; ++c)	{		fontResource->GetCharcterInfo(m_Text[c], charInfo);		letterWidth = charInfo.advancedWidth * scale;		letterHeight = textMetrics.Height * scale;		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 * scale);					break;				}			}			letterBottomLeftX -= (gm.gmCellIncX * scale);		}		// set vertex information		// set uv's				// set indicies		letterBottomLeftX += letterWidth;		previousCharIndex = m_Text[c];	}
Maybe you get no kerning pair because kerning is disabled in your HDC or HFONT.
Try rendering a whole string to see if it is kerned like it should.

Omae Wa Mou Shindeiru

I got sick of tweaking these numbers and constantly finding problem pairs, so I wrote my own kerner in the end. I have a [256][256] array of character advances and I look that up with the current letter and the next one.

To get this array filled in, I render each letter to a blank image and then blur it by one or more pixels (based on the average size of the letters, so bigger fonts blur out more. The actual heuristic for this took some fiddling).

Then I test render them one over the other and check for a collision. If there is, I move the 2nd one right another pixel and check again. It takes about a minute to do all 64K of combos and it works absolutely perfectly.
------------------------------Great Little War Game
Quote:Original post by LorenzoGatti
Maybe you get no kerning pair because kerning is disabled in your HDC or HFONT.
Try rendering a whole string to see if it is kerned like it should.


I end up getting kerning pairs for some values, just no ones like "nj".

Quote:Original post by Rubicon
I got sick of tweaking these numbers and constantly finding problem pairs, so I wrote my own kerner in the end. I have a [256][256] array of character advances and I look that up with the current letter and the next one.

To get this array filled in, I render each letter to a blank image and then blur it by one or more pixels (based on the average size of the letters, so bigger fonts blur out more. The actual heuristic for this took some fiddling).

Then I test render them one over the other and check for a collision. If there is, I move the 2nd one right another pixel and check again. It takes about a minute to do all 64K of combos and it works absolutely perfectly.


Do you have a code example of this? This seems like it could expensive and for custom fonts with specific kerning, it seems like this could be wrong.
Quote:Original post by simotix
Quote:Original post by Anon Mike
There's nothing you need to query other than what is already returned to you by GetCharABCWidths. 'j' in Times New Roman 12pt has an 'A' width of -1 for example.

Interesting, perhaps I was trying to make it more difficult than it really was.

Yeah, I've said that two or three times now...

Quote:How would you recommend calculating the width? I was going to just use the "B" width and scale.

Which width are you talking about? There are at least three, and possible more, that are relevant.
  • The 'A' width, i.e., the left side bearing.
  • The black width, i.e., the width of the bitmap needed to hold the glyph
  • The advance width, i.e., the distance the cursor gets moved after drawing a glyph

The data returned by GetCharABCWidths gives you all these. The 'A' width is abcA. The black width is abcB. The advance width is abcA+abcB+abcC.

Your problem, as I understand it, has nothing whatsoever to do with kerning. Mucking around with GetKerningPairs will not help you and just confuses the issue.

FreeType also will not help you. Your problem seems to be one of properly making use of the data you have, not how you get that data in the first place.
-Mike
Quote:Original post by simotix
Do you have a code example of this? This seems like it could expensive and for custom fonts with specific kerning, it seems like this could be wrong.
Nothing sane to post here - far too longwinded, but it should be easy enough from my description, surely. I can help with specific questions if you wish though. I have this in a standalone app that outputs the final texture page and kerning data, so speed isn't really an issue (plus a minute is worse case)

tbh I have seen a few problems with this system, so I shouldn't have used the word "perfect". It is still by far and away the best method imo though, as problem fonts never work anyway and are best avoided.

I use freetype2 to do the glyph rendering. It has better kerning info than windows and can also handle mac fonts (they are different in many ways) but its native output still seemed inferior to my blatant brute force method, possibly because they use info from the font file which isn't always correct. Especially with crappy free fonts you can find on the net.
------------------------------Great Little War Game
Quote:Original post by Anon Mike Your problem seems to be one of properly making use of the data you have, not how you get that data in the first place.


That is correct, I am not sure how to properly use the data that I have. How would you recommend I use it? If I use it the following way, my letters still will not be placed correctly.

	float letterBottomLeftX = 0.0f;	float letterHeight = 1.0f;	float letterWidth = 1.0f;	float scale = 1.0f;	for(U32 c = 0; c < numberOfCharacters; ++c)	{		fon->GetCharcterInfo(charValue, charInfo);		//float advancedWith = (charInfo.A * scale) + (charInfo.B * scale) + (charInfo.C * scale);		letterWidth = charInfo.B * scale;		letterHeight = textMetrics.Height * scale;		if(c != 0)		{			letterBottomLeftX += (charInfo.A * scale);		}		// vertex placement, beginning at letterBottomLeftX				// texture placement		// index placement		letterBottomLeftX += letterWidth;		letterBottomLeftX += (charInfo.C * scale);	}

This topic is closed to new replies.

Advertisement