Skip to content

Commit

Permalink
Merge pull request #83 from Alex293/feature/accessibility-and-weight-…
Browse files Browse the repository at this point in the history
…support

[WIP] add dynamic type and weight support
  • Loading branch information
zhgchgli0718 authored Dec 3, 2024
2 parents 8c84e10 + 820ec74 commit a8baa6d
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 70 deletions.
157 changes: 89 additions & 68 deletions Sources/ZMarkupParser/Core/MarkupStyle/MarkupStyleFont.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public struct MarkupStyleFont: MarkupStyleItem {
}
public enum FontFamily {
case familyNames([String])

#if canImport(UIKit)
func getFont(size: CGFloat) -> UIFont? {
switch self {
Expand Down Expand Up @@ -53,106 +53,105 @@ 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)
init?(font: NSFont) {
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])
}
}

#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
Expand All @@ -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)
}
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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)
}
}

Expand Down Expand Up @@ -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
4 changes: 2 additions & 2 deletions Tests/ZMarkupParserTests/Core/MarkupStyleFontTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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()
}
Expand Down

0 comments on commit a8baa6d

Please sign in to comment.