为什么sizeWithFont的整个单词与其字母大小的总和不同?

时间:2014-03-18 10:13:49

标签: ios objective-c nsstring uifont

为什么整个单词的sizeWithFont与其字母大小的总和产生不同的结果?

以下是包含和不包含字距的文本示例:

CGFloat fontSize = 36;
UIFont *font = [UIFont systemFontOfSize:fontSize];

NSArray *wordsToTest = @[@"IIIIIIIII", @"VAVAVAVAV"];

for (NSString *testWord in wordsToTest) {
    CGFloat widthOfTextWithFont = [testWord sizeWithFont:font].width;

    CGFloat widthOfTextUsingIteration = 0;
    for (int i = 0; i < [testWord length]; i++) {
        NSRange range = NSMakeRange(i, 1);
        NSString *nextLetter = [testWord substringWithRange:range];

        widthOfTextUsingIteration += [nextLetter sizeWithFont:font].width;
    }

    NSDictionary *attributesKerningDisabled = @{NSFontAttributeName : font,
                                                NSKernAttributeName : @0.0F};
    NSAttributedString *attributedStringWithDisabledKerning = [[NSAttributedString alloc] initWithString:testWord attributes:attributesKerningDisabled];


    NSDictionary *attributesKerningEnabled = @{NSFontAttributeName : font,
                                               NSKernAttributeName : @1.0F};
    NSAttributedString *attributedStringWithEnabledKerning = [[NSAttributedString alloc] initWithString:testWord attributes:attributesKerningEnabled];


    NSLog(@"Test for string: %@", testWord);
    NSLog(@"Length of the whole word: %f", widthOfTextWithFont);
    NSLog(@"Length of word, using iteration: %f", widthOfTextUsingIteration);
    NSLog(@"Length of attributed string with enabled kerning: %f", [attributedStringWithEnabledKerning size].width);
    NSLog(@"Length of attributed string with disabled kerning: %f", [attributedStringWithDisabledKerning size].width);
}

结果如下:

Test for string: IIIIIIIII
Length of the whole word: 84.000000
Length of word, using iteration: 90.000000
Length of attributed string with enabled kerning: 93.000000
Length of attributed string with disabled kerning: 84.000000

Test for string: VAVAVAVAV
Length of the whole word: 204.000000
Length of word, using iteration: 206.000000
Length of attributed string with enabled kerning: 200.000000
Length of attributed string with disabled kerning: 204.000000

这是Apple文档的一部分:

  

NSKernAttributeName   此属性的值是包含浮点值的NSNumber对象。此值指定调整kern-pair字符的点数。字距调整可防止特定字符之间出现不需要的空间,具体取决于字体。值0表示禁用字距调整。此属性的默认值为0.

2 个答案:

答案 0 :(得分:3)

大概是因为字距调整。字距调整是改变连续字母之间间距的过程,以使文本看起来更好看。

当您获得完整字符串的大小时,将应用字距调整。对于个别角色来说,没有什么可以克服,所以可以考虑间距。

Kerning on wikipedia


我不相信你使用NSKernAttributeName会关闭字距。我认为使用“克恩”这个术语。在这种情况下是不准确的,它实际上只是指字符之间的额外间距,而不是为字体定义的字距。

答案 1 :(得分:1)

正如@AnindyaSengupta所指出的那样,如果你对整个文本的所有字形前进的总和进行四舍五入,或者如果你将每个字形前进向上舍入,则向上舍入是不同的。 两个CoreText函数说明了这些差异:

/* round up the the advances for the whole text */
static double getTextAdvances(NSString *text, CTFontRef font)
{
    unichar *characters = malloc(sizeof(unichar) * text.length);
    CGGlyph *glyphs = malloc(sizeof(CGGlyph) * text.length);
    CGSize *advances = malloc(sizeof(CGSize) * text.length);

    [text getCharacters:characters range:NSMakeRange(0, text.length)];
    CTFontGetGlyphsForCharacters(font, characters, glyphs, text.length);

    CTFontGetAdvancesForGlyphs(font, kCTFontHorizontalOrientation, glyphs, advances, text.length);

    free(characters);
    free(glyphs);

    CGFloat sum = 0.;
    for (NSUInteger i = 0; i < text.length; i++)
        sum += advances[i].width;

    free(advances);

    // round up the whole advances
    return ceilf(sum);
}

/* round up each glyphs advances */
static double getCharactersAdvancesSum(NSString *text, CTFontRef font)
{
    unichar *characters = malloc(sizeof(unichar) * text.length);
    CGGlyph *glyphs = malloc(sizeof(CGGlyph) * text.length);
    CGSize *advances = malloc(sizeof(CGSize) * text.length);

    [text getCharacters:characters range:NSMakeRange(0, text.length)];
    CTFontGetGlyphsForCharacters(font, characters, glyphs, text.length);

    CTFontGetAdvancesForGlyphs(font, kCTFontHorizontalOrientation, glyphs, advances, text.length);

    free(characters);
    free(glyphs);

    CGFloat sum = 0.;
    for (NSUInteger i = 0; i < text.length; i++)
        // round up advance width
        sum += ceilf(advances[i].width);

    free(advances);

    return sum;
}

    // test method
    CGFloat fontSize = 36;
    UIFont *font = [UIFont systemFontOfSize:fontSize];

    NSArray *wordsToTest = @[@"IIIIIIIII", @"VAVAVAVAV"];

    for (NSString *testWord in wordsToTest) {
        // … snip

        NSLog(@"Test for string: %@", testWord);
        NSLog(@"Length of the whole word: %f", widthOfTextWithFont);
        NSLog(@"Length of word, using iteration: %f", widthOfTextUsingIteration);
        // … skip kerning logs
        NSLog(@"Round up whole text advances %f", getTextAdvances(testWord, (CTFontRef)font));
        NSLog(@"Round up per Characters Advances %f", getCharactersAdvancesSum(testWord, (CTFontRef)font));
    }

测试结果:

Test for string: IIIIIIIII
Length of the whole word: 84.000000
Length of word, using iteration: 90.000000

Round up whole text advances 84.000000
Round up per Characters Advances 90.000000

Test for string: VAVAVAVAV
Length of the whole word: 204.000000
Length of word, using iteration: 206.000000

Round up whole text advances 204.000000
Round up per Characters Advances 206.000000