Search code examples
iosswiftuilabelcalayercore-text

Kerning issue when rendering string using the CoreText


I am trying to render string using the CoreText api. Each character is rendered in the CAShapeLayer and i am getting the layer offset (x coordinate) for each character like this:

let offset = CTLineGetOffsetForStringIndex(line, glyphIndex, nil)

But the resulting offset doesn't seem to respect kerning between letters. Here is a an image of what i mean - the top label is a UILabel, the bottom one is my custom rendering:

enter image description here

As you can see the "A" letter in my custom label is placed further from the "W" letter.

I would really appreciate any help. Thanks in advance.


Solution

  • In case someone has the same problem, i used an approach from this place: https://github.com/MichMich/XMCircleType/blob/master/XMCircleType/Views/XMCircleTypeView.m, the kerningForCharacter function. Here is the function itself:

    - (float)kerningForCharacter:(NSString *)currentCharacter afterCharacter:(NSString *)previousCharacter
    {
        //Create a unique cache key
        NSString *kerningCacheKey = [NSString stringWithFormat:@"%@%@", previousCharacter, currentCharacter];
    
        //Look for kerning in the cache dictionary
        NSNumber *cachedKerning = [self.kerningCacheDictionary objectForKey:kerningCacheKey];
    
        //If kerning is found: return.
        if (cachedKerning) {
            return [cachedKerning floatValue];
        }
    
        //Otherwise, calculate.
        float totalSize = [[NSString stringWithFormat:@"%@%@", previousCharacter, currentCharacter] sizeWithAttributes:self.textAttributes].width;
        float currentCharacterSize = [currentCharacter sizeWithAttributes:self.textAttributes].width;
        float previousCharacterSize = [previousCharacter sizeWithAttributes:self.textAttributes].width;
    
        float kerning = (currentCharacterSize + previousCharacterSize) - totalSize;
    
        //Store kerning in cache.
        [self.kerningCacheDictionary setValue:@(kerning) forKey:kerningCacheKey];
    
        //Return kerning.
        return kerning;
    }