198 lines
6.6 KiB
Python
Executable file
198 lines
6.6 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
# Icon checker: test that icon themes contain all needed icons
|
|
# Author: Martin Owens <doctormo@geek-2.com>
|
|
# Licensed under GPL version 2 or any later version, read the file "COPYING" for more information.
|
|
|
|
import fnmatch
|
|
import os
|
|
import sys
|
|
|
|
from collections import defaultdict
|
|
|
|
THEME_PATH = os.path.join('.', 'share', 'icons')
|
|
IGNORE_THEMES = [
|
|
'application',
|
|
'Tango',
|
|
]
|
|
IGNORE_ICONS = [
|
|
# These are hard coded as symbolic in the gtk source code
|
|
'list-add-symbolic.svg',
|
|
'list-add.svg',
|
|
'list-remove-symbolic.svg',
|
|
'list-remove.svg',
|
|
'accessories-character-map.svg',
|
|
'accessories-character-map-symbolic.svg',
|
|
'applications-graphics.svg',
|
|
'applications-graphics-symbolic.svg',
|
|
'dialog-information.svg',
|
|
'dialog-information-symbolic.svg',
|
|
'edit-find.svg',
|
|
'edit-find-symbolic.svg',
|
|
'folder-open.svg',
|
|
'folder-open-symbolic.svg',
|
|
'tools-check-spelling.svg',
|
|
'tools-check-spelling-symbolic.svg',
|
|
'dialog-warning.svg',
|
|
'dialog-warning-symbolic.svg',
|
|
'format-justify-center.svg',
|
|
'format-justify-center-symbolic.svg',
|
|
'format-justify-fill.svg',
|
|
'format-justify-fill-symbolic.svg',
|
|
'format-justify-left.svg',
|
|
'format-justify-left-symbolic.svg',
|
|
'format-justify-right.svg',
|
|
'format-justify-right-symbolic.svg',
|
|
'dialog-warning-symbolic.svg',
|
|
'edit-clear.svg',
|
|
'edit-clear-symbolic.svg',
|
|
'view-refresh-symbolic.svg',
|
|
'view-refresh.svg',
|
|
# Those are illustrations rather than icons
|
|
'feBlend-icon-symbolic.svg',
|
|
'feColorMatrix-icon-symbolic.svg',
|
|
'feComponentTransfer-icon-symbolic.svg',
|
|
'feComposite-icon-symbolic.svg',
|
|
'feConvolveMatrix-icon-symbolic.svg',
|
|
'feDiffuseLighting-icon-symbolic.svg',
|
|
'feDisplacementMap-icon-symbolic.svg',
|
|
'feFlood-icon-symbolic.svg',
|
|
'feGaussianBlur-icon-symbolic.svg',
|
|
'feImage-icon-symbolic.svg',
|
|
'feMerge-icon-symbolic.svg',
|
|
'feMorphology-icon-symbolic.svg',
|
|
'feOffset-icon-symbolic.svg',
|
|
'feSpecularLighting-icon-symbolic.svg',
|
|
'feTile-icon-symbolic.svg',
|
|
'feTurbulence-icon-symbolic.svg',
|
|
'feBlend-icon.svg',
|
|
'feColorMatrix-icon.svg',
|
|
'feComponentTransfer-icon.svg',
|
|
'feComposite-icon.svg',
|
|
'feConvolveMatrix-icon.svg',
|
|
'feDiffuseLighting-icon.svg',
|
|
'feDisplacementMap-icon.svg',
|
|
'feFlood-icon.svg',
|
|
'feGaussianBlur-icon.svg',
|
|
'feImage-icon.svg',
|
|
'feMerge-icon.svg',
|
|
'feMorphology-icon.svg',
|
|
'feOffset-icon.svg',
|
|
'feSpecularLighting-icon.svg',
|
|
'feTile-icon.svg',
|
|
'feTurbulence-icon.svg',
|
|
]
|
|
|
|
NO_PROBLEM,\
|
|
BAD_SYMBOLIC_NAME,\
|
|
BAD_SCALABLE_NAME,\
|
|
MISSING_FROM,\
|
|
ONLY_FOUND_IN = range(5)
|
|
|
|
def icon_themes():
|
|
for name in os.listdir(THEME_PATH):
|
|
filename = os.path.join(THEME_PATH, name)
|
|
if name in IGNORE_THEMES or not os.path.isdir(filename):
|
|
continue
|
|
yield name, filename
|
|
|
|
def find_errors_in(themes):
|
|
data = defaultdict(set)
|
|
bad_symbolic = []
|
|
bad_scalable = []
|
|
names = set()
|
|
|
|
for name, path in themes:
|
|
for root, dirs, files in os.walk(path):
|
|
orig = root
|
|
root = root[len(path)+1:]
|
|
if '/' not in root:
|
|
continue
|
|
(kind, root) = root.split('/', 1)
|
|
if kind not in ("symbolic", "scalable"):
|
|
continue # Not testing cursors, maybe later.
|
|
|
|
theme_name = f"{name}-{kind}"
|
|
names.add(theme_name)
|
|
for fname in files:
|
|
if fname in IGNORE_ICONS:
|
|
continue
|
|
if not fname.endswith('.svg'):
|
|
continue
|
|
|
|
if kind == "symbolic":
|
|
if not fname.endswith('-symbolic.svg'):
|
|
bad_symbolic.append(os.path.join(orig, fname))
|
|
continue
|
|
else:
|
|
# Make filenames consistant for comparison
|
|
fname = fname.replace('-symbolic.svg', '.svg')
|
|
elif kind == "scalable" and fname.endswith('-symbolic.svg'):
|
|
bad_scalable.append(os.path.join(orig, fname))
|
|
continue
|
|
|
|
filename = os.path.join(root, fname)
|
|
data[filename].add(theme_name)
|
|
|
|
if bad_symbolic:
|
|
yield BAD_SYMBOLIC_NAME, bad_symbolic
|
|
if bad_scalable:
|
|
yield BAD_SCALABLE_NAME, bad_scalable
|
|
|
|
only_found_in = defaultdict(list)
|
|
missing_from = defaultdict(list)
|
|
|
|
for filename in sorted(data):
|
|
datum = data[filename]
|
|
if datum == names:
|
|
continue
|
|
elif len(datum) == 1:
|
|
only_found_in[list(datum)[0]].append(filename)
|
|
continue
|
|
for theme in (names - datum):
|
|
missing_from[theme].append(filename)
|
|
|
|
if only_found_in:
|
|
yield ONLY_FOUND_IN, only_found_in
|
|
if missing_from:
|
|
yield MISSING_FROM, missing_from
|
|
|
|
if __name__ == '__main__':
|
|
errors = list(find_errors_in(icon_themes()))
|
|
if errors:
|
|
count = 0
|
|
for error, themes in errors:
|
|
if isinstance(themes, list):
|
|
count += len(themes)
|
|
elif isinstance(themes, dict):
|
|
count += sum([len(v) for v in themes.values()])
|
|
sys.stderr.write(f" == {count} problems found in icon themes! == \n\n")
|
|
for error, themes in errors:
|
|
if error is BAD_SCALABLE_NAME:
|
|
sys.stderr.write(f"Scalable themes should not have symbolic icons in them (They end with -symbolic.svg so won't be used):\n")
|
|
for name in themes:
|
|
sys.stderr.write(f" - {name}\n")
|
|
sys.stderr.write("\n")
|
|
elif error is BAD_SYMBOLIC_NAME:
|
|
sys.stderr.write(f"Symbolic themes should only have symbolic icons in them (They don't end with -symbolic.svg so can't be used):\n")
|
|
for name in themes:
|
|
sys.stderr.write(f" - {name}\n")
|
|
sys.stderr.write("\n")
|
|
elif error is MISSING_FROM:
|
|
for theme in themes:
|
|
sys.stderr.write(f"Icons missing from {theme}:\n")
|
|
for name in themes[theme]:
|
|
sys.stderr.write(f" - {name}\n")
|
|
sys.stderr.write("\n")
|
|
elif error is ONLY_FOUND_IN:
|
|
for theme in themes:
|
|
sys.stderr.write(f"Icons only found in {theme}:\n")
|
|
for name in themes[theme]:
|
|
sys.stderr.write(f" + {name}\n")
|
|
sys.stderr.write("\n")
|
|
else:
|
|
pass
|
|
|
|
sys.exit(5)
|
|
|
|
# vi:sw=4:expandtab:
|