mirror of
https://github.com/googlefonts/noto-emoji.git
synced 2025-06-08 15:57:59 +00:00
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).
This commit is contained in:
parent
b1246768db
commit
89c6545e63
2 changed files with 79 additions and 11 deletions
49
scour_svg.sh
Executable file
49
scour_svg.sh
Executable 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
|
||||||
|
|
|
@ -72,18 +72,19 @@ class _Text_Node(object):
|
||||||
class SvgCleaner(object):
|
class SvgCleaner(object):
|
||||||
"""Strip out unwanted parts of an svg file, primarily the xml declaration and
|
"""Strip out unwanted parts of an svg file, primarily the xml declaration and
|
||||||
doctype lines, comments, and some attributes of the outermost <svg> element.
|
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
|
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
|
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
|
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
|
keep width and height, and will elsewhere assume these are the dimensions
|
||||||
used for the character box."""
|
used for the character box."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, strip=False):
|
||||||
self.reader = SvgCleaner._Reader()
|
self.reader = SvgCleaner._Reader()
|
||||||
self.cleaner = SvgCleaner._Cleaner()
|
self.cleaner = SvgCleaner._Cleaner()
|
||||||
self.writer = SvgCleaner._Writer()
|
self.writer = SvgCleaner._Writer(strip)
|
||||||
|
|
||||||
class _Reader(object):
|
class _Reader(object):
|
||||||
"""Loosely based on fonttools's XMLReader. This generates a tree of nodes,
|
"""Loosely based on fonttools's XMLReader. This generates a tree of nodes,
|
||||||
|
@ -130,7 +131,7 @@ class SvgCleaner(object):
|
||||||
|
|
||||||
class _Cleaner(object):
|
class _Cleaner(object):
|
||||||
def _clean_elem(self, node):
|
def _clean_elem(self, node):
|
||||||
viewBox, width, height = None, None, None
|
viewBox, x, y, width, height = None, None, None, None, None
|
||||||
nattrs = {}
|
nattrs = {}
|
||||||
for k, v in node.attrs.items():
|
for k, v in node.attrs.items():
|
||||||
if node.name == 'svg' and k in [
|
if node.name == 'svg' and k in [
|
||||||
|
@ -153,14 +154,25 @@ class SvgCleaner(object):
|
||||||
nattrs[k] = v
|
nattrs[k] = v
|
||||||
|
|
||||||
if node.name == 'svg':
|
if node.name == 'svg':
|
||||||
|
if viewBox:
|
||||||
|
x, y, width, height = viewBox.split()
|
||||||
if not width or not height:
|
if not width or not height:
|
||||||
if not viewBox:
|
if not viewBox:
|
||||||
raise ValueError('no viewBox, width, or height')
|
raise ValueError('no viewBox, width, or height')
|
||||||
width, height = viewBox.split()[2:]
|
|
||||||
nattrs['width'] = width
|
nattrs['width'] = width
|
||||||
nattrs['height'] = height
|
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
|
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.
|
# 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
|
# if a 'g' element has no attrs and only one subnode, replace it with the
|
||||||
# subnode.
|
# subnode.
|
||||||
|
@ -212,6 +224,9 @@ class SvgCleaner(object):
|
||||||
"""For text nodes, replaces sequences of whitespace with a single space.
|
"""For text nodes, replaces sequences of whitespace with a single space.
|
||||||
For elements, replaces sequences of whitespace in attributes, and
|
For elements, replaces sequences of whitespace in attributes, and
|
||||||
removes unwanted attributes from <svg> elements."""
|
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):
|
def _write_node(self, node, lines, indent):
|
||||||
"""Node is a node generated by _Reader, either a TextNode or an
|
"""Node is a node generated by _Reader, either a TextNode or an
|
||||||
|
@ -222,7 +237,7 @@ class SvgCleaner(object):
|
||||||
if node.text:
|
if node.text:
|
||||||
lines.append(node.text)
|
lines.append(node.text)
|
||||||
else:
|
else:
|
||||||
margin = ' ' * indent
|
margin = '' if self._strip else ' ' * indent
|
||||||
line = [margin]
|
line = [margin]
|
||||||
line.append('<%s' % node.name)
|
line.append('<%s' % node.name)
|
||||||
# custom sort attributes of svg, yes this is a hack
|
# custom sort attributes of svg, yes this is a hack
|
||||||
|
@ -258,7 +273,7 @@ class SvgCleaner(object):
|
||||||
# the result.
|
# the result.
|
||||||
lines = []
|
lines = []
|
||||||
self._write_node(root, lines, 0)
|
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):
|
def tree_from_text(self, svg_text):
|
||||||
return self.reader.from_text(svg_text)
|
return self.reader.from_text(svg_text)
|
||||||
|
@ -276,7 +291,7 @@ class SvgCleaner(object):
|
||||||
return self.tree_to_text(tree)
|
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
|
regex = re.compile(match_pat) if match_pat else None
|
||||||
count = 0
|
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)
|
out_dir = tool_utils.ensure_dir_exists(out_dir, clean=clean)
|
||||||
|
|
||||||
cleaner = SvgCleaner()
|
cleaner = SvgCleaner(strip)
|
||||||
for file_name in os.listdir(in_dir):
|
for file_name in os.listdir(in_dir):
|
||||||
if regex and not regex.match(file_name):
|
if regex and not regex.match(file_name):
|
||||||
continue
|
continue
|
||||||
|
@ -320,6 +335,9 @@ def main():
|
||||||
metavar='regex', default=None)
|
metavar='regex', default=None)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-l', '--loglevel', help='log level name/value', default='warning')
|
'-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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
tool_utils.setup_logging(args.loglevel)
|
tool_utils.setup_logging(args.loglevel)
|
||||||
|
@ -331,7 +349,8 @@ def main():
|
||||||
logging.info('Writing output to %s', args.out_dir)
|
logging.info('Writing output to %s', args.out_dir)
|
||||||
|
|
||||||
clean_svg_files(
|
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__':
|
if __name__ == '__main__':
|
||||||
|
|
Loading…
Add table
Reference in a new issue