Skip to content

Commit 6e9f9e3

Browse files
committed
Add skin tone modifiing scripts
1 parent 9745b6d commit 6e9f9e3

18 files changed

+879
-0
lines changed

skintone/blob/base.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "base",
3+
"extension": "",
4+
"tolerance": 5,
5+
"colors":
6+
{
7+
"skin": "#FEE133",
8+
"stroke": "#eb8f00",
9+
"dark": "#242424",
10+
"tongue": "#e04c74",
11+
"tongue_dark": "#ab3d2e",
12+
"blue": "#40c0e7",
13+
"blue_stroke": "#47a9b0",
14+
"water": "#5f7aff",
15+
"water_stroke": "#4864ed"
16+
}
17+
}

skintone/blob/blob.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "blob",
3+
"extension": "blob",
4+
"colors":
5+
{
6+
"skin": "#fcc21b",
7+
"stroke": "#fcc21b",
8+
"dark": "#2f2f2f",
9+
"tongue": "#d7598b",
10+
"tongue_dark": "#d7598b",
11+
"blue": "#4fafd8",
12+
"blue_stroke": "#4fafd8",
13+
"water": "#ffffff",
14+
"water_stroke": "#ffffff"
15+
}
16+
}

skintone/blobify.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import generate_skincolor
2+
import remove_gradient
3+
import emoji
4+
import argparse
5+
import os
6+
7+
8+
def main():
9+
"""
10+
Die main-Funktion, welche die Skin-Modifier verarbeitet
11+
:return: Nix
12+
"""
13+
# Alle Kommandozeilenargumente hinzufügen
14+
parser = argparse.ArgumentParser()
15+
group = parser.add_mutually_exclusive_group(required = True)
16+
group.add_argument('--input_file', '-i', help='Input file', metavar='ifile')
17+
group.add_argument('--input_dir', '-d', help='Input directory', metavar='idir', default = '.')
18+
parser.add_argument('--mod_dir', '-m', help='Modifier directory', metavar='mdir', default = './blob')
19+
parser.add_argument('--base_name', '-b', help='Name of the base skin color', metavar='bname', default='base')
20+
# Zu dict verarbeiten
21+
args = vars(parser.parse_args())
22+
# SKin-Modifier erstellen
23+
modifiers = generate_skincolor.generate_modifiers(args['mod_dir'])
24+
# Wurde ein Verzeichnis gewählt?
25+
if args['input_dir']:
26+
process_folder(args['input_dir'], modifiers, args['base_name'])
27+
else:
28+
process_file(modifiers, modifiers, args['base_name'])
29+
30+
31+
def process_folder(path: str, modifiers, base) -> None:
32+
"""
33+
Entfernt die Verläufe für alle Dateien eines Ordners
34+
:param path: Der Pfad zur Datei
35+
:param modifiers: Die Modifikatoren
36+
:param base: Der Basistyp
37+
:return: Nix (ändert die Datei)
38+
"""
39+
files = os.listdir(path)
40+
errors = []
41+
for file in files:
42+
# Nur SVG wird derzeit unterstützt
43+
if os.path.splitext(file)[-1].lower() in {'.svg'}:
44+
err = process_file(os.path.join(path, file), modifiers, base, True)
45+
if err:
46+
errors.append(err)
47+
print('Es sind {} Fehler aufgetreten bei folgenden Dateien:\n {}'.format(len(errors), '\n '.join(errors)))
48+
49+
50+
def process_file(path, modifiers, base, folder = False):
51+
try:
52+
# Entferne Verläufe
53+
remove_gradient.process_file(path)
54+
# Erstelle ein Emoji-Objekt
55+
emoji_ = emoji.Emoji(modifiers, path, base)
56+
# Und wende die Modifier an
57+
emoji_.batch_modify()
58+
except Exception as e:
59+
print('Es ist ein Fehler aufgetreten beim Bearbeiten von: {}'.format(path))
60+
print(e)
61+
return path
62+
63+
64+
if __name__ == '__main__':
65+
main()

