feat(RES-34): Fix plist font filename (#14)
All checks were successful
gitea-openium/resgen.swift/pipeline/head This commit looks good

Reviewed-on: #14
This commit is contained in:
2025-05-05 09:53:05 +02:00
parent 8442c89944
commit 756de4f1de
96 changed files with 3028 additions and 2852 deletions

View File

@ -5,31 +5,34 @@
// Created by Thibaut Schmitt on 17/01/2022.
//
import Foundation
import ArgumentParser
import Foundation
// swiftlint:disable no_grouping_extension
struct FontsOptions: ParsableArguments {
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
var forceGeneration = false
@Argument(help: "Input files where fonts ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
var inputFile: String
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
var extensionOutputPath: String
@Option(help: "Tell if it will generate static properties or methods")
var staticMembers: Bool = false
@Option(help: "Extension name. If not specified, it will generate an Font extension.")
var extensionName: String = Fonts.defaultExtensionName
@Option(help: "Extension name. If not specified, it will generate an UIFont extension.")
var extensionNameUIKit: String = Fonts.defaultExtensionNameUIKit
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+FontsMyApp.swift")
var extensionSuffix: String = ""
@Option(name: .customLong("info-plist-paths"), help: "Info.plist paths (array). Will be used to update UIAppFonts content")
fileprivate var infoPlistPathsRaw: String = ""
}
@ -37,29 +40,29 @@ struct FontsOptions: ParsableArguments {
// MARK: - Computed var
extension FontsOptions {
// MARK: - SwiftUI
var extensionFileName: String {
if extensionSuffix.isEmpty == false {
return "\(extensionName)+\(extensionSuffix).swift"
}
return "\(extensionName).swift"
}
var extensionFilePath: String {
"\(extensionOutputPath)/\(extensionFileName)"
}
// MARK: - UIKit
var extensionFileNameUIKit: String {
if extensionSuffix.isEmpty == false {
return "\(extensionNameUIKit)+\(extensionSuffix).swift"
}
return "\(extensionNameUIKit).swift"
}
var extensionFilePathUIKit: String {
"\(extensionOutputPath)/\(extensionFileNameUIKit)"
}

View File

@ -1,98 +1,109 @@
//
// Fonts.swift
//
//
//
// Created by Thibaut Schmitt on 13/12/2021.
//
import ToolCore
import Foundation
import ArgumentParser
import Foundation
import ToolCore
struct Fonts: ParsableCommand {
// MARK: - CommandConfiguration
static var configuration = CommandConfiguration(
abstract: "A utility to generate an helpful etension to access your custom font from code and also Info.plist UIAppsFont content.",
version: ResgenSwiftVersion
)
// MARK: - Static
static let toolName = "Fonts"
static let defaultExtensionName = "Font"
static let defaultExtensionNameUIKit = "UIFont"
// MARK: - Command Options
@OptionGroup var options: FontsOptions
// MARK: - Run
public func run() throws {
func run() throws {
print("[\(Self.toolName)] Starting fonts generation")
print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate fonts")
// Check requirements
guard checkRequirements() else { return }
print("[\(Self.toolName)] Will generate fonts")
// Get fonts to generate
let fontsToGenerate = FontFileParser.parse(options.inputFile)
// Get real font names
let inputFolder = URL(fileURLWithPath: options.inputFile).deletingLastPathComponent().relativePath
let fontsNames = FontsToolHelper.getFontPostScriptName(for: fontsToGenerate,
inputFolder: inputFolder)
let inputFolder = URL(fileURLWithPath: options.inputFile)
.deletingLastPathComponent()
.relativePath
let fontsNames = FontsToolHelper.getFontPostScriptName(
for: fontsToGenerate,
inputFolder: inputFolder
)
// Generate extension
FontExtensionGenerator.writeExtensionFile(fontsNames: fontsNames,
staticVar: options.staticMembers,
extensionName: options.extensionName,
extensionFilePath: options.extensionFilePath,
isSwiftUI: true)
FontExtensionGenerator.writeExtensionFile(fontsNames: fontsNames,
staticVar: options.staticMembers,
extensionName: options.extensionNameUIKit,
extensionFilePath: options.extensionFilePathUIKit,
isSwiftUI: false)
FontExtensionGenerator.writeExtensionFile(
fontsNames: fontsNames,
staticVar: options.staticMembers,
extensionName: options.extensionName,
extensionFilePath: options.extensionFilePath,
isSwiftUI: true
)
FontExtensionGenerator.writeExtensionFile(
fontsNames: fontsNames,
staticVar: options.staticMembers,
extensionName: options.extensionNameUIKit,
extensionFilePath: options.extensionFilePathUIKit,
isSwiftUI: false
)
print("Info.plist has been updated with:")
print("\(FontPlistGenerator.generatePlistUIAppsFontContent(for: fontsNames, infoPlistPaths: options.infoPlistPaths))")
print("[\(Self.toolName)] Fonts generated")
}
// MARK: - Requirements
private func checkRequirements() -> Bool {
let fileManager = FileManager()
// Check input file exists
guard fileManager.fileExists(atPath: options.inputFile) else {
let error = FontsToolError.fileNotExists(options.inputFile)
print(error.description)
Fonts.exit(withError: error)
Self.exit(withError: error)
}
// Extension for UIKit and SwiftUI should have different name
guard options.extensionName != options.extensionNameUIKit else {
let error = FontsToolError.extensionNamesCollision(options.extensionName)
print(error.description)
Fonts.exit(withError: error)
Self.exit(withError: error)
}
// Check if needed to regenerate
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration,
inputFilePath: options.inputFile,
extensionFilePath: options.extensionFilePath) else {
guard GeneratorChecker.shouldGenerate(
force: options.forceGeneration,
inputFilePath: options.inputFile,
extensionFilePath: options.extensionFilePath
) else {
print("[\(Self.toolName)] Fonts are already up to date :) ")
return false
}
return true
}
}

View File

@ -8,27 +8,28 @@
import Foundation
enum FontsToolError: Error {
case extensionNamesCollision(String)
case fcScan(String, Int32, String?)
case inputFolderNotFound(String)
case fileNotExists(String)
case writeExtension(String, String)
var description: String {
switch self {
case .extensionNamesCollision(let extensionName):
return "error: [\(Fonts.toolName)] Error on extension names, extension name and SwiftUI extension name should be different (\(extensionName) is used on both)"
case .fcScan(let path, let code, let output):
case let .fcScan(path, code, output):
return "error: [\(Fonts.toolName)] Error while getting fontName (fc-scan --format %{postscriptname} \(path). fc-scan exit with \(code) and output is: \(output ?? "no output")"
case .inputFolderNotFound(let inputFolder):
return "error: [\(Fonts.toolName)] Input folder not found: \(inputFolder)"
case .fileNotExists(let filename):
return "error: [\(Fonts.toolName)] File \(filename) does not exists"
case .writeExtension(let filename, let info):
case let .writeExtension(filename, info):
return "error: [\(Fonts.toolName)] An error occured while writing extension in \(filename): \(info)"
}
}

View File

@ -1,6 +1,6 @@
//
// FontsToolHelper.swift
//
//
//
// Created by Thibaut Schmitt on 13/12/2021.
//
@ -8,32 +8,32 @@
import Foundation
import ToolCore
class FontsToolHelper {
enum FontsToolHelper {
static func getFontPostScriptName(for fonts: [String], inputFolder: String) -> [FontName] {
let fontsFilenames = Self.getFontsFilenames(fromInputFolder: inputFolder)
.filter { fontNameWithPath in
let fontName = URL(fileURLWithPath: fontNameWithPath)
.deletingPathExtension()
.lastPathComponent
if fonts.contains(fontName) {
return true
}
return false
}
let fontsFilesnamesWithPath = fontsFilenames.map {
"\(inputFolder)/\($0)"
}
return fontsFilesnamesWithPath.compactMap {
Self.getFontName(atPath: $0)
}
}
// MARK: - Private
private static func getFontsFilenames(fromInputFolder inputFolder: String) -> [String] {
// Get a enumerator for all files
let fileManager = FileManager()
@ -42,32 +42,41 @@ class FontsToolHelper {
print(error.description)
Fonts.exit(withError: error)
}
let enumerator: FileManager.DirectoryEnumerator = fileManager.enumerator(atPath: inputFolder)!
let enumerator: FileManager.DirectoryEnumerator = fileManager.enumerator(atPath: inputFolder)! // swiftlint:disable:this force_unwrapping
// Filters font files
let fontsFileNames: [String] = (enumerator.allObjects as! [String])
let fontsFileNames: [String] = (enumerator.allObjects as! [String]) // swiftlint:disable:this force_cast
.filter {
if $0.hasSuffix(".ttf") || $0.hasSuffix(".otf") {
return true
}
return false
}
return fontsFileNames
}
private static func getFontName(atPath path: String) -> String {
//print("fc-scan --format %{postscriptname} \(path)")
private static func getFontName(atPath path: String) -> FontName {
// print("fc-scan --format %{postscriptname} \(path)")
// Get real font name
let task = Shell.shell(["fc-scan", "--format", "%{postscriptname}", path])
guard let fontName = task.output, task.terminationStatus == 0 else {
guard let postscriptName = task.output, task.terminationStatus == 0 else {
let error = FontsToolError.fcScan(path, task.terminationStatus, task.output)
print(error.description)
Fonts.exit(withError: error)
}
return fontName
let pathURL = URL(fileURLWithPath: path)
let filename = pathURL
.deletingPathExtension()
.lastPathComponent
return FontName(
postscriptName: postscriptName,
filename: filename,
fileExtension: pathURL.pathExtension
)
}
}

View File

@ -8,37 +8,44 @@
import Foundation
import ToolCore
class FontPlistGenerator {
enum FontPlistGenerator {
static func generatePlistUIAppsFontContent(for fonts: [FontName], infoPlistPaths: [String]) -> String {
let fontsToAddToPlist = fonts
.compactMap { $0 }
// Update each plist
infoPlistPaths.forEach { infoPlist in
// Remove UIAppFonts value
Shell.shell(launchPath: "/usr/libexec/PlistBuddy",
["-c", "delete :UIAppFonts", infoPlist])
Shell.shell(
launchPath: "/usr/libexec/PlistBuddy",
["-c", "delete :UIAppFonts", infoPlist]
)
// Add UIAppFonts empty array
debugPrint("Will PlistBuddy -c add :UIAppFonts array \(infoPlist)")
Shell.shell(launchPath: "/usr/libexec/PlistBuddy",
["-c", "add :UIAppFonts array", infoPlist])
Shell.shell(
launchPath: "/usr/libexec/PlistBuddy",
["-c", "add :UIAppFonts array", infoPlist]
)
// Fill array with fonts
fontsToAddToPlist
.forEach {
Shell.shell(launchPath: "/usr/libexec/PlistBuddy",
["-c", "add :UIAppFonts: string \($0)", infoPlist])
.forEach { fontName in
Shell.shell(
launchPath: "/usr/libexec/PlistBuddy",
["-c", "add :UIAppFonts: string \(fontName.filename).\(fontName.fileExtension)", infoPlist]
)
}
}
var plistData = "<key>UIAppFonts</key>\n\t<array>\n"
fontsToAddToPlist
.forEach {
plistData += "\t\t<string>\($0)</string>\n"
.forEach { fontName in
plistData += "\t\t<string>\(fontName.filename).\(fontName.fileExtension)</string>\n"
}
plistData += "\t</array>"
return plistData
}
}

View File

@ -8,45 +8,51 @@
import Foundation
import ToolCore
class FontExtensionGenerator {
enum FontExtensionGenerator {
private static func getFontNameEnum(fontsNames: [String]) -> String {
private static func getFontNameEnum(fontsNames: [FontName]) -> String {
var enumDefinition = " enum FontName: String {\n"
fontsNames.forEach {
enumDefinition += " case \($0.fontNameSanitize) = \"\($0)\"\n"
enumDefinition += " case \($0.fontNameSanitize) = \"\($0.postscriptName)\"\n"
}
enumDefinition += " }\n"
return enumDefinition
}
static func writeExtensionFile(fontsNames: [String],
staticVar: Bool,
extensionName: String,
extensionFilePath: String,
isSwiftUI: Bool) {
static func writeExtensionFile(
fontsNames: [FontName],
staticVar: Bool,
extensionName: String,
extensionFilePath: String,
isSwiftUI: Bool
) {
// Create extension content
let extensionContent = Self.getExtensionContent(fontsNames: fontsNames,
staticVar: staticVar,
extensionName: extensionName,
isSwiftUI: isSwiftUI)
let extensionContent = Self.getExtensionContent(
fontsNames: fontsNames,
staticVar: staticVar,
extensionName: extensionName,
isSwiftUI: isSwiftUI
)
// Write content
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
do {
try extensionContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8)
} catch let error {
} catch {
let error = FontsToolError.writeExtension(extensionFilePath, error.localizedDescription)
print(error.description)
Fonts.exit(withError: error)
}
}
static func getExtensionContent(fontsNames: [String],
staticVar: Bool,
extensionName: String,
isSwiftUI: Bool) -> String {
static func getExtensionContent(
fontsNames: [FontName],
staticVar: Bool,
extensionName: String,
isSwiftUI: Bool
) -> String {
[
Self.getHeader(extensionClassname: extensionName, isSwiftUI: isSwiftUI),
Self.getFontNameEnum(fontsNames: fontsNames),
@ -55,34 +61,34 @@ class FontExtensionGenerator {
]
.joined(separator: "\n")
}
private static func getHeader(extensionClassname: String, isSwiftUI: Bool) -> String {
"""
// Generated by ResgenSwift.\(Fonts.toolName) \(ResgenSwiftVersion)
import \(isSwiftUI ? "SwiftUI" : "UIKit")
extension \(extensionClassname) {\n
"""
}
private static func getFontMethods(fontsNames: [FontName], staticVar: Bool, isSwiftUI: Bool) -> String {
let pragma = " // MARK: - Getter"
var propertiesOrMethods: [String] = fontsNames
.unique()
.map {
$0.getProperty(isStatic: staticVar, isSwiftUI: isSwiftUI)
}
propertiesOrMethods.insert(pragma, at: 0)
return propertiesOrMethods.joined(separator: "\n\n")
}
private static func getFooter() -> String {
"""
}
"""
}
}

View File

@ -7,13 +7,21 @@
import Foundation
typealias FontName = String
// swiftlint:disable no_grouping_extension
struct FontName: Hashable {
let postscriptName: String
let filename: String
let fileExtension: String
}
extension FontName {
var fontNameSanitize: String {
self.removeCharacters(from: "[]+-_")
postscriptName.removeCharacters(from: "[]+-_")
}
func getProperty(isStatic: Bool, isSwiftUI: Bool) -> String {
if isSwiftUI {
if isStatic {

View File

@ -7,10 +7,13 @@
import Foundation
class FontFileParser {
enum FontFileParser {
static func parse(_ inputFile: String) -> [String] {
let inputFileContent = try! String(contentsOfFile: inputFile,
encoding: .utf8)
let inputFileContent = try! String( // swiftlint:disable:this force_try
contentsOfFile: inputFile,
encoding: .utf8
)
return inputFileContent.components(separatedBy: CharacterSet.newlines)
}
}