diff --git a/Sources/ZMarkupParser/Core/MarkupStyle/MarkupStyleFont.swift b/Sources/ZMarkupParser/Core/MarkupStyle/MarkupStyleFont.swift index bb506d1..f3cb9d5 100644 --- a/Sources/ZMarkupParser/Core/MarkupStyle/MarkupStyleFont.swift +++ b/Sources/ZMarkupParser/Core/MarkupStyle/MarkupStyleFont.swift @@ -24,7 +24,7 @@ public struct MarkupStyleFont: MarkupStyleItem { } public enum FontFamily { case familyNames([String]) - + #if canImport(UIKit) func getFont(size: CGFloat) -> UIFont? { switch self { @@ -53,30 +53,27 @@ public struct MarkupStyleFont: MarkupStyleItem { } public enum FontWeightStyle: String, CaseIterable { case ultraLight, light, thin, regular, medium, semibold, bold, heavy, black - + public init?(rawValue: String) { guard let matchedStyle = FontWeightStyle.allCases.first(where: { style in return rawValue.lowercased().contains(style.rawValue) }) else { return nil } - + self = matchedStyle } - + #if canImport(UIKit) init?(font: UIFont) { if let traits = font.fontDescriptor.fontAttributes[.traits] as? [UIFontDescriptor.TraitKey: Any], let weight = traits[.weight] as? UIFont.Weight { self = weight.convertFontWeightStyle() return - } else if font.fontDescriptor.symbolicTraits.contains(.traitBold) { - self = .bold - return } else if let weightName = font.fontDescriptor.object(forKey: .face) as? String, let weight = Self.init(rawValue: weightName) { self = weight return } - + return nil } #elseif canImport(AppKit) @@ -84,50 +81,51 @@ public struct MarkupStyleFont: MarkupStyleItem { if let traits = font.fontDescriptor.fontAttributes[.traits] as? [NSFontDescriptor.TraitKey: Any], let weight = traits[.weight] as? NSFont.Weight { self = weight.convertFontWeightStyle() return - } else if font.fontDescriptor.symbolicTraits.contains(.bold) { - self = .bold - return } else if let weightName = font.fontDescriptor.object(forKey: .face) as? String, let weight = Self.init(rawValue: weightName) { self = weight return } - + return nil } #endif } - + public var size: CGFloat? public var weight: FontWeight? public var italic: Bool? + public var bold: Bool? public var familyName: FontFamily? - - public init(size: CGFloat? = nil, weight: FontWeight? = nil, italic: Bool? = nil, familyName: FontFamily? = nil) { + + public init(size: CGFloat? = nil, weight: FontWeight? = nil, bold: Bool? = nil, italic: Bool? = nil, familyName: FontFamily? = nil) { self.size = size self.weight = weight + self.bold = bold self.italic = italic self.familyName = familyName } - + mutating func fillIfNil(from: MarkupStyleFont?) { self.size = self.size ?? from?.size self.weight = self.weight ?? from?.weight self.italic = self.italic ?? from?.italic + self.bold = self.bold ?? from?.bold self.familyName = self.familyName ?? from?.familyName } - + func isNil() -> Bool { return !([size, - weight, - italic, - familyName] as [Any?]).contains(where: { $0 != nil}) + weight, + italic, + bold, + familyName] as [Any?]).contains(where: { $0 != nil}) } - + func sizeOf(string: String) -> CGSize? { guard let font = getFont() else { return nil } - + return (string as NSString).size(withAttributes: [.font: font]) } } @@ -135,24 +133,25 @@ public struct MarkupStyleFont: MarkupStyleItem { #if canImport(UIKit) extension MarkupStyleFont { - + public init(_ font: UIFont) { self.size = font.pointSize self.italic = font.fontDescriptor.symbolicTraits.contains(.traitItalic) + self.bold = font.fontDescriptor.symbolicTraits.contains(.traitBold) if let fontWeight = FontWeightStyle.init(font: font) { self.weight = FontWeight.style(fontWeight) } self.familyName = .familyNames([font.familyName]) } - + func getFont() -> UIFont? { guard !isNil() else { return nil } - - var traits: [UIFontDescriptor.SymbolicTraits] = [] - + + var traits: UIFontDescriptor.SymbolicTraits = [] + let size = (self.size ?? MarkupStyle.default.font.size) ?? UIFont.systemFontSize let weight = self.weight?.convertToUIFontWeight() ?? .regular - + // There is no direct method in UIFont to specify the font family, italic and weight together. let font: UIFont @@ -163,28 +162,16 @@ extension MarkupStyleFont { // System Font font = UIFont.systemFont(ofSize: size, weight: weight) } - - if weight.rawValue >= UIFont.Weight.medium.rawValue { - traits.append(.traitBold) + + if bold == true { + traits.insert(.traitBold) } - + if let italic = self.italic, italic { - traits.append(.traitItalic) - } - - if traits.isEmpty { - return font - } else { - return withTraits(font: font, traits: traits) + traits.insert(.traitItalic) } - } - - private func withTraits(font: UIFont, traits: [UIFontDescriptor.SymbolicTraits]) -> UIFont { - guard let descriptor = font.fontDescriptor - .withSymbolicTraits(UIFontDescriptor.SymbolicTraits(traits)) else { - return font - } - return UIFont(descriptor: descriptor, size: font.pointSize) + + return font.with(weight: weight, symbolicTraits: traits) } } @@ -245,6 +232,27 @@ private extension UIFont.Weight { } } +private extension UIFont { + + /// Returns a font object that is the same as the receiver but which has the specified weight and symbolic traits + func with(weight: Weight, symbolicTraits: UIFontDescriptor.SymbolicTraits) -> UIFont { + + var mergedsymbolicTraits = fontDescriptor.symbolicTraits + mergedsymbolicTraits.formUnion(symbolicTraits) + + var traits = fontDescriptor.fontAttributes[.traits] as? [UIFontDescriptor.TraitKey: Any] ?? [:] + traits[.weight] = weight + traits[.symbolic] = mergedsymbolicTraits.rawValue + + var fontAttributes: [UIFontDescriptor.AttributeName: Any] = [:] + fontAttributes[.family] = familyName + fontAttributes[.traits] = traits + + let font = UIFont(descriptor: UIFontDescriptor(fontAttributes: fontAttributes), size: pointSize) + return UIFontMetrics.default.scaledFont(for: font) + } +} + #elseif canImport(AppKit) extension MarkupStyleFont { @@ -258,15 +266,15 @@ extension MarkupStyleFont { self.familyName = .familyNames([familyName]) } } - + func getFont() -> NSFont? { guard !isNil() else { return nil } - - var traits: [NSFontDescriptor.SymbolicTraits] = [] - + + var traits: NSFontDescriptor.SymbolicTraits = [] + let size = (self.size ?? MarkupStyle.default.font.size) ?? NSFont.systemFontSize let weight = self.weight?.convertToUIFontWeight() ?? .regular - + // There is no direct method in UIFont to specify the font family, italic and weight together. let font: NSFont @@ -277,25 +285,16 @@ extension MarkupStyleFont { // System Font font = NSFont.systemFont(ofSize: size, weight: weight) } - - if weight.rawValue >= NSFont.Weight.medium.rawValue { - traits.append(.bold) + + if bold == true { + traits.insert(.bold) } - + if let italic = self.italic, italic { - traits.append(.italic) - } - - if traits.isEmpty { - return font - } else { - return withTraits(font: font, traits: traits) + traits.insert(.italic) } - } - - private func withTraits(font: NSFont, traits: [NSFontDescriptor.SymbolicTraits]) -> NSFont { - let descriptor = font.fontDescriptor.withSymbolicTraits(NSFontDescriptor.SymbolicTraits(traits)) - return NSFont(descriptor: descriptor, size: font.pointSize) ?? font + + return font.with(weight: weight, symbolicTraits: traits) } } @@ -356,4 +355,26 @@ private extension NSFont.Weight { } } +private extension NSFont { + + /// Returns a font object that is the same as the receiver but which has the specified weight and symbolic traits + func with(weight: Weight, symbolicTraits: NSFontDescriptor.SymbolicTraits) -> NSFont { + + var mergedsymbolicTraits = fontDescriptor.symbolicTraits + mergedsymbolicTraits.formUnion(symbolicTraits) + + var traits = fontDescriptor.fontAttributes[.traits] as? [NSFontDescriptor.TraitKey: Any] ?? [:] + traits[.weight] = weight + traits[.symbolic] = mergedsymbolicTraits.rawValue + + var fontAttributes: [NSFontDescriptor.AttributeName: Any] = [:] + fontAttributes[.family] = familyName + fontAttributes[.traits] = traits + + let font = NSFont(descriptor: NSFontDescriptor(fontAttributes: fontAttributes), size: pointSize) + // return UIFontMetrics.default.scaledFont(for: font) + return font ?? self + } +} + #endif diff --git a/Tests/ZMarkupParserTests/Core/MarkupStyleFontTests.swift b/Tests/ZMarkupParserTests/Core/MarkupStyleFontTests.swift index 19c68e1..7342675 100644 --- a/Tests/ZMarkupParserTests/Core/MarkupStyleFontTests.swift +++ b/Tests/ZMarkupParserTests/Core/MarkupStyleFontTests.swift @@ -17,7 +17,7 @@ import AppKit final class MarkupStyleFontTests: XCTestCase { func testInit() { #if canImport(UIKit) - let markupStyleFont = MarkupStyleFont(UIFont.boldSystemFont(ofSize: 16)) + let markupStyleFont = MarkupStyleFont(UIFont.systemFont(ofSize: 16, weight: .bold)) if case let .style(weight) = markupStyleFont.weight, weight == .bold { // Success XCTAssertEqual(markupStyleFont.size, 16) @@ -161,7 +161,7 @@ final class MarkupStyleFontTests: XCTestCase { XCTAssertEqual(resultMarkupStyleFont.size, markupStyleFont.size!) if case let .style(weight) = resultMarkupStyleFont.weight { - XCTAssertEqual(weight, .bold) + XCTAssertEqual(weight, .heavy) } else { XCTFail() }