diff --git a/skintone/blob/base.json b/skintone/blob/base.json new file mode 100644 index 000000000..9b7be2cc3 --- /dev/null +++ b/skintone/blob/base.json @@ -0,0 +1,17 @@ +{ + "name": "base", + "extension": "", + "tolerance": 5, + "colors": + { + "skin": "#FEE133", + "stroke": "#eb8f00", + "dark": "#242424", + "tongue": "#e04c74", + "tongue_dark": "#ab3d2e", + "blue": "#40c0e7", + "blue_stroke": "#47a9b0", + "water": "#5f7aff", + "water_stroke": "#4864ed" + } +} \ No newline at end of file diff --git a/skintone/blob/blob.json b/skintone/blob/blob.json new file mode 100644 index 000000000..4bdbe2b06 --- /dev/null +++ b/skintone/blob/blob.json @@ -0,0 +1,16 @@ +{ + "name": "blob", + "extension": "blob", + "colors": + { + "skin": "#fcc21b", + "stroke": "#fcc21b", + "dark": "#2f2f2f", + "tongue": "#d7598b", + "tongue_dark": "#d7598b", + "blue": "#4fafd8", + "blue_stroke": "#4fafd8", + "water": "#ffffff", + "water_stroke": "#ffffff" + } +} \ No newline at end of file diff --git a/skintone/blobify.py b/skintone/blobify.py new file mode 100644 index 000000000..e5ad8e8ac --- /dev/null +++ b/skintone/blobify.py @@ -0,0 +1,65 @@ +import generate_skincolor +import remove_gradient +import emoji +import argparse +import os + + +def main(): + """ + Die main-Funktion, welche die Skin-Modifier verarbeitet + :return: Nix + """ + # Alle Kommandozeilenargumente hinzufügen + parser = argparse.ArgumentParser() + group = parser.add_mutually_exclusive_group(required = True) + group.add_argument('--input_file', '-i', help='Input file', metavar='ifile') + group.add_argument('--input_dir', '-d', help='Input directory', metavar='idir', default = '.') + parser.add_argument('--mod_dir', '-m', help='Modifier directory', metavar='mdir', default = './blob') + parser.add_argument('--base_name', '-b', help='Name of the base skin color', metavar='bname', default='base') + # Zu dict verarbeiten + args = vars(parser.parse_args()) + # SKin-Modifier erstellen + modifiers = generate_skincolor.generate_modifiers(args['mod_dir']) + # Wurde ein Verzeichnis gewählt? + if args['input_dir']: + process_folder(args['input_dir'], modifiers, args['base_name']) + else: + process_file(modifiers, modifiers, args['base_name']) + + +def process_folder(path: str, modifiers, base) -> None: + """ + Entfernt die Verläufe für alle Dateien eines Ordners + :param path: Der Pfad zur Datei + :param modifiers: Die Modifikatoren + :param base: Der Basistyp + :return: Nix (ändert die Datei) + """ + files = os.listdir(path) + errors = [] + for file in files: + # Nur SVG wird derzeit unterstützt + if os.path.splitext(file)[-1].lower() in {'.svg'}: + err = process_file(os.path.join(path, file), modifiers, base, True) + if err: + errors.append(err) + print('Es sind {} Fehler aufgetreten bei folgenden Dateien:\n {}'.format(len(errors), '\n '.join(errors))) + + +def process_file(path, modifiers, base, folder = False): + try: + # Entferne Verläufe + remove_gradient.process_file(path) + # Erstelle ein Emoji-Objekt + emoji_ = emoji.Emoji(modifiers, path, base) + # Und wende die Modifier an + emoji_.batch_modify() + except Exception as e: + print('Es ist ein Fehler aufgetreten beim Bearbeiten von: {}'.format(path)) + print(e) + return path + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/skintone/emoji.py b/skintone/emoji.py new file mode 100644 index 000000000..b5766cea1 --- /dev/null +++ b/skintone/emoji.py @@ -0,0 +1,116 @@ +from modifier import * +import re + + +class Emoji: + + # There are some cases where an FE0F character has to be applied. + # This is the case for gender symbols which should be represented as emojis and not as characters + fe0f_chars = {'♀', '♂'} + + def __init__(self, modifiers: dict, path: str, base: str, end: bool = False): + """ + Create a new Emoji + :param modifiers: All skin modifiers available to this emoji + :param path: The path of the base SVG file + :param base: Name of the base type + :param end: True/False explicitly sets the FE0F character. fe0f_chars is used if None + """ + # Assignments + self.modifiers = modifiers + self.path = path + self.directory = os.path.dirname(path) + self.base = modifiers[base] + self.name = os.path.splitext(os.path.basename(path))[0] + self.fextension = os.path.splitext(os.path.basename(path))[1] + self.content = self.load() + if end is not None: + # Explicit + self.end = end + else: + # Implicit + self.end = False + # Does it contain an "FE0F indicator"? + for char in Emoji.fe0f_chars: + if(char in path): + self.end = True + break + + def batch_modify(self): + """ + new_modified_file for all Modifiers + :return: None + """ + for name, modifier in self.modifiers.items(): + # You don't need to convert from base to base + if modifier != self.base: + self.new_modified_file(modifier) + print('{} auf Emoji {} angewendet.'.format(name, self.name)) + + def new_modified_file(self, modifier: Modifier): + """ + Creates a new skin tone variant file + :param modifier: The Modifier + :return: None + """ + # Update the SVG file + new_content = self.generate_modified(modifier) + # Save file + self.save(new_content, modifier.extension) + + def load(self) -> str: + """Gets the (text) content of the base SVG file of this Emoji. + :return: the SVG file's content""" + try: + with open(self.path) as file: + return file.read() + except FileNotFoundError: + print('File "{}" not found!'.format(self.path)) + + def generate_modified(self, modifier: Modifier): + """ + Creates a new skin tone variant of this Emoji + :param modifier: The Modifier which has to be applied + :return: The altered SVG file's content + """ + # We're going to work on a copy of the content + content = self.content + # All colors are indexed by their name. We'll replace one by one + for old, new in modifier.replace(self.base).items(): + # Create the regular expression which will be used + old_regex = re.compile(old, re.IGNORECASE) + # ...Apply it + content = old_regex.sub(new, content) + return content + + def save(self, content: str, extension: str) -> None: + """ + Save the new skin tone variant + :param content: The new content which has been created + :param extension: Any new characters which have to be added (usually 200d + the skin tone modifier) + :return: None (writes the file) + """ + # Well, this should be obvious... + with open(self.generate_path(extension), 'w') as file: + file.write(content) + + def generate_path(self, extension: str) -> str: + """ + Creates the file path of the newly created variant + :param extension: All characters which have to be added to this emoji. + :return: A str containing the path/to/emoji_variant.svg + """ + # Which directory? (It will be saved in the same one as the base emoji) + directory = self.directory + # Which is the base name of the file? (e.g. emoji_u1f973) + basename = self.name + # The file extension (.svg) + fileextension = self.fextension + base_seq = basename.split('_') + base_seq.insert(2, extension) + # Add FE0F? + if self.end: + base_seq.append('fe0f') + basename = '_'.join(base_seq) + # Stitch it together and return the whole file path + return os.path.join(directory, basename) + fileextension diff --git a/skintone/generate_skincolor.py b/skintone/generate_skincolor.py new file mode 100644 index 000000000..209cf5748 --- /dev/null +++ b/skintone/generate_skincolor.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- + +import os +import sys +import argparse +from modifier import * +from emoji import * + + +def main(): + """ + The main function doing all the work + :return: Nothing + """ + # We'll need all those command line arguments + parser = argparse.ArgumentParser() + group = parser.add_mutually_exclusive_group(required = True) + group.add_argument('--input_file', '-i', help='Input file.svg', metavar='ifile') + group.add_argument('--input_dir', '-d', help='Directory containing all base files.svg', metavar='idir', default = '.') + parser.add_argument('--mod_dir', '-m', help='Directory containing all modifier.json', metavar='mdir', default = './skins') + parser.add_argument('--base_name', '-b', help='Name of the base skin color (without file extensions)', metavar='bname', default='base') + parser.add_argument('--add_end', '-e', help='Do you want to add an fe0f ZWJ-sequence?', default='auto', choices=['y','n','auto'], required=False) + # Make a dict out of it + args = vars(parser.parse_args()) + end = False if args['add_end'].lower() == 'n' else (True if args['add_end'].lower() == 'y' else None) + # Create skin-Modifiers + modifiers = generate_modifiers(args['mod_dir']) + # Did the user chose a dir or a file? + if args['input_dir']: + multi_process(args['input_dir'], modifiers, args['base_name'], end) + else: + # Create this one Emoji object + emoji = Emoji(modifiers, args['input_file'], args['base_name'], end) + # Apply modifiers + emoji.batch_modify() + + +def generate_modifiers(path: str) -> dict: + """ + Parses all skin modifiers in their directory + :param path: Directory containing the JSON files for the modifiers + :return: A str-Modifier dict containing the modifiers, sorted by their name (name: modifier) + """ + modifiers = {} + for file in os.listdir(path): + # Ignore non-JSON files + if os.path.splitext(file)[-1].lower() == '.json': + # Create one Modifier out of this JSON file + modifier = Modifier.generate_from_json(os.path.join(path, file)) + modifiers.update({modifier.name: modifier}) + return modifiers + + +def multi_process(directory: str, modifiers: dict, base: str, end: bool = False): + """ + Processes one directory of Emoji files + :param directory: The directory containing the base SVG files + :param modifiers: All Modifiers (probably provided by generate_modifiers) + :param base: The name of the base skin color used in the base SVG files + :param end: If an FE0F sequence should be added + :return: Nothing (Files will be written) + """ + files = os.listdir(directory) + for file in files: + # Ignore non-SVG-files + if os.path.splitext(file)[-1].lower() in {'.svg'}: + # Create a new Emoji-object + emoji = Emoji(modifiers, os.path.join(directory, file), base, end) + # Apply modifiers + emoji.batch_modify() + + +if __name__ == '__main__': + main() diff --git a/skintone/generate_skincolor_de.py b/skintone/generate_skincolor_de.py new file mode 100644 index 000000000..b2bc61582 --- /dev/null +++ b/skintone/generate_skincolor_de.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- + +import os +import sys +import argparse +from modifier import * +from emoji import * + + +def main(): + """ + Die main-Funktion, welche die Skin-Modifier verarbeitet + :return: Nix + """ + # Alle Kommandozeilenargumente hinzufügen + parser = argparse.ArgumentParser() + group = parser.add_mutually_exclusive_group(required = True) + group.add_argument('--input_file', '-i', help='Input file', metavar='ifile') + group.add_argument('--input_dir', '-d', help='Input directory', metavar='idir', default = '.') + parser.add_argument('--mod_dir', '-m', help='Modifier directory', metavar='mdir', default = './skins') + parser.add_argument('--base_name', '-b', help='Name of the base skin color', metavar='bname', default='base') + parser.add_argument('--add_end', '-e', help='Do you want to add an fe0f ZWJ-sequence?', default='n', choices=['y','n','auto'], required=False) + # Zu dict verarbeiten + args = vars(parser.parse_args()) + end = False if args['add_end'].lower() == 'n' else (True if args['add_end'].lower() == 'y' else None) + # Skin-Modifier erstellen + modifiers = generate_modifiers(args['mod_dir']) + # Wurde ein Verzeichnis gewählt? + if args['input_dir']: + multi_process(args['input_dir'], modifiers, args['base_name'], end) + else: + # Erstelle ein Emoji-Objekt + emoji = Emoji(modifiers, args['input_file'], args['base_name'], end) + # Und wende die Modifier an + emoji.batch_modify() + + +def generate_modifiers(path: str) -> dict: + """ + Holt alle Skin-Modifier aus dem Ordner + :param path: Der Ordner mit den JSON-Dateien + :return: Ein dict mit name: Modifier + """ + modifiers = {} + for file in os.listdir(path): + # Ist es überhaupt eine JSON-Datei? + if os.path.splitext(file)[-1].lower() == '.json': + # Erstelle aus der JSON-Datei und füge es ein + modifier = Modifier.generate_from_json(os.path.join(path, file)) + modifiers.update({modifier.name: modifier}) + return modifiers + + +def multi_process(directory: str, modifiers: dict, base: str, end: bool = False): + """ + Verarbeitet ein ganzes Verzeichnis mit Emojis + :param directory: Der Ordner + :param modifiers: Die Skin-Modifier + :param base: Der Name des Basis-Typen + :param end: Ob noch eine fe0f-Sequenz angefügt werden soll. + :return: Nix + """ + files = os.listdir(directory) + for file in files: + # Nur SVG wird derzeit unterstützt + if os.path.splitext(file)[-1].lower() in {'.svg'}: + # Erstelle ein Emoji-Objekt + emoji = Emoji(modifiers, os.path.join(directory, file), base, end) + # Und wende die Modifier an + emoji.batch_modify() + + +if __name__ == '__main__': + main() diff --git a/skintone/gradient.py b/skintone/gradient.py new file mode 100644 index 000000000..3635a262b --- /dev/null +++ b/skintone/gradient.py @@ -0,0 +1,151 @@ +from modifier import HexString +import re + + +class Gradient: + """ + Ein Farbverlauf mit verschiedenen Stops + """ + def __init__(self, stops: list, raw: str): + """ + Erstelle einen neuen Farbverlauf + :param stops: Die einzelnen Stops + :param raw: Der ursprüngliche String + """ + self.stops = stops + self.raw = raw + + def calculate_average(self) -> str: + """ + Berechnet die durchschnittliche Farbe mit Berücksichtigung der Offsets + :return: Die Farbe als String + """ + # Platzhalter + av_color = [] + colors = [] + # Gehe jeden Stop durch + for stop in self.stops: + # Hole die einzelnen Farbkomponenten + components = stop.components + for i, component in enumerate(components): + # Hole die Länge/Breite ab + length = stop.length() + # Und berechne die Gewichtung + components[i] = length * component.value + colors.append(components) + # Jetzt sind die Komponenten wichtig + for component in zip(*colors): + av_color.append(HexString(hex(round(sum(component)))[2:])) + # Zum String machen + return '#' + ''.join([str(component) for component in av_color]) + + def first_color(self) -> str: + return self.stops[0].color + + @staticmethod + def from_xml(in_str: str) -> list: + """ + Erstelle Farbverläufe aus einem XML-String + :param in_str: Die XML-Datei als String + :return: Dei Farbverläufe als Liste + """ + # Erstellt den reg. Ausdruck + regex = re.compile(r'(<[a-z]*Gradient .*>)((.|\n)+)()', re.IGNORECASE) + # Sucht alle Farbverläufe raus + results = regex.findall(in_str) + resulting_gradients = [] + # Erstellt die Objekte + for result in results: + stops = Stop.from_xml(result[1]) + resulting_gradients.append(Gradient(stops, ''.join(result))) + return resulting_gradients + + def replace_stops(self) -> str: + """ + Ersetzt die Farben der Stops durch den Durchschnitt + :return: Der XML-String mit den Änderungen + """ + regex = re.compile(r'') + return regex.sub(r''.format(self.first_color().upper()), self.raw) + + def __str__(self): + return 'Farbverlauf mit {} Stops'.format(len(self.stops)) + + def __getitem__(self, key): + return self.stops[key] + + +class Stop: + def __init__(self, offset: float, color: str, next_: float, prev: float): + """ + Erstellt einen neuen Stop im Farbverlauf + :param offset: Die Position + :param color: Die Farbe als Hex-String + :param next_: Die nächste Position + :param prev: Die vorherige Position + """ + self.next = next_ + self.prev = prev + self.offset = offset + self.color = color + + def __str__(self): + return 'Stop @ {}, Color: {}'.format(self.offset, self.color) + + @property + def components(self) -> list: + """ + Gibt die Komponenten als HexString aus + :return: Eine Liste von HexStrings (3 Stück; RGB) + """ + if len(self.color) == 7: # Gibt nur RGB + return [HexString(self.color[1:3]), HexString(self.color[3:5]), HexString(self.color[5:7])] + + @components.setter + def components(self, values): + self.color = '#' + ''.join(values) + + def __len__(self) -> int: + """ + Gibt an, wie breit der Streifen ist (als int, was i.d.R 0 ist) + :return: Die Breite als int + """ + upper_ = (self.next + self.offset) // 2 # prev-------self---|---next + lower_ = (self.prev + self.offset) // 2 # prev---|---self-------next + return int(abs(upper_ - lower_)) + + def length(self) -> float: + """ + Gibt die Breite des Streifens mit dieser Farbe zurück + :return: Die Breite als float + """ + upper_ = (self.next + self.offset) / 2 # prev-------self---|---next + lower_ = (self.prev + self.offset) / 2 # prev---|---self-------next + return abs(upper_ - lower_) # prev---|==========|---next + + @staticmethod + def from_xml(in_str: str) -> list: + """ + Erstellt Stops aus einem XML-String + :param in_str: Der String + :return: Eine Liste mit Stop-Objekten + """ + # reg. Ausdruck erstellen + regex = re.compile(r"""""", re.IGNORECASE) + results = regex.findall(in_str) + stops = [] + # Parse die Resultate + for i, result in enumerate(results): + # Gibt es einen vorherigen Stop? + if i > 0: + prev = results[i-1][0] + else: + prev = 0 + # Gibt es einen nachfolgenden Stop? + if i < len(results) - 1: + next_ = results[i+1][0] + else: + next_ = 1 + # Das Muster ist: + stops.append(Stop(float(result[0]), result[2], float(prev), float(next_))) + return stops \ No newline at end of file diff --git a/skintone/modifier.py b/skintone/modifier.py new file mode 100644 index 000000000..83b663566 --- /dev/null +++ b/skintone/modifier.py @@ -0,0 +1,189 @@ +import json +import os + + +class Modifier: + """An Object containing all relevant information about one skin tone Modifier""" + + def __init__(self, name: str, colors: dict, extension: str, tolerance: int = 2, *args, **kwargs): + """ + Creates a new skin tone modifier + :param name: The name of this skin tone + :param colors: All colors in 'name: color hex'-format + :param extension: All characters to be added to the original emoji sequence (e.g. _200d_1f3b0) + :param tolerance: The color 'radius' which is matched + """ + self.name = name + self.colors = colors + self.extension = extension + self.tolerance = tolerance + + def replace(self, base) -> dict: + """ + Creates a dict containing all replacements + :param base: The base Modifier + :return: A dict containing the replacement rules as regular expressions + (e.g.: {'#(00|01|02)(00|01|02)(00|01|02)': '#ffffff', '#(10|11|12|13|14)(32|33|34|35|36)(54|55|56|57|58)': '#28923'} + """ + # Create a new dict + replace = dict() + # Color: The color's name (e.g. 'skin') + # Value: The actual color code + for color, value in base.colors.items(): + try: + # Try to find an appropiate replacement + replace.update({base.generate_tolerance(value): self.colors[color]}) + except KeyError: + try: + # We'll now try to ignore any extensions added with '_' + # (e.g. "hand_2" -> "hand", "skin_boo_ya" -> "skin") + replace.update({base.generate_tolerance(value): self.colors[color.split('_')[0]]}) + except KeyError: + # Replacement not found + print('Didn\'t find replacement for color {} from {} (Name: "{}" or "{}") in {}.'.format(value, base.name, color, color.split('_')[0], self.name)) + return replace + + def generate_tolerance(self, val: str) -> str: + """ + Generates a color radius to get a little tolerance + Please note this is really bad code. + Even in comparison to the rest of this crap. + :param val: The color's hex code (e.g. #12345D) + :return: a regular expression covering this radius (e.g: #(10|11|12|13|14)(32|33|34|35|36)(5B|5C|5D|5E|5F)) + """ + if len(val) == 7: # RGB + pairs = [val[1:3], val[3:5], val[5:7]] + else: # RGBA + pairs = [val[1:3], val[3:5], val[5:7], val[7:9]] + # Placeholder for the new color components + new_pairs = [] + for pair in pairs: + # Create a new Hex String with the two digits + hex = HexString(pair, 0, 0xff, 2) + # This one will contain all variations + # (42 will become [40,41,42,43,44]) + vals = [] + # Go through all possible values + for plus in range(-self.tolerance, self.tolerance + 1): + try: + # Try to add an offset + vals.append(hex + plus) + except ValueError: + # Ignore if the maximum range is exceeded + pass + # Apply the new values + new_pairs.append('({})'.format('|'.join((str(val) for val in vals)))) + return '#' + ''.join(new_pairs) + + @staticmethod + def generate_from_json(file: str): + """ + Creates a new Modifier object out of a JSON file + :param file: The file path + :return: A new Modifier parsed from this JSON file + """ + try: + # Open file + with open(file) as json_file: + # Load JSON table + jdict = json.loads(json_file.read()) + # Do we have a name? + if 'name' in jdict: + return Modifier(**jdict) + else: + # If not, we'll just use the file name + return Modifier(name= os.path.splitext(os.path.basename(file))[0], **jdict) + except FileNotFoundError: + print("File not found ¯\_(ツ)_/¯") + except json.JSONDecodeError: + print("This is not a valid JSON file >:(") + + def __str__(self): + return '{} (uxxxx_{}): Skin tone modifier with {} different colors'.format(self.name, self.extension, len(self.colors)) + + def detailed_info(self) -> str: + """ + Returns more detailed information on this Modifier + :return: A str containing some details + """ + return '{} (uxxxx_{}):\n {}'.format(self.name, self.extension, '\n '.join([': '.join(item) for item in list(self.colors.items())])) + + +class HexString: + """ + This is a simple data type to handle conversions and some basic operations on hexadecimal strings. + """ + + def __init__(self, string: str, min_: int = 0, max_: int = 0xff, length: int = 2): + """ + Create a new HexString + :param string: The string representation without the 0x-prefix + :param min_: The min allowed value + :param max_: The max allowed value + :param length: The max zfill length + """ + self.string = string.zfill(length) + self.value = int(string, 16) + self.min_ = min_ + self.max_ = max_ + self.length = length + + def __add__(self, other): + """ + Add another HexString or int + :param other: summand + :return: A new HexString with this operation applied + """ + if type(other) == int: + # Add + result = HexString(hex(self.value + other)[2:], self.min_, self.max_) + # Test for range + if result.value in range(self.min_, self.max_ + 1): + return result + else: + raise ValueError('Value not in allowed range') + if type(other) == HexString: + # Add + result = HexString(hex(self.value + other.value)[2:], self.min_, self.max_) + # Test for range + if result.value in range(self.min_, self.max_ + 1): + return result + else: + raise ValueError('Value not in allowed range') + + def __sub__(self, other): + """ + Sub another HexString or int + :param other: Subtrahend + :return: A new HexString with this operation applied + """ + if type(other) == int: + # Sub + result = HexString(hex(self.value - other)[2:], self.min_, self.max_) + # Test for range + if result.value in range(self.min_, self.max_ + 1): + return result + else: + raise ValueError('Value not in allowed range') + if type(other) == HexString: + # Sub + result = HexString(hex(self.value - other.value)[2:], self.min_, self.max_) + # Test for range + if result.value in range(self.min_, self.max_ + 1): + return result + else: + raise ValueError('Value not in allowed range') + + def __mul__(self, other): + result = HexString(hex(self.value * other)[2:], self.min_, self.max_) + # Test for range + if result.value in range(self.min_, self.max_ + 1): + return result + else: + raise ValueError('Value not in allowed range') + + def __len__(self): + return self.length + + def __str__(self): + return self.string.zfill(self.length) diff --git a/skintone/remove_gradient.py b/skintone/remove_gradient.py new file mode 100644 index 000000000..325123925 --- /dev/null +++ b/skintone/remove_gradient.py @@ -0,0 +1,60 @@ +import argparse +from gradient import * +import os + + +def main(): + """ + Die main-Funktion, welche die Farbverläufe entfernt + :return: Nix + """ + # Alle Kommandozeilenargumente hinzufügen + parser = argparse.ArgumentParser() + group = parser.add_mutually_exclusive_group(required = True) + group.add_argument('--input_file', '-i', help='Input file', metavar='ifile') + group.add_argument('--input_dir', '-d', help='Input directory', metavar='idir', default = '.') + # Zu dict verarbeiten + args = vars(parser.parse_args()) + if args['input_dir']: + process_folder(args['input_dir']) + else: + process_file(args['input_file']) + + +def process_file(path: str) -> None: + """ + Entfernt die Verläufe + :param path: Der Pfad zur Datei + :return: Nix (ändert die Datei) + """ + with open(path, 'r') as file: + text = file.read() + # Erstelle den Reg. Ausdruck + regex = re.compile( + r'(<(linear|radial)Gradient .*>)(( |\n)*)()(( |\n)*)*(( |\n)*)()', + re.IGNORECASE) + text = regex.sub(r'\1\3\5\8\10', text) + with open(path, 'w') as file: + file.seek(0) + file.truncate() + file.write(text) + + +def process_folder(path: str) -> None: + """ + Entfernt die Verläufe für alle Dateien eines Ordners + :param path: Der Pfad zur Datei + :return: Nix (ändert die Datei) + """ + files = os.listdir(path) + for file in files: + # Nur SVG wird derzeit unterstützt + if os.path.splitext(file)[-1].lower() in {'.svg'}: + print(path) + print(os.path.join(path, file)) + process_file(os.path.join(path, file)) + + +if __name__ == '__main__': + main() + diff --git a/skintone/skins/.DS_Store b/skintone/skins/.DS_Store new file mode 100644 index 000000000..701a3c903 Binary files /dev/null and b/skintone/skins/.DS_Store differ diff --git a/skintone/skins/base.json b/skintone/skins/base.json new file mode 100644 index 000000000..884fda3d5 --- /dev/null +++ b/skintone/skins/base.json @@ -0,0 +1,19 @@ +{ + "name": "base", + "extension": "", + "colors": + { + "skin": "#fac01b", + "skin_2": "#ffb300", + "skin_3": "#fcc21b", + "hand": "#fbc11b", + "hand_2": "#fac036", + "shadow": "#e49800", + "shadow_2": "#e48c15", + "shadow_3": "#e59600", + "ear": "#e39400", + "hair": "#6d4c41", + "hair_beard": "#6d4c41", + "pullover": "#00bfa5" + } +} \ No newline at end of file diff --git a/skintone/skins/dark.json b/skintone/skins/dark.json new file mode 100644 index 000000000..94f2ce5bd --- /dev/null +++ b/skintone/skins/dark.json @@ -0,0 +1,13 @@ +{ + "name": "dark", + "extension": "1f3ff", + "colors": + { + "skin": "#70534a", + "hand": "#70534a", + "shadow": "#563e37", + "ear": "#563e37", + "hair": "#232020", + "pullover": "#00bfa5" + } +} \ No newline at end of file diff --git a/skintone/skins/light.json b/skintone/skins/light.json new file mode 100644 index 000000000..18ae0dfa0 --- /dev/null +++ b/skintone/skins/light.json @@ -0,0 +1,14 @@ +{ + "name": "light", + "extension": "1f3fb", + "colors": + { + "skin": "#fadcbc", + "hand": "#fadcbc", + "shadow": "#dba689", + "ear": "#dba689", + "hair": "#312d2d", + "hair_beard": "212121", + "pullover": "#00bfa5" + } +} \ No newline at end of file diff --git a/skintone/skins/medium.json b/skintone/skins/medium.json new file mode 100644 index 000000000..ebc3d72f5 --- /dev/null +++ b/skintone/skins/medium.json @@ -0,0 +1,13 @@ +{ + "name": "medium", + "extension": "1f3fd", + "colors": + { + "skin": "#bf8f68", + "hand": "#bf8f68", + "shadow": "#99674f", + "ear": "#99674f", + "hair": "#6d4c41", + "pullover": "#00bfa5" + } +} \ No newline at end of file diff --git a/skintone/skins/medium_dark.json b/skintone/skins/medium_dark.json new file mode 100644 index 000000000..da5e38e76 --- /dev/null +++ b/skintone/skins/medium_dark.json @@ -0,0 +1,13 @@ +{ + "name": "medium_dark", + "extension": "1f3fe", + "colors": + { + "skin": "#9b643c", + "hand": "#9b643c", + "shadow": "#7a4c32", + "ear": "#7a4c32", + "hair": "#47352d", + "pullover": "#00bfa5" + } +} \ No newline at end of file diff --git a/skintone/skins/medium_light.json b/skintone/skins/medium_light.json new file mode 100644 index 000000000..110e0991c --- /dev/null +++ b/skintone/skins/medium_light.json @@ -0,0 +1,13 @@ +{ + "name": "medium_light", + "extension": "1f3fc", + "colors": + { + "skin": "#e0bb95", + "hand": "#e0bb95", + "shadow": "#c48e6a", + "ear": "#c48e6a", + "hair": "#bfa055", + "pullover": "#00bfa5" + } +} \ No newline at end of file diff --git a/skintone/skins/redhead.json b/skintone/skins/redhead.json new file mode 100644 index 000000000..f8360a0d5 --- /dev/null +++ b/skintone/skins/redhead.json @@ -0,0 +1,16 @@ +{ + "name": "redhead", + "extension": "1f9b0", + "colors": + { + "skin": "#fac01b", + "hand": "#fbc11b", + "hand_2": "#fac036", + "shadow": "#e49800", + "shadow_2": "#e48c15", + "ear": "#e39400", + "hair": "#eb4a1e", + "hair_beard": "#e1450a", + "pullover": "#00bfa5" + } +} \ No newline at end of file diff --git a/skintone/skins/white.json b/skintone/skins/white.json new file mode 100644 index 000000000..e27d4a12e --- /dev/null +++ b/skintone/skins/white.json @@ -0,0 +1,16 @@ +{ + "name": "white", + "extension": "1f9b3", + "colors": + { + "skin": "#fac01b", + "hand": "#fbc11b", + "hand_2": "#fac036", + "shadow": "#e49800", + "shadow_2": "#e48c15", + "ear": "#e39400", + "hair": "#ececec", + "hair_beard": "#f1f1f1", + "pullover": "#00bfa5" + } +} \ No newline at end of file