Files
divkit/client/ios/DivKit/Extensions/FontFeatureSettingsParser.swift
belegenium dc33c82cff Add os(iOS) directive for macos build
commit_hash:fd50a713c0d0051aa4c60f0e29d1f4ae1db1a922
2025-07-18 20:33:52 +03:00

88 lines
2.6 KiB
Swift

#if os(iOS)
import CoreText
import UIKit
extension UIFont {
func withFontFeatureSettings(_ value: String) -> UIFont {
var featureSettings: [[UIFontDescriptor.FeatureKey: Int]] = featureSettings ?? [[:]]
value.split(separator: ",")
.compactMap { Feature($0.trimmingCharacters(in: [" "])) }
.forEach { featureSettings.setFeature($0) }
var attributes = fontDescriptor.fontAttributes
attributes.removeValue(forKey: .featureSettings)
let descriptor = UIFontDescriptor(fontAttributes: attributes)
.addingAttributes([.featureSettings: featureSettings])
return UIFont(descriptor: descriptor, size: 0.0)
}
var featureSettings: [[UIFontDescriptor.FeatureKey: Int]]? {
fontDescriptor.fontAttributes
.first { $0.key == .featureSettings }?
.value as? [[UIFontDescriptor.FeatureKey: Int]]
}
}
extension [[UIFontDescriptor.FeatureKey: Int]] {
fileprivate mutating func setFeature(_ feature: Feature) {
let featureDict = [
UIFontDescriptor.FeatureKey.featureIdentifier: feature.type,
UIFontDescriptor.FeatureKey.typeIdentifier: feature.selector,
]
if feature.isEnabled {
append(featureDict)
} else {
removeAll { $0 == featureDict }
}
}
}
private struct Feature {
let type: Int
let selector: Int
let isEnabled: Bool
init?(_ value: String) {
let items = value.split(separator: " ")
let count = items.count
if count == 0 || count > 2 {
DivKitLogger.error("Invalid font feature: \(value)")
return nil
}
let rawName = items[0]
guard rawName.starts(with: "'") || rawName.starts(with: "\"") else {
DivKitLogger.error("Invalid font feature: \(value)")
return nil
}
let name = rawName.trimmingCharacters(in: ["'", "\""])
guard let params = featureParams[name] else {
DivKitLogger.error("Unknown font feature: \(name)")
return nil
}
type = params.0
selector = params.1
if count == 1 {
isEnabled = true
} else {
let state = items[1].lowercased()
isEnabled = state == "on" || state == "1"
}
}
}
private let featureParams: [String: (Int, Int)] = [
"c2sc": (kUpperCaseType, kUpperCaseSmallCapsSelector),
"frac": (kFractionsType, kDiagonalFractionsSelector),
"lnum": (kNumberCaseType, kUpperCaseNumbersSelector),
"ordn": (kVerticalPositionType, kOrdinalsSelector),
"pnum": (kNumberSpacingType, kProportionalNumbersSelector),
"smcp": (kLowerCaseType, kLowerCaseSmallCapsSelector),
"subs": (kVerticalPositionType, kInferiorsSelector),
"sups": (kVerticalPositionType, kSuperiorsSelector),
"tnum": (kNumberSpacingType, kMonospacedNumbersSelector),
]
#endif