Update svg tooling.
Tweaked svg_cleaner a bit to remove display none, add option to strip whitespace. Stripping viewBox was too aggressive, two of our emoji have non-default viewBox values, so emit it when x or y are not zero. Added script to invoke scour (now must be part of installation).pull/225/head
parent
b1246768db
commit
89c6545e63
|
@ -0,0 +1,49 @@
|
|||
#!/bin/bash
|
||||
|
||||
SRC_DIR=""
|
||||
DST_DIR=""
|
||||
|
||||
SCOUR_ARGS="--strip-xml-prolog --enable-viewboxing --enable-id-stripping --enable-comment-stripping --shorten-ids --no-line-breaks --strip-xml-space"
|
||||
|
||||
while [ $# != 0 ]; do
|
||||
case "$1" in
|
||||
-s) SRC_DIR=${2}
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-d) DST_DIR=${2}
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
*) echo "unrecognized arg $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$SRC_DIR" ]; then
|
||||
echo "missing source directory"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
if [ ! -d "$SRC_DIR" ]; then
|
||||
echo "source dirctory '$SRC_DIR' does not exist"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
if [ -z "$DST_DIR" ]; then
|
||||
echo "missing destination directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$DST_DIR" ]; then
|
||||
echo "creating destination directory '$DST_DIR'"
|
||||
mkdir -p "$DST_DIR"
|
||||
fi
|
||||
|
||||
for file in "$SRC_DIR"/*.svg; do
|
||||
dst="${file##*/}"
|
||||
echo $dst
|
||||
scour $SCOUR_ARGS -i "$file" -o "$DST_DIR/$dst"
|
||||
done
|
||||
|
|
@ -72,18 +72,19 @@ class _Text_Node(object):
|
|||
class SvgCleaner(object):
|
||||
"""Strip out unwanted parts of an svg file, primarily the xml declaration and
|
||||
doctype lines, comments, and some attributes of the outermost <svg> element.
|
||||
The id will be replaced when it is inserted into the font. viewBox causes
|
||||
The id will be replaced when it is inserted into the font. (viewBox causes
|
||||
unwanted scaling when used in a font and its effect is difficult to
|
||||
predict. version is unneeded, xml:space is ignored (we're processing spaces
|
||||
predict, but for outside a font we need to keep it sometimes so we keep it).
|
||||
version is unneeded, xml:space is ignored (we're processing spaces
|
||||
so a request to maintain them has no effect). enable-background appears to
|
||||
have no effect. x and y on the outermost svg element have no effect. We
|
||||
keep width and height, and will elsewhere assume these are the dimensions
|
||||
used for the character box."""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, strip=False):
|
||||
self.reader = SvgCleaner._Reader()
|
||||
self.cleaner = SvgCleaner._Cleaner()
|
||||
self.writer = SvgCleaner._Writer()
|
||||
self.writer = SvgCleaner._Writer(strip)
|
||||
|
||||
class _Reader(object):
|
||||
"""Loosely based on fonttools's XMLReader. This generates a tree of nodes,
|
||||
|
@ -130,7 +131,7 @@ class SvgCleaner(object):
|
|||
|
||||
class _Cleaner(object):
|
||||
def _clean_elem(self, node):
|
||||
viewBox, width, height = None, None, None
|
||||
viewBox, x, y, width, height = None, None, None, None, None
|
||||
nattrs = {}
|
||||
for k, v in node.attrs.items():
|
||||
if node.name == 'svg' and k in [
|
||||
|
@ -153,14 +154,25 @@ class SvgCleaner(object):
|
|||
nattrs[k] = v
|
||||
|
||||
if node.name == 'svg':
|
||||
if viewBox:
|
||||
x, y, width, height = viewBox.split()
|
||||
if not width or not height:
|
||||
if not viewBox:
|
||||
raise ValueError('no viewBox, width, or height')
|
||||
width, height = viewBox.split()[2:]
|
||||
nattrs['width'] = width
|
||||
nattrs['height'] = height
|
||||
# keep for svg use outside of font
|
||||
if viewBox and (int(x) != 0 or int(y) != 0):
|
||||
logging.warn('viewbox "%s" x: %s y: %s' % (viewBox, x, y));
|
||||
nattrs['viewBox'] = viewBox
|
||||
node.attrs = nattrs
|
||||
|
||||
# if display:none, skip this and its children
|
||||
style = node.attrs.get('style')
|
||||
if (style and 'display:none' in style) or node.attrs.get('display') == 'none':
|
||||
node.contents = []
|
||||
return
|
||||
|
||||
# scan contents. remove any empty text nodes, or empty 'g' element nodes.
|
||||
# if a 'g' element has no attrs and only one subnode, replace it with the
|
||||
# subnode.
|
||||
|
@ -212,6 +224,9 @@ class SvgCleaner(object):
|
|||
"""For text nodes, replaces sequences of whitespace with a single space.
|
||||
For elements, replaces sequences of whitespace in attributes, and
|
||||
removes unwanted attributes from <svg> elements."""
|
||||
def __init__(self, strip):
|
||||
logging.warning('writer strip: %s' % strip);
|
||||
self._strip = strip
|
||||
|
||||
def _write_node(self, node, lines, indent):
|
||||
"""Node is a node generated by _Reader, either a TextNode or an
|
||||
|
@ -222,7 +237,7 @@ class SvgCleaner(object):
|
|||
if node.text:
|
||||
lines.append(node.text)
|
||||
else:
|
||||
margin = ' ' * indent
|
||||
margin = '' if self._strip else ' ' * indent
|
||||
line = [margin]
|
||||
line.append('<%s' % node.name)
|
||||
# custom sort attributes of svg, yes this is a hack
|
||||
|
@ -258,7 +273,7 @@ class SvgCleaner(object):
|
|||
# the result.
|
||||
lines = []
|
||||
self._write_node(root, lines, 0)
|
||||
return '\n'.join(lines)
|
||||
return ''.join(lines) if self._strip else '\n'.join(lines)
|
||||
|
||||
def tree_from_text(self, svg_text):
|
||||
return self.reader.from_text(svg_text)
|
||||
|
@ -276,7 +291,7 @@ class SvgCleaner(object):
|
|||
return self.tree_to_text(tree)
|
||||
|
||||
|
||||
def clean_svg_files(in_dir, out_dir, match_pat=None, clean=False):
|
||||
def clean_svg_files(in_dir, out_dir, match_pat=None, clean=False, strip=False):
|
||||
regex = re.compile(match_pat) if match_pat else None
|
||||
count = 0
|
||||
|
||||
|
@ -286,7 +301,7 @@ def clean_svg_files(in_dir, out_dir, match_pat=None, clean=False):
|
|||
|
||||
out_dir = tool_utils.ensure_dir_exists(out_dir, clean=clean)
|
||||
|
||||
cleaner = SvgCleaner()
|
||||
cleaner = SvgCleaner(strip)
|
||||
for file_name in os.listdir(in_dir):
|
||||
if regex and not regex.match(file_name):
|
||||
continue
|
||||
|
@ -320,6 +335,9 @@ def main():
|
|||
metavar='regex', default=None)
|
||||
parser.add_argument(
|
||||
'-l', '--loglevel', help='log level name/value', default='warning')
|
||||
parser.add_argument(
|
||||
'-w', '--strip_whitespace', help='remove newlines and indentation',
|
||||
action='store_true')
|
||||
args = parser.parse_args()
|
||||
|
||||
tool_utils.setup_logging(args.loglevel)
|
||||
|
@ -331,7 +349,8 @@ def main():
|
|||
logging.info('Writing output to %s', args.out_dir)
|
||||
|
||||
clean_svg_files(
|
||||
args.in_dir, args.out_dir, match_pat=args.regex, clean=args.clean)
|
||||
args.in_dir, args.out_dir, match_pat=args.regex, clean=args.clean,
|
||||
strip=args.strip_whitespace)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
Loading…
Reference in New Issue