feat: support custom fonts (#6)
+ remove legacy LoaderConfig properties
This commit is contained in:
parent
4b3b7f10e8
commit
60be3dd5d1
4 changed files with 115 additions and 21 deletions
|
|
@ -2,5 +2,6 @@ globalThis.__PYON_LOADER__ = {
|
|||
loaderName: "BunnyTweak",
|
||||
loaderVersion: "0.2.0",
|
||||
hasThemeSupport: true,
|
||||
storedTheme: null
|
||||
storedTheme: null,
|
||||
fontPatch: 2
|
||||
}
|
||||
|
|
|
|||
95
Sources/PyoncordTweak/Fonts.x.swift
Normal file
95
Sources/PyoncordTweak/Fonts.x.swift
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import PyoncordTweakC
|
||||
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] {
|
||||
return UIFont(name: replacementName, size: size)!
|
||||
}
|
||||
|
||||
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)
|
||||
.addingAttributes(descriptor.fontAttributes)
|
||||
|
||||
return orig.fontWithDescriptor(replacementDescriptor, size: size)
|
||||
}
|
||||
|
||||
return orig.fontWithDescriptor(descriptor, size: size)
|
||||
}
|
||||
}
|
||||
|
||||
func patchFonts(_ main: [String: String], fontDefName: String) {
|
||||
for (fontName, url) in main {
|
||||
os_log("Replacing font %{public}@ with URL: %{public}@", log: pyoncordLog, type: .info, fontName, url)
|
||||
|
||||
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 {
|
||||
os_log("Attempting to register font %{public}@ from %{public}@", log: pyoncordLog, type: .info, fontName, url)
|
||||
|
||||
let parent = fontCachePath.deletingLastPathComponent()
|
||||
if !FileManager.default.fileExists(atPath: parent.path) {
|
||||
os_log("Creating parent directory: %{public}@", log: pyoncordLog, type: .debug, parent.path)
|
||||
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) {
|
||||
os_log("Downloading font %{public}@ from %{public}@", log: pyoncordLog, type: .debug, fontName, url)
|
||||
if let data = try? Data(contentsOf: URL(string: url)!) {
|
||||
os_log("Writing font data to: %{public}@", log: pyoncordLog, type: .debug, fontCachePath.path)
|
||||
try? data.write(to: fontCachePath)
|
||||
}
|
||||
}
|
||||
|
||||
if let data = try? Data(contentsOf: fontCachePath) {
|
||||
os_log("Registering font %{public}@ with provider", log: pyoncordLog, type: .debug, fontName)
|
||||
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) {
|
||||
os_log("Failed to deregister font %{public}@: %{public}@", log: pyoncordLog, type: .error,
|
||||
font!.postScriptName! as String,
|
||||
String(describing: unregisterError!.takeUnretainedValue()))
|
||||
}
|
||||
}
|
||||
|
||||
if CTFontManagerRegisterGraphicsFont(font!, &error) {
|
||||
fontMap[fontName] = font!.postScriptName! as String
|
||||
os_log("Successfully registered font %{public}@ to %{public}@", log: pyoncordLog, type: .info, fontName, font!.postScriptName! as String)
|
||||
} else {
|
||||
os_log("Failed to register font %{public}@: %{public}@", log: pyoncordLog, type: .error, fontName, String(describing: error!.takeUnretainedValue()))
|
||||
}
|
||||
} else {
|
||||
os_log("Failed to read font data from: %{public}@", log: pyoncordLog, type: .error, fontCachePath.path)
|
||||
}
|
||||
} catch {
|
||||
os_log("Failed to register font %{public}@: %{public}@", log: pyoncordLog, type: .error, fontName, error.localizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,16 +12,13 @@ struct CustomLoadUrl: Codable {
|
|||
|
||||
struct LoaderConfig: Codable {
|
||||
let customLoadUrl: CustomLoadUrl
|
||||
let loadReactDevTools: Bool
|
||||
var loadPyoncord: Bool? = nil
|
||||
}
|
||||
|
||||
let defaultLoaderConfig = LoaderConfig(
|
||||
customLoadUrl: CustomLoadUrl(
|
||||
enabled: false,
|
||||
url: URL(string: "http://localhost:4040/bunny.js")!
|
||||
),
|
||||
loadReactDevTools: false
|
||||
)
|
||||
)
|
||||
|
||||
let pyoncordDirectory = getPyoncordDirectory()
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class LoadHook: ClassHook<RCTCxxBridge> {
|
|||
|
||||
let pyoncordDirectory = getPyoncordDirectory()
|
||||
|
||||
var bundle = try? Data(contentsOf: pyoncordDirectory.appendingPathComponent("bunny.js"))
|
||||
var bundle = try? Data(contentsOf: pyoncordDirectory.appendingPathComponent("bundle.js"))
|
||||
|
||||
let group = DispatchGroup()
|
||||
|
||||
|
|
@ -52,13 +52,8 @@ class LoadHook: ClassHook<RCTCxxBridge> {
|
|||
loaderConfig.customLoadUrl.url.absoluteString)
|
||||
bundleUrl = loaderConfig.customLoadUrl.url
|
||||
} else {
|
||||
if loaderConfig.loadPyoncord == true {
|
||||
bundleUrl = URL(
|
||||
string: "https://raw.githubusercontent.com/pyoncord/pyoncord/builds/pyoncord.js")!
|
||||
} else {
|
||||
bundleUrl = URL(
|
||||
string: "https://raw.githubusercontent.com/pyoncord/detta-builds/main/bunny.js")!
|
||||
}
|
||||
bundleUrl = URL(
|
||||
string: "https://raw.githubusercontent.com/pyoncord/bunny-builds/main/bunny.js")!
|
||||
}
|
||||
|
||||
os_log("Fetching JS bundle", log: pyoncordLog, type: .info)
|
||||
|
|
@ -75,7 +70,7 @@ class LoadHook: ClassHook<RCTCxxBridge> {
|
|||
if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
|
||||
os_log("Successfully fetched JS Bundle", log: pyoncordLog, type: .debug)
|
||||
bundle = data
|
||||
try? bundle?.write(to: pyoncordDirectory.appendingPathComponent("bunny.js"))
|
||||
try? bundle?.write(to: pyoncordDirectory.appendingPathComponent("bundle.js"))
|
||||
|
||||
let etag = httpResponse.allHeaderFields["Etag"] as? String
|
||||
try? etag?.write(
|
||||
|
|
@ -131,12 +126,18 @@ class LoadHook: ClassHook<RCTCxxBridge> {
|
|||
}
|
||||
|
||||
struct PyoncordTweak: Tweak {
|
||||
func tweakDidActivate() {
|
||||
if let themeData = try? Data(
|
||||
contentsOf: pyoncordDirectory.appendingPathComponent("current-theme.json")) {
|
||||
let theme = try? JSONDecoder().decode(Theme.self, from: themeData)
|
||||
if let semanticColors = theme?.data.semanticColors { swizzleDCDThemeColor(semanticColors) }
|
||||
if let rawColors = theme?.data.rawColors { swizzleUIColor(rawColors) }
|
||||
}
|
||||
func tweakDidActivate() {
|
||||
if let themeData = try? Data(
|
||||
contentsOf: pyoncordDirectory.appendingPathComponent("current-theme.json")) {
|
||||
let theme = try? JSONDecoder().decode(Theme.self, from: themeData)
|
||||
if let semanticColors = theme?.data.semanticColors { swizzleDCDThemeColor(semanticColors) }
|
||||
if let rawColors = theme?.data.rawColors { swizzleUIColor(rawColors) }
|
||||
}
|
||||
|
||||
if let fontData = try? Data(
|
||||
contentsOf: pyoncordDirectory.appendingPathComponent("fonts.json")) {
|
||||
let fonts = try? JSONDecoder().decode(FontDefinition.self, from: fontData)
|
||||
if let main = fonts?.main { patchFonts(main, fontDefName: fonts!.name) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue