2024-05-18 20:53:21 +08:00
|
|
|
import BunnyTweakC
|
2024-05-18 18:36:12 +08:00
|
|
|
import Orion
|
|
|
|
|
import UIKit
|
|
|
|
|
import os
|
|
|
|
|
import Foundation
|
|
|
|
|
|
|
|
|
|
// This is not the full implementation, we only take what we need in here
|
|
|
|
|
// For full definition, find it from the core codebase (JS side)
|
|
|
|
|
struct FontDefinition: Codable {
|
|
|
|
|
let name: String
|
|
|
|
|
let main: [String: String]?
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var fontMap: [String: String] = [:]
|
|
|
|
|
|
|
|
|
|
class FontsHook: ClassHook<UIFont> {
|
|
|
|
|
class func fontWithName(_ name: String, size: CGFloat) -> UIFont {
|
|
|
|
|
if let replacementName = fontMap[name] {
|
2024-05-19 17:41:59 +08:00
|
|
|
let replacementDescriptor = UIFontDescriptor(name: replacementName, size: size)
|
|
|
|
|
let fallbackDescriptor = replacementDescriptor.addingAttributes([.name: [name]])
|
|
|
|
|
let finalDescriptor = replacementDescriptor.addingAttributes([.cascadeList: [fallbackDescriptor]])
|
|
|
|
|
|
|
|
|
|
return orig.fontWithDescriptor(finalDescriptor, size: size)
|
2024-05-18 18:36:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return orig.fontWithName(name, size: size)
|
|
|
|
|
}
|
|
|
|
|
class func fontWithDescriptor(_ descriptor: UIFontDescriptor, size: CGFloat) -> UIFont {
|
|
|
|
|
if let replacementName = fontMap[descriptor.postscriptName] {
|
|
|
|
|
let replacementDescriptor = UIFontDescriptor(name: replacementName, size: size)
|
2024-05-19 17:41:59 +08:00
|
|
|
let finalDescriptor = replacementDescriptor.addingAttributes([.cascadeList: [descriptor]])
|
2024-05-18 18:36:12 +08:00
|
|
|
|
2024-05-19 17:41:59 +08:00
|
|
|
return orig.fontWithDescriptor(finalDescriptor, size: size)
|
2024-05-18 18:36:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return orig.fontWithDescriptor(descriptor, size: size)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func patchFonts(_ main: [String: String], fontDefName: String) {
|
|
|
|
|
for (fontName, url) in main {
|
2024-05-18 20:53:21 +08:00
|
|
|
os_log("Replacing font %{public}@ with URL: %{public}@", log: bunnyLog, type: .info, fontName, url)
|
2024-05-18 18:36:12 +08:00
|
|
|
|
|
|
|
|
let fontExtension = URL(string: url)!.pathExtension
|
|
|
|
|
let fontCachePath = pyoncordDirectory
|
|
|
|
|
.appendingPathComponent("downloads", isDirectory: true)
|
|
|
|
|
.appendingPathComponent("fonts", isDirectory: true)
|
|
|
|
|
.appendingPathComponent(fontDefName, isDirectory: true)
|
|
|
|
|
.appendingPathComponent("\(fontName).\(fontExtension)")
|
|
|
|
|
|
|
|
|
|
do {
|
2024-05-18 20:53:21 +08:00
|
|
|
os_log("Attempting to register font %{public}@ from %{public}@", log: bunnyLog, type: .info, fontName, url)
|
2024-05-18 18:36:12 +08:00
|
|
|
|
|
|
|
|
let parent = fontCachePath.deletingLastPathComponent()
|
|
|
|
|
if !FileManager.default.fileExists(atPath: parent.path) {
|
2024-05-18 20:53:21 +08:00
|
|
|
os_log("Creating parent directory: %{public}@", log: bunnyLog, type: .debug, parent.path)
|
2024-05-18 18:36:12 +08:00
|
|
|
try FileManager.default.createDirectory(
|
|
|
|
|
at: parent, withIntermediateDirectories: true, attributes: nil)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// JS side should download these already, but just in case...
|
|
|
|
|
if !FileManager.default.fileExists(atPath: fontCachePath.path) {
|
2024-05-18 20:53:21 +08:00
|
|
|
os_log("Downloading font %{public}@ from %{public}@", log: bunnyLog, type: .debug, fontName, url)
|
2024-05-18 18:36:12 +08:00
|
|
|
if let data = try? Data(contentsOf: URL(string: url)!) {
|
2024-05-18 20:53:21 +08:00
|
|
|
os_log("Writing font data to: %{public}@", log: bunnyLog, type: .debug, fontCachePath.path)
|
2024-05-18 18:36:12 +08:00
|
|
|
try? data.write(to: fontCachePath)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let data = try? Data(contentsOf: fontCachePath) {
|
2024-05-18 20:53:21 +08:00
|
|
|
os_log("Registering font %{public}@ with provider", log: bunnyLog, type: .debug, fontName)
|
2024-05-18 18:36:12 +08:00
|
|
|
let provider = CGDataProvider(data: data as CFData)
|
|
|
|
|
let font = CGFont(provider!)
|
|
|
|
|
var error: Unmanaged<CFError>?
|
|
|
|
|
|
|
|
|
|
// This does not work with system/app fonts unfortunately. Throws a CTFontManagerError.systemRequired
|
|
|
|
|
if let existingFont = CGFont(font!.postScriptName!) {
|
|
|
|
|
var unregisterError: Unmanaged<CFError>?
|
|
|
|
|
if !CTFontManagerUnregisterGraphicsFont(existingFont, &unregisterError) {
|
2024-05-18 20:53:21 +08:00
|
|
|
os_log("Failed to deregister font %{public}@: %{public}@", log: bunnyLog, type: .error,
|
2024-05-18 18:36:12 +08:00
|
|
|
font!.postScriptName! as String,
|
|
|
|
|
String(describing: unregisterError!.takeUnretainedValue()))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if CTFontManagerRegisterGraphicsFont(font!, &error) {
|
|
|
|
|
fontMap[fontName] = font!.postScriptName! as String
|
2024-05-18 20:53:21 +08:00
|
|
|
os_log("Successfully registered font %{public}@ to %{public}@", log: bunnyLog, type: .info, fontName, font!.postScriptName! as String)
|
2024-05-18 18:36:12 +08:00
|
|
|
} else {
|
2024-05-18 20:53:21 +08:00
|
|
|
os_log("Failed to register font %{public}@: %{public}@", log: bunnyLog, type: .error, fontName, String(describing: error!.takeUnretainedValue()))
|
2024-05-18 18:36:12 +08:00
|
|
|
}
|
|
|
|
|
} else {
|
2024-05-18 20:53:21 +08:00
|
|
|
os_log("Failed to read font data from: %{public}@", log: bunnyLog, type: .error, fontCachePath.path)
|
2024-05-18 18:36:12 +08:00
|
|
|
}
|
|
|
|
|
} catch {
|
2024-05-18 20:53:21 +08:00
|
|
|
os_log("Failed to register font %{public}@: %{public}@", log: bunnyLog, type: .error, fontName, error.localizedDescription)
|
2024-05-18 18:36:12 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|