skintone/emoji.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
from modifier import *
2+
import re
3+
4+
5+
class Emoji:
6+
7+
# There are some cases where an FE0F character has to be applied.
8+
# This is the case for gender symbols which should be represented as emojis and not as characters
9+
fe0f_chars = {'♀', '♂'}
10+
11+
def __init__(self, modifiers: dict, path: str, base: str, end: bool = False):
12+
"""
13+
Create a new Emoji
14+
:param modifiers: All skin modifiers available to this emoji
15+
:param path: The path of the base SVG file
16+
:param base: Name of the base type
17+
:param end: True/False explicitly sets the FE0F character. fe0f_chars is used if None
18+
"""
19+
# Assignments
20+
self.modifiers = modifiers
21+
self.path = path
22+
self.directory = os.path.dirname(path)
23+
self.base = modifiers[base]
24+
self.name = os.path.splitext(os.path.basename(path))[0]
25+
self.fextension = os.path.splitext(os.path.basename(path))[1]
26+
self.content = self.load()
27+
if end is not None:
28+
# Explicit
29+
self.end = end
30+
else:
31+
# Implicit
32+
self.end = False
33+
# Does it contain an "FE0F indicator"?
34+
for char in Emoji.fe0f_chars:
35+
if(char in path):
36+
self.end = True
37+
break
38+
39+
def batch_modify(self):
40+
"""
41+
new_modified_file for all Modifiers
42+
:return: None
43+
"""
44+
for name, modifier in self.modifiers.items():
45+
# You don't need to convert from base to base
46+
if modifier != self.base:
47+
self.new_modified_file(modifier)
48+
print('{} auf Emoji {} angewendet.'.format(name, self.name))
49+
50+
def new_modified_file(self, modifier: Modifier):
51+
"""
52+
Creates a new skin tone variant file
53+
:param modifier: The Modifier
54+
:return: None
55+
"""
56+
# Update the SVG file
57+
new_content = self.generate_modified(modifier)
58+
# Save file
59+
self.save(new_content, modifier.extension)
60+
61+
def load(self) -> str:
62+
"""Gets the (text) content of the base SVG file of this Emoji.
63+
:return: the SVG file's content"""
64+
try:
65+
with open(self.path) as file:
66+
return file.read()
67+
except FileNotFoundError:
68+
print('File "{}" not found!'.format(self.path))
69+
70+
def generate_modified(self, modifier: Modifier):
71+
"""
72+
Creates a new skin tone variant of this Emoji
73+
:param modifier: The Modifier which has to be applied
74+
:return: The altered SVG file's content
75+
"""
76+
# We're going to work on a copy of the content
77+
content = self.content
78+
# All colors are indexed by their name. We'll replace one by one
79+
for old, new in modifier.replace(self.base).items():
80+
# Create the regular expression which will be used
81+
old_regex = re.compile(old, re.IGNORECASE)
82+
# ...Apply it
83+
content = old_regex.sub(new, content)
84+
return content
85+
86+
def save(self, content: str, extension: str) -> None:
87+
"""
88+
Save the new skin tone variant
89+
:param content: The new content which has been created
90+
:param extension: Any new characters which have to be added (usually 200d + the skin tone modifier)
91+
:return: None (writes the file)
92+
"""
93+
# Well, this should be obvious...
94+
with open(self.generate_path(extension), 'w') as file:
95+
file.write(content)
96+
97+
def generate_path(self, extension: str) -> str:
98+
"""
99+
Creates the file path of the newly created variant
100+
:param extension: All characters which have to be added to this emoji.
101+
:return: A str containing the path/to/emoji_variant.svg
102+
"""
103+
# Which directory? (It will be saved in the same one as the base emoji)
104+
directory = self.directory
105+
# Which is the base name of the file? (e.g. emoji_u1f973)
106+
basename = self.name
107+
# The file extension (.svg)
108+
fileextension = self.fextension
109+
base_seq = basename.split('_')
110+
base_seq.insert(2, extension)
111+
# Add FE0F?
112+
if self.end:
113+
base_seq.append('fe0f')
114+
basename = '_'.join(base_seq)
115+
# Stitch it together and return the whole file path
116+
return os.path.join(directory, basename) + fileextension

skintone/generate_skincolor.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import os
4+
import sys
5+
import argparse
6+
from modifier import *
7+
from emoji import *
8+
9+
10+
def main():
11+
"""
12+
The main function doing all the work
13+
:return: Nothing
14+
"""
15+
# We'll need all those command line arguments
16+
parser = argparse.ArgumentParser()
17+
group = parser.add_mutually_exclusive_group(required = True)
18+
group.add_argument('--input_file', '-i', help='Input file.svg', metavar='ifile')
19+
group.add_argument('--input_dir', '-d', help='Directory containing all base files.svg', metavar='idir', default = '.')
20+
parser.add_argument('--mod_dir', '-m', help='Directory containing all modifier.json', metavar='mdir', default = './skins')
21+
parser.add_argument('--base_name', '-b', help='Name of the base skin color (without file extensions)', metavar='bname', default='base')
22+
parser.add_argument('--add_end', '-e', help='Do you want to add an fe0f ZWJ-sequence?', default='auto', choices=['y','n','auto'], required=False)
23+
# Make a dict out of it
24+
args = vars(parser.parse_args())
25+
end = False if args['add_end'].lower() == 'n' else (True if args['add_end'].lower() == 'y' else None)
26+
# Create skin-Modifiers
27+
modifiers = generate_modifiers(args['mod_dir'])
28+
# Did the user chose a dir or a file?
29+
if args['input_dir']:
30+
multi_process(args['input_dir'], modifiers, args['base_name'], end)
31+
else:
32+
# Create this one Emoji object
33+
emoji = Emoji(modifiers, args['input_file'], args['base_name'], end)
34+
# Apply modifiers
35+
emoji.batch_modify()
36+
37+
38+
def generate_modifiers(path: str) -> dict:
39+
"""
40+
Parses all skin modifiers in their directory
41+
:param path: Directory containing the JSON files for the modifiers
42+
:return: A str-Modifier dict containing the modifiers, sorted by their name (name: modifier)
43+
"""
44+
modifiers = {}
45+
for file in os.listdir(path):
46+
# Ignore non-JSON files
47+
if os.path.splitext(file)[-1].lower() == '.json':
48+
# Create one Modifier out of this JSON file
49+
modifier = Modifier.generate_from_json(os.path.join(path, file))
50+
modifiers.update({modifier.name: modifier})
51+
return modifiers
52+
53+
54+
def multi_process(directory: str, modifiers: dict, base: str, end: bool = False):
55+
"""
56+
Processes one directory of Emoji files
57+
:param directory: The directory containing the base SVG files
58+
:param modifiers: All Modifiers (probably provided by generate_modifiers)
59+
:param base: The name of the base skin color used in the base SVG files
60+
:param end: If an FE0F sequence should be added
61+
:return: Nothing (Files will be written)
62+
"""
63+
files = os.listdir(directory)
64+
for file in files:
65+
# Ignore non-SVG-files
66+
if os.path.splitext(file)[-1].lower() in {'.svg'}:
67+
# Create a new Emoji-object
68+
emoji = Emoji(modifiers, os.path.join(directory, file), base, end)
69+
# Apply modifiers
70+
emoji.batch_modify()
71+
72+
73+
if __name__ == '__main__':
74+
main()

