resgen.swift/Sources/Imagium/XcassetsGenerator.swift
Thibaut Schmitt efd04299a8
Some checks failed
gitea-openium/resgen.swift/pipeline/head There was a failure building this commit
Mise à jour des headers des fichiers générés
2022-07-25 09:56:54 +02:00

190 lines
7.7 KiB
Swift

//
// File.swift
//
//
// Created by Thibaut Schmitt on 24/01/2022.
//
import Foundation
import ToolCore
class XcassetsGenerator {
static let outputImageExtension = "png"
let forceGeneration: Bool
// MARK: - Init
init(forceGeneration: Bool) {
self.forceGeneration = forceGeneration
}
// MARK: - Assets generation
func generateXcassets(inputPath: String, imagesToGenerate: [ImageToGen], xcassetsPath: String) {
let fileManager = FileManager()
let svgConverter = Imagium.getSvgConverterPath()
let allSubFiles = fileManager.getAllRegularFileIn(directory: inputPath)
var generatedAssetsPaths = [String]()
// Generate new assets
imagesToGenerate.forEach { imageToGen in
// Get image path
let imageData: (path: String, ext: String) = {
for subfile in allSubFiles {
if subfile.hasSuffix("/" + imageToGen.name + ".svg") {
return (subfile, "svg")
}
if subfile.hasSuffix("/" + imageToGen.name + ".png") {
return (subfile, "png")
}
if subfile.hasSuffix("/" + imageToGen.name + ".jpg") {
return (subfile, "jpg")
}
if subfile.hasSuffix("/" + imageToGen.name + ".jepg") {
return (subfile, "jepg")
}
}
let error = ImagiumError.unknownImageExtension(imageToGen.name)
print(error.localizedDescription)
Imagium.exit(withError: error)
}()
// Create imageset folder
let imagesetName = "\(imageToGen.name).imageset"
let imagesetPath = "\(xcassetsPath)/\(imagesetName)"
Shell.shell("mkdir", "-p", imagesetPath)
// Store managed images path
generatedAssetsPaths.append(imagesetName)
// Generate output images path
let output1x = "\(imagesetPath)/\(imageToGen.name).\(XcassetsGenerator.outputImageExtension)"
let output2x = "\(imagesetPath)/\(imageToGen.name)@2x.\(XcassetsGenerator.outputImageExtension)"
let output3x = "\(imagesetPath)/\(imageToGen.name)@3x.\(XcassetsGenerator.outputImageExtension)"
// Check if we need to convert image
if self.shouldBypassGeneration(for: imageToGen, xcassetImagePath: output1x) {
print("\(imageToGen.name) -> Not regenerating")
return
}
// Convert image
let convertArguments = imageToGen.convertArguments
if imageData.ext == "svg" {
// /usr/local/bin/rsvg-convert path/to/image.png -w 200 -h 300 -o path/to/output.png
// /usr/local/bin/rsvg-convert path/to/image.png -w 200 -o path/to/output.png
// /usr/local/bin/rsvg-convert path/to/image.png -h 300 -o path/to/output.png
var command1x = ["\(svgConverter)", "\(imageData.path)"]
var command2x = ["\(svgConverter)", "\(imageData.path)"]
var command3x = ["\(svgConverter)", "\(imageData.path)"]
self.addConvertArgument(command: &command1x, convertArgument: convertArguments.x1)
self.addConvertArgument(command: &command2x, convertArgument: convertArguments.x2)
self.addConvertArgument(command: &command3x, convertArgument: convertArguments.x3)
command1x.append(contentsOf: ["-o", output1x])
command2x.append(contentsOf: ["-o", output2x])
command3x.append(contentsOf: ["-o", output3x])
Shell.shell(command1x)
Shell.shell(command2x)
Shell.shell(command3x)
} else {
// convert path/to/image.png -resize 200x300 path/to/output.png
// convert path/to/image.png -resize 200x path/to/output.png
// convert path/to/image.png -resize x300 path/to/output.png
Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x1.width ?? "")x\(convertArguments.x1.height ?? "")", output1x)
Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x2.width ?? "")x\(convertArguments.x2.height ?? "")", output2x)
Shell.shell("convert", "\(imageData.path)", "-resize", "\(convertArguments.x3.width ?? "")x\(convertArguments.x3.height ?? "")", output3x)
}
// Write Content.json
let imagesetContentJson = imageToGen.contentJson
let contentJsonFilePath = "\(imagesetPath)/Contents.json"
if fileManager.fileExists(atPath: contentJsonFilePath) == false {
Shell.shell("touch", "\(contentJsonFilePath)")
}
let contentJsonFilePathURL = URL(fileURLWithPath: contentJsonFilePath)
try! imagesetContentJson.write(to: contentJsonFilePathURL, atomically: true, encoding: .utf8)
print("\(imageToGen.name) -> Generated")
}
// Success info
let generatedAssetsCount = generatedAssetsPaths.count
print("Images generated: \(generatedAssetsCount)")
// Delete old assets
let allImagesetName = Set(fileManager.getAllImageSetFolderIn(directory: xcassetsPath))
let imagesetToRemove = allImagesetName.subtracting(Set(generatedAssetsPaths))
imagesetToRemove.forEach {
print("Will remove: \($0)")
}
imagesetToRemove.forEach { itemToRemove in
try! fileManager.removeItem(atPath: "\(xcassetsPath)/\(itemToRemove)")
}
print("Removed \(imagesetToRemove.count) images")
}
// MARK: - Helpers: SVG command
private func addConvertArgument(command: inout [String], convertArgument: ConvertArgument) {
if let width = convertArgument.width, width.isEmpty == false {
command.append("-w")
command.append("\(width)")
}
if let height = convertArgument.height, height.isEmpty == false {
command.append("-h")
command.append("\(height)")
}
}
// MARK: - Helpers: bypass generation
private func shouldBypassGeneration(for image: ImageToGen, xcassetImagePath: String) -> Bool {
guard forceGeneration == false else {
return false
}
let fileManager = FileManager()
// File not exists -> do not bypass
guard fileManager.fileExists(atPath: xcassetImagePath) else {
return false
}
// Info unavailable -> do not bypass
let taskWidth = Shell.shell("identify", "-format", "%w", xcassetImagePath)
let taskHeight = Shell.shell("identify", "-format", "%h", xcassetImagePath)
guard taskWidth.terminationStatus == 0,
taskHeight.terminationStatus == 0 else {
return false
}
let currentWidth = Int(taskWidth.output ?? "-1") ?? -1
let currentheight = Int(taskHeight.output ?? "-1") ?? -1
// Info unavailable -> do not bypass
guard currentWidth > 0 && currentheight > 0 else {
return false
}
// Check width and height
if image.width != -1 && currentWidth == image.width {
return true
}
if image.height != -1 && currentheight == image.height {
return true
}
return false
}
}