Command line to generate R2Font

This commit is contained in:
2021-12-13 13:55:55 +01:00
parent e159b2e752
commit ce3cac90be
34 changed files with 369 additions and 422 deletions

View File

@ -0,0 +1,31 @@
//
// CLIToolCore.swift
//
//
// Created by Thibaut Schmitt on 13/12/2021.
//
import Foundation
public class Shell {
@discardableResult
public static func shell(_ args: String...) -> (terminationStatus: Int32, output: String?) {
let task = Process()
task.launchPath = "/usr/bin/env"
task.arguments = args
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
task.waitUntilExit()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
guard let output: String = String(data: data, encoding: .utf8) else {
return (terminationStatus: task.terminationStatus, output: nil)
}
return (terminationStatus: task.terminationStatus, output: output)
}
}

View File

@ -0,0 +1,26 @@
//
// File.swift
//
//
// Created by Thibaut Schmitt on 13/12/2021.
//
import Foundation
extension String {
func removeCharacters(from forbiddenChars: CharacterSet) -> String {
let passed = self.unicodeScalars.filter { !forbiddenChars.contains($0) }
return String(String.UnicodeScalarView(passed))
}
func removeCharacters(from: String) -> String {
return removeCharacters(from: CharacterSet(charactersIn: from))
}
}
extension Sequence where Iterator.Element: Hashable {
func unique() -> [Iterator.Element] {
var seen: [Iterator.Element: Bool] = [:]
return self.filter { seen.updateValue(true, forKey: $0) == nil }
}
}

View File

@ -0,0 +1,59 @@
//
// FontToolContentGenerator.swift
//
//
// Created by Thibaut Schmitt on 13/12/2021.
//
import Foundation
class FontToolContentGenerator {
static func getExtensionHeader(fontsNames: [String]) -> String {
"""
// Generated from FontToolCore
// \(fontsNames.joined(separator: " "))
import UIKit
"""
}
static func getFontNameEnum(fontsNames: [String]) -> String {
var enumDefinition = "\tenum FontName: String {"
fontsNames.forEach {
//debugPrint("Name: \($0.removeCharacters(from: "[]+-_"))")
enumDefinition += "\t\tcase \($0.removeCharacters(from: "[]+-_")) = \"\($0)\"\n"
}
enumDefinition += "\t}\n"
return enumDefinition
}
static func getFontMethods(fontsNames: [String], isUIFontExtension: Bool) -> String {
var methods = "\t// MARK: - Getter\n"
fontsNames
.unique()
.forEach {
let fontNameSanitize = $0.removeCharacters(from: "[]+-_")
if isUIFontExtension {
methods += """
\n\tstatic let \(fontNameSanitize): ((_ size: CGFloat) -> UIFont) = { size in
\tUIFont(name: FontName.\(fontNameSanitize).rawValue, size: size)!
\t}\n
"""
} else {
methods += """
\n\tfunc \(fontNameSanitize)(withSize size: CGFloat) -> UIFont {
\tUIFont(name: FontName.\(fontNameSanitize).rawValue, size: size)!
\t}\n
"""
}
}
return methods
}
}

View File

@ -0,0 +1,27 @@
//
// File.swift
//
//
// Created by Thibaut Schmitt on 13/12/2021.
//
import Foundation
enum FontToolError: Error {
case fcScan(String, Int32, String?)
case inputFolderNotFound(String)
var description: String {
switch self {
case .fcScan(let path, let code, let output):
return """
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 "Input folder not found: \(inputFolder)"
}
}
}

View File

@ -0,0 +1,48 @@
//
// FontToolHelper.swift
//
//
// Created by Thibaut Schmitt on 13/12/2021.
//
import Foundation
import CLIToolCore
class FontToolHelper {
static func getFontsData(fromInputFolder inputFolder: String) -> (fontsNames: [String], fontsFileNames: [String]) {
// Get a enumerator for all files
let fileManager = FileManager()
guard fileManager.fileExists(atPath: inputFolder) else {
FontTool.exit(withError: FontToolError.fcScan(path, task.terminationStatus, task.output))
}
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
}
// Get font name (font name and font file name can be different)
let fontsNames = fontsFileNames.compactMap { getFontName(atPath: "\(inputFolder)/\($0)") }
return (fontsNames: fontsNames, fontsFileNames: 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 {
FontTool.exit(withError: FontToolError.fcScan(path, task.terminationStatus, task.output))
}
return fontName
}
}

View File

@ -0,0 +1,69 @@
//
// FontTool.swift
//
//
// Created by Thibaut Schmitt on 13/12/2021.
//
import Foundation
import CLIToolCore
import ArgumentParser
struct FontTool: ParsableCommand {
static let defaultExtensionName = "UIFont"
@Argument(help: "Input folder where fonts to generate are.")
var inputFolder: String
@Option(help: "Path where to generate the extension.")
var extensionOutputPath: String
@Option(help: "Extension name. If not specified, it will generate an UIFont extension")
var extensionName: String = Self.defaultExtensionName
public func run() throws {
print("[FontTool] Starting font generation")
let fontsData = FontToolHelper.getFontsData(fromInputFolder: inputFolder)
let extensionHeader = FontToolContentGenerator.getExtensionHeader(fontsNames: fontsData.fontsNames)
let extensionDefinitionOpening = "extension \(extensionName) {\n"
let extensionFontsNamesEnum = FontToolContentGenerator.getFontNameEnum(fontsNames: fontsData.fontsNames)
let extensionFontsMethods = FontToolContentGenerator.getFontMethods(fontsNames: fontsData.fontsNames, isUIFontExtension: isUIFontExtension())
let extensionDefinitionClosing = "}"
generateExtensionFile(extensionHeader,
extensionDefinitionOpening,
extensionFontsNamesEnum,
extensionFontsMethods,
extensionDefinitionClosing)
print("[FontTool] Font generated")
}
func generateExtensionFile(_ args: String...) {
// Create output path
let extensionFilePath = "\(extensionOutputPath)/\(extensionName)+Font.swift"
// Create file if not exists
let fileManager = FileManager()
if fileManager.fileExists(atPath: extensionFilePath) == false {
Shell.shell("touch", "\(extensionFilePath)")
}
// Create extension content
let extensionContent = args.joined(separator: "\n")
// Write content
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
try! extensionContent.write(to: extensionFilePathURL, atomically: true, encoding: .utf8)
}
// MARK: - Helpers
private func isUIFontExtension() -> Bool {
extensionName == Self.defaultExtensionName
}
}
FontTool.main()

View File

@ -0,0 +1 @@
print("Welcome ResgenSwift")