How can I deal with ligatures in Apple's SF Mono count programmatically in Swift?

178 Views Asked by At

Note: I'm posting this in Q & A style since it's a corner case question and I want to post it to StackOverflow for the record, on the small chance that it saves someone some time and spares them confusion.

In XCode I have a theme setup where the Console uses SF Mono font, because I want things to land in columns vertically, for example lists of numbers should stack properly so they could be added up on columns.

But I notice some skew in certain columns with certain text, where things don't not line up and I assume it was a bug in my code, but I can't find the problem.

1

There are 1 best solutions below

0
clearlight On

The problem is that SF Mono contains ligatures "fi" and "fl" which combine two characters into a single monospaced character, which can cause disagreements with calculations about string width based on the character count, when using SF Mono. To account for this I modified some swift String extensions I use to justify text (for the sake of columnar alignment in the XCode console primarily, but for other contexts as well, for example in UITableView, UITextView, UITextField, etc...)

extension String {

    var ligatureCount : Int {
        self.components(separatedBy: "fi").count - 1 + self.components(separatedBy: "fl").count - 1
    }
    
    func rightJustified(_ width: Int, _ padChar : String = " ",  truncate: Bool = false) -> String {
        guard width > count else {
            return truncate ? String(suffix(width)) : self
        }
        return String(repeating: padChar, count: (width - count) + ligatureCount) + self
    }

    func leftJustified(_ width: Int, _ padChar : String = " ", truncate: Bool = false) -> String {
        guard width > count else {
            return truncate ? String(prefix(width)) : self
        }
        return self + String(repeating: padChar, count: (width - count) + ligatureCount)
    }
}