Skip to content

Commit

Permalink
Changed requirement for lxml to xml. (mateosss#46)
Browse files Browse the repository at this point in the history
Other changes:
- Edited main script to prioritise using inkscape, but will fallback
to ImageMagick's convert if inkscape not found.
  • Loading branch information
heyzec committed Apr 2, 2021
1 parent c8948cd commit bf3c6f2
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 31 deletions.
76 changes: 61 additions & 15 deletions convert.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,89 @@
#!/usr/bin/env python3

import os
import lxml.etree as ET
import re
import xml.etree.ElementTree as ET
import xml.dom.minidom

def inkscape_export_svg2png(color, src_path, dst_path):
NS = {"svg" : 'http://www.w3.org/2000/svg'}
def inkscape_convert_svg2png(color, src_path, dst_path):
SVG_URI = 'http://www.w3.org/2000/svg'
FRAC = 0.7
TEMPFILE = 'temp.svg'

dom = ET.parse(src_path)
def parse_with_map(source):
"""Parses file, returns a tuple containing the parsed ElementTree and a namespace map (dict).
The ElementTree object returned is the same as if parsed using xml.etree.ElementTree.parse. For
some reason, ElementTree objects by the xml package will not provide a namespace map, unlike the
lxml package.
"""

root = None
ns_map = []
for event, node in ET.iterparse(source, events=['start-ns', 'start']):
if event == 'start-ns':
ns_map.append(node)
elif event == 'start':
if root is None:
root = node
return (ET.ElementTree(root), dict(ns_map))

def int_ignore_units(s):
return int(''.join(ch for ch in s if ch.isdigit()))

def prettify(xml_string):
return '\n'.join(line for line in xml.dom.minidom.parseString(xml_string).toprettyxml(
indent=' ').splitlines() if not line.isspace() and line != '')

# Fixes undefined namespace tags in output xml (not a big issue)
dom, ns_map = parse_with_map(src_path)
for key, value in ns_map.items():
ET.register_namespace(key, value)

root = dom.getroot()
width, height = int(root.attrib['width']), int(root.attrib['height'])
interval = (1-FRAC)*width/2
width, height = int_ignore_units(root.attrib['width']), int_ignore_units(root.attrib['height'])
width_gap, height_gap = (1-FRAC)*width/2, (1-FRAC)*height/2

elements = root.findall('svg:path', namespaces=NS)
group = ET.SubElement(root, "g")
# Group all elements that are child of <svg> while changing their 'style' attributes
elements = root.findall('svg:*', namespaces={'svg': SVG_URI})
group = ET.SubElement(root, 'g')
for element in elements:
element.attrib['style'] = f'fill:{color};fill-opacity:1'
if any(map(element.tag.endswith, ['defs', 'metadata'])): # Don't group these special tags
continue
root.remove(element)
for child in element.iter(): # Changes all decendents (.iter will also include itself)
if 'style' in child.attrib:
child.attrib['style'] = re.sub(r'(?<=fill:)\S+?(?=;)', color, child.attrib['style'])
else:
child.attrib['style'] = f'fill:{color};'
group.append(element)
group.attrib['transform'] = f"matrix({FRAC},0,0,{FRAC},{interval},{interval})"
# Shrink the svg by a factor of FRAC for padding around icon
group.attrib['transform'] = f"matrix({FRAC},0,0,{FRAC},{width_gap},{height_gap})"

et = ET.ElementTree(root)
et.write(TEMPFILE, pretty_print=True)
xml_string = ET.tostring(root).decode()
xml_string = prettify(xml_string)
with open(TEMPFILE, 'w') as f:
f.write(xml_string)

cmd = f"inkscape --export-filename='{dst_path}' {TEMPFILE} -w 72 2>/dev/null"
exit_code = os.system(cmd)
os.remove(TEMPFILE)
return exit_code

def magick_convert_svg2png(color, src_path, dst_path):
command = (
cmd = (
r"convert -trim -scale 36x36 -extent 72x72 -gravity center "
r"-define png:color-type=6 -background none -colorspace sRGB -channel RGB "
rf"-threshold -1 -density 300 -fill \{color} +opaque none "
rf"{src_path} {dst_path}"
)
return os.system(command)
return os.system(cmd)

# For demostration purposes
if __name__ == '__main__':
svg2png = inkscape_convert_svg2png
# svg2png = magick_convert_svg2png
for file in os.listdir('./icons'):
basename, ext = os.path.splitext(file)
if ext == '.svg':
inkscape_export_svg2png('#FFFFFF', f'icons/{basename}.svg', f'icons/INK_{basename}.png')
svg2png('#FFFFFF', f'icons/{basename}.svg', f'icons/{basename}.png')
38 changes: 22 additions & 16 deletions matter.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/python3

# Standard library modules
import sys
import os
import re
Expand All @@ -11,7 +12,8 @@
from subprocess import run, check_call, PIPE
from shutil import which, rmtree, copytree, copyfile

from convert import inkscape_export_svg2png
# Local Matter modules
from convert import inkscape_convert_svg2png, magick_convert_svg2png

# Configuration constants

Expand Down Expand Up @@ -236,30 +238,34 @@ def is_icon_downloaded(icon_name):


def convert_icon_svg2png(icon_name):
if not has_command("convert"):
error(
"Stop. The `convert` command from imagemagick was not found",
"Also consider installing `inkscape` for the best results",
)
if not has_command("inkscape"):
warning("Resulting icons could look a bit off, consider installing inkscape")
if not has_command("convert"):
error(
"Stop. Both `inkscape` and `convert` command from imagemagick was not found",
"Consider installing `inkscape` for the best results",
)
else:
command = "convert"
else:
command = "inkscape"

color = (
parse_color(user_args.iconcolor)
if user_args.iconcolor
else parse_color(user_args.foreground)
)
src_path = ICON_SVG_PATHF.format(icon_name)
dst_path = ICON_PNG_PATHF.format(icon_name)
# command = (
# r"convert -trim -scale 36x36 -extent 72x72 -gravity center "
# r"-define png:color-type=6 -background none -colorspace sRGB -channel RGB "
# rf"-threshold -1 -density 300 -fill \{color} +opaque none "
# rf"{src_path} {dst_path}"
# )
# exit_code = sh(command)
exit_code = inkscape_export_svg2png(color, src_path, dst_path)

if command == "convert":
warning("Resulting icons could look a bit off, consider installing inkscape")
converter = magick_convert_svg2png
elif command == "inkscape":
converter = inkscape_convert_svg2png

exit_code = converter(color, src_path, dst_path)
if exit_code != 0:
error("Stop. The convert command returned an error")
error(f"Stop. The `{command}` command returned an error")


def get_available_fonts():
Expand Down

0 comments on commit bf3c6f2

Please sign in to comment.