Skip to content

Commit

Permalink
feat: allow custom .svg imports
Browse files Browse the repository at this point in the history
- feat: internal function for standardising svg data (hopefully preventing 8ec4bbc from ever happening again)
- docs: start work on migrating to more general terminology (svg as opposed to pride flag)
  • Loading branch information
phthallo committed Oct 28, 2024
1 parent adf24e9 commit 803633e
Show file tree
Hide file tree
Showing 11 changed files with 251 additions and 34 deletions.
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
graft src/pyflagoras/flags
include src/pyflagoras/flag_aliases.json
graft src/pyflagoras/data
30 changes: 18 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
# pyflagoras
🏳️‍🌈 A Python command line interface tool for generating pride flags from images. The program accepts any image file and outputs the flag as a `.png` file.
🏳️‍🌈 A Python command line interface tool for theming `.svg` files using user-provided images. The program accepts any image file, an `.svg` file and outputs the flag as a `.png` file.

https://github.com/phthallo/pyflagoras/assets/84078890/8927779e-5e1c-411e-9d14-6bf1bdb8396d

(To see more examples of flags generated using this program, visit [examples.md](/examples.md)!)

## How does it work?
Pyflagoras, at a minimum, needs the name of a pride flag and a path to an image to work.
Pyflagoras, at a minimum, needs the name of a pride flag, or a path to an `.svg`, and a path to an image to work.

It uses [Pillow](https://pillow.readthedocs.io/en/stable/) to generate a list of colours in the image.

The program then obtains the flag's `.svg` file from the alias used, and extracts the all the colours used in the flag. It uses an [algorithm](https://github.com/phthallo/pyflagoras/blob/main/src/pyflagoras/similarity_algorithms.py#L2) to calculate which colours are the most similar based on their RGB codes.

Finally, the colours are swapped out. The `.svg` is converted into a `.pdf` using [svglib](https://github.com/deeplook/svglib) then into a `.png` using [PyMuPDF](https://github.com/pymupdf/PyMuPDF).

tl;dr - if you ever wanted the Slack logo to have the same colours as a cool photo you took you absolutely can.

## Installation
You can install this package from [pypi.org](https://pypi.org)! Open a terminal and run the following:
```
Expand All @@ -23,9 +25,9 @@ pip install pyflagoras
## Usage
```
$ pyflagoras --help
usage: pyflagoras [-h] [-f FLAG] [-n NAME] [--verbose] [--svg] [--version] [-l] image
usage: pyflagoras [-h] [-f FLAG] [-n NAME] [--verbose] [--svg] [--version] [-l] image
A command line interface tool for generating pride flags from images.
A command line interface tool for theming .svg files using user-provided images.
positional arguments:
image Path to the image to generate a flag from.
Expand All @@ -35,22 +37,22 @@ positional arguments:
options:
-h, --help show this help message and exit
-f FLAG, --flag FLAG The alias of the flag to generate.
-f FLAG, --flag FLAG The alias of the flag to generate OR path to a custom .svg file.
Examples:
intersexinclusive
nonbinary
/foo/bar/file.svg
Default:
progresspride
-n NAME, --name NAME Customise the name of the final .png. The following can be used as part of the file name:
-n NAME, --name NAME Customise the name of the final .png. The following can be used as part of the file name:
Format placeholders:
{n}: File name (e.g celeste_classic)
{N}: File name (full) (e.g celeste_classic.png)
{f}: Flag name (e.g Progress Pride)
{F}: Flag ID (e.g progressPride_2018)
{f}: Flag name (e.g Progress Pride) [N/A for custom .svg]
{F}: Flag ID (e.g progressPride_2018) [N/A for custom .svg]
Examples:
pyflagoras celeste_classic.png -n "{f}_{n}" [renders Progress Pride_celeste_classic.png]
pyflagoras celeste_classic.png -n "{f}_{n}" [renders Progress Pride_celeste_classic.png]
Default:
{n}_{F} [renders celeste_classic_progressPride_2018.png]
{n}_{F} [renders celeste_classic_progressPride_2018.png]
--verbose Enable verbosity (for general info and debugging)
--svg Generate the flag's .svg file in addition to the .png
Expand Down Expand Up @@ -80,9 +82,13 @@ Substitute `py` for `python3` as necessary.
```bash
py -m pip install dist/pyflagoras-x.x.x-py3-none-any.whl
```
5. To preview your changes without having to build, run the following to install Pyflagoras in editable mode.
```bash
py -m pip install -e .
```
## Contributions
The current pride flags have been sourced from @/JoeHart's [Pride Flag API](https://github.com/JoeHart/pride-flag-api).
The current 'pre-loaded' `.svg` files are pride flags! They have been sourced from @/JoeHart's [Pride Flag API](https://github.com/JoeHart/pride-flag-api).
To add a new flag, see [addingflags.md](/addingflags.md).
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"
pyflagoras = "pyflagoras:__main__.main"
[project]
name = "pyflagoras"
version = "0.3.4"
version = "0.4.4"
dependencies = [
"pillow>=10.3.0",
"pymupdf>=1.24.7",
Expand Down
4 changes: 2 additions & 2 deletions src/pyflagoras/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
import json
from importlib.resources import files

flag_aliases_list = "\n".join((json.loads(files("pyflagoras").joinpath("flag_aliases.json").read_text(encoding="utf-8"))).keys())
__version__ = "0.3.4"
flag_aliases_list = "\n".join((json.loads(files("pyflagoras.data").joinpath("flag_aliases.json").read_text(encoding="utf-8"))).keys())
__version__ = "0.4.4"
__list__ = flag_aliases_list
20 changes: 10 additions & 10 deletions src/pyflagoras/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

parser = argparse.ArgumentParser(
prog='pyflagoras',
description='A command line interface tool for generating pride flags from images.',
description='A command line interface tool for theming .svg files using user-provided images.',
epilog="Documentation, issues and more: https://github.com/phthallo/pyflagoras",
formatter_class=argparse.RawTextHelpFormatter)

Expand All @@ -23,10 +23,10 @@
'--flag',
default="progresspride",
help=
'''The alias of the flag to generate.
'''The alias of the flag to generate OR path to a custom .svg file.
Examples:
intersexinclusive
nonbinary
/foo/bar/file.svg
Default:
progresspride''',
type=str
Expand All @@ -41,8 +41,8 @@
Format placeholders:
{n}: File name (e.g celeste_classic)
{N}: File name (full) (e.g celeste_classic.png)
{f}: Flag name (e.g Progress Pride)
{F}: Flag ID (e.g progressPride_2018)
{f}: Flag name (e.g Progress Pride) [N/A for custom .svg]
{F}: Flag ID (e.g progressPride_2018) [N/A for custom .svg]
Examples:
pyflagoras celeste_classic.png -n "{f}_{n}" [renders Progress Pride_celeste_classic.png]
Default:
Expand Down Expand Up @@ -71,11 +71,11 @@
def main() -> None:
args = parser.parse_args()
pyflag = Pyflagoras(
image=args.image,
flag=args.flag,
name=args.name,
svg=args.svg,
verbose = args.verbose
image=args.image, # The image to source the resultant file's colours from.
flag=args.flag, # SVG to use as a 'template'
name=args.name, # The file name of the resultant file.
svg=args.svg, # Enable saving of the SVG (intemediary step) of the resultant file, or go straight to PNG.
verbose = args.verbose # Enable logging for debugging.
)
logging.basicConfig(level=args.verbose, format="🏳️‍🌈%(funcName)17s() %(message)s")

Expand Down
149 changes: 149 additions & 0 deletions src/pyflagoras/data/colour_aliases.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
{
"aliceblue": "#F0F8FF",
"antiquewhite": "#FAEBD7",
"aqua": "#00FFFF",
"aquamarine": "#7FFFD4",
"azure": "#F0FFFF",
"beige": "#F5F5DC",
"bisque": "#FFE4C4",
"black": "#000000",
"blanchedalmond": "#FFEBCD",
"blue": "#0000FF",
"blueviolet": "#8A2BE2",
"brown": "#A52A2A",
"burlywood": "#DEB887",
"cadetblue": "#5F9EA0",
"chartreuse": "#7FFF00",
"chocolate": "#D2691E",
"coral": "#FF7F50",
"cornflowerblue": "#6495ED",
"cornsilk": "#FFF8DC",
"crimson": "#DC143C",
"cyan": "#00FFFF",
"darkblue": "#00008B",
"darkcyan": "#008B8B",
"darkgoldenrod": "#B8860B",
"darkgray": "#A9A9A9",
"darkgreen": "#006400",
"darkgrey": "#A9A9A9",
"darkkhaki": "#BDB76B",
"darkmagenta": "#8B008B",
"darkolivegreen": "#556B2F",
"darkorange": "#FF8C00",
"darkorchid": "#9932CC",
"darkred": "#8B0000",
"darksalmon": "#E9967A",
"darkseagreen": "#8FBC8F",
"darkslateblue": "#483D8B",
"darkslategray": "#2F4F4F",
"darkslategrey": "#2F4F4F",
"darkturquoise": "#00CED1",
"darkviolet": "#9400D3",
"deeppink": "#FF1493",
"deepskyblue": "#00BFFF",
"dimgray": "#696969",
"dimgrey": "#696969",
"dodgerblue": "#1E90FF",
"firebrick": "#B22222",
"floralwhite": "#FFFAF0",
"forestgreen": "#228B22",
"fuchsia": "#FF00FF",
"gainsboro": "#DCDCDC",
"ghostwhite": "#F8F8FF",
"gold": "#FFD700",
"goldenrod": "#DAA520",
"gray": "#808080",
"grey": "#808080",
"green": "#008000",
"greenyellow": "#ADFF2F",
"honeydew": "#F0FFF0",
"hotpink": "#FF69B4",
"indianred": "#CD5C5C",
"indigo": "#4B0082",
"ivory": "#FFFFF0",
"khaki": "#F0E68C",
"lavender": "#E6E6FA",
"lavenderblush": "#FFF0F5",
"lawngreen": "#7CFC00",
"lemonchiffon": "#FFFACD",
"lightblue": "#ADD8E6",
"lightcoral": "#F08080",
"lightcyan": "#E0FFFF",
"lightgoldenrodyellow": "#FAFAD2",
"lightgray": "#D3D3D3",
"lightgreen": "#90EE90",
"lightgrey": "#D3D3D3",
"lightpink": "#FFB6C1",
"lightsalmon": "#FFA07A",
"lightseagreen": "#20B2AA",
"lightskyblue": "#87CEFA",
"lightslategray": "#778899",
"lightslategrey": "#778899",
"lightsteelblue": "#B0C4DE",
"lightyellow": "#FFFFE0",
"lime": "#00FF00",
"limegreen": "#32CD32",
"linen": "#FAEBD7",
"magenta": "#FF00FF",
"maroon": "#800000",
"mediumaquamarine": "#66CDAA",
"mediumblue": "#0000CD",
"mediumorchid": "#BA55D3",
"mediumpurple": "#9370DB",
"mediumseagreen": "#3CB371",
"mediumslateblue": "#7B68EE",
"mediumspringgreen": "#00FA9A",
"mediumturquoise": "#48D1CC",
"mediumvioletred": "#C71585",
"midnightblue": "#191970",
"mintcream": "#F5FFFA",
"mistyrose": "#FFE4E1",
"moccasin": "#FFE4B5",
"navajowhite": "#FFDEAD",
"navy": "#000080",
"oldlace": "#FDF5E6",
"olive": "#808000",
"olivedrab": "#6B8E23",
"orange": "#FFA500",
"orangered": "#FF4500",
"orchid": "#DA70D6",
"palegoldenrod": "#EEE8AA",
"palegreen": "#98FB98",
"paleturquoise": "#AFEEEE",
"palevioletred": "#DB7093",
"papayawhip": "#FFEFD5",
"peachpuff": "#FFDAB9",
"peru": "#CD853F",
"pink": "#FFC0CB",
"plum": "#DDA0DD",
"powderblue": "#B0E0E6",
"purple": "#800080",
"red": "#FF0000",
"rosybrown": "#BC8F8F",
"royalblue": "#4169E1",
"saddlebrown": "#8B4513",
"salmon": "#FA8072",
"sandybrown": "#F4A460",
"seagreen": "#2E8B57",
"seashell": "#FFF5EE",
"sienna": "#A0522D",
"silver": "#C0C0C0",
"skyblue": "#87CEEB",
"slateblue": "#6A5ACD",
"slategray": "#708090",
"slategrey": "#708090",
"snow": "#FFFAFA",
"springgreen": "#00FF7F",
"steelblue": "#4682B4",
"tan": "#D2B48C",
"teal": "#008080",
"thistle": "#D8BFD8",
"tomato": "#FF6347",
"turquoise": "#40E0D0",
"violet": "#EE82EE",
"wheat": "#F5DEB3",
"white": "#FFFFFF",
"whitesmoke": "#F5F5F5",
"yellow": "#FFFF00",
"yellowgreen": "#9ACD32"
}
File renamed without changes.
30 changes: 25 additions & 5 deletions src/pyflagoras/flag_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,35 @@
import os
import logging
from importlib.resources import files
from pathlib import Path
from .utils import svg_format

def flag_attr(flag_alias: str) -> str:
"""
Returns information about the flag and whether it was custom.
Arguments:
flag_alias (str): Either the path to the .svg file or the alias of an included pride flag.
Returns:
str: flag data in json
"""
flag_alias = flag_alias.lower()
all_flags = json.loads(files("pyflagoras").joinpath("flag_aliases.json").read_text(encoding="utf-8"))
if flag_alias not in all_flags:
raise Exception(f"The alias '{flag_alias}' is not associated with a flag.")
else:
all_flags = json.loads(files("pyflagoras.data").joinpath("flag_aliases.json").read_text(encoding="utf-8"))
if os.path.exists(flag_alias): # Check if custom .svg is being loaded.
flag_name = Path(flag_alias).stem # Use .svg file name as final name.
with open(flag_alias, "r", encoding="utf-8") as file:
data = svg_format(file.read()) # Run standardiser on custom svgs
location = json.dumps({
"name": flag_name, # Custom .svgs don't have this kind of metadata associated with them so... oops
"id": flag_name, # "
"svg": data
})
elif flag_alias in all_flags: # Check if existing .svg is being loaded.
flag_name = all_flags[flag_alias]
location = files("pyflagoras.flags").joinpath(flag_name+".json").read_text(encoding="utf-8")
location = files("pyflagoras.flags").joinpath(flag_name+".json").read_text(encoding="utf-8")
else:
raise Exception(f"The alias '{flag_alias}' is not associated with a flag.")
data = json.loads(location)
logging.info(f"Loading attributes of flag '{flag_name}'")
return data
Expand Down
1 change: 1 addition & 0 deletions src/pyflagoras/image_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
import numpy as np
import logging

def extract_colours(img: str, verbose: bool) -> list[tuple]:
"""
Returns the RGB codes of the colours in the ijmage
Expand Down
4 changes: 2 additions & 2 deletions src/pyflagoras/pyflagoras.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def assign_rgb(rgb_similarity: list[list[tuple[tuple]]]) -> dict:

def replace_colours(flag_svg: str, optimum_pairs: dict) -> str:
"""
Replaces the hex codes of the colours within the original flag's SVG with the most similar colour from the image.
Replaces the hex codes of the colours within the original flag's .svg with the most similar colour from the image.
Arguments:
flag_svg (str): The svg of the flag (from flags/.*.json)
Expand All @@ -80,7 +80,7 @@ def run(self):
image_colours = extract_colours(self.image, self.verbose)
flag_attributes = flag_attr(self.flag)
svg_colours = re.findall(r"#(?:[0-9a-fA-F]{3}){1,2}", flag_attributes["svg"]) # This should return a list of hex codes found in the svg data
logging.info(f"Finding all hex codes in SVG... {len(svg_colours)} found: {svg_colours}")
logging.info(f"Finding all hex codes in .svg: {len(svg_colours)} found: {svg_colours}")
format_r = [hex_rgb(i) for i in svg_colours] # convert each colour into its RGB tuple so similarity can be compared.
if self.verbose:
generate_similarity = Pyflagoras.parse_similarity(format_r, image_colours[0])
Expand Down
Loading

0 comments on commit 803633e

Please sign in to comment.