Compare commits

...

4 Commits

Author SHA1 Message Date
Torsten Ueberschar
a6fb080c4f add pyproject.toml 2024-09-17 10:38:50 +02:00
Torsten Ueberschar
8377066ab8 fix demo data 2024-09-17 10:38:40 +02:00
Torsten Ueberschar
c9c34493b8 take cwd as base directory 2024-09-17 10:10:33 +02:00
Torsten Ueberschar
660e75d719 paths are not all required 2024-09-16 11:34:42 +02:00
10 changed files with 507 additions and 157 deletions

View File

@@ -13,11 +13,17 @@ python -m venv .venv
source .venv/bin/activate
```
Build and install executable:
Build executable:
```bash
python setup.py build
python setup.py install
pip install --upgrade build
python -m build
```
Install the executable:
```bash
pip install .
```
See where it is installed:
@@ -27,6 +33,13 @@ which invoice
## usage
```bash
invoice -h
```
There are two yaml files describing your invoice:
- `invoice.yaml` contains the invoice data

29
pyproject.toml Normal file
View File

@@ -0,0 +1,29 @@
[project]
name = "simple-invoice-generator"
version = "0.2.0"
description = "Simple invoice generator for freelancers and small businesses"
readme = "README.md"
authors = [{name = "Torsten Ueberschar", email = "tu@uesome.de"}]
requires-python = ">=3.10"
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers, Freelancers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3.10",
]
dependencies = [
'PyYAML',
'Jinja2',
'xhtml2pdf',
'Markdown'
]
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
[project.scripts]
invoice = "main:main"

View File

@@ -1,36 +0,0 @@
from setuptools import setup, find_packages
setup(
name='simple-invoice-generator',
version='0.1.0',
description='Simple invoice generator for freelancers and small businesses',
long_description=open('README.md').read(),
long_description_content_type='text/markdown',
author='Torsten Ueberschar',
author_email='tu@uesome.de',
url='https://git.uesome.de/torsten/simple-invoice-generator',
packages=find_packages('src'),
package_dir={'': 'src'},
install_requires=[
'PyYAML',
'Jinja2',
'xhtml2pdf',
'Markdown',
],
extras_require={
'dev': ['pytest', 'coverage'],
# Extra dependencies for development and testing
},
classifiers=[
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers, Freelancers',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3.10',
# Add more classifiers as needed
],
entry_points={
'console_scripts': [
'invoice = main:main',
]
}
)

View File

@@ -63,7 +63,7 @@ class HtmlTemplate:
source_html, # the HTML to convert
path=str(self.path_to_template / 'fonts'),
dest=result_file
) # file handle to recieve result
) # file handle to receive result
# close output file
result_file.close() # close output file

View File

@@ -2,7 +2,7 @@ import argparse
import yaml
import locale
from pathlib import Path
from pathlib import Path, PurePath
from datetime import datetime
from invoice_generator import html_generator
@@ -47,15 +47,18 @@ def merge_envelope_data_into_invoice_data(invoice_data, source_data):
def main():
locale.setlocale(locale.LC_ALL, 'de_DE.utf8')
cwd = Path.cwd().resolve()
print('Simple invoice generator for freelancers and small businesses by Torsten Ueberschar')
print()
parser = argparse.ArgumentParser(description='Read invoice and envelope data from yaml file')
parser.add_argument('-b', '--base', type=str, required=True, help='base directory for invoice and envelope files')
parser.add_argument('-i', '--invoice', type=str, required=True, help='Invoice file name')
parser.add_argument('-b', '--base', type=str, required=False, default=cwd,
help='base directory for invoice and envelope files (default: (current working directory) %(default)s)')
parser.add_argument('-e', '--envelope', type=str, required=False, default='envelope.yaml',
help='Envelope file name')
parser.add_argument('-t', '--template', type=str, required=False, default='template',
help='directory for template files')
help='Envelope file name (default: %(default)s)')
parser.add_argument('-t', '--template', type=str, required=False, default='templates',
help='directory for template files (default: %(default)s)')
parser.add_argument('-i', '--invoice', type=str, required=True, help='Invoice file name')
args = parser.parse_args()
@@ -128,12 +131,20 @@ def main():
print(f'Error: {e}')
return
generator = html_generator.HtmlTemplate(args.template)
if not PurePath(args.template).is_absolute():
template_dir = (Path(args.base) / args.template).resolve()
else:
template_dir = Path(args.template).resolve()
generator = html_generator.HtmlTemplate(template_dir)
template = generator.prepare_template(invoice_data, envelope_data)
try:
print('Generating invoice...')
invoice_pdf = Path(invoice_data.Id).with_suffix('.pdf')
output_path = Path(args.base) / 'pdf'
output_path.mkdir(parents=True, exist_ok=True)
output_path = output_path / f'{invoice_data.Id}.pdf'
invoice_pdf = output_path.resolve()
print(f'Invoice PDF: {invoice_pdf}')
generator.convert_html_to_pdf(template, invoice_pdf)
except Exception as e:

View File

@@ -3,16 +3,16 @@ CustomerId: KD01234
#InvoiceDate: 2023-12-23
Positions:
- Title: "Zuckerwatte fressen Ganz besonders langer Text"
- Title: "Zuckerwatte essen"
SubTitle: "Leistungszeitraum: 11/2022"
PricePerUnit: 100
Quantity: 100
- Title: "Aschlecken"
- Title: "Sinnloses Zeug"
PricePerUnit: 99.99
Quantity: 12
- Title: "Aschkriechen"
- Title: "Irgend was anderes"
SubTitle: "Leistungszeitraum: 10/2022"
Quantity: 3

View File

@@ -1,48 +1,47 @@
AddressContent:
LogoFile: Images/logo.svg
AddressBoxSender: "Abs.: Torsten Ueberschar - Pfarrweg 1 - 57439 Attendorn"
AddressBoxSender: "Abs.: Klaus Kleinkariert, Musterstraße 1, 12345 Musterhausen"
Contents:
- Text: |
![logo](images/logo.svg)
Torsten Ueberschar
Pfarrweg 1
57439 Attendorn
![logo](images/logo.png)
Klaus Kleinkariert
Musterstraße 1
12345 Musterhausen
- Text: |
**Kontakt**
tu@uesome.de
+49 2734 4239271
klaus@klaus-superheld.de
+49 800 12345678
- Text: |
**Steuern**
USt-IdNr.: DE313460724
USt-IdNr.: DE123456789
- Text: |
**Bankverbindung**
Torsten Ueberschar
DE67 1001 1001 2626 8627 86
NTSBDEB1XXX
N26 Bank GmbH
Klaus Kleinkariert
DE12 1234 1234 1234 1234 12
ABCDTTCXXX
Geiz Bank GmbH
Invoice:
Vat: 19.0
Introduction: |
### Rechnung
*Sehr geehrte Damen und Herren*,
für folgende in Ihrem Auftrag ausgeführten Leistungen erlaube ich mir zu berechnen
Footer: |
Bitte überweisen Sie den Rechnungsbetrag unter Angabe der Rechnungsnummer auf mein Konto bis zum %%ZahlungsZiel%%.
Mit freundlichen Grüßen
*Torsten Uebeschar*
*Klaus Musterhausen*
Customers:
- CustomerId: KD01234
PricePerUnit: 88.88
DueDate: 30
PricePerUnit: 43.50
DueDate: 14
AddressField: |
Klaus Peter Klausen
Am Klausenhof 1
04711 Klausenhausen
Beispiel GmbH
Beispielstraße 12
12345 Musterhausen

417
test_data/pdf/RG004211.pdf Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@@ -1,83 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 2119 1061" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
<g transform="matrix(1,0,0,1,-169.939,-343.077)">
<g transform="matrix(2.16305,0,0,2.16305,-430.189,551.712)">
<path d="M461.264,6.409L461.264,147.034C461.264,156.409 465.951,161.097 475.326,161.097L503.451,161.097C512.826,161.097 517.514,156.409 517.514,147.034L517.514,6.409L559.701,6.409L559.701,147.034C559.701,184.534 540.951,203.284 503.451,203.284L475.326,203.284C437.826,203.284 419.076,184.534 419.076,147.034L419.076,6.409L461.264,6.409Z" style="fill:rgb(27,118,215);fill-rule:nonzero;"/>
</g>
<g transform="matrix(2.16305,0,0,2.16305,-462.013,551.712)">
<path d="M714.389,83.753L714.389,125.941L630.014,125.941L630.014,161.097L714.389,161.097L714.389,203.284L587.826,203.284L587.826,6.409L714.389,6.409L714.389,48.597L630.014,48.597L630.014,83.753L714.389,83.753Z" style="fill:rgb(27,118,215);fill-rule:nonzero;"/>
</g>
<g transform="matrix(2.16305,0,0,2.16305,-620.427,551.712)">
<path d="M742.514,203.284L742.514,161.097L826.889,161.097C836.264,161.097 840.951,156.409 840.951,147.034L840.951,140.003C840.951,130.628 836.264,125.941 826.889,125.941L798.764,125.941C761.264,125.941 742.514,107.191 742.514,69.691L742.514,62.659C742.514,25.159 761.264,6.409 798.764,6.409L883.139,6.409L883.139,48.597L798.764,48.597C789.389,48.597 784.701,53.284 784.701,62.659L784.701,69.691C784.701,79.066 789.389,83.753 798.764,83.753L826.889,83.753C864.389,83.753 883.139,102.503 883.139,140.003L883.139,147.034C883.139,184.534 864.389,203.284 826.889,203.284L742.514,203.284Z" style="fill:rgb(15,168,89);fill-rule:nonzero;"/>
<clipPath id="_clip1">
<path d="M742.514,203.284L742.514,161.097L826.889,161.097C836.264,161.097 840.951,156.409 840.951,147.034L840.951,140.003C840.951,130.628 836.264,125.941 826.889,125.941L798.764,125.941C761.264,125.941 742.514,107.191 742.514,69.691L742.514,62.659C742.514,25.159 761.264,6.409 798.764,6.409L883.139,6.409L883.139,48.597L798.764,48.597C789.389,48.597 784.701,53.284 784.701,62.659L784.701,69.691C784.701,79.066 789.389,83.753 798.764,83.753L826.889,83.753C864.389,83.753 883.139,102.503 883.139,140.003L883.139,147.034C883.139,184.534 864.389,203.284 826.889,203.284L742.514,203.284Z" clip-rule="nonzero"/>
</clipPath>
<g clip-path="url(#_clip1)">
<g transform="matrix(1,0,0,1,73.2367,0)">
<path d="M714.389,83.753L714.389,125.941L630.014,125.941L630.014,161.097L714.389,161.097L714.389,203.284L587.826,203.284L587.826,6.409L714.389,6.409L714.389,48.597L630.014,48.597L630.014,83.753L714.389,83.753Z" style="fill-rule:nonzero;"/>
</g>
</g>
</g>
<g transform="matrix(2.16305,0,0,2.16305,-814.758,551.712)">
<path d="M1087.05,6.409L1146.11,6.409L1192.51,125.941L1238.92,6.409L1297.98,6.409L1297.98,203.284L1255.8,203.284L1255.8,83.753L1210.8,203.284L1174.23,203.284L1129.23,83.753L1129.23,203.284L1087.05,203.284L1087.05,6.409Z" style="fill:rgb(202,4,4);fill-rule:nonzero;"/>
</g>
<g transform="matrix(2.16305,0,0,2.16305,-661.589,551.712)">
<path d="M1058.92,147.034C1058.92,184.534 1040.17,203.284 1002.67,203.284L967.514,203.284C930.014,203.284 911.264,184.534 911.264,147.034L911.264,62.659C911.264,25.159 930.014,6.409 967.514,6.409L1002.67,6.409C1040.17,6.409 1058.92,25.159 1058.92,62.659L1058.92,147.034ZM953.451,147.034C953.451,156.409 958.139,161.097 967.514,161.097L1002.67,161.097C1012.05,161.097 1016.73,156.409 1016.73,147.034L1016.73,62.659C1016.73,53.284 1012.05,48.597 1002.67,48.597L967.514,48.597C958.139,48.597 953.451,53.284 953.451,62.659L953.451,147.034Z" style="fill:rgb(15,168,89);fill-rule:nonzero;"/>
<clipPath id="_clip2">
<path d="M1058.92,147.034C1058.92,184.534 1040.17,203.284 1002.67,203.284L967.514,203.284C930.014,203.284 911.264,184.534 911.264,147.034L911.264,62.659C911.264,25.159 930.014,6.409 967.514,6.409L1002.67,6.409C1040.17,6.409 1058.92,25.159 1058.92,62.659L1058.92,147.034ZM953.451,147.034C953.451,156.409 958.139,161.097 967.514,161.097L1002.67,161.097C1012.05,161.097 1016.73,156.409 1016.73,147.034L1016.73,62.659C1016.73,53.284 1012.05,48.597 1002.67,48.597L967.514,48.597C958.139,48.597 953.451,53.284 953.451,62.659L953.451,147.034Z" clip-rule="nonzero"/>
</clipPath>
<g clip-path="url(#_clip2)">
<g transform="matrix(1,0,0,1,-70.8119,0)">
<path d="M1087.05,6.409L1146.11,6.409L1192.51,125.941L1238.92,6.409L1297.98,6.409L1297.98,203.284L1255.8,203.284L1255.8,83.753L1210.8,203.284L1174.23,203.284L1129.23,83.753L1129.23,203.284L1087.05,203.284L1087.05,6.409Z" style="fill-rule:nonzero;"/>
</g>
</g>
</g>
<g transform="matrix(2.16305,0,0,2.16305,-853.475,551.712)">
<path d="M1452.67,83.753L1452.67,125.941L1368.3,125.941L1368.3,161.097L1452.67,161.097L1452.67,203.284L1326.11,203.284L1326.11,6.409L1452.67,6.409L1452.67,48.597L1368.3,48.597L1368.3,83.753L1452.67,83.753Z" style="fill:rgb(202,4,4);fill-rule:nonzero;"/>
</g>
<g transform="matrix(6.69295,0,0,6.69295,-1739.71,-1575.25)">
<path d="M344.179,415.947C344.179,414.531 343.651,413.379 342.739,412.587C341.971,411.891 341.227,411.603 339.643,411.363L337.651,411.051C336.763,410.907 335.947,410.619 335.419,410.163C334.891,409.707 334.651,409.011 334.651,408.171C334.651,406.251 335.971,404.979 338.275,404.979C340.051,404.979 341.227,405.483 342.259,406.467L343.435,405.291C341.971,403.995 340.531,403.419 338.347,403.419C334.963,403.419 332.875,405.315 332.875,408.219C332.875,409.611 333.331,410.667 334.195,411.435C334.963,412.107 335.971,412.515 337.315,412.731L339.307,413.019C340.531,413.211 341.011,413.379 341.539,413.859C342.115,414.363 342.379,415.083 342.379,415.995C342.379,418.011 340.843,419.163 338.323,419.163C336.403,419.163 335.083,418.731 333.667,417.315L332.443,418.539C334.027,420.147 335.731,420.795 338.275,420.795C341.851,420.795 344.179,418.947 344.179,415.947Z" style="fill-rule:nonzero;"/>
<path d="M359.299,412.107C359.299,408.195 359.179,406.707 357.571,405.099C356.515,404.043 355.099,403.419 353.323,403.419C351.547,403.419 350.107,404.043 349.051,405.099C347.443,406.707 347.347,408.195 347.347,412.107C347.347,416.019 347.443,417.507 349.051,419.115C350.107,420.171 351.547,420.795 353.323,420.795C355.099,420.795 356.515,420.171 357.571,419.115C359.179,417.507 359.299,416.019 359.299,412.107ZM357.475,412.107C357.475,415.611 357.355,416.859 356.275,417.963C355.483,418.755 354.475,419.163 353.323,419.163C352.171,419.163 351.163,418.755 350.371,417.963C349.291,416.859 349.171,415.611 349.171,412.107C349.171,408.603 349.291,407.355 350.371,406.251C351.163,405.459 352.171,405.051 353.323,405.051C354.475,405.051 355.483,405.459 356.275,406.251C357.355,407.355 357.475,408.603 357.475,412.107Z" style="fill-rule:nonzero;"/>
<path d="M374.203,405.195L374.203,403.563L363.595,403.563L363.595,420.651L365.419,420.651L365.419,413.115L372.907,413.115L372.907,411.483L365.419,411.483L365.419,405.195L374.203,405.195Z" style="fill-rule:nonzero;"/>
<path d="M387.475,405.195L387.475,403.563L375.763,403.563L375.763,405.195L380.707,405.195L380.707,420.651L382.531,420.651L382.531,405.195L387.475,405.195Z" style="fill-rule:nonzero;"/>
<path d="M408.307,403.563L406.363,403.563L403.027,417.339L399.259,403.563L397.627,403.563L393.859,417.339L390.523,403.563L388.579,403.563L392.947,420.651L394.651,420.651L398.443,406.971L402.235,420.651L403.939,420.651L408.307,403.563Z" style="fill-rule:nonzero;"/>
<path d="M422.827,420.651L416.563,403.563L415.027,403.563L408.739,420.651L410.683,420.651L412.051,416.787L419.515,416.787L420.883,420.651L422.827,420.651ZM418.987,415.203L412.603,415.203L415.819,406.179L418.987,415.203Z" style="fill-rule:nonzero;"/>
<path d="M437.539,420.651L433.483,412.899C435.691,412.443 437.203,410.811 437.203,408.315C437.203,405.363 435.115,403.563 432.139,403.563L425.587,403.563L425.587,420.651L427.411,420.651L427.411,413.067L431.515,413.067L435.403,420.651L437.539,420.651ZM435.379,408.339C435.379,410.427 433.963,411.459 431.971,411.459L427.411,411.459L427.411,405.195L431.971,405.195C433.963,405.195 435.379,406.251 435.379,408.339Z" style="fill-rule:nonzero;"/>
<path d="M451.939,420.651L451.939,419.019L443.155,419.019L443.155,412.851L450.643,412.851L450.643,411.219L443.155,411.219L443.155,405.195L451.939,405.195L451.939,403.563L441.331,403.563L441.331,420.651L451.939,420.651Z" style="fill-rule:nonzero;"/>
<path d="M473.395,415.011L473.395,403.563L471.571,403.563L471.571,414.867C471.571,417.459 469.915,419.163 467.419,419.163C464.923,419.163 463.291,417.459 463.291,414.867L463.291,403.563L461.467,403.563L461.467,415.011C461.467,418.419 463.963,420.795 467.419,420.795C470.875,420.795 473.395,418.419 473.395,415.011Z" style="fill-rule:nonzero;"/>
<path d="M490.723,420.651L490.723,403.563L488.899,403.563L488.899,417.219L479.827,403.563L478.099,403.563L478.099,420.651L479.923,420.651L479.923,406.947L488.995,420.651L490.723,420.651Z" style="fill-rule:nonzero;"/>
<path d="M507.523,412.107C507.523,409.491 507.523,406.995 506.011,405.363C504.859,404.115 503.443,403.563 501.523,403.563L495.667,403.563L495.667,420.651L501.523,420.651C503.443,420.651 504.859,420.099 506.011,418.851C507.523,417.219 507.523,414.723 507.523,412.107ZM505.699,412.107C505.699,414.483 505.675,416.595 504.571,417.771C503.707,418.731 502.507,419.019 501.211,419.019L497.491,419.019L497.491,405.195L501.211,405.195C502.507,405.195 503.707,405.483 504.571,406.443C505.675,407.619 505.699,409.731 505.699,412.107Z" style="fill-rule:nonzero;"/>
<path d="M532.435,420.651L532.435,403.563L530.611,403.563L525.187,415.683L519.619,403.563L517.795,403.563L517.795,420.651L519.619,420.651L519.619,407.667L524.371,417.963L525.931,417.963L530.611,407.667L530.611,420.651L532.435,420.651Z" style="fill-rule:nonzero;"/>
<path d="M547.987,420.651L547.987,419.019L539.203,419.019L539.203,412.851L546.691,412.851L546.691,411.219L539.203,411.219L539.203,405.195L547.987,405.195L547.987,403.563L537.379,403.563L537.379,420.651L547.987,420.651Z" style="fill-rule:nonzero;"/>
<path d="M563.635,412.107C563.635,409.491 563.635,406.995 562.123,405.363C560.971,404.115 559.555,403.563 557.635,403.563L551.779,403.563L551.779,420.651L557.635,420.651C559.555,420.651 560.971,420.099 562.123,418.851C563.635,417.219 563.635,414.723 563.635,412.107ZM561.811,412.107C561.811,414.483 561.787,416.595 560.683,417.771C559.819,418.731 558.619,419.019 557.323,419.019L553.603,419.019L553.603,405.195L557.323,405.195C558.619,405.195 559.819,405.483 560.683,406.443C561.787,407.619 561.811,409.731 561.811,412.107Z" style="fill-rule:nonzero;"/>
<rect x="567.931" y="403.563" width="1.824" height="17.088" style="fill-rule:nonzero;"/>
<path d="M585.307,420.651L585.307,419.019L576.523,419.019L576.523,412.851L584.011,412.851L584.011,411.219L576.523,411.219L576.523,405.195L585.307,405.195L585.307,403.563L574.699,403.563L574.699,420.651L585.307,420.651Z" style="fill-rule:nonzero;"/>
<path d="M601.723,420.651L601.723,403.563L599.899,403.563L599.899,417.219L590.827,403.563L589.099,403.563L589.099,420.651L590.923,420.651L590.923,406.947L599.995,420.651L601.723,420.651Z" style="fill-rule:nonzero;"/>
<path d="M345.571,439.251L345.571,427.803L343.747,427.803L343.747,439.107C343.747,441.699 342.091,443.403 339.595,443.403C337.099,443.403 335.467,441.699 335.467,439.107L335.467,427.803L333.643,427.803L333.643,439.251C333.643,442.659 336.139,445.035 339.595,445.035C343.051,445.035 345.571,442.659 345.571,439.251Z" style="fill-rule:nonzero;"/>
<path d="M360.883,444.891L360.883,443.259L352.099,443.259L352.099,437.091L359.587,437.091L359.587,435.459L352.099,435.459L352.099,429.435L360.883,429.435L360.883,427.803L350.275,427.803L350.275,444.891L360.883,444.891Z" style="fill-rule:nonzero;"/>
<path d="M376.411,440.163C376.411,438.123 375.259,436.707 373.627,436.107C375.067,435.579 376.147,434.163 376.147,432.363C376.147,429.507 374.083,427.803 371.107,427.803L364.675,427.803L364.675,444.891L371.323,444.891C374.347,444.891 376.411,443.259 376.411,440.163ZM374.587,440.115C374.587,442.035 373.267,443.259 371.155,443.259L366.499,443.259L366.499,436.995L371.155,436.995C373.267,436.995 374.587,438.195 374.587,440.115ZM374.323,432.387C374.323,434.427 372.859,435.363 370.963,435.363L366.499,435.363L366.499,429.435L370.963,429.435C372.859,429.435 374.323,430.347 374.323,432.387Z" style="fill-rule:nonzero;"/>
<path d="M391.315,444.891L391.315,443.259L382.531,443.259L382.531,437.091L390.019,437.091L390.019,435.459L382.531,435.459L382.531,429.435L391.315,429.435L391.315,427.803L380.707,427.803L380.707,444.891L391.315,444.891Z" style="fill-rule:nonzero;"/>
<path d="M407.059,444.891L403.003,437.139C405.211,436.683 406.723,435.051 406.723,432.555C406.723,429.603 404.635,427.803 401.659,427.803L395.107,427.803L395.107,444.891L396.931,444.891L396.931,437.307L401.035,437.307L404.923,444.891L407.059,444.891ZM404.899,432.579C404.899,434.667 403.483,435.699 401.491,435.699L396.931,435.699L396.931,429.435L401.491,429.435C403.483,429.435 404.899,430.491 404.899,432.579Z" style="fill-rule:nonzero;"/>
<path d="M421.147,440.187C421.147,438.771 420.619,437.619 419.707,436.827C418.939,436.131 418.195,435.843 416.611,435.603L414.619,435.291C413.731,435.147 412.915,434.859 412.387,434.403C411.859,433.947 411.619,433.251 411.619,432.411C411.619,430.491 412.939,429.219 415.243,429.219C417.019,429.219 418.195,429.723 419.227,430.707L420.403,429.531C418.939,428.235 417.499,427.659 415.315,427.659C411.931,427.659 409.843,429.555 409.843,432.459C409.843,433.851 410.299,434.907 411.163,435.675C411.931,436.347 412.939,436.755 414.283,436.971L416.275,437.259C417.499,437.451 417.979,437.619 418.507,438.099C419.083,438.603 419.347,439.323 419.347,440.235C419.347,442.251 417.811,443.403 415.291,443.403C413.371,443.403 412.051,442.971 410.635,441.555L409.411,442.779C410.995,444.387 412.699,445.035 415.243,445.035C418.819,445.035 421.147,443.187 421.147,440.187Z" style="fill-rule:nonzero;"/>
<path d="M436.219,439.947L434.395,439.947C433.939,442.059 432.427,443.403 430.291,443.403C429.139,443.403 428.131,442.995 427.339,442.203C426.259,441.099 426.139,439.851 426.139,436.347C426.139,432.843 426.259,431.595 427.339,430.491C428.131,429.699 429.139,429.291 430.291,429.291C432.427,429.291 433.891,430.635 434.347,432.747L436.219,432.747C435.619,429.555 433.387,427.659 430.291,427.659C428.515,427.659 427.075,428.283 426.019,429.339C424.411,430.947 424.315,432.435 424.315,436.347C424.315,440.259 424.411,441.747 426.019,443.355C427.075,444.411 428.515,445.035 430.291,445.035C433.363,445.035 435.619,443.139 436.219,439.947Z" style="fill-rule:nonzero;"/>
<path d="M452.011,444.891L452.011,427.803L450.187,427.803L450.187,435.459L442.075,435.459L442.075,427.803L440.251,427.803L440.251,444.891L442.075,444.891L442.075,437.091L450.187,437.091L450.187,444.891L452.011,444.891Z" style="fill-rule:nonzero;"/>
<path d="M468.859,444.891L462.595,427.803L461.059,427.803L454.771,444.891L456.715,444.891L458.083,441.027L465.547,441.027L466.915,444.891L468.859,444.891ZM465.019,439.443L458.635,439.443L461.851,430.419L465.019,439.443Z" style="fill-rule:nonzero;"/>
<path d="M483.571,444.891L479.515,437.139C481.723,436.683 483.235,435.051 483.235,432.555C483.235,429.603 481.147,427.803 478.171,427.803L471.619,427.803L471.619,444.891L473.443,444.891L473.443,437.307L477.547,437.307L481.435,444.891L483.571,444.891ZM481.411,432.579C481.411,434.667 479.995,435.699 478.003,435.699L473.443,435.699L473.443,429.435L478.003,429.435C479.995,429.435 481.411,430.491 481.411,432.579Z" style="fill-rule:nonzero;"/>
</g>
<g transform="matrix(12.2512,0,0,12.2512,-6531.47,-6259.35)">
<g transform="matrix(1,0,0,1,20.1367,39.3522)">
<rect x="535.943" y="508.648" width="9.08" height="9.08"/>
</g>
<g transform="matrix(1,0,0,1,11.0571,39.3522)">
<rect x="535.943" y="508.648" width="9.08" height="9.08" style="fill:rgb(27,118,215);"/>
</g>
<g transform="matrix(1,0,0,1,20.1367,30.2726)">
<rect x="535.943" y="508.648" width="9.08" height="9.08" style="fill:rgb(202,4,4);"/>
</g>
<g transform="matrix(1,0,0,1,20.1367,48.4318)">
<rect x="535.943" y="508.648" width="9.08" height="9.08" style="fill:rgb(0,159,77);"/>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 17 KiB