1
Fork 0
mirror of https://github.com/Steffo99/emblematic.git synced 2024-11-21 14:24:18 +00:00

Things work correctly now

This commit is contained in:
Steffo 2023-03-10 23:51:35 +01:00
parent c08acba846
commit c7b949cadb
Signed by: steffo
GPG key ID: 2A24051445686895
16 changed files with 216 additions and 94 deletions

2
.vscode/launch.json vendored
View file

@ -10,7 +10,7 @@
"request": "launch",
"module": "emblematic",
"justMyCode": true,
"args": ["--output-file=output.svg", "--background-file=/store/Documents/Workspaces/RYGhub/logos/current/bg.svg", "--foreground-file=/home/steffo/Pictures/Font Awesome/svgs/solid/apple-whole.svg"]
"args": ["basic", "--background=/store/Documents/Workspaces/RYGhub/logos/current/watermark.svg", "--icon=/home/steffo/Pictures/Font Awesome/svgs", "--fill=#feedb4", "--output-dir=/home/steffo/Workspaces/Steffo99/emblematic-1/output"]
}
]
}

View file

@ -1,12 +1,8 @@
# TODO: If you're building a library, remove this file!
FROM python:3-alpine AS system
# TODO: Add whatever dependency your image may require
RUN apk add --update build-base python3-dev py-pip musl-dev
RUN pip install "poetry"
FROM system AS workdir
# TODO: Use the name of your project
WORKDIR /usr/src/emblematic
FROM workdir AS dependencies
@ -20,9 +16,8 @@ RUN poetry install
FROM package AS entrypoint
ENV PYTHONUNBUFFERED=1
ENTRYPOINT ["poetry", "run", "python", "-m"]
# TODO: Set the name of your Python module
CMD ["emblematic"]
ENTRYPOINT ["poetry", "run", "python", "-m", "emblematic"]
CMD []
FROM entrypoint AS labels
# TODO: Set a Docker image title

View file

@ -2,11 +2,11 @@
Simple icon generator
\[ **[Documentation]** | **[Available on PyPI]** | [Repository] \]
\[ **[Documentation]** | **[PyPI]** | [Repository] \]
<!-- Add an image or some examples here, if available! -->
[Documentation]: https://emblematic.readthedocs.io/latest/
[Available on PyPI]: https://pypi.org/project/emblematic
[PyPI]: https://pypi.org/project/emblematic
[Repository]: https://github.com/Steffo99/emblematic/

View file

@ -1,8 +0,0 @@
version: "3.9"
services:
emblematic:
image: "ghcr.io/Steffo99/emblematic:latest"
restart: unless-stopped
env_file:
- "stack.env"

17
docs/source/api.rst Normal file
View file

@ -0,0 +1,17 @@
*******************************************************************************
API reference
*******************************************************************************
============
``.compose``
============
.. automodule:: emblematic.compose
==========
``.files``
==========
.. automodule:: emblematic.files

View file

@ -96,7 +96,6 @@ nitpicky = False
# Intersphinx URLs
intersphinx_mapping = {
"python": ("https://docs.python.org/3.10", None),
"poetry":
}
# Manpages URL
manpages_url = "https://man.archlinux.org/"

View file

@ -15,7 +15,8 @@ Table of contents
.. toctree::
installation
usage
api
============

View file

@ -40,7 +40,7 @@ Using pip
$ # On Fish
$ source .venv/bin/activate.fish
.. code-block:: wincon
.. code-block:: doscon
> ; On Windows
> .venv/Scripts/activate.ps1
@ -50,7 +50,7 @@ Using pip
To install |this| using :mod:`pip`:
#. Add |this| to your `requirements.txt` file:
#. Add |this| to your ``requirements.txt`` file:
.. code-block:: text
@ -75,6 +75,7 @@ To install |this| using :mod:`pipx`:
$ pipx install emblematic
===========
From source
===========
@ -133,7 +134,7 @@ To contribute to |this|, you need to setup the project's environment using :mod:
.. hint::
Setting ``virtualenvs.in-project`` to :data:`True` is recommended!
It is recommended to set ``virtualenvs.in-project`` to :data:`True`!
.. code-block:: console

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

55
docs/source/usage.rst Normal file
View file

