1
Fork 0
mirror of https://github.com/Steffo99/emblematic.git synced 2024-11-21 22:34:19 +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", "request": "launch",
"module": "emblematic", "module": "emblematic",
"justMyCode": true, "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 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 apk add --update build-base python3-dev py-pip musl-dev
RUN pip install "poetry" RUN pip install "poetry"
FROM system AS workdir FROM system AS workdir
# TODO: Use the name of your project
WORKDIR /usr/src/emblematic WORKDIR /usr/src/emblematic
FROM workdir AS dependencies FROM workdir AS dependencies
@ -20,9 +16,8 @@ RUN poetry install
FROM package AS entrypoint FROM package AS entrypoint
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
ENTRYPOINT ["poetry", "run", "python", "-m"] ENTRYPOINT ["poetry", "run", "python", "-m", "emblematic"]
# TODO: Set the name of your Python module CMD []
CMD ["emblematic"]
FROM entrypoint AS labels FROM entrypoint AS labels
# TODO: Set a Docker image title # TODO: Set a Docker image title

View file

@ -2,11 +2,11 @@
Simple icon generator Simple icon generator
\[ **[Documentation]** | **[Available on PyPI]** | [Repository] \] \[ **[Documentation]** | **[PyPI]** | [Repository] \]
<!-- Add an image or some examples here, if available! --> <!-- Add an image or some examples here, if available! -->
[Documentation]: https://emblematic.readthedocs.io/latest/ [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/ [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 URLs
intersphinx_mapping = { intersphinx_mapping = {
"python": ("https://docs.python.org/3.10", None), "python": ("https://docs.python.org/3.10", None),
"poetry":
} }
# Manpages URL # Manpages URL
manpages_url = "https://man.archlinux.org/" manpages_url = "https://man.archlinux.org/"

View file

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

View file

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

View file

@ -1,37 +1,84 @@
"""
Command-line interface for :mod:`emblematic`.
Implemented with :mod:`click`.
"""
import click import click
import bs4 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( @click.option(
"-o", "--output-file", "output_file", "-b", "--background", "bg_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,
type=click.File(mode="r"), type=click.File(mode="r"),
required=True,
help="SVG file to be used as background.",
) )
@click.option( @click.option(
"-f", "--foreground-file", "foreground_file", "-i", "--icon", "icon_paths",
help="File to use as the foreground of the icon.", type=click.Path(exists=True),
required=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): @click.option(
background_soup = bs4.BeautifulSoup(background_file, "lxml-xml") "-f", "--fill", "icon_fill",
foreground_soup = bs4.BeautifulSoup(foreground_file, "lxml-xml") 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 output_dir = pathlib.Path(output_dir)
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)
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__": 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 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.") raise ValueError("bg is not a <svg> tag.")
if fg.name != "svg": if icon.name != "svg":
raise ValueError("fg is not a <svg> tag.") raise ValueError("fg is not a <svg> tag.")
bg = bg.__copy__() background = background.__copy__()
bg.attrs["id"] = "emblematic-bg" background.attrs["id"] = "emblematic-background"
bg.attrs["width"] = "100%" background.attrs["width"] = "100%"
bg.attrs["height"] = "100%" background.attrs["height"] = "100%"
fg = fg.__copy__() icon = icon.__copy__()
fg.attrs["id"] = "emblematic-fg" icon.attrs["id"] = "emblematic-icon"
fg.attrs["width"] = "63%" icon.attrs["width"] = "63%"
fg.attrs["height"] = "63%" icon.attrs["height"] = "63%"
fg.attrs["preserveAspectRation"] = "xMidYMid meet" icon.attrs["preserveAspectRatio"] = "xMidYMid meet"
fg.attrs["transform"] = "translate(185, 185)" icon.attrs["transform"] = "translate(370, 370)"
doc = bs4.BeautifulSoup(""" doc = bs4.BeautifulSoup("""
<?xml version="1.0" encoding="UTF-8"?> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2000 2000">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000">
</svg> </svg>
""") """, features="lxml-xml")
container: bs4.Tag = doc.svg container: bs4.Tag = doc.svg
container.append(bg) container.append(background)
container.append(fg) container.append(icon)
return doc 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. # Up to five keywords related to your project.
# See also: https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/#keywords # 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. # Any number of trove classifiers that apply to your project.
# See the list at: https://pypi.org/classifiers/ # See the list at: https://pypi.org/classifiers/
classifiers = [ classifiers = [
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10", "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 # ADVANCED: specify the packages exported by your project