From c2fe745813fe7c054a41006a9bd7ab06c8e16ed9 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 29 Aug 2017 15:39:45 +1000 Subject: [PATCH] data/gnome: add a quick and dirty SVG checker for the GNOME SVGs Checks for the three layers we need and that any buttons in the files are consecutive and have the right layers Fixes #196 Co-authored-by: Jente Hidskes --- circle.yml | 4 +- data/gnome/check-svg.py | 118 ++++++++++++++++++++++++++++++++++++++++ meson.build | 7 ++- 3 files changed, 125 insertions(+), 4 deletions(-) create mode 100755 data/gnome/check-svg.py diff --git a/circle.yml b/circle.yml index 3d6535ec..aa25460b 100644 --- a/circle.yml +++ b/circle.yml @@ -56,7 +56,7 @@ libratbag_references: fedora_install: &fedora_install run: name: Install prerequisites - command: dnf install -y tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel doxygen graphviz valgrind python3-gobject python3-evdev glib2-devel + command: dnf install -y tree git gcc gcc-c++ meson check-devel libudev-devel libevdev-devel doxygen graphviz valgrind python3-gobject python3-evdev glib2-devel python3-lxml fedora_settings: &fedora_settings @@ -81,7 +81,7 @@ ubuntu_settings: &ubuntu_settings apt-get install -y software-properties-common add-apt-repository universe apt-get update - apt-get install -y tree git gcc g++ pkg-config meson check libudev-dev libevdev-dev libsystemd-dev doxygen graphviz valgrind python3-gi python3-evdev libglib2.0-dev: + apt-get install -y tree git gcc g++ pkg-config meson check libudev-dev libevdev-dev libsystemd-dev doxygen graphviz valgrind python3-gi python3-evdev libglib2.0-dev python3-lxml - checkout - *start_dbus - *build_and_test diff --git a/data/gnome/check-svg.py b/data/gnome/check-svg.py new file mode 100755 index 00000000..2da3812e --- /dev/null +++ b/data/gnome/check-svg.py @@ -0,0 +1,118 @@ +#!/usr/bin/python3 +# vim: set expandtab shiftwidth=4 tabstop=4: + +import sys +from lxml import etree +import logging + +ns = {'svg': 'http://www.w3.org/2000/svg'} +style_query = '//svg:rect[@id=\"{}\"][contains(@style, \"{}\")]' + +logger = None + +class SVGLogger(logging.Logger): + def __init__(self, name, level=logging.NOTSET): + self.success = True + return super().__init__(name, level) + + def error(self, msg, *args, **kwargs): + self.success = False + return super().error(msg, *args, **kwargs) + + @classmethod + def get_logger(cls, path): + logging.setLoggerClass(SVGLogger) + logging.basicConfig(level=logging.DEBUG) + return logging.getLogger(path) + + +def check_size(root): + width = float(root.attrib['width']) + height = float(root.attrib['height']) + if not 400 < width < 500: + logger.error("Width is outside of range: {}".format(width)) + if not 400 < height < 500: + logger.error("Height is outside of range: {}".format(height)) + + +def check_layers(root): + """ + Check there are layers (well, groups) for the components we require. + """ + layer_ids = [g.attrib['id'] for g in root.iterfind('svg:g', ns)] + + for layer in ["Device", "Buttons", "LEDs"]: + if layer not in layer_ids: + logger.error("Missing layer: {}".format(layer)) + + +def check_elements(root, prefix, required=0): + """ + Checks for elements of the form 'prefixN' in the root tag. Any elements + found must be consecutive or an error is printed, i.e. if there's a + 'button8' there has to be a 'button7'. + + If required is nonzero, an error is logged for any missing element with + an index less than required. + """ + + # elements can be paths and rects + # This includes leaders and lines + element_ids = [p.attrib['id'] for p in root.xpath('//svg:path', namespaces=ns) if p.attrib['id'].startswith(prefix)] + element_ids += [p.attrib['id'] for p in root.xpath('//svg:rect', namespaces=ns) if p.attrib['id'].startswith(prefix)] + element_ids += [g.attrib['id'] for g in root.xpath('//svg:g', namespaces=ns) if g.attrib['id'].startswith(prefix)] + + idx = 0 + highest = -1 + for idx in range(0, 20): + e = '{}{}'.format(prefix, idx) + if e in element_ids: + highest = idx + previous = '{}{}'.format(prefix, idx - 1) + if idx > 0 and previous not in element_ids: + logger.error("Non-consecutive {}: {}".format(prefix, e)) + + leader = '{}{}-leader'.format(prefix, idx) + if leader not in element_ids: + logger.error("Missing {} for {}".format(leader, e)) + else: + element = root.xpath(style_query.format(leader, 'text-align'), namespaces=ns) + if element is None or len(element) != 1 or element[0] is None: + logger.error("Missing style property for {}".format(leader)) + + path = '{}{}-path'.format(prefix, idx) + if path not in element_ids: + logger.error("Missing {} for {}".format(path, e)) + elif idx < required: + logger.error("Missing {}: {}".format(prefix, e)) + + logger.info("Found {} {}s".format(highest + 1, prefix)) + + +def check_leds(root): + check_elements(root, "led") + + +def check_buttons(root): + check_elements(root, "button", 3) + + +def check_svg(path): + svg = etree.parse(path) + root = svg.getroot() + + check_size(root) + check_layers(root) + check_buttons(root) + check_leds(root) + +if __name__ == "__main__": + success = True + for path in sys.argv[1:]: + logger = SVGLogger.get_logger(path) + check_svg(path) + if not logger.success: + success = False + + if not success: + sys.exit(1) diff --git a/meson.build b/meson.build index 4475a00f..ef19914d 100644 --- a/meson.build +++ b/meson.build @@ -306,7 +306,7 @@ default_svg_files = [ install_data(default_svg_files, install_dir : join_paths(get_option('datadir'), 'libratbag', 'default')) -gnome_svg_files = [ +gnome_svg_files = files( 'data/gnome/fallback.svg', 'data/gnome/logitech-g303.svg', 'data/gnome/logitech-g403.svg', @@ -316,10 +316,13 @@ gnome_svg_files = [ 'data/gnome/logitech-g700.svg', 'data/gnome/logitech-g900.svg', 'data/gnome/roccat-kone-xtd.svg', -] +) install_data(gnome_svg_files, install_dir : join_paths(get_option('datadir'), 'libratbag', 'gnome')) +test_svg_files = find_program('data/gnome/check-svg.py') +test('check-svg', test_svg_files, args : gnome_svg_files) + #### tests #### enable_tests = get_option('enable-tests') if enable_tests