【问题标题】:Why is sizeWithFont for whole words different from the sum of sizes of its letters?为什么整个单词的 sizeWithFont 与其字母大小的总和不同?
【发布时间】:2014-04-23 22:43:53
【问题描述】:

为什么整个单词的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 对象。此值指定调整紧缩对字符的点数。字距调整可防止特定字符之间出现不需要的空间,并且取决于字体。值 0 表示禁用字距调整。此属性的默认值为 0。

【问题讨论】:

    标签: ios objective-c nsstring uifont


    【解决方案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
    

    【讨论】:

      【解决方案2】:

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

      当您获得完整字符串的大小时,将应用字距调整。对于单个字符,无需紧排,因此可以考虑 ho 间距。

      (Kerning on wikipedia)


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

      【讨论】:

      • 我使用 NSAttributedString 并设置 NSKernAttributeName = 0(禁用字距调整)。禁用字距调整的属性文本的宽度返回与 [mainText sizeWithFont:boldFont] 相同的值。
      • 我认为该设置不会关闭字距调整,它只是允许您添加额外的间距...
      • 我更改了我的示例,向您展示启用和禁用字距调整的文本差异。
      • 此外,当您计算每个字母的长度时,会发生一些近似值或舍入。这种情况发生的次数与单词中的任何数字一样多。所以累积起来,这给了你一个误差范围。结果,对于 9 个字母的单词,你得到的长度差异为 2,我敢打赌,如果你使用 20 个字母的单词,差异会更大。
      • 你可以相信任何你想要的,但我在我的例子中向你展示了这两种情况。字距调整有效,它可以使文本更宽或更窄。
      猜你喜欢
      • 2017-09-26
      • 1970-01-01
      • 2011-08-24
      • 2016-01-19
      • 1970-01-01
      • 2013-09-30
      • 2011-10-02
      • 1970-01-01
      • 2021-04-29
      相关资源
      最近更新 更多