2016-08-15 11:59:53 -07:00
|
|
|
"""Rudimentary support for loading MutatorMath .designspace files."""
|
|
|
|
from __future__ import print_function, division, absolute_import
|
|
|
|
from fontTools.misc.py23 import *
|
|
|
|
try:
|
|
|
|
import xml.etree.cElementTree as ET
|
|
|
|
except ImportError:
|
|
|
|
import xml.etree.ElementTree as ET
|
|
|
|
|
|
|
|
__all__ = ['load', 'loads']
|
|
|
|
|
2017-02-25 20:45:47 -08:00
|
|
|
namespaces = {'xml': '{http://www.w3.org/XML/1998/namespace}'}
|
2017-02-22 21:22:34 -08:00
|
|
|
|
2017-02-27 16:35:02 +00:00
|
|
|
|
|
|
|
def _xml_parse_location(et):
|
2016-08-15 11:59:53 -07:00
|
|
|
loc = {}
|
|
|
|
for dim in et.find('location'):
|
|
|
|
assert dim.tag == 'dimension'
|
|
|
|
name = dim.attrib['name']
|
|
|
|
value = float(dim.attrib['xvalue'])
|
|
|
|
assert name not in loc
|
|
|
|
loc[name] = value
|
|
|
|
return loc
|
|
|
|
|
2017-02-27 16:35:02 +00:00
|
|
|
|
|
|
|
def _load_item(et):
|
2016-08-15 11:59:53 -07:00
|
|
|
item = dict(et.attrib)
|
2017-02-27 16:35:02 +00:00
|
|
|
for element in et:
|
|
|
|
if element.tag == 'location':
|
|
|
|
value = _xml_parse_location(et)
|
2016-08-15 11:59:53 -07:00
|
|
|
else:
|
|
|
|
value = {}
|
2017-02-27 16:35:02 +00:00
|
|
|
if 'copy' in element.attrib:
|
|
|
|
value['copy'] = bool(int(element.attrib['copy']))
|
2016-08-15 11:59:53 -07:00
|
|
|
# TODO load more?!
|
2017-02-27 16:35:02 +00:00
|
|
|
item[element.tag] = value
|
2016-08-15 11:59:53 -07:00
|
|
|
return item
|
|
|
|
|
2017-02-27 16:35:02 +00:00
|
|
|
|
|
|
|
def _xml_parse_axis_or_map(element):
|
2017-02-25 20:45:47 -08:00
|
|
|
dic = {}
|
2017-02-27 16:35:02 +00:00
|
|
|
for name in element.attrib:
|
2017-02-25 20:45:47 -08:00
|
|
|
if name in ['name', 'tag']:
|
2017-02-27 16:35:02 +00:00
|
|
|
dic[name] = element.attrib[name]
|
2017-02-25 20:45:47 -08:00
|
|
|
else:
|
2017-02-27 16:35:02 +00:00
|
|
|
dic[name] = float(element.attrib[name])
|
2017-02-25 20:45:47 -08:00
|
|
|
return dic
|
|
|
|
|
2017-02-27 16:35:02 +00:00
|
|
|
|
|
|
|
def _load_axis(et):
|
|
|
|
item = _xml_parse_axis_or_map(et)
|
2017-02-25 20:45:47 -08:00
|
|
|
maps = []
|
|
|
|
labelnames = {}
|
2017-02-27 16:35:02 +00:00
|
|
|
for element in et:
|
|
|
|
assert element.tag in ['labelname', 'map']
|
|
|
|
if element.tag == 'labelname':
|
|
|
|
lang = element.attrib["{0}lang".format(namespaces['xml'])]
|
|
|
|
labelnames[lang] = element.text
|
|
|
|
elif element.tag == 'map':
|
|
|
|
maps.append(_xml_parse_axis_or_map(element))
|
2017-02-25 20:45:47 -08:00
|
|
|
if labelnames:
|
|
|
|
item['labelname'] = labelnames
|
|
|
|
if maps:
|
|
|
|
item['map'] = maps
|
|
|
|
return item
|
|
|
|
|
2017-02-27 16:35:02 +00:00
|
|
|
|
2016-08-15 11:59:53 -07:00
|
|
|
def _load(et):
|
2017-02-26 07:49:44 -08:00
|
|
|
designspace = {}
|
2016-08-15 11:59:53 -07:00
|
|
|
ds = et.getroot()
|
2017-02-25 20:45:47 -08:00
|
|
|
|
2017-02-27 16:17:13 +00:00
|
|
|
axes_element = ds.find('axes')
|
|
|
|
if axes_element is not None:
|
|
|
|
axes = []
|
|
|
|
for et in axes_element:
|
2017-02-27 16:35:02 +00:00
|
|
|
axes.append(_load_axis(et))
|
2017-02-27 16:17:13 +00:00
|
|
|
designspace['axes'] = axes
|
2017-02-22 21:22:34 -08:00
|
|
|
|
2017-02-27 16:17:13 +00:00
|
|
|
sources_element = ds.find('sources')
|
|
|
|
if sources_element is not None:
|
|
|
|
sources = []
|
|
|
|
for et in sources_element:
|
2017-02-27 16:35:02 +00:00
|
|
|
sources.append(_load_item(et))
|
2017-02-27 16:21:35 +00:00
|
|
|
designspace['sources'] = sources
|
2016-08-15 11:59:53 -07:00
|
|
|
|
2017-02-27 16:17:13 +00:00
|
|
|
instances_element = ds.find('instances')
|
|
|
|
if instances_element is not None:
|
|
|
|
instances = []
|
|
|
|
for et in instances_element:
|
2017-02-27 16:35:02 +00:00
|
|
|
instances.append(_load_item(et))
|
2017-02-27 16:17:13 +00:00
|
|
|
designspace['instances'] = instances
|
2016-08-15 11:59:53 -07:00
|
|
|
|
2017-02-26 07:49:44 -08:00
|
|
|
return designspace
|
2016-08-15 11:59:53 -07:00
|
|
|
|
2017-02-27 16:35:02 +00:00
|
|
|
|
2016-08-15 11:59:53 -07:00
|
|
|
def load(filename):
|
2017-02-25 20:45:47 -08:00
|
|
|
"""Load designspace from a file name or object.
|
2017-02-27 18:11:28 +00:00
|
|
|
Returns a dictionary containing three (optional) items:
|
2017-02-27 16:21:35 +00:00
|
|
|
- list of "axes"
|
|
|
|
- list of "sources" (aka masters)
|
|
|
|
- list of "instances"
|
|
|
|
"""
|
2016-08-15 11:59:53 -07:00
|
|
|
return _load(ET.parse(filename))
|
|
|
|
|
2017-02-27 16:35:02 +00:00
|
|
|
|
2016-08-15 11:59:53 -07:00
|
|
|
def loads(string):
|
|
|
|
"""Load designspace from a string."""
|
|
|
|
return _load(ET.fromstring(string))
|
2017-04-12 15:29:42 -07:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
import sys
|
|
|
|
from pprint import pprint
|
|
|
|
for f in sys.argv[1:]:
|
|
|
|
pprint(load(f))
|