From c202acc82311d5ad8111657384b536f34e39258fa694b856eaae469d450c1e51 Mon Sep 17 00:00:00 2001 From: 51m0n Date: Mon, 30 Mar 2026 00:26:18 +0000 Subject: [PATCH] initial --- .devcontainer/devcontainer.json | 26 +++ .gitignore | 4 + .vscode/launch.json | 16 ++ 3T_logo_master.svg | 327 ++++++++++++++++++++++++++++++++ Dockerfile | 17 ++ README.md | 46 +++++ Tienne-Regular.ttf | Bin 0 -> 72176 bytes configs/3t_angry.json | 7 + configs/3t_full.json | 8 + configs/3t_logo_green.json | 19 ++ configs/3t_logo_purple.json | 19 ++ configs/3t_mid.json | 11 ++ requirements.txt | 3 + svg_processor.py | 212 +++++++++++++++++++++ 14 files changed, 715 insertions(+) create mode 100755 .devcontainer/devcontainer.json create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 3T_logo_master.svg create mode 100755 Dockerfile create mode 100755 README.md create mode 100755 Tienne-Regular.ttf create mode 100644 configs/3t_angry.json create mode 100755 configs/3t_full.json create mode 100644 configs/3t_logo_green.json create mode 100644 configs/3t_logo_purple.json create mode 100755 configs/3t_mid.json create mode 100755 requirements.txt create mode 100755 svg_processor.py diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100755 index 0000000..da08b1f --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,26 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-dockerfile +{ + "name": "Existing Dockerfile", + "build": { + // Sets the run context to one level up instead of the .devcontainer folder. + "context": "..", + // Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename. + "dockerfile": "../Dockerfile" + } + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Uncomment the next line to run commands after the container is created. + // "postCreateCommand": "cat /etc/os-release", + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "devcontainer" +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..977acd3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.jpg +*.png +*_optimized.svg +*.pyc diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..26dc9a7 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python Debugger: Current File with Arguments", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "args": ["3T_logo_master.svg", "configs/3t_full.json", "dbgout"] + } + ] +} \ No newline at end of file diff --git a/3T_logo_master.svg b/3T_logo_master.svg new file mode 100644 index 0000000..c743b10 --- /dev/null +++ b/3T_logo_master.svg @@ -0,0 +1,327 @@ + + + + + + + 3t.network logo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 3t + + .network + + + + 3t.network logo + + + + diff --git a/Dockerfile b/Dockerfile new file mode 100755 index 0000000..7228dd3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM python + +# Set working directory +WORKDIR /app + +COPY requirements.txt /app/ + +# Install Python packages +RUN pip install --no-cache-dir -r requirements.txt + +# Copy the script +COPY svg_processor.py /app/ + +# TODO, actually make this do the generation + +# Default command (can be overridden) +CMD ["python", "svg_processor.py"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100755 index 0000000..221b02d --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# SVG Processor + +This Python script processes an SVG file by removing specified elements, embedding fonts, and exporting to multiple formats. + +## Features + +- Remove SVG elements based on IDs specified in a JSON file +- Embed fonts from external files into the SVG as base64 +- Optimize the SVG using scour +- Export to optimized SVG, PNG, JPEG, and thumbnail JPEG + +## Requirements + +- Python 3.9+ +- Libraries: lxml, cairosvg, scour + +## Docker Usage + +Build the Docker image: + +```bash +docker build -t svg-processor . +``` + +Run the container: + +```bash +docker run -v /path/to/input:/input -v /path/to/output:/output svg-processor python svg_processor.py /input/input.svg /input/config.json /output +``` + +Replace `/path/to/input` and `/path/to/output` with actual paths. + +## JSON Configuration Format + +See `sample_config.json` for an example. + +- `remove`: Array of element IDs to remove +- `fonts`: Object mapping font-family names to font file paths +- `size`: Override the size - useful when the size of the new image cannot be automatically calulated + +## Output + +- `optimized.svg`: Optimized SVG with embedded fonts +- `output.png`: PNG version +- `output.jpg`: JPEG version +- `thumbnail.jpg`: Thumbnail JPEG (128x128 max) \ No newline at end of file diff --git a/Tienne-Regular.ttf b/Tienne-Regular.ttf new file mode 100755 index 0000000000000000000000000000000000000000000000000000000000000000..a125d19d48432d06bac9afe79056d2fed65b7c8fdc33888fb58917e08d7cb7d2 GIT binary patch literal 72176 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 literal 0 HcmV?d00001 diff --git a/configs/3t_angry.json b/configs/3t_angry.json new file mode 100644 index 0000000..94f2e84 --- /dev/null +++ b/configs/3t_angry.json @@ -0,0 +1,7 @@ +{ + "file_name":"3t_full_angry", + "remove": [], + "fonts": { + "tienne": "Tienne-Regular.ttf" + } +} \ No newline at end of file diff --git a/configs/3t_full.json b/configs/3t_full.json new file mode 100755 index 0000000..72476c6 --- /dev/null +++ b/configs/3t_full.json @@ -0,0 +1,8 @@ +{ + "file_name":"3t_full", + "remove": [ + "Angry eye brows"], + "fonts": { + "tienne": "Tienne-Regular.ttf" + } +} \ No newline at end of file diff --git a/configs/3t_logo_green.json b/configs/3t_logo_green.json new file mode 100644 index 0000000..58ec703 --- /dev/null +++ b/configs/3t_logo_green.json @@ -0,0 +1,19 @@ +{ + "file_name": "3t_logo_green", + "remove": [ + "Angry eye brows", + ".network", + "background", + "3t", + "background_purple" + ], + "fonts": { + "tienne": "Tienne-Regular.ttf" + }, + "size":{ + "vx":"165", + "vy":"88", + "x":"1936.4955", + "y":"1842.5645" + } +} \ No newline at end of file diff --git a/configs/3t_logo_purple.json b/configs/3t_logo_purple.json new file mode 100644 index 0000000..b8c1624 --- /dev/null +++ b/configs/3t_logo_purple.json @@ -0,0 +1,19 @@ +{ + "file_name": "3t_logo_purple", + "remove": [ + "Angry eye brows", + ".network", + "background", + "3t", + "background_green" + ], + "fonts": { + "tienne": "Tienne-Regular.ttf" + }, + "size":{ + "vx":"165", + "vy":"88", + "x":"1936.4955", + "y":"1842.5645" + } +} \ No newline at end of file diff --git a/configs/3t_mid.json b/configs/3t_mid.json new file mode 100755 index 0000000..3df371a --- /dev/null +++ b/configs/3t_mid.json @@ -0,0 +1,11 @@ +{ + "file_name": "3t_mid", + "remove": [ + "Angry eye brows", + ".network", + "full_background" + ], + "fonts": { + "tienne": "Tienne-Regular.ttf" + } +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100755 index 0000000..73adf15 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +lxml +cairosvg +scour \ No newline at end of file diff --git a/svg_processor.py b/svg_processor.py new file mode 100755 index 0000000..327e13c --- /dev/null +++ b/svg_processor.py @@ -0,0 +1,212 @@ +import argparse +import json +import base64 +import os +from pathlib import Path +import re +import shutil +from lxml import etree +import cairosvg +import scour.scour +from PIL import Image + +def convert_to_pixels(value, dpi=96): + """Convert SVG length values to pixels""" + if value is None: + return 0 + + value = str(value).strip() + + # Handle different units + match = re.match(r'^([\d.]+)(in|cm|mm|pt|px)?$', value) + if not match: + return float(value) if value else 0 + + num, unit = match.groups() + num = float(num) + + if unit == 'in': + return num * dpi + elif unit == 'cm': + return num * dpi / 2.54 + elif unit == 'mm': + return num * dpi / 25.4 + elif unit == 'pt': + return num * dpi / 72 + else: # px or no unit + return num + +def get_bounding_box(svg_tree): + """Calculate bounds of all visible elements""" + max_x = float('-inf') + max_y = float('-inf') + + for element in svg_tree.xpath("//*"): + # Skip non-visual elements + tag_name = etree.QName(element).localname + if tag_name in ('defs', 'style', 'metadata', "svg", "page"): + continue + + # Get position/size attributes - simplified approach + x = float(element.get('x', 0)) + y = float(element.get('y', 0)) + # need to detect transform in parent + transformx = 0 + transformy = 0 + parent = element.getparent() + transform_parent = parent.get('transform', "") + + if transform_parent and "translate" in transform_parent: + t = transform_parent.removeprefix("translate").replace("(","").replace(")","") + transformx_str, transformy_str = transform_parent.removeprefix("translate").replace("(","").replace(")","").split(",") + transformx = float(transformx_str) + transformy = float(transformy_str) + + transform_element = element.get('transform', "") + if transform_element and "translate" in transform_element: + transformx_str, transformy_str = transform_element.removeprefix("translate").replace("(","").replace(")","").split(",") + transformx = transformx + float(transformx_str) + transformy = transformy + float(transformy_str) + + width = float(convert_to_pixels(element.get('width', 0))) + height = float(convert_to_pixels(element.get('height', 0))) + + x = x + transformx + y = y + transformy + + + max_x = max(max_x, x + width) + max_y = max(max_y, y + height) + + if max_x == float('inf'): # no elements found + return 100, 100 + + return max_x , max_y + +def update_size(svg_tree, size_dict): + max_x = 0 + max_y = 0 + vx = 0 + vy = 0 + if size_dict: + max_x = size_dict.get("x",0) + max_y = size_dict.get("y",0) + vx = size_dict.get("vx",0) + vy = size_dict.get("vy",0) + + if max_x ==0 or max_y == 0: + max_x , max_y = get_bounding_box(svg_tree) + + svg_root = svg_tree.getroot() + svg_root.set('viewBox', f"{vx} {vy} {max_x} {max_y}") + svg_root.set('width', f"{max_x}") + svg_root.set('height', f"{max_y}") + + +def remove_elements(svg_tree, remove_ids): + """Remove elements with specified IDs from the SVG tree.""" + namespaces = { + 'inkscape': 'http://www.inkscape.org/namespaces/inkscape' + } + for element_id in remove_ids: + element = svg_tree.find(f".//*[@id='{element_id}']") + if element is not None: + element.getparent().remove(element) + + element = svg_tree.find(f".//*[@inkscape:label='{element_id}']", namespaces=namespaces) + if element is not None: + element.getparent().remove(element) + + +def embed_fonts(svg_tree, fonts_dict): + """Embed fonts specified in fonts_dict into the SVG.""" + defs = svg_tree.find(".//{http://www.w3.org/2000/svg}defs") + if defs is None: + defs = etree.SubElement(svg_tree.getroot(), "{http://www.w3.org/2000/svg}defs") + + style = etree.SubElement(defs, "{http://www.w3.org/2000/svg}style") + style.text = "" + + for font_family, font_path in fonts_dict.items(): + if os.path.exists(font_path): + with open(font_path, 'rb') as f: + font_data = base64.b64encode(f.read()).decode('utf-8') + mime_type = "font/ttf" # Assume TTF, adjust if needed + data_url = f"data:{mime_type};base64,{font_data}" + style.text += f"@font-face {{ font-family: '{font_family}'; src: url('{data_url}'); }}\n" + + if not os.path.exists(f"/usr/local/share/fonts/{Path(font_path).name}"): + shutil.copy2(font_path, "/usr/local/share/fonts",) + +def optimize_svg(svg_tree): + """Optimize the SVG using scour.""" + svg_string = etree.tostring(svg_tree, encoding='unicode') + options = scour.scour.sanitizeOptions() + #options.enable_viewboxing = True + options.enable_id_stripping = True + options.enable_comment_stripping = True + options.shorten_ids = True + options.disable_style_to_xml = True + options.indent_type = None + optimized_svg = scour.scour.scourString(svg_string, options) + return optimized_svg + +def main(): + parser = argparse.ArgumentParser(description="Process SVG file: remove elements, embed fonts, export optimized formats.") + parser.add_argument("input_svg", help="Path to input SVG file") + parser.add_argument("json_file", help="Path to JSON file with removal and font info") + parser.add_argument("output_dir", help="Output directory for generated files") + + args = parser.parse_args() + + # Load JSON + with open(args.json_file, 'r') as f: + config = json.load(f) + + remove_ids = config.get("remove", []) + fonts_dict = config.get("fonts", {}) + size_dict = config.get("size", {}) + + # Parse SVG + svg_tree = etree.parse(args.input_svg) + + # Remove elements + remove_elements(svg_tree, remove_ids) + + # Embed fonts + embed_fonts(svg_tree, fonts_dict) + + update_size(svg_tree, size_dict) + + # Optimize SVG + optimized_svg = optimize_svg(svg_tree) + + # Ensure output directory exists + os.makedirs(args.output_dir, exist_ok=True) + + prefix = "" + if config.get("file_name"): + prefix = f"{config.get('file_name')}_" + + # Write optimized SVG + svg_output = os.path.join(args.output_dir, f"{prefix}optimized.svg") + with open(svg_output, 'w') as f: + f.write(optimized_svg) + + # Convert to PNG + png_output = os.path.join(args.output_dir, f"{prefix}output.png") + cairosvg.svg2png(bytestring=optimized_svg.encode('utf-8'), write_to=png_output, unsafe=True) + + # Convert to JPEG + jpeg_output = os.path.join(args.output_dir, f"{prefix}output.jpg") + # Cairosvg doesn't directly support JPEG, so use the PNG to JPEG + img = Image.open(jpeg_output.replace('.jpg', '.png')) + img.convert("RGB").save(jpeg_output, 'JPEG') + + # Create thumbnail JPEG + thumbnail_output = os.path.join(args.output_dir, f"{prefix}thumbnail.jpg") + img.thumbnail((128, 128)) # Example size, adjust as needed + img.convert("RGB").save(thumbnail_output, 'JPEG') + +if __name__ == "__main__": + main() \ No newline at end of file