From c05b7103a2ffa0fd42244215c319dd073e66648b Mon Sep 17 00:00:00 2001 From: Cyklon_3000 <67823272+Cyklon3000@users.noreply.github.com> Date: Fri, 23 Aug 2024 22:53:44 +0200 Subject: [PATCH 1/4] ci: add python script for batch svg to .ico conversion --- batch_convert_to_ico.py | 99 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 batch_convert_to_ico.py diff --git a/batch_convert_to_ico.py b/batch_convert_to_ico.py new file mode 100644 index 00000000..562593b6 --- /dev/null +++ b/batch_convert_to_ico.py @@ -0,0 +1,99 @@ +import os +import subprocess +from pathlib import Path +from typing import Tuple, List, Dict + +def convert_svg_to_ico(input_folder:str, output_folder:str, sizes:Tuple[int, ...]=(16,32,48,64,256)): + """ + Converts a folder of .svg icons to a folder of .ico icons of various sizes. + Icons can be swapped based on a maximum size attributed to .svg icons, if their name ends with '-{size}px.svg'. + This function requires Imagemagick to be installed. + + Args: + input_folder (str): The path to the folder containing the .svg icons. + output_folder (str): The path to the folder where the .ico icons will be saved. + sizes (Tuple[int, ...], optional): The sizes of the .ico icons. Defaults to (16, 32, 48, 64, 256). + """ + + def ends_with_px(string:str) -> bool: + if not string.endswith('px'): return False + parts:Tuple[str, ...] = string.rsplit('-', 1) + if len(parts) != 2: return False + return parts[1][:-2].isdigit() + + sizes = sorted(sizes) + + base_filenames:List[str] = [filename for filename in os.listdir(input_folder) if filename.lower().endswith('.svg') + and not any([ends_with_px(filename[:-4].lower()) for s in sizes])] + alt_filenames:List[str] = [filename for filename in os.listdir(input_folder) if filename.lower().endswith('.svg') + and any([ends_with_px(filename[:-4].lower()) for s in sizes[:-1]])] + + # Iterate through all base .svg files in the input folder + for base_filename in base_filenames: + # print(base_filename) + inputs:List[Dict[str, int | str]] = [] + + for size in sizes: + assumed_filename:str = base_filename[:-4] + f'-{size}px.svg' + if assumed_filename not in alt_filenames: continue + + alt_input_path:str = os.path.join(input_folder, assumed_filename) + + inputs.append({'path': alt_input_path, 'maximum_size': size}) + + # Add version that comes for sizes above alt max sizes + inputs.append({'path': os.path.join(input_folder, base_filename), 'maximum_size': sizes[-1]}) + + # Step 1: Convert input.svg's to throughput.png's using Imagemagick + throughput_paths:List[str] = [os.path.join(output_folder, f'{base_filename[:-4]}-{size_index}.png') for size_index in range(len(sizes))] + size_index:int = 0 + # print(inputs) + input:Dict[str, int | str] = inputs.pop(0) + for size_index in range(len(sizes)): + # Go to next input if the current needed size is greater than input's maximum + if sizes[size_index] > input['maximum_size']: + input:Dict[str, int | str] = inputs.pop(0) + # print(input) + current_size:int = sizes[size_index] + throughput_path:str = throughput_paths[size_index] + try: + # magick convert -background transparent -resize x + subprocess.run([ + 'magick', + 'convert', + '-background', 'transparent', + input['path'], + '-resize', f'{current_size}x{current_size}', + throughput_path + ], check=True) + # print(f"Converted {base_filename} to {throughput_path}") + except subprocess.CalledProcessError as e: + print(f"SVG2PNG: Error converting {base_filename}: {e}") + size_index += 1 + + # Step 2: Combine throughput.png's to final output.ico using Imagemagick + output_filename:str = os.path.splitext(base_filename)[0] + '.ico' + output_path:str = os.path.join(output_folder, output_filename) + try: + # magick convert input-1.png input-2.png ... input-n.png output.ico + subprocess.run([ + 'magick', + 'convert', + '-background', 'transparent',] + + throughput_paths + + [output_path], + check=True) + print(f"Converted {base_filename} to {output_filename}") + except subprocess.CalledProcessError as e: + print(f"PNG2ICO: Error converting {base_filename}: {e}") + + # Create output folder if it doesn't exist + Path(output_folder).mkdir(parents=True, exist_ok=True) + for throughput_path in throughput_paths: + os.remove(throughput_path) + +if __name__ == "__main__": + input_folder:str = "test_svg/" # Input folder containing .svg files + output_folder:str = "test_ico/" # Output folder for converted .ico files (sizes 16, 32, 48, 64, 256) + + convert_svg_to_ico(input_folder, output_folder) \ No newline at end of file From 5cf61d6bc37daf3724bc0c0b0d790a225127e3af Mon Sep 17 00:00:00 2001 From: Cyklon_3000 <67823272+Cyklon3000@users.noreply.github.com> Date: Fri, 23 Aug 2024 23:00:22 +0200 Subject: [PATCH 2/4] Revert "ci: add python script for batch svg to .ico conversion" This reverts commit c05b7103a2ffa0fd42244215c319dd073e66648b. --- batch_convert_to_ico.py | 99 ----------------------------------------- 1 file changed, 99 deletions(-) delete mode 100644 batch_convert_to_ico.py diff --git a/batch_convert_to_ico.py b/batch_convert_to_ico.py deleted file mode 100644 index 562593b6..00000000 --- a/batch_convert_to_ico.py +++ /dev/null @@ -1,99 +0,0 @@ -import os -import subprocess -from pathlib import Path -from typing import Tuple, List, Dict - -def convert_svg_to_ico(input_folder:str, output_folder:str, sizes:Tuple[int, ...]=(16,32,48,64,256)): - """ - Converts a folder of .svg icons to a folder of .ico icons of various sizes. - Icons can be swapped based on a maximum size attributed to .svg icons, if their name ends with '-{size}px.svg'. - This function requires Imagemagick to be installed. - - Args: - input_folder (str): The path to the folder containing the .svg icons. - output_folder (str): The path to the folder where the .ico icons will be saved. - sizes (Tuple[int, ...], optional): The sizes of the .ico icons. Defaults to (16, 32, 48, 64, 256). - """ - - def ends_with_px(string:str) -> bool: - if not string.endswith('px'): return False - parts:Tuple[str, ...] = string.rsplit('-', 1) - if len(parts) != 2: return False - return parts[1][:-2].isdigit() - - sizes = sorted(sizes) - - base_filenames:List[str] = [filename for filename in os.listdir(input_folder) if filename.lower().endswith('.svg') - and not any([ends_with_px(filename[:-4].lower()) for s in sizes])] - alt_filenames:List[str] = [filename for filename in os.listdir(input_folder) if filename.lower().endswith('.svg') - and any([ends_with_px(filename[:-4].lower()) for s in sizes[:-1]])] - - # Iterate through all base .svg files in the input folder - for base_filename in base_filenames: - # print(base_filename) - inputs:List[Dict[str, int | str]] = [] - - for size in sizes: - assumed_filename:str = base_filename[:-4] + f'-{size}px.svg' - if assumed_filename not in alt_filenames: continue - - alt_input_path:str = os.path.join(input_folder, assumed_filename) - - inputs.append({'path': alt_input_path, 'maximum_size': size}) - - # Add version that comes for sizes above alt max sizes - inputs.append({'path': os.path.join(input_folder, base_filename), 'maximum_size': sizes[-1]}) - - # Step 1: Convert input.svg's to throughput.png's using Imagemagick - throughput_paths:List[str] = [os.path.join(output_folder, f'{base_filename[:-4]}-{size_index}.png') for size_index in range(len(sizes))] - size_index:int = 0 - # print(inputs) - input:Dict[str, int | str] = inputs.pop(0) - for size_index in range(len(sizes)): - # Go to next input if the current needed size is greater than input's maximum - if sizes[size_index] > input['maximum_size']: - input:Dict[str, int | str] = inputs.pop(0) - # print(input) - current_size:int = sizes[size_index] - throughput_path:str = throughput_paths[size_index] - try: - # magick convert -background transparent -resize x - subprocess.run([ - 'magick', - 'convert', - '-background', 'transparent', - input['path'], - '-resize', f'{current_size}x{current_size}', - throughput_path - ], check=True) - # print(f"Converted {base_filename} to {throughput_path}") - except subprocess.CalledProcessError as e: - print(f"SVG2PNG: Error converting {base_filename}: {e}") - size_index += 1 - - # Step 2: Combine throughput.png's to final output.ico using Imagemagick - output_filename:str = os.path.splitext(base_filename)[0] + '.ico' - output_path:str = os.path.join(output_folder, output_filename) - try: - # magick convert input-1.png input-2.png ... input-n.png output.ico - subprocess.run([ - 'magick', - 'convert', - '-background', 'transparent',] - + throughput_paths - + [output_path], - check=True) - print(f"Converted {base_filename} to {output_filename}") - except subprocess.CalledProcessError as e: - print(f"PNG2ICO: Error converting {base_filename}: {e}") - - # Create output folder if it doesn't exist - Path(output_folder).mkdir(parents=True, exist_ok=True) - for throughput_path in throughput_paths: - os.remove(throughput_path) - -if __name__ == "__main__": - input_folder:str = "test_svg/" # Input folder containing .svg files - output_folder:str = "test_ico/" # Output folder for converted .ico files (sizes 16, 32, 48, 64, 256) - - convert_svg_to_ico(input_folder, output_folder) \ No newline at end of file From c152e24887e96850cd1791881a5fd7c067f96cf2 Mon Sep 17 00:00:00 2001 From: Cyklon_3000 <67823272+Cyklon3000@users.noreply.github.com> Date: Fri, 23 Aug 2024 23:09:01 +0200 Subject: [PATCH 3/4] feat: add python script for batch svg to .ico conversion --- batch_convert_to_ico.py | 107 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 batch_convert_to_ico.py diff --git a/batch_convert_to_ico.py b/batch_convert_to_ico.py new file mode 100644 index 00000000..2a05b6b2 --- /dev/null +++ b/batch_convert_to_ico.py @@ -0,0 +1,107 @@ +import os +from typing import Tuple, List, Dict +import subprocess +from pathlib import Path + +def convert_svg_to_ico(input_folder:str, output_folder:str, sizes:Tuple[int, ...]=(16,32,48,64,256)): + """ + Converts a folder of .svg icons to a folder of .ico icons of various sizes. + Icons can be swapped based on a maximum size attributed to .svg icons, if their name ends with '-{size}px.svg'. + This function requires Imagemagick to be installed. + + Args: + input_folder (str): The path to the folder containing the .svg icons. + output_folder (str): The path to the folder where the .ico icons will be saved. + sizes (Tuple[int, ...], optional): The sizes of the .ico icons. Defaults to (16, 32, 48, 64, 256). + """ + + def ends_with_px(string:str) -> bool: + if not string.endswith('px'): return False + parts:Tuple[str, ...] = string.rsplit('-', 1) + if len(parts) != 2: return False + return parts[1][:-2].isdigit() + + sizes = sorted(sizes) + + base_filenames:List[str] = [filename for filename in os.listdir(input_folder) if filename.lower().endswith('.svg') + and not any([ends_with_px(filename[:-4].lower()) for s in sizes])] + alt_filenames:List[str] = [filename for filename in os.listdir(input_folder) if filename.lower().endswith('.svg') + and any([ends_with_px(filename[:-4].lower()) for s in sizes[:-1]])] + + # Iterate through all base .svg files in the input folder + for base_filename in base_filenames: + # print(base_filename) + inputs:List[Dict[str, int | str]] = [] + + for size in sizes: + assumed_filename:str = base_filename[:-4] + f'-{size}px.svg' + if assumed_filename not in alt_filenames: continue + + alt_input_path:str = os.path.join(input_folder, assumed_filename) + + inputs.append({'path': alt_input_path, 'maximum_size': size}) + + # Add version that comes for sizes above alt max sizes + inputs.append({'path': os.path.join(input_folder, base_filename), 'maximum_size': sizes[-1]}) + + # Step 1: Convert input.svg's to throughput.png's using Imagemagick + throughput_paths:List[str] = [os.path.join(output_folder, f'{base_filename[:-4]}-{size_index}.png') for size_index in range(len(sizes))] + size_index:int = 0 + # print(inputs) + input:Dict[str, int | str] = inputs.pop(0) + for size_index in range(len(sizes)): + # Go to next input if the current needed size is greater than input's maximum + if sizes[size_index] > input['maximum_size']: + input:Dict[str, int | str] = inputs.pop(0) + # print(input) + current_size:int = sizes[size_index] + throughput_path:str = throughput_paths[size_index] + try: + # magick convert -background transparent -resize x + subprocess.run([ + 'magick', + 'convert', + '-background', 'transparent', + input['path'], + '-resize', f'{current_size}x{current_size}', + throughput_path + ], check=True) + # print(f"Converted {base_filename} to {throughput_path}") + except subprocess.CalledProcessError as e: + print(f"SVG2PNG: Error converting {base_filename}: {e}") + size_index += 1 + + # Step 2: Combine throughput.png's to final output.ico using Imagemagick + output_filename:str = os.path.splitext(base_filename)[0] + '.ico' + output_path:str = os.path.join(output_folder, output_filename) + try: + # magick convert input-1.png input-2.png ... input-n.png output.ico + subprocess.run([ + 'magick', + 'convert', + '-background', 'transparent',] + + throughput_paths + + [output_path], + check=True) + print(f"Converted {base_filename} to {output_filename}") + except subprocess.CalledProcessError as e: + print(f"PNG2ICO: Error converting {base_filename}: {e}") + + # Create output folder if it doesn't exist + Path(output_folder).mkdir(parents=True, exist_ok=True) + for throughput_path in throughput_paths: + os.remove(throughput_path) + +if __name__ == "__main__": + # Input folder containing .svg files + input_folder:str = input("Input folder (leave blank for default): ") + # Output folder for converted .ico files + output_folder:str = input("Output folder (leave blank for default): ") + # List of icon sizes + sizes:List[int] = [s for s in map(int, input("Icon sizes (leave blank for default): ").split()) if s > 0] + + input_folder = "svg/" if not input_folder else input_folder + output_folder = "ico/" if not output_folder else output_folder + sizes = [16, 32, 48, 64, 256] if not sizes else sizes + + convert_svg_to_ico(input_folder, output_folder) \ No newline at end of file From 5e116ab52668a881e328477914370f2d05ef7d77 Mon Sep 17 00:00:00 2001 From: Cyklon_3000 <67823272+Cyklon3000@users.noreply.github.com> Date: Fri, 23 Aug 2024 23:52:57 +0200 Subject: [PATCH 4/4] docs: updated readme with relevant formatting information --- README.md | 2 ++ batch_convert_to_ico.py | 17 ++++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index dac03353..9d0c7de5 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ We provided svg, ai, and fig template to make it easier. The specifications incl - Name of the icon must be written in snake case. - Because of we should not replace existing icon, new icon must be numbered that starts with 1. - First icon must be not numbered. +- If an icon variant for an existing icon is created it's maximum size in pixel - seperated with a dash, after potential numbering - is appended. The size must be followed by the unit shorthand 'px'. ``` Folder11 @@ -56,6 +57,7 @@ Folder11 ├──📄 my_folder.svg ├──📄 my_folder-1.svg └──📄 my_folder-2.svg + └──📄 my_folder-2-32px.svg ``` ### Commit Naming diff --git a/batch_convert_to_ico.py b/batch_convert_to_ico.py index 2a05b6b2..5b0df76d 100644 --- a/batch_convert_to_ico.py +++ b/batch_convert_to_ico.py @@ -21,6 +21,14 @@ def ends_with_px(string:str) -> bool: if len(parts) != 2: return False return parts[1][:-2].isdigit() + def delete_folder(folder_path): + folder = Path(folder_path) + if folder.exists() and folder.is_dir(): + for item in folder.iterdir(): + if item.is_file(): + item.unlink() + folder.rmdir() + sizes = sorted(sizes) base_filenames:List[str] = [filename for filename in os.listdir(input_folder) if filename.lower().endswith('.svg') @@ -45,7 +53,8 @@ def ends_with_px(string:str) -> bool: inputs.append({'path': os.path.join(input_folder, base_filename), 'maximum_size': sizes[-1]}) # Step 1: Convert input.svg's to throughput.png's using Imagemagick - throughput_paths:List[str] = [os.path.join(output_folder, f'{base_filename[:-4]}-{size_index}.png') for size_index in range(len(sizes))] + Path("temp_pngs").mkdir(parents=True, exist_ok=True) + throughput_paths:List[str] = [os.path.join("temp_pngs", f'{base_filename[:-4]}-{size_index}.png') for size_index in range(len(sizes))] size_index:int = 0 # print(inputs) input:Dict[str, int | str] = inputs.pop(0) @@ -72,6 +81,7 @@ def ends_with_px(string:str) -> bool: size_index += 1 # Step 2: Combine throughput.png's to final output.ico using Imagemagick + Path(output_folder).mkdir(parents=True, exist_ok=True) output_filename:str = os.path.splitext(base_filename)[0] + '.ico' output_path:str = os.path.join(output_folder, output_filename) try: @@ -87,10 +97,7 @@ def ends_with_px(string:str) -> bool: except subprocess.CalledProcessError as e: print(f"PNG2ICO: Error converting {base_filename}: {e}") - # Create output folder if it doesn't exist - Path(output_folder).mkdir(parents=True, exist_ok=True) - for throughput_path in throughput_paths: - os.remove(throughput_path) + delete_folder("temp_pngs") if __name__ == "__main__": # Input folder containing .svg files