From 4c91f51aed76f8affcad7ec7721cdbde817de54e Mon Sep 17 00:00:00 2001 From: Jany Belluz Date: Mon, 12 Feb 2018 12:25:12 +0000 Subject: [PATCH] [designspaceLib] Add properties to designspace documents --- Lib/fontTools/designspaceLib/__init__.py | 65 ++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/Lib/fontTools/designspaceLib/__init__.py b/Lib/fontTools/designspaceLib/__init__.py index adf620f2f..6d7eac102 100644 --- a/Lib/fontTools/designspaceLib/__init__.py +++ b/Lib/fontTools/designspaceLib/__init__.py @@ -5,6 +5,7 @@ import collections import logging import os import posixpath +import plistlib import xml.etree.ElementTree as ET # from mutatorMath.objects.location import biasFromLocations, Location @@ -16,7 +17,35 @@ import xml.etree.ElementTree as ET - warpmap is stored in its axis element """ -__all__ = [ 'DesignSpaceDocumentError', 'DesignSpaceDocument', 'SourceDescriptor', 'InstanceDescriptor', 'AxisDescriptor', 'RuleDescriptor', 'BaseDocReader', 'BaseDocWriter'] +__all__ = [ + 'DesignSpaceDocumentError', 'DesignSpaceDocument', 'SourceDescriptor', + 'InstanceDescriptor', 'AxisDescriptor', 'RuleDescriptor', 'BaseDocReader', + 'BaseDocWriter' +] + + +def to_plist(value): + try: + # Python 2 + string = plistlib.writePlistToString(value) + except AttributeError: + # Python 3 + string = plistlib.dumps(value).decode() + return ET.fromstring(string).getchildren()[0] + + +def from_plist(element): + if element is None: + return {} + plist = ET.Element('plist') + plist.append(element) + string = ET.tostring(plist) + try: + # Python 2 + return plistlib.readPlistFromString(string) + except AttributeError: + # Python 3 + return plistlib.loads(string, fmt=plistlib.FMT_XML) def posix(path): @@ -92,6 +121,7 @@ class SourceDescriptor(SimpleDescriptor): def __init__(self): self.filename = None # the original path as found in the document self.path = None # the absolute path, calculated from filename + self.font = None # optional: an instance of defcon.Font self.name = None self.location = None self.copyLib = False @@ -185,7 +215,8 @@ class InstanceDescriptor(SimpleDescriptor): 'styleMapFamilyName', 'styleMapStyleName', 'kerning', - 'info'] + 'info', + 'lib'] def __init__(self): self.filename = None # the original path as found in the document @@ -205,6 +236,7 @@ class InstanceDescriptor(SimpleDescriptor): self.mutedGlyphNames = [] self.kerning = True self.info = True + self.lib = {} path = posixpath_property("_path") filename = posixpath_property("_filename") @@ -340,6 +372,10 @@ class BaseDocWriter(object): self.root.append(ET.Element("instances")) for instanceObject in self.documentObject.instances: self._addInstance(instanceObject) + + if self.documentObject.lib: + self._addLib(self.documentObject.lib) + if pretty: _indent(self.root, whitespace=self._whiteSpace) tree = ET.ElementTree(self.root) @@ -493,6 +529,10 @@ class BaseDocWriter(object): if instanceObject.info: infoElement = ET.Element('info') instanceElement.append(infoElement) + if instanceObject.lib: + libElement = ET.Element('lib') + libElement.append(to_plist(instanceObject.lib)) + instanceElement.append(libElement) self.root.findall('.instances')[0].append(instanceElement) def _addSource(self, sourceObject): @@ -540,6 +580,11 @@ class BaseDocWriter(object): sourceElement.append(locationElement) self.root.findall('.sources')[0].append(sourceElement) + def _addLib(self, dict): + libElement = ET.Element('lib') + libElement.append(to_plist(dict)) + self.root.append(libElement) + def _writeGlyphElement(self, instanceElement, instanceObject, glyphName, data): glyphElement = ET.Element('glyph') if data.get('mute'): @@ -596,6 +641,7 @@ class BaseDocReader(object): self.readRules() self.readSources() self.readInstances() + self.readLib() def getSourcePaths(self, makeGlyphs=True, makeKerning=True, makeInfo=True): paths = [] @@ -874,8 +920,14 @@ class BaseDocReader(object): self.readGlyphElement(glyphElement, instanceObject) for infoElement in instanceElement.findall("info"): self.readInfoElement(infoElement, instanceObject) + for libElement in instanceElement.findall('lib'): + self.readLibElement(libElement, instanceObject) self.documentObject.instances.append(instanceObject) + def readLibElement(self, libElement, instanceObject): + """Read the lib element for the given instance.""" + instanceObject.lib = from_plist(libElement.getchildren()[0]) + def readInfoElement(self, infoElement, instanceObject): """ Read the info element. @@ -962,12 +1014,18 @@ class BaseDocReader(object): glyphData['masters'] = glyphSources instanceObject.glyphs[glyphName] = glyphData + def readLib(self): + """Read the lib element for the whole document.""" + for libElement in self.root.findall(".lib"): + self.documentObject.lib = from_plist(libElement.getchildren()[0]) + class DesignSpaceDocument(object): """ Read, write data from the designspace file""" def __init__(self, readerClass=None, writerClass=None): self.logger = logging.getLogger("DesignSpaceDocumentLog") self.path = None + self.filename = None self.formatVersion = None self.sources = [] self.instances = [] @@ -975,6 +1033,7 @@ class DesignSpaceDocument(object): self.rules = [] self.default = None # name of the default master self.defaultLoc = None + self.lib = {} # if readerClass is not None: self.readerClass = readerClass @@ -1086,7 +1145,7 @@ class DesignSpaceDocument(object): if self.path is not None: descriptor.filename = self._posixRelativePath(descriptor.path) if instances: - for descriptor in self.instances: + for descriptor in self.instances: if descriptor.filename is not None and not force: continue if self.path is not None: