Files
GLEGram-iOS/Swiftgram/SGSettingsUI/Sources/FontReplacementPickerController.swift
Leeksov 4647310322 GLEGram 12.5 — Initial public release
Based on Swiftgram 12.5 (Telegram iOS 12.5).
All GLEGram features ported and organized in GLEGram/ folder.

Features: Ghost Mode, Saved Deleted Messages, Content Protection Bypass,
Font Replacement, Fake Profile, Chat Export, Plugin System, and more.

See CHANGELOG_12.5.md for full details.
2026-04-06 09:48:12 +03:00

164 lines
6.3 KiB
Swift
Executable File

// MARK: Swiftgram - Font replacement picker (A-Font style)
import SGSimpleSettings
import Foundation
import UIKit
import CoreText
import CoreGraphics
import Display
import SwiftSignalKit
import TelegramPresentationData
import ItemListUI
import AccountContext
/// Bundled default fonts shown at the top of the picker (display name, bundle filename without extension).
private let bundledDefaultFonts: [(displayName: String, fileName: String)] = [
(displayName: "Minecraft Default Bold", fileName: "MinecraftDefault-Bold-2"),
]
/// Registers a .ttf from the given bundle if present and returns its PostScript name, or nil.
private func registerBundledFont(bundle: Bundle, fileName: String) -> String? {
guard let path = bundle.path(forResource: fileName, ofType: "ttf") else {
return nil
}
let url = URL(fileURLWithPath: path)
guard let provider = CGDataProvider(url: url as CFURL),
let cgFont = CGFont(provider),
let name = cgFont.postScriptName as String?, !name.isEmpty else {
return nil
}
CTFontManagerRegisterFontURLs([url] as CFArray, .process, true, nil)
return name
}
public enum FontReplacementPickerMode {
case main
case bold
}
private struct FontReplacementPickerArguments {
let selectFont: (String) -> Void
let dismiss: () -> Void
}
private struct FontReplacementPickerEntry: ItemListNodeEntry {
let entryId: Int
let fontName: String
let displayTitle: String
var section: ItemListSectionId { 0 }
var stableId: Int { entryId }
var id: Int { entryId }
static func == (lhs: FontReplacementPickerEntry, rhs: FontReplacementPickerEntry) -> Bool {
lhs.entryId == rhs.entryId && lhs.fontName == rhs.fontName
}
static func < (lhs: FontReplacementPickerEntry, rhs: FontReplacementPickerEntry) -> Bool {
lhs.entryId < rhs.entryId
}
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
let args = arguments as! FontReplacementPickerArguments
let fontSize = presentationData.fontSize.itemListBaseFontSize
let textColor = presentationData.theme.list.itemAccentColor
let attributedTitle: NSAttributedString?
if fontName.isEmpty {
attributedTitle = nil
} else if let font = UIFont(name: fontName, size: fontSize) {
attributedTitle = NSAttributedString(string: displayTitle, font: font, textColor: textColor)
} else {
attributedTitle = nil
}
return ItemListDisclosureItem(
presentationData: presentationData,
title: displayTitle,
attributedTitle: attributedTitle,
label: "",
sectionId: section,
style: .blocks,
disclosureStyle: .none,
action: {
args.selectFont(self.fontName)
args.dismiss()
}
)
}
}
public func FontReplacementPickerController(context: AccountContext, mode: FontReplacementPickerMode, onSave: @escaping () -> Void) -> ViewController {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
var dismissImpl: (() -> Void)?
// Re-register downloaded fonts so they appear in the list
registerAllDownloadedFonts()
let (fontNames, bundledDisplayNames): ([String], [String: String]) = {
var list: [String] = []
var bundledMap: [String: String] = [:]
for (displayName, fileName) in bundledDefaultFonts {
if let postScriptName = registerBundledFont(bundle: .main, fileName: fileName), !list.contains(postScriptName) {
list.append(postScriptName)
bundledMap[postScriptName] = displayName
}
}
for family in UIFont.familyNames.sorted() {
for name in UIFont.fontNames(forFamilyName: family).sorted() {
if !list.contains(name) {
list.append(name)
}
}
}
return (list.sorted(), bundledMap)
}()
let selectFont: (String) -> Void = { name in
switch mode {
case .main:
SGSimpleSettings.shared.fontReplacementName = name
SGSimpleSettings.shared.fontReplacementFilePath = "" // only "Import from file" sets path
case .bold:
SGSimpleSettings.shared.fontReplacementBoldName = name
SGSimpleSettings.shared.fontReplacementBoldFilePath = ""
}
onSave()
dismissImpl?()
}
let arguments = FontReplacementPickerArguments(
selectFont: selectFont,
dismiss: { dismissImpl?() }
)
var entries: [FontReplacementPickerEntry] = []
let systemTitle = presentationData.strings.baseLanguageCode == "ru" ? "Системный" : "System"
let autoTitle = presentationData.strings.baseLanguageCode == "ru" ? "Авто" : "Auto"
entries.append(FontReplacementPickerEntry(entryId: 0, fontName: "", displayTitle: mode == .main ? systemTitle : autoTitle))
for (idx, name) in fontNames.enumerated() {
let displayTitle = bundledDisplayNames[name] ?? name
entries.append(FontReplacementPickerEntry(entryId: idx + 1, fontName: name, displayTitle: displayTitle))
}
let controllerState = ItemListControllerState(
presentationData: ItemListPresentationData(presentationData),
title: .text(mode == .main ? (presentationData.strings.baseLanguageCode == "ru" ? "Шрифт" : "Font") : (presentationData.strings.baseLanguageCode == "ru" ? "Жирный шрифт" : "Bold font")),
leftNavigationButton: nil,
rightNavigationButton: nil,
backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)
)
let listState = ItemListNodeState(
presentationData: ItemListPresentationData(presentationData),
entries: entries,
style: .blocks,
ensureVisibleItemTag: nil,
initialScrollToItem: nil
)
let signal: Signal<(ItemListControllerState, (ItemListNodeState, FontReplacementPickerArguments)), NoError> = .single((controllerState, (listState, arguments)))
let controller = ItemListController(context: context, state: signal)
dismissImpl = { [weak controller] in
controller?.dismiss()
}
return controller
}