feat(RES-34): Fix plist font filename (#14)
All checks were successful
gitea-openium/resgen.swift/pipeline/head This commit looks good
All checks were successful
gitea-openium/resgen.swift/pipeline/head This commit looks good
Reviewed-on: #14
This commit is contained in:
@ -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)"
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)"
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
"""
|
||||
}
|
||||
|
||||
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user