@ -0,0 +1,55 @@
*******************************************************************************
Usage
*******************************************************************************
:mod:`emblematic` currently supports only a single mode of operation.
=======================
Generate a basic emblem
=======================
A *basic emblem* can be generated by running:
.. code-block:: console
$ emblematic basic --background="./bg.svg" --icon="./icon.svg" --fill="#feedb4" --output-dir="./output/"
It is composed by:
1. taking the SVG background image contained in the file given as the ``--background`` option, such as the following:
.. figure:: ryg6-bg.svg
:width: 150
:height: 150
2. overlaying a rescaled version of the SVG foreground icon contained in the file given as the ``--icon`` option, filled with the color given in the ``--fill`` option, such as the following:
.. figure:: fontawesome-ice-cream.svg
:width: 150
:height: 150
3. converting the resulting document to a 2000x2000 PNG file for better compatibility with applications (very few support correctly the ``preserveAspectRatio`` property):
.. figure:: ryg6-ice-cream.png
:width: 150
:height: 150
---------------------------------
Multiple emblems with one command
---------------------------------
Multiple emblem files can be generated at once.
* Pass the ``--icon`` parameter multiple times to generate emblems with the same settings but different icons:
.. code-block:: console
$ emblematic basic --background="./bg.svg" --icon="./icon1.svg" --icon="./icon2.svg" --icon="./icon3.svg" --fill="#feedb4" --output-dir="./output/"
* Pass a directory as the ``--icon`` parameter to render all contained files matched by the ``**/*.svg`` glob:
.. code-block:: console
$ emblematic basic --background="./bg.svg" --icon="./fontawesome/" --fill="#feedb4" --output-dir="./output/"

View file

@ -1,5 +1,8 @@
# If you are building a **library**, use this file to export objects!
from . import compose
from . import files
__all__ = (
# "",
"compose",
"files",
)

View file

@ -1,37 +1,84 @@
"""
Command-line interface for :mod:`emblematic`.
Implemented with :mod:`click`.
"""
import click
import bs4
import pathlib
import cairosvg
from .files import get_svgs
from .compose import compose_basic
@click.command()
@click.group()
def main():
pass
@main.command("basic")
@click.option(
"-o", "--output-file", "output_file",
help="File to output the icon to.",
required=True,
type=click.File(mode="w"),
)
@click.option(
"-b", "--background-file", "background_file",
help="File to use as the background of the icon.",
required=True,
"-b", "--background", "bg_file",
type=click.File(mode="r"),
required=True,
help="SVG file to be used as background.",
)
@click.option(
"-f", "--foreground-file", "foreground_file",
help="File to use as the foreground of the icon.",
"-i", "--icon", "icon_paths",
type=click.Path(exists=True),
required=True,
type=click.File(mode="r"),
multiple=True,
help="SVG files or directories of files to be used as foreground.",
)
def main(output_file, background_file, foreground_file):
background_soup = bs4.BeautifulSoup(background_file, "lxml-xml")
foreground_soup = bs4.BeautifulSoup(foreground_file, "lxml-xml")
@click.option(
"-f", "--fill", "icon_fill",
type=str,
required=True,
help="The color to fill icons with."
)
@click.option(
"-o", "--output-dir", "output_dir",
type=click.Path(exists=True, file_okay=False, dir_okay=True),
required=True,
help="The directory where output files should be placed in."
)
def basic(bg_file, icon_paths, icon_fill, output_dir):
icon_paths = map(pathlib.Path, icon_paths)
icon_paths = map(get_svgs, icon_paths)
icon_paths = sum(icon_paths, start=[])
foreground_tag = foreground_soup.path
foreground_scaled_tag = bs4.BeautifulSoup("""<g id="icon" transform="matrix(2,0,0,2,-4.3209502e-5,512)"></g>""", "lxml-xml")
foreground_scaled_tag.g.append(foreground_tag)
output_dir = pathlib.Path(output_dir)
background_soup.svg.append(foreground_scaled_tag)
bg_doc = bs4.BeautifulSoup(bg_file, features="lxml-xml")
bg = bg_doc.svg
for icon_path in icon_paths:
icon_path: pathlib.Path
output_path = output_dir.joinpath(f"{icon_path.stem}.png")
with open(icon_path) as icon_file:
click.echo(icon_path, nl=False)
icon_doc = bs4.BeautifulSoup(icon_file, features="lxml-xml")
icon = icon_doc.svg
icon.path.attrs["fill"] = icon_fill
click.echo("", nl=False)
svg_doc = compose_basic(background=bg, icon=icon)
click.echo("", nl=False)
svg_bytes = bytes(svg_doc.prettify(), encoding="utf8")
click.echo("", nl=False)
png_bytes = cairosvg.svg2png(bytestring=svg_bytes)
with open(output_path, mode="wb") as output_file:
click.echo(output_path, nl=False)
output_file.write(png_bytes)
click.echo()
output_file.write(background_soup.prettify())
if __name__ == "__main__":

View file

@ -1,36 +1,43 @@
"""
Module containing the functions used to generate ``<svg>`` icons from other ``<svg>`` :class:`bs4.Tag`.
"""
import bs4
def compose_basic(bg: bs4.Tag, fg: bs4.Tag) -> bs4.Tag:
def compose_basic(background: bs4.Tag, icon: bs4.Tag) -> bs4.Tag:
"""
Create a nice icon from the given background ``<svg>`` and the given foreground ``<svg>``.
Create a new and nice ``<svg>`` icon from the given background ``<svg>`` and the given foreground ``<svg>``.
"""
if bg.name != "svg":
if background.name != "svg":
raise ValueError("bg is not a <svg> tag.")
if fg.name != "svg":
if icon.name != "svg":
raise ValueError("fg is not a <svg> tag.")
bg = bg.__copy__()
bg.attrs["id"] = "emblematic-bg"
bg.attrs["width"] = "100%"
bg.attrs["height"] = "100%"
background = background.__copy__()
background.attrs["id"] = "emblematic-background"
background.attrs["width"] = "100%"
background.attrs["height"] = "100%"
fg = fg.__copy__()
fg.attrs["id"] = "emblematic-fg"
fg.attrs["width"] = "63%"
fg.attrs["height"] = "63%"
fg.attrs["preserveAspectRation"] = "xMidYMid meet"
fg.attrs["transform"] = "translate(185, 185)"
icon = icon.__copy__()
icon.attrs["id"] = "emblematic-icon"
icon.attrs["width"] = "63%"
icon.attrs["height"] = "63%"
icon.attrs["preserveAspectRatio"] = "xMidYMid meet"
icon.attrs["transform"] = "translate(370, 370)"
doc = bs4.BeautifulSoup("""
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2000 2000">
</svg>
""")
""", features="lxml-xml")
container: bs4.Tag = doc.svg
container.append(bg)
container.append(fg)
container.append(background)
container.append(icon)
return doc
__all__ = (
"compose_basic",
)

22
emblematic/files.py Normal file
View file

@ -0,0 +1,22 @@
"""
Module containing functions to operate on files or directories.
"""
import pathlib
def get_svgs(path: pathlib.Path):
"""
Find all SVGs in the given path.
"""
if not path.exists():
raise ValueError("Given path does not exist.")
if not path.is_dir():
return [path]
return list(path.glob("**/*.svg"))
__all__ = (
"get_svgs",
)

View file

@ -1,22 +0,0 @@
"""
Module containing function to process `Font Awesome <https://fontawesome.com/>`_ SVG icons.
"""
import typing as t
import bs4
import pathlib
def get_icons(path: pathlib.Path):
if not path.exists():
raise ValueError("Given path does not exist.")
if not path.is_dir():
raise ValueError("Given path is not a directory.")
svgs = path.joinpath("svgs")
return svgs.glob("**/*.svg")
__all__ = (
"get_icons",
)

View file

@ -57,19 +57,24 @@ documentation = "https://emblematic.readthedocs.io/latest/"
# Up to five keywords related to your project.
# See also: https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/#keywords
#keywords = [
# "",
# "",
# "",
# "",
# "",
#]
keywords = [
"icon",
"avatar",
"emblem",
"logo",
"symbol",
]
# Any number of trove classifiers that apply to your project.
# See the list at: https://pypi.org/classifiers/
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Development Status :: 3 - Alpha",
"Environment :: Console",
"License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
"Topic :: Multimedia :: Graphics",
"Typing :: Typed",
]
# ADVANCED: specify the packages exported by your project