【问题标题】:Inconsistent behavior of kerning in NSAttributedStringNSAttributedString 中字距调整的不一致行为
【发布时间】:2019-03-01 16:07:30
【问题描述】:

我需要使用Kern attributeNSAttributedString。正如我在文档中看到的那样,该属性的默认值为 0.0。但是我遇到了短语Hello, world 的奇怪行为(对于短语“Hello”,一切都很好):

NSDictionary<NSString*, id>* attributes = @{NSFontAttributeName: [UIFont systemFontOfSize:12]};
NSString* text = @"Hello, World";
NSAttributedString* string = [[NSAttributedString alloc] initWithString:text attributes:attributes];
CGSize size1 = [string size];

NSMutableDictionary<NSString*, id>* attributesWithKernel = [attributes mutableCopy];
attributesWithKernel[NSKernAttributeName] = @(0.0);
NSAttributedString* stringWithKern = [[NSAttributedString alloc] initWithString:text attributes:attributesWithKernel];
CGSize size2 = [stringWithKern size];   
XCTAssertTrue(CGSizeEqualToSize(size1, size2)); //here test falls
//size1 = size1 = (width = 68.8125, height = 14.3203125)
//size2 = (width = 69.515625, height = 14.3203125)

要使 size1 和 size2 相等,字距应该相等 -7.105427357601002e-15 ,我知道这非常接近 0.0,但很奇怪,因为这会改变几乎一个像素的宽度。
NSAttributedString 具有相同的Objective-CSwift 中的行为,例如 swift:

    let text = "Hello, World"
    let attributes : [NSAttributedString.Key : Any] = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: UIFont.systemFontSize)]
    let str = NSAttributedString(string: text, attributes: attributes)
    let size = str.size()

    var attributesWithKern = attributes
    attributesWithKern[NSAttributedString.Key.kern] = NSNumber(value: 0.0)
    let strWithKern = NSAttributedString(string: text, attributes: attributesWithKern)
    let sizeWithKern = strWithKern.size()

    XCTAssertTrue(size == sizeWithKern)

我该如何解决这种行为?

附言 现在,如果键等于 0.0,我只需从属性字符串中删除 NSKernAttributeKey,但我认为这不是一个好的解决方案。

【问题讨论】:

  • This question about different result of size method for attributes with and without default value for kerning.

标签: ios objective-c swift nsattributedstring kerning


【解决方案1】:

我认为这里的文档是错误的,因此值得打开 radar。如果未设置任何值,则将其解释为“正常字距调整”。当设置为 0 时,它被解释为“禁用字距调整”,这就是为什么宽度稍宽的原因(字距调整通常略为负,带来了字距对字符,如此字体中的“W”和“o”,有点更近)。我认为没有任何方法可以在不删除属性的情况下明确请求“默认字距调整”。

出于您的目的,我相信您在值为零时删除该值是正确的,因为您需要默认字距调整,而不是禁用字距调整。

您的微小负值起作用的原因是因为它不是零,所以它不会禁用字距调整,但它是如此之小以至于行为非常非常接近默认值,并且您正在运行 Double in中间计算(或者可能是 Float 的精度,取决于它的内部实现方式)。您应该发现您的测试通过了任何小于(接近于零)的值,而不仅仅是那个值。例如,在我的测试中,阳性 7e-15 也有效。

【讨论】:

    【解决方案2】:

    我根据您的代码进行了一些测试,并确认此行为看起来像一个错误。关键是有些字母字符串是相等的,有些则不是。例如带有“Hello Mo”或“Hello Oo”的字符串是相等的,但例如“Hello WoWoWo”有很大不同。所以我们在这里看到为“W”添加了一些字距调整,没有任何理由。它也可以取决于选择的字体,但我没有测试它。我在这里看到您使用的唯一解决方案 - 如果 NSKernAttributeKey 等于 0,则删除它。

    【讨论】:

    • "关键是有些字母字符串相等,有些则不相等。"这完全符合字距调整的预期。 “此值指定调整紧缩对字符的点数。”在这种字体中,H 和 e 不是紧缩对字符,但 W 和 o 是。上面的例子看起来是紧缩的,而下面的例子看起来是非紧缩的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-25
    • 2014-10-10
    相关资源
    最近更新 更多