// // NSAttributedString+NetNewsWire.swift // NetNewsWire // // Created by Nate Weaver on 2020-04-07. // Copyright © 2020 Ranchero Software. All rights reserved. // import Parser #if canImport(AppKit) import AppKit typealias Font = NSFont typealias FontDescriptor = NSFontDescriptor private let boldTrait = NSFontDescriptor.SymbolicTraits.bold private let italicTrait = NSFontDescriptor.SymbolicTraits.italic private let monoSpaceTrait = NSFontDescriptor.SymbolicTraits.monoSpace #else import UIKit typealias Font = UIFont typealias FontDescriptor = UIFontDescriptor private let boldTrait = UIFontDescriptor.SymbolicTraits.traitBold private let italicTrait = UIFontDescriptor.SymbolicTraits.traitItalic private let monoSpaceTrait = UIFontDescriptor.SymbolicTraits.traitMonoSpace #endif extension NSAttributedString { /// Adds a font and color to an attributed string. /// /// - Parameters: /// - baseFont: The font to add. func adding(font baseFont: Font) -> NSAttributedString { let mutable = self.mutableCopy() as! NSMutableAttributedString let fullRange = NSRange(location: 0, length: mutable.length) let size = baseFont.pointSize let baseDescriptor = baseFont.fontDescriptor let baseSymbolicTraits = baseDescriptor.symbolicTraits mutable.enumerateAttribute(.font, in: fullRange, options: []) { (font: Any?, range: NSRange, stop: UnsafeMutablePointer) in guard let font = font as? Font else { return } let currentDescriptor = font.fontDescriptor let symbolicTraits = baseSymbolicTraits.union(currentDescriptor.symbolicTraits) var descriptor = currentDescriptor.addingAttributes(baseDescriptor.fontAttributes) #if canImport(AppKit) descriptor = descriptor.withSymbolicTraits(symbolicTraits) #else descriptor = descriptor.withSymbolicTraits(symbolicTraits)! #endif let newFont = Font(descriptor: descriptor, size: size) mutable.addAttribute(.font, value: newFont as Any, range: range) } return mutable.copy() as! NSAttributedString } private enum InTag { case none case opening case closing } private enum Style { case bold case italic case superscript case `subscript` case underline case strikethrough case monospace init?(forTag: String) { switch forTag { case "b", "strong": self = .bold case "i", "em", "cite", "var", "dfn": self = .italic case "sup": self = .superscript case "sub": self = .subscript case "u", "ins": self = .underline case "s", "del": self = .strikethrough case "code", "samp", "tt", "kbd": self = .monospace default: return nil } } } /// Returns an attributed string initialized from HTML text containing basic inline stylistic tags. /// /// - Parameters: /// - html: The HTML text. /// - locale: The locale used for quotation marks when parsing `` tags. convenience init(html: String, locale: Locale = Locale.current) { let baseFont = Font.systemFont(ofSize: Font.systemFontSize) var inTag: InTag = .none var tag = "" var currentStyles = CountedSet