skintone/generate_skincolor_de.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import os
4+
import sys
5+
import argparse
6+
from modifier import *
7+
from emoji import *
8+
9+
10+
def main():
11+
"""
12+
Die main-Funktion, welche die Skin-Modifier verarbeitet
13+
:return: Nix
14+
"""
15+
# Alle Kommandozeilenargumente hinzufügen
16+
parser = argparse.ArgumentParser()
17+
group = parser.add_mutually_exclusive_group(required = True)
18+
group.add_argument('--input_file', '-i', help='Input file', metavar='ifile')
19+
group.add_argument('--input_dir', '-d', help='Input directory', metavar='idir', default = '.')
20+
parser.add_argument('--mod_dir', '-m', help='Modifier directory', metavar='mdir', default = './skins')
21+
parser.add_argument('--base_name', '-b', help='Name of the base skin color', metavar='bname', default='base')
22+
parser.add_argument('--add_end', '-e', help='Do you want to add an fe0f ZWJ-sequence?', default='n', choices=['y','n','auto'], required=False)
23+
# Zu dict verarbeiten
24+
args = vars(parser.parse_args())
25+
end = False if args['add_end'].lower() == 'n' else (True if args['add_end'].lower() == 'y' else None)
26+
# Skin-Modifier erstellen
27+
modifiers = generate_modifiers(args['mod_dir'])
28+
# Wurde ein Verzeichnis gewählt?
29+
if args['input_dir']:
30+
multi_process(args['input_dir'], modifiers, args['base_name'], end)
31+
else:
32+
# Erstelle ein Emoji-Objekt
33+
emoji = Emoji(modifiers, args['input_file'], args['base_name'], end)
34+
# Und wende die Modifier an
35+
emoji.batch_modify()
36+
37+
38+
def generate_modifiers(path: str) -> dict:
39+
"""
40+
Holt alle Skin-Modifier aus dem Ordner
41+
:param path: Der Ordner mit den JSON-Dateien
42+
:return: Ein dict mit name: Modifier
43+
"""
44+
modifiers = {}
45+
for file in os.listdir(path):
46+
# Ist es überhaupt eine JSON-Datei?
47+
if os.path.splitext(file)[-1].lower() == '.json':
48+
# Erstelle aus der JSON-Datei und füge es ein
49+
modifier = Modifier.generate_from_json(os.path.join(path, file))
50+
modifiers.update({modifier.name: modifier})
51+
return modifiers
52+
53+
54+
def multi_process(directory: str, modifiers: dict, base: str, end: bool = False):
55+
"""
56+
Verarbeitet ein ganzes Verzeichnis mit Emojis
57+
:param directory: Der Ordner
58+
:param modifiers: Die Skin-Modifier
59+
:param base: Der Name des Basis-Typen
60+
:param end: Ob noch eine fe0f-Sequenz angefügt werden soll.
61+
:return: Nix
62+
"""
63+
files = os.listdir(directory)
64+
for file in files:
65+
# Nur SVG wird derzeit unterstützt
66+
if os.path.splitext(file)[-1].lower() in {'.svg'}:
67+
# Erstelle ein Emoji-Objekt
68+
emoji = Emoji(modifiers, os.path.join(directory, file), base, end)
69+
# Und wende die Modifier an
70+
emoji.batch_modify()
71+
72+
73+
if __name__ == '__main__':
74+
main()

0 commit comments

Comments
 (0)