91 lines
3.1 KiB
Swift
91 lines
3.1 KiB
Swift
import CoreGraphics
|
|
|
|
/**
|
|
* Queries custom native font names from the Info.plist `UIAppFonts`.
|
|
*/
|
|
internal func queryCustomNativeFonts() -> [String] {
|
|
// [0] Read from main bundle's Info.plist
|
|
guard let fontFilePaths = Bundle.main.object(forInfoDictionaryKey: "UIAppFonts") as? [String] else {
|
|
return []
|
|
}
|
|
|
|
// [1] Get font family names for each font file
|
|
let fontFamilies: [[String]] = fontFilePaths.compactMap { fontFilePath in
|
|
guard let fontUrl = Bundle.main.url(forResource: fontFilePath, withExtension: nil) else {
|
|
return []
|
|
}
|
|
guard let fontDescriptors = CTFontManagerCreateFontDescriptorsFromURL(fontUrl as CFURL) as? [CTFontDescriptor] else {
|
|
return []
|
|
}
|
|
return fontDescriptors.compactMap { descriptor in
|
|
return CTFontDescriptorCopyAttribute(descriptor, kCTFontFamilyNameAttribute) as? String
|
|
}
|
|
}
|
|
|
|
// [2] Retrieve font names by family names
|
|
return fontFamilies.flatMap { fontFamilyNames in
|
|
return fontFamilyNames.flatMap { fontFamilyName in
|
|
#if os(iOS) || os(tvOS)
|
|
return UIFont.fontNames(forFamilyName: fontFamilyName)
|
|
#elseif os(macOS)
|
|
return NSFontManager.shared.availableMembers(ofFontFamily: fontFamilyName)?.compactMap { $0[0] as? String } ?? []
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Loads the font from the given url and returns it as ``CGFont``.
|
|
*/
|
|
internal func loadFont(fromUrl url: CFURL, alias: String) throws -> CGFont {
|
|
guard let provider = CGDataProvider(url: url),
|
|
let cgFont = CGFont(provider) else {
|
|
throw FontCreationFailedException(alias)
|
|
}
|
|
|
|
return cgFont
|
|
}
|
|
|
|
/**
|
|
Registers the given font to make it discoverable through font descriptor matching.
|
|
*/
|
|
internal func registerFont(fontUrl: CFURL, fontFamilyAlias: String) throws {
|
|
var error: Unmanaged<CFError>?
|
|
|
|
if !CTFontManagerRegisterFontsForURL(fontUrl, .process, &error), let error = error?.takeRetainedValue() {
|
|
let fontError = CTFontManagerError(rawValue: CFErrorGetCode(error))
|
|
|
|
switch fontError {
|
|
case .alreadyRegistered, .duplicatedName:
|
|
// Ignore the error if:
|
|
// - this exact font instance was already registered or
|
|
// - another instance already registered with the same name (assuming it's most likely the same font anyway)
|
|
return
|
|
default:
|
|
throw FontRegistrationFailedException(FontRegistrationErrorInfo(fontFamilyAlias: fontFamilyAlias, cfError: error, ctFontManagerError: fontError))
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Unregisters the given font, so the app will no longer be able to render it.
|
|
Returns a boolean indicating if the font is successfully unregistered after this function completes.
|
|
*/
|
|
internal func unregisterFont(url: CFURL) throws -> Bool {
|
|
var error: Unmanaged<CFError>?
|
|
|
|
if !CTFontManagerUnregisterFontsForURL(url, .process, &error), let error = error?.takeRetainedValue() {
|
|
if let ctFontManagerError = CTFontManagerError(rawValue: CFErrorGetCode(error as CFError)) {
|
|
switch ctFontManagerError {
|
|
case .systemRequired, .inUse:
|
|
return false
|
|
case .notRegistered:
|
|
return true
|
|
default:
|
|
throw UnregisteringFontFailedException(error)
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|