Rightpoint / BonMot

Beautiful, easy attributed strings in Swift
MIT License
3.54k stars 196 forks source link

Text is truncated unexpectedly with tracking property #403

Closed brannpark closed 3 years ago

brannpark commented 3 years ago

When I use StringStyle with tracking option like below,

let text = "東京都港区赤坂5丁目4−5"
let style = StringStyle(
        .tracking(.point(-0.24))
let label = UILabel()
label.attributedText = text.styled(with: style)

The text displayed on screen is tail truncated "東京都港区赤坂5丁目..." I found that this problem is occured when NSAttributedString has both NSAttributedString.Key "BonMotTransformationsAttributeName" and "Kern" So, when I change code like below, then whole text is rendered correctly.

let attrText = NSMutableAttributedString(attributedString: style.attributedString(from: text))
attrText.removeAttribute(BonMotTransformationsAttributeName, range: .init(location: 0, length: attrText.length))
label.attributedText = attrText

What's happening under the render process? Does BonMot have the responsibility to fix this?

ZevEisenberg commented 3 years ago

I don't know why you're seeing this problem, but I'll give you some context that may help you debug it.

BonMot applies BonMotTransformationsAttributeName to the last character as a marker so that it can remove kerning from the last character in some cases, but not others. Let me explain:

The way UIKit handles kerning/tracking, it's applied after every chracter, including the last one. So if I were adding space to the text "BonMot," it would render like this: B o n M o t (note the trailing final space). This extra space is used when calculating the width of the label, so if the label is centered on screen based on its width, it ends up looking like it's too far to the left.

BonMot tries to be clever about this: if you include .tracking in your StringStyle, it removes the NSAttributedString.Key.kern from the final character during the transformation to NSAttributedString. However, this is only appropriate on the last character of a string, and you might be composing multiple strings together. Check out the stripTrailingKerning property here, and the removeKerningFromLastCharacter() method here. BonMot is doing some juggling to make sure it strips trailing kerning, but only if the string is not going to have another string appended after it.

I'm guessing the real problem is that having the trailing kerning trimmed off your particular string is throwing off the string size calculations that Apple is using. If this is true, you should be able to work around it by making this change:

-label.attributedText = text.styled(with: style)
+label.attributedText = text.styled(with: style, stripTrailingKerning: false)

And if that does fix it, you might want to file a bug with Apple, since it looks like a text measurement bug.

brannpark commented 3 years ago

FYI, the stripTrailingKerning option does not helpful for this issue. It looks like a text measurement bug Apple made as you mentioned. If the attributes has kern and any other custom Attribute key, then the text is tail truncated when the text is japanese. But, if the font is set to Hiragino(Japanese font installed in iOS), not system font, then whole text is rendered normally. for example,

let attributes: [NSAttributedString.Key: Any] = [
            .font: UIFont.systemFont(ofSize: 20),
            .kern: 0,
]  
let text = NSAttributedString(string: "東京都港区赤坂5丁目4−5", attributes: attributes)
label.attributedText = text

-> Okay!

let attributes: [NSAttributedString.Key: Any] = [
            .font: UIFont.systemFont(ofSize: 20),
            .kern: 0,
            .init("Custom"): 0
]  
let text = NSAttributedString(string: "東京都港区赤坂5丁目4−5", attributes: attributes)
label.attributedText = text

-> Tail truncated!

let attributes: [NSAttributedString.Key: Any] = [
            .font: UIFont(name: "HiraginoSans-W6", size: 20)!,
            .kern: 0,
            .init("Custom"): 0
]  
let text = NSAttributedString(string: "東京都港区赤坂5丁目4−5", attributes: attributes)
label.attributedText = text

-> Okay!

ZevEisenberg commented 3 years ago

So weird! Glad you got to the bottom of it. Would you be interested in filing a bug to Apple at bugreport.apple.com, and include a link to this issue?