Publish v1.0
Some checks failed
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
Some checks failed
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
Reviewed-on: #1
This commit is contained in:
44
Sources/ResgenSwift/Fonts/FontOptions.swift
Normal file
44
Sources/ResgenSwift/Fonts/FontOptions.swift
Normal file
@ -0,0 +1,44 @@
|
||||
//
|
||||
// FontsOptions.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 17/01/2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ArgumentParser
|
||||
|
||||
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 UIFont extension. Using default extension name will generate static property.")
|
||||
var extensionName: String = Fonts.defaultExtensionName
|
||||
|
||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+FontsMyApp.swift")
|
||||
var extensionSuffix: String = ""
|
||||
}
|
||||
|
||||
// MARK: - Computed var
|
||||
|
||||
extension FontsOptions {
|
||||
var extensionFileName: String {
|
||||
if extensionSuffix.isEmpty == false {
|
||||
return "\(extensionName)+\(extensionSuffix).swift"
|
||||
}
|
||||
return "\(extensionName).swift"
|
||||
}
|
||||
|
||||
var extensionFilePath: String {
|
||||
"\(extensionOutputPath)/\(extensionFileName)"
|
||||
}
|
||||
}
|
83
Sources/ResgenSwift/Fonts/Fonts.swift
Normal file
83
Sources/ResgenSwift/Fonts/Fonts.swift
Normal file
@ -0,0 +1,83 @@
|
||||
//
|
||||
// Fonts.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 13/12/2021.
|
||||
//
|
||||
|
||||
import ToolCore
|
||||
import Foundation
|
||||
import ArgumentParser
|
||||
|
||||
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 = "UIFont"
|
||||
|
||||
// MARK: - Command Options
|
||||
|
||||
@OptionGroup var options: FontsOptions
|
||||
|
||||
// MARK: - Run
|
||||
|
||||
public 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)
|
||||
|
||||
// Generate extension
|
||||
FontExtensionGenerator.writeExtensionFile(fontsNames: fontsNames,
|
||||
staticVar: options.staticMembers,
|
||||
extensionName: options.extensionName,
|
||||
extensionFilePath: options.extensionFilePath)
|
||||
|
||||
print("Info.plist information:")
|
||||
print("\(FontPlistGenerator.generatePlistUIAppsFontContent(for: fontsNames))")
|
||||
|
||||
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.localizedDescription)
|
||||
Fonts.exit(withError: error)
|
||||
}
|
||||
|
||||
// Check if needed to regenerate
|
||||
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
|
||||
}
|
||||
}
|
31
Sources/ResgenSwift/Fonts/FontsToolError.swift
Normal file
31
Sources/ResgenSwift/Fonts/FontsToolError.swift
Normal file
@ -0,0 +1,31 @@
|
||||
//
|
||||
// FontsToolError.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 13/12/2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum FontsToolError: Error {
|
||||
case fcScan(String, Int32, String?)
|
||||
case inputFolderNotFound(String)
|
||||
case fileNotExists(String)
|
||||
case writeExtension(String, String)
|
||||
|
||||
var localizedDescription: String {
|
||||
switch self {
|
||||
case .fcScan(let path, let code, let 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):
|
||||
return "error:[\(Fonts.toolName)] An error occured while writing extension in \(filename): \(info)"
|
||||
}
|
||||
}
|
||||
}
|
73
Sources/ResgenSwift/Fonts/FontsToolHelper.swift
Normal file
73
Sources/ResgenSwift/Fonts/FontsToolHelper.swift
Normal file
@ -0,0 +1,73 @@
|
||||
//
|
||||
// FontsToolHelper.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 13/12/2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ToolCore
|
||||
|
||||
class 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()
|
||||
guard fileManager.fileExists(atPath: inputFolder) else {
|
||||
let error = FontsToolError.inputFolderNotFound(inputFolder)
|
||||
print(error.localizedDescription)
|
||||
Fonts.exit(withError: error)
|
||||
}
|
||||
|
||||
let enumerator: FileManager.DirectoryEnumerator = fileManager.enumerator(atPath: inputFolder)!
|
||||
|
||||
// Filters font files
|
||||
let fontsFileNames: [String] = (enumerator.allObjects as! [String])
|
||||
.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)")
|
||||
// Get real font name
|
||||
let task = Shell.shell(["fc-scan", "--format", "%{postscriptname}", path])
|
||||
|
||||
guard let fontName = task.output, task.terminationStatus == 0 else {
|
||||
let error = FontsToolError.fcScan(path, task.terminationStatus, task.output)
|
||||
print(error.localizedDescription)
|
||||
Fonts.exit(withError: error)
|
||||
}
|
||||
|
||||
return fontName
|
||||
}
|
||||
}
|
22
Sources/ResgenSwift/Fonts/Generator/FontPlistGenerator.swift
Normal file
22
Sources/ResgenSwift/Fonts/Generator/FontPlistGenerator.swift
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// FontPlistGenerator.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 29/08/2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class FontPlistGenerator {
|
||||
static func generatePlistUIAppsFontContent(for fonts: [FontName]) -> String {
|
||||
var plistData = "<key>UIAppFonts</key>\n\t<array>\n"
|
||||
fonts
|
||||
.compactMap { $0 }
|
||||
.forEach {
|
||||
plistData += "\t\t<string>\($0)</string>\n"
|
||||
}
|
||||
plistData += "\t</array>"
|
||||
|
||||
return plistData
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
//
|
||||
// FontToolContentGenerator.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 13/12/2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ToolCore
|
||||
|
||||
class FontExtensionGenerator {
|
||||
|
||||
static func writeExtensionFile(fontsNames: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) {
|
||||
// Create extension content
|
||||
let extensionContent = Self.getExtensionContent(fontsNames: fontsNames,
|
||||
staticVar: staticVar,
|
||||
extensionName: extensionName)
|
||||
|
||||
// Write content
|
||||
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
||||
do {
|
||||
try extensionContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8)
|
||||
} catch (let error) {
|
||||
let error = FontsToolError.writeExtension(extensionFilePath, error.localizedDescription)
|
||||
print(error.localizedDescription)
|
||||
Fonts.exit(withError: error)
|
||||
}
|
||||
}
|
||||
|
||||
static func getExtensionContent(fontsNames: [String], staticVar: Bool, extensionName: String) -> String {
|
||||
[
|
||||
Self.getHeader(extensionClassname: extensionName),
|
||||
Self.getFontNameEnum(fontsNames: fontsNames),
|
||||
Self.getFontMethods(fontsNames: fontsNames, staticVar: staticVar),
|
||||
Self.getFooter()
|
||||
]
|
||||
.joined(separator: "\n")
|
||||
}
|
||||
|
||||
private static func getHeader(extensionClassname: String) -> String {
|
||||
"""
|
||||
// Generated by ResgenSwift.\(Fonts.toolName) \(ResgenSwiftVersion)
|
||||
|
||||
import UIKit
|
||||
|
||||
extension \(extensionClassname) {\n
|
||||
"""
|
||||
}
|
||||
|
||||
private static func getFontNameEnum(fontsNames: [String]) -> String {
|
||||
var enumDefinition = " enum FontName: String {\n"
|
||||
|
||||
fontsNames.forEach {
|
||||
enumDefinition += " case \($0.fontNameSanitize) = \"\($0)\"\n"
|
||||
}
|
||||
enumDefinition += " }\n"
|
||||
|
||||
return enumDefinition
|
||||
}
|
||||
|
||||
private static func getFontMethods(fontsNames: [FontName], staticVar: Bool) -> String {
|
||||
let pragma = " // MARK: - Getter"
|
||||
|
||||
var propertiesOrMethods: [String] = fontsNames
|
||||
.unique()
|
||||
.map {
|
||||
if staticVar {
|
||||
return $0.staticProperty
|
||||
} else {
|
||||
return $0.method
|
||||
}
|
||||
}
|
||||
|
||||
propertiesOrMethods.insert(pragma, at: 0)
|
||||
return propertiesOrMethods.joined(separator: "\n\n")
|
||||
}
|
||||
|
||||
private static func getFooter() -> String {
|
||||
"""
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
32
Sources/ResgenSwift/Fonts/Model/FontName.swift
Normal file
32
Sources/ResgenSwift/Fonts/Model/FontName.swift
Normal file
@ -0,0 +1,32 @@
|
||||
//
|
||||
// FontName.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 29/08/2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
typealias FontName = String
|
||||
|
||||
extension FontName {
|
||||
var fontNameSanitize: String {
|
||||
self.removeCharacters(from: "[]+-_")
|
||||
}
|
||||
|
||||
var method: String {
|
||||
"""
|
||||
func \(fontNameSanitize)(withSize size: CGFloat) -> UIFont {
|
||||
UIFont(name: FontName.\(fontNameSanitize).rawValue, size: size)!
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
var staticProperty: String {
|
||||
"""
|
||||
static let \(fontNameSanitize): ((_ size: CGFloat) -> UIFont) = { size in
|
||||
UIFont(name: FontName.\(fontNameSanitize).rawValue, size: size)!
|
||||
}
|
||||
"""
|
||||
}
|
||||
}
|
16
Sources/ResgenSwift/Fonts/Parser/FontFileParser.swift
Normal file
16
Sources/ResgenSwift/Fonts/Parser/FontFileParser.swift
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// FontFileParser.swift
|
||||
//
|
||||
//
|
||||
// Created by Thibaut Schmitt on 29/08/2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class FontFileParser {
|
||||
static func parse(_ inputFile: String) -> [String] {
|
||||
let inputFileContent = try! String(contentsOfFile: inputFile,
|
||||
encoding: .utf8)
|
||||
return inputFileContent.components(separatedBy: CharacterSet.newlines)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user