#!/usr/bin/env python3 ########################################################################### # Copyright (C) 2022 Stefan Riesenberger, Miepee, famfo # # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . ########################################################################### import argparse import glob import ntpath import os import re import subprocess import sys # Dependencies: (optional) git, inkscape, python 3.9 # deletes raster graphics with given name def delete_graphics(name): """Delete each PNG file in name""" for file in name: file = file.removeprefix('svg/').removesuffix('.svg') for directory in glob.glob(f"./png/**/{file}.png", recursive=True): print(f"deleting: {directory}") os.remove(directory) # rasters SVGs via Inkscape def raster_graphics(files, sizes = None): """Rasters each file in files via Inkscape""" sizes = [72, 512, 1024] if sizes is None else sizes frogs = len(files)-1 for file in files: name = ntpath.basename(file).replace(".svg", "") print('\n------------------------------') print(f"{frogs} remaining...\nRastering {name}\n") frogs = frogs - 1 for size in sizes: # make sure the subdirectories exist raster_path = f"./png/{size}/{os.path.dirname(file.removeprefix('svg/').removesuffix('.svg'))}" if not os.path.exists(raster_path) : os.makedirs(raster_path ,exist_ok=True) # invoke Inkscape to raster the given vector graphics subprocess.run(["inkscape", file, "-C", "-h", str(size), f"--export-filename={raster_path}/{name}.png"],timeout=30, check=True) def print_pipe(out): if out != '': print(out) # git add given files def git_add_raster(files): """Git add's each file in files""" for file in files: file = file.removeprefix('svg/').removesuffix('.svg') stream = os.popen(f"git add ./png/**/{file}.png") print_pipe(stream.read()) # git commit given files def git_commit_raster(): """Creates a new autogenerated git commit with the PNGs""" stream = os.popen('git commit -m "[png] generate PNGs"') print_pipe(stream.read()) # create a new tag with a description containing the added, modified, deleted and renamed files def create_tag(added, modified, deleted, renamed_old, renamed_new): """Creates a new git tag and names it appropriately""" if len(renamed_old) != len(renamed_new): raise Exception("Mismatch of renamed_old/renamed_new length!") # get tags stream = os.popen('git tag -l "auto-v*"') output = stream.read() tags = output.split('\n') tags.remove('') # create tag message added = '\n'.join([x.removeprefix('svg/').removesuffix('.svg') for x in added]) modified = '\n'.join([x.removeprefix('svg/').removesuffix('.svg') for x in modified]) deleted = '\n'.join([x.removeprefix('svg/').removesuffix('.svg') for x in deleted]) i = 0 renamed = '' while i < len(renamed_old): old = renamed_old[i].removeprefix('svg/').removesuffix('.svg') new = renamed_new[i].removeprefix('svg/').removesuffix('.svg') renamed = f'{renamed}\n{old} -> {new}' i+=1 message = f'**New:**\n{added}\n\n**Updated:**\n{modified}\n\n**Renamed:**{renamed}\n\n**Removed:**\n{deleted}' # create new tag stream = os.popen(f"git tag auto-v{len(tags)} -m '{message}'") print_pipe(stream.read()) # -------------------------------# # main section # -------------------------------# def main(): """Main Method""" # parse arguments parser = argparse.ArgumentParser(description='Automate rasterization of SVGs to PNGs.') mutex_group = parser.add_mutually_exclusive_group(required=True) mutex_group.add_argument("-a", "--all", action="store_true", help="Generate PNGs of all SVGs.") mutex_group.add_argument("-s", "--specific", metavar='S', type=str, nargs="+", help="Generate PNGs for each S in the \"svg\" folder. " + "The '.svg' suffix is optional.") parser.add_argument("-r", "--resolution", metavar='R', type=int, nargs="+", help="Custom resolutions that will be generated instead of the defaults (72, 512, 1024).") mutex_group.add_argument("-g", "--git", action="store_true", help="Generates PNGs of SVGs that changed since last auto tag and creates a git commit with them. " + "Requires 'git' to be installed and located in PATH.") # this will error out on invalid argument configurations args = parser.parse_args() for res in args.resolution or []: if res <= 0 or res >= 2147483647: print("Resolutions have to be in the range between 0 and 2147483647!") exit(1) sizes = args.resolution files = [] # if "git" parameter was specified, regenerate since last tag + create git commit if args.git: # get modified, added, renamed, deleted SVGs since last tag # call this on windows a) with PS for the $() and b) replace grep with PS' equivalent git_command = 'git diff --name-status $(git describe --tags --abbrev=0 --match "auto-v*") HEAD' if sys.platform == "win32": os.environ["COMSPEC"] = 'powershell' stream = os.popen(f"{git_command} | Select-String svg/") else: stream = os.popen(f"{git_command} | grep svg/") output = stream.read() files = output.split('\n') files.remove('') if len(files) == 0: print('No SVGs to regenerate!') sys.exit(1) split_files = list() additions = list() modifications = list() deleted = list() renamed_old = list() renamed_new = list() for s in reversed(files): split_file = s.split('\t') if 'A' == split_file[0]: additions.append(split_file[1]) elif 'M' == split_file[0]: modifications.append(split_file[1]) elif 'D' == split_file[0]: deleted.append(split_file[1]) elif re.match(r"R[0-9]+", split_file[0]): renamed_old.append(split_file[1]) renamed_new.append(split_file[2]) else: print(f'Unrecognized git action: {split_file}') sys.exit(1) print('\nAdditions:') raster_graphics(additions, sizes) git_add_raster(additions) print('\nModifications:') raster_graphics(modifications, sizes) git_add_raster(modifications) print('\nRenamed:') delete_graphics(renamed_old) raster_graphics(renamed_new) git_add_raster(renamed_old) git_add_raster(renamed_new) print('\nDeletions:') delete_graphics(deleted) git_add_raster(deleted) # commit and tag git_commit_raster() create_tag(additions, modifications, deleted, renamed_old, renamed_new) # if "all" parameter was specified, raster all svgs including subfolders if args.all: files = list(glob.glob('svg/**/*.svg', recursive=True)) raster_graphics(files, sizes) # if "specific" parameter was specified, only render those if args.specific: for file in args.specific: file = file.removeprefix('svg/').removesuffix('.svg') if os.path.exists(f"svg/{file}.svg"): files.append(f"svg/{file}.svg") raster_graphics(files, sizes) if __name__ == "__main__": main()