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
Doug Felt 2018-08-09 17:29:34 -07:00
parent b1246768db
commit 89c6545e63
2 changed files with 79 additions and 11 deletions

49
scour_svg.sh 100755
View File

@ -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

View File

@ -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__':