UCanvas::StrLen does not take kerning into account and returns a wrong size

I wanted to create a small Slate Widget that displays two pieces of text in diffrent font sizes directly next to each other. After rendering the first piece, and advancing my rendering position by the value UCanvas::StrLen( … ) for it, I drew the second piece. The result was the following:


As you can see, the second piece is displaced. After tracing the problem through the sourcecode, I discovered that UCanvas::MeasureStringInternal used the Kerningvalues returned by UFont::GetCharKerning(…), and that these seem be 0 for all characters, which seems to be connected to the newer SlateFonts, whose Kerning values are stored in the FSlateFontCache. My solution to this problem was to write a new measuring method. Please keep in mind that this most likely does not cover all diffrent types of text formatting that can occur:

FVector2D CalculateStringLength(FString String, FSlateFontInfo& FontInfo,float Scale)
{
	//Emtpy string
	if (String.Len() == 0)
	{
		return FVector2D(0, 0);
	}

	//Characterlist from the SlateFontCache
	TSharedRef<FSlateFontCache> FontCache = FSlateApplication::Get().GetRenderer()->GetFontCache();
	FCharacterList& CharacterList = FontCache->GetCharacterList(FontInfo, Scale);

	const TCHAR* pCurrentPos;                        //where are we now in the string ?
	const TCHAR* pPrevPos = 0;						 //which was the last string ( needed to scaling )
	const TCHAR* pText = *String;					 //Chararray to be iterated
	const int32 TextLength = String.Len();           //Length of the String
	const int32 YIncrement = FontCache->GetMaxCharacterHeight(FontInfo, Scale);

	FVector2D Size = FVector2D(0, YIncrement);
	//While our pointer is valid and we are within the bounds of the chararray ...
	for (pCurrentPos = pText; *pCurrentPos && pCurrentPos < pText + TextLength; ++pCurrentPos)
	{
		TCHAR currentChar = *pCurrentPos;
		Size.X += CharacterList[currentChar].XAdvance; //Add the width of the current char to the counter

		if (currentChar == TEXT('\n') && pCurrentPos + 1 < pText + TextLength && pCurrentPos + 1)
		{ // Char is a newline and string is not at end
			Size.Y += YIncrement;
			pPrevPos = 0; //No kerning when beginning a new line
		}
		else if (pPrevPos != 0)
		{
			//Do not forget to take Kerning into account
			Size.X += FontCache->GetKerning(FontCache->GetDefaultFontData(FontInfo), FontInfo.Size, *pPrevPos, currentChar, Scale);
		}

		//Cache last char
		pPrevPos = pCurrentPos;
	}

	return Size;
}

These one uses the values provided by the SlateFontInfo and yields correct result:


Can anyone else confirm this problem ?

Ah, despite the fact that Canvas accepts Slate font data, you’re not actually using Slate here :slight_smile:

UFont::GetCharKerning should be returning the correct kerning for your font, however Canvas text measuring can currently only work with the “legacy” font info from the UFont - see UFont::GetLegacySlateFontInfo() - so if you’re passing a custom FSlateFontInfo when drawing your FCanvasTextItem, then your measuring may be off.

A better way to measure the text if you know you’re dealing with a composite font (which you are) is to just use the Slate font measurer directly.

const TSharedRef<FSlateFontMeasure> FontMeasure = FSlateApplication::Get().GetRenderer()->GetFontMeasureService();
 FontMeasure->Measure(YourText, YourFontInfo);

Alternatively, once you’ve called Draw on your FCanavsTextItem, its DrawnSize variable will be filled in with the size of the text that was just drawn.

Could you post the code used when setting up the FCanvasTextItem(s) that you draw with, and also a link to your font (if possible, I realise it may not be available publicly or freely).

Thank you, your hint about the measuring system already helped ma a lot. Sorry for the late anwser ;(