-
-
Notifications
You must be signed in to change notification settings - Fork 7
/
icon_builder.py
executable file
·136 lines (112 loc) · 4.95 KB
/
icon_builder.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#!/usr/bin/python3
import sys, subprocess, os, glob
import multiprocessing as mp
def isSymlinkBroken(path):
if os.path.islink(path):
#Generate path to symlink target
linkPath = str(os.path.dirname(path)) + "/" + str(os.readlink(path))
if not os.path.isfile(linkPath):
#Symlink is broken
return True
#Either not a symlink, or not broken
return False
def getCommandExitCode(command):
return subprocess.run(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode
def getCommandOutput(command):
output = subprocess.run(command, capture_output=True).stdout.decode("utf-8").split("\n")
return output
def getAssetResolutionDir(resolution):
convertDict = {
"32": "37",
"48": "56",
"64": "74"
}
if resolution in convertDict:
return convertDict[str(resolution)]
else:
return str(resolution)
#Generates the given icon for all required resolutions
def generateIconResolutions(inputFile, iconType, iconResolutions):
#Generate output file path by swapping to a png
outputFile = inputFile.replace(".svg", ".png")
outputFile = outputFile.replace("svg/", "")
outputFile = outputFile.rsplit("/", 1)[0] + "/resolution/" + outputFile.rsplit("/", 1)[1]
generateIcon(inputFile, outputFile, iconType, iconResolutions)
#Wrapper to prepare and generate and icon for the set
def prepareIcon(icon, buildDir, iconType, iconResolution):
#Work out output file and generate icon
outputFile = f"{buildDir}/" + icon.split("/", 2)[2]
outputFile = outputFile.rsplit("/", 1)[0] + f"/{iconResolution}px/" + outputFile.rsplit("/", 1)[1]
outputFile = outputFile.replace(".svg", ".png")
generateIcon(icon, outputFile, iconType, [iconResolution])
#Generates all icons for a specific resolution
def generateIconSet(buildDir, iconType, iconColour, iconResolution):
if iconColour == "coloured":
iconColour = "icons"
else:
iconColour = "icons-colourless"
#Create a list of all svg input files
iconData = glob.glob(f"assets/svg/{iconType}/*.svg")
#Pack icon paths and shared icon data together for multiprocessing
for i in range(0, len(iconData)):
iconData[i] = [iconData[i], buildDir, iconType, iconResolution]
#Map icons to available cores, using the wrapper
with mp.Pool(mp.cpu_count() * 2) as pool:
task = pool.starmap_async(prepareIcon, iconData)
task.wait()
#Generates the given icon for all required resolutions
def generateIcon(inputFile, outputFile, iconType, iconResolutions):
#Generate output file for each resolution allowed
outputFileOrig = outputFile
for resolution in iconResolutions:
#Generate path to outputFile for specific resolution
outputFile = outputFileOrig.replace("/resolution/", f"/{resolution}px/")
#Create the directories for the output file if missing
outputDir = os.path.dirname(outputFile)
if not os.path.exists(outputDir):
os.makedirs(outputDir, exist_ok=True)
if iconType == "select":
resolution = getAssetResolutionDir(resolution)
else:
resolution = str(resolution)
#Get process ID for use as a temporary file, if required
tempFile = outputDir + "/" + str(os.getpid()) + ".png"
#Generate the icon
print(f"Processing {inputFile} -> {outputFile} ({tempFile})")
getCommandExitCode(["inkscape", f"{inkscapeExport}={tempFile}", "-h", resolution, inputFile])
#Compress the icon and move to final destination
print(f"Compressing {outputFile}...")
getCommandExitCode(["optipng", "-quiet", "-strip", "all", "-nc", tempFile])
os.rename(tempFile, outputFile)
def checkFiles(buildDir):
if not os.path.exists(buildDir):
print(f"Build directory '{buildDir}' doesn't exist, exiting")
exit(1)
for file in glob.glob(f"{buildDir}/svg/*/*.svg"):
if isSymlinkBroken(file):
print(f"{file} is a broken symlink, exiting")
exit(1)
if "/icons/" in file:
if not os.path.exists(file.replace("/icons/", "/icons-colourless/")):
print(f"{file} is missing a colourless counterpart, exiting")
exit(1)
#Figure out inkscape generation option
inkscapeVersion = getCommandOutput(["inkscape", "--version"])[0].split(" ")[1]
inkscapeVersion = inkscapeVersion.split(".")
inkscapeVersion = float(f"{inkscapeVersion[0]}.{inkscapeVersion[1]}")
if inkscapeVersion >= 1.0:
inkscapeExport = "--export-filename"
else:
inkscapeExport = "--export-png"
if __name__ == "__main__":
if sys.argv[1] == "--generate":
#Pass generateIcon() icon to build, icon type and resolutions to build for
#Generates all resolutions for that specific icon
generateIconResolutions(str(sys.argv[4]), str(sys.argv[2]), sys.argv[3].split())
elif sys.argv[1] == "--custom":
#Pass generateIconSet() the build dir, icon type, icon colour and resolution to build for
#Generates all icons for that specific resolution
generateIconSet(str(sys.argv[4]), str(sys.argv[2]), str(sys.argv[5]), str(sys.argv[3]))
elif sys.argv[1] == "--check-files":
#Pass checkFiles() the build directory
checkFiles(str(sys.argv[2]))