Skip to content

Instantly share code, notes, and snippets.

@madig
Created May 18, 2018 10:31
Show Gist options
  • Save madig/5df1e759f9108cc9cd729a4b6c3767ff to your computer and use it in GitHub Desktop.
Save madig/5df1e759f9108cc9cd729a4b6c3767ff to your computer and use it in GitHub Desktop.
--- c:\\...\\development\\glyphslib\\Lib\\glyphsLib\\classes.py (original)
+++ c:\\...\\development\\glyphslib\\Lib\\glyphsLib\\classes.py (refactored)
@@ -33,6 +33,36 @@
from collections import OrderedDict
from fontTools.misc.py23 import unicode, basestring, UnicodeIO, unichr, open
from glyphsLib.affine import Affine
+from glyphsLib.classes import GSLayer
+from typing import List
+from glyphsLib.classes import GSPath
+from typing import Optional
+from glyphsLib.classes import GSNode
+from _sre import SRE_Match
+from glyphsLib.classes import GSBackgroundLayer
+from typing import Union
+from glyphsLib.classes import segment
+from glyphsLib.types import Rect
+from glyphsLib.types import Point
+from typing import Tuple
+from glyphsLib.classes import GSGlyph
+from typing import Any
+from typing import Dict
+from mypy_extensions import NoReturn
+from glyphsLib.types import UnicodesList
+from glyphsLib.classes import GSFeature
+from glyphsLib.classes import GSInstance
+from typing import Callable
+from typing import Iterator
+from glyphsLib.classes import GSCustomParameter
+from glyphsLib.classes import GSFontMaster
+from glyphsLib.classes import GSClass
+from glyphsLib.classes import LayersIterator
+from glyphsLib.classes import GSAnchor
+from glyphsLib.classes import LayerAnchorsProxy
+from glyphsLib.classes import GSComponent
+from glyphsLib.classes import GSAlignmentZone
+from pathlib import WindowsPath
logger = logging.getLogger(__name__)
@@ -188,6 +218,7 @@
def parse_hint_target(line=None):
+ # type: (Optional[str]) -> Optional[Point]
if line is None:
return None
if line[0] == "{":
@@ -197,10 +228,12 @@
def isString(string):
+ # type: (str) -> bool
return isinstance(string, (str, unicode))
def transformStructToScaleAndRotation(transform):
+ # type: (Union[List[float], List[int]]) -> Tuple[float, float, float]
Det = transform[0] * transform[3] - transform[1] * transform[2]
_sX = math.sqrt(math.pow(transform[0], 2) + math.pow(transform[1], 2))
_sY = math.sqrt(math.pow(transform[2], 2) + math.pow(transform[3], 2))
@@ -255,6 +288,7 @@
_wrapperKeysTranslate = {}
def __init__(self):
+ # type: () -> None
for key in self._classesForName.keys():
if not hasattr(self, key):
klass = self._classesForName[key]
@@ -270,12 +304,14 @@
setattr(self, key, value)
def __repr__(self):
+ # type: () -> str
content = ""
if hasattr(self, "_dict"):
content = str(self._dict)
return "<%s %s>" % (self.__class__.__name__, content)
def classForName(self, name):
+ # type: (str) -> Union[Callable, None, type]
return self._classesForName.get(name, str)
def default_attr_value(self, attr_name):
@@ -292,6 +328,7 @@
# Users of the library should only rely on the object-oriented API that is
# documented at https://docu.glyphsapp.com/
def __setitem__(self, key, value):
+ # type: (str, Any) -> None
if isinstance(value, bytes) and key in self._classesForName:
new_type = self._classesForName[key]
if new_type is unicode:
@@ -305,6 +342,7 @@
setattr(self, key, value)
def shouldWriteValueForKey(self, key):
+ # type: (str) -> bool
getKey = self._wrapperKeysTranslate.get(key, key)
value = getattr(self, getKey)
klass = self._classesForName[key]
@@ -323,9 +361,11 @@
class Proxy(object):
def __init__(self, owner):
+ # type: (Any) -> None
self._owner = owner
def __repr__(self):
+ # type: () -> str
"""Return list-lookalike of representation string of objects"""
strings = []
for currItem in self:
@@ -333,12 +373,14 @@
return "(%s)" % (', '.join(strings))
def __len__(self):
+ # type: () -> int
values = self.values()
if values is not None:
return len(values)
return 0
def pop(self, i):
+ # type: (int) -> GSNode
if type(i) == int:
node = self[i]
del self[i]
@@ -347,12 +389,14 @@
raise(KeyError)
def __iter__(self):
+ # type: () -> Iterator[Any]
values = self.values()
if values is not None:
for element in values:
yield element
def index(self, value):
+ # type: (GSNode) -> int
return self.values().index(value)
def __copy__(self):
@@ -362,6 +406,7 @@
return [x.copy() for x in self.values()]
def setter(self, values):
+ # type: (List[GSCustomParameter]) -> None
method = self.setterMethod()
if type(values) == list:
method(values)
@@ -377,6 +422,7 @@
class LayersIterator:
def __init__(self, owner):
+ # type: (GSGlyph) -> None
self.curInd = 0
self._owner = owner
self._orderedLayers = None
@@ -388,6 +434,7 @@
return self.__next__()
def __next__(self):
+ # type: () -> GSLayer
if self._owner.parent:
if self.curInd >= len(self._owner.layers):
raise StopIteration
@@ -401,6 +448,7 @@
@property
def orderedLayers(self):
+ # type: () -> List[GSLayer]
if not self._orderedLayers:
glyphLayerIds = [
l.associatedMasterId
@@ -432,6 +480,7 @@
...
"""
def __getitem__(self, Key):
+ # type: (Union[int, str]) -> GSFontMaster
if type(Key) == slice:
return self.values().__getitem__(Key)
if type(Key) is int:
@@ -462,6 +511,7 @@
raise(KeyError)
def __delitem__(self, Key):
+ # type: (int) -> Optional[Any]
if type(Key) is int:
if Key < 0:
Key = self.__len__() + Key
@@ -471,9 +521,11 @@
return self.remove(OldFontMaster)
def values(self):
+ # type: () -> List[GSFontMaster]
return self._owner._masters
def append(self, FontMaster):
+ # type: (GSFontMaster) -> None
FontMaster.font = self._owner
if not FontMaster.id:
FontMaster.id = str(uuid.uuid4()).upper()
@@ -487,6 +539,7 @@
glyph.layers.append(newLayer)
def remove(self, FontMaster):
+ # type: (GSFontMaster) -> None
# First remove all layers in all glyphs that reference this master
for glyph in self._owner.glyphs:
@@ -497,14 +550,17 @@
self._owner._masters.remove(FontMaster)
def insert(self, Index, FontMaster):
+ # type: (int, GSFontMaster) -> None
FontMaster.font = self._owner
self._owner._masters.insert(Index, FontMaster)
def extend(self, FontMasters):
+ # type: (List[GSFontMaster]) -> None
for FontMaster in FontMasters:
self.append(FontMaster)
def setter(self, values):
+ # type: (List[GSFontMaster]) -> None
if isinstance(values, Proxy):
values = list(values)
self._owner._masters = values
@@ -521,6 +577,7 @@
...
"""
def __getitem__(self, key):
+ # type: (Union[int, str]) -> Optional[GSGlyph]
if type(key) == slice:
return self.values().__getitem__(key)
@@ -547,11 +604,13 @@
raise KeyError # TODO: add other access methods
def __contains__(self, item):
+ # type: (str) -> bool
if isString(item):
return self._get_glyph_by_string(item) is not None
return item in self._owner._glyphs
def _get_glyph_by_string(self, key):
+ # type: (str) -> Optional[GSGlyph]
# FIXME: (jany) looks inefficient
if isinstance(key, basestring):
# by glyph name
@@ -571,6 +630,7 @@
return None
def values(self):
+ # type: () -> List[GSGlyph]
return self._owner._glyphs
def items(self):
@@ -581,6 +641,7 @@
return items
def append(self, glyph):
+ # type: (GSGlyph) -> None
self._owner._setupGlyph(glyph)
self._owner._glyphs.append(glyph)
@@ -590,9 +651,11 @@
self._owner._glyphs.extend(list(objects))
def __len__(self):
+ # type: () -> int
return len(self._owner._glyphs)
def setter(self, values):
+ # type: (List[GSGlyph]) -> None
if isinstance(values, Proxy):
values = list(values)
self._owner._glyphs = values
@@ -608,6 +671,7 @@
class FontClassesProxy(Proxy):
def __getitem__(self, key):
+ # type: (Union[int, str]) -> GSClass
if isinstance(key, (slice, int)):
return self.values().__getitem__(key)
if isinstance(key, (str, unicode)):
@@ -629,6 +693,7 @@
raise KeyError
def __delitem__(self, key):
+ # type: (str) -> None
if isinstance(key, int):
del self.values()[key]
elif isinstance(key, (str, unicode)):
@@ -639,25 +704,31 @@
# FIXME: (jany) def __contains__
def append(self, item):
+ # type: (GSClass) -> None
self.values().append(item)
item._parent = self._owner
def insert(self, key, item):
+ # type: (int, GSClass) -> None
self.values().insert(key, item)
item._parent = self._owner
def extend(self, items):
+ # type: (List[GSClass]) -> None
self.values().extend(items)
for value in items:
value._parent = self._owner
def remove(self, item):
+ # type: (GSClass) -> None
self.values().remove(item)
def values(self):
+ # type: () -> List[GSClass]
return self._owner._classes
def setter(self, values):
+ # type: (List[GSClass]) -> None
if isinstance(values, Proxy):
values = list(values)
self._owner._classes = values
@@ -667,6 +738,7 @@
class GlyphLayerProxy(Proxy):
def __getitem__(self, key):
+ # type: (Union[int, str]) -> Optional[GSLayer]
self._ensureMasterLayers()
if isinstance(key, slice):
return self.values().__getitem__(key)
@@ -679,6 +751,7 @@
return self._owner._layers[key]
def __setitem__(self, key, layer):
+ # type: (str, GSLayer) -> None
if isinstance(key, int) and self._owner.parent:
OldLayer = self._owner._layers.values()[key]
if key < 0:
@@ -695,6 +768,7 @@
raise KeyError
def __delitem__(self, key):
+ # type: (int) -> None
if isinstance(key, int) and self._owner.parent:
if key < 0:
key = self.__len__() + key
@@ -703,20 +777,25 @@
del(self._owner._layers[key])
def __iter__(self):
+ # type: () -> LayersIterator
return LayersIterator(self._owner)
def __len__(self):
+ # type: () -> int
return len(self.values())
def keys(self):
+ # type: () -> odict_keys
self._ensureMasterLayers()
return self._owner._layers.keys()
def values(self):
+ # type: () -> odict_values
self._ensureMasterLayers()
return self._owner._layers.values()
def append(self, layer):
+ # type: (GSLayer) -> None
assert layer is not None
self._ensureMasterLayers()
if not layer.associatedMasterId:
@@ -728,17 +807,21 @@
self._owner._layers[layer.layerId] = layer
def extend(self, layers):
+ # type: (List[GSLayer]) -> None
for layer in layers:
self.append(layer)
def remove(self, layer):
+ # type: (GSLayer) -> Optional[Any]
return self._owner.removeLayerForKey_(layer.layerId)
def insert(self, index, layer):
+ # type: (int, GSLayer) -> None
self._ensureMasterLayers()
self.append(layer)
def setter(self, values):
+ # type: (List[GSLayer]) -> None
newLayers = OrderedDict()
if (type(values) == list or
type(values) == tuple or
@@ -755,6 +838,7 @@
self._owner._layers = newLayers
def _ensureMasterLayers(self):
+ # type: () -> None
# Ensure existence of master-linked layers (even for iteration, len() etc.) if accidentally deleted
if not self._owner.parent:
return
@@ -769,12 +853,14 @@
self.__setitem__(master.id, newLayer)
def plistArray(self):
+ # type: () -> List[GSLayer]
return list(self._owner._layers.values())
class LayerAnchorsProxy(Proxy):
def __getitem__(self, key):
+ # type: (Union[int, str]) -> GSAnchor
if isinstance(key, (slice, int)):
return self.values().__getitem__(key)
elif isinstance(key, (str, unicode)):
@@ -785,6 +871,7 @@
raise KeyError
def __setitem__(self, key, anchor):
+ # type: (str, GSAnchor) -> None
if isinstance(key, (str, unicode)):
anchor.name = key
for i, a in enumerate(self._owner._anchors):
@@ -797,6 +884,7 @@
raise TypeError
def __delitem__(self, key):
+ # type: (str) -> None
if isinstance(key, int):
del self._owner._anchors[key]
elif isinstance(key, (str, unicode)):
@@ -807,9 +895,11 @@
return
def values(self):
+ # type: () -> List[GSAnchor]
return self._owner._anchors
def append(self, anchor):
+ # type: (GSAnchor) -> None
for i, a in enumerate(self._owner._anchors):
if a.name == anchor.name:
anchor._parent = self._owner
@@ -821,11 +911,13 @@
raise ValueError("Anchor must have name")
def extend(self, anchors):
+ # type: (List[GSAnchor]) -> None
for anchor in anchors:
anchor._parent = self._owner
self._owner._anchors.extend(anchors)
def remove(self, anchor):
+ # type: (GSAnchor) -> Optional[Any]
if isinstance(anchor, (str, unicode)):
anchor = self.values()[anchor]
return self._owner._anchors.remove(anchor)
@@ -835,9 +927,11 @@
self._owner._anchors.insert(index, anchor)
def __len__(self):
+ # type: () -> int
return len(self._owner._anchors)
def setter(self, anchors):
+ # type: (Union[List[GSAnchor], LayerAnchorsProxy]) -> None
if isinstance(anchors, Proxy):
anchors = list(anchors)
self._owner._anchors = anchors
@@ -847,6 +941,7 @@
class IndexedObjectsProxy(Proxy):
def __getitem__(self, key):
+ # type: (Union[int, slice]) -> Any
if isinstance(key, (slice, int)):
return self.values().__getitem__(key)
else:
@@ -860,34 +955,42 @@
raise KeyError
def __delitem__(self, key):
+ # type: (int) -> None
if isinstance(key, int):
del self.values()[key]
else:
raise KeyError
def values(self):
+ # type: () -> Any
return getattr(self._owner, self._objects_name)
def append(self, value):
+ # type: (Any) -> None
self.values().append(value)
value._parent = self._owner
def extend(self, values):
+ # type: (Any) -> None
self.values().extend(values)
for value in values:
value._parent = self._owner
def remove(self, value):
+ # type: (Any) -> None
self.values().remove(value)
def insert(self, index, value):
+ # type: (int, Any) -> None
self.values().insert(index, value)
value._parent = self._owner
def __len__(self):
+ # type: () -> int
return len(self.values())
def setter(self, values):
+ # type: (Union[List[GSComponent], List[GSNode], List[GSPath]]) -> None
setattr(self._owner, self._objects_name, list(values))
for value in self.values():
value._parent = self._owner
@@ -897,6 +1000,7 @@
_objects_name = "_paths"
def __init__(self, owner):
+ # type: (Union[GSBackgroundLayer, GSLayer]) -> None
super(LayerPathsProxy, self).__init__(owner)
@@ -904,6 +1008,7 @@
_objects_name = "_hints"
def __init__(self, owner):
+ # type: (Union[GSBackgroundLayer, GSLayer]) -> None
super(LayerHintsProxy, self).__init__(owner)
@@ -911,6 +1016,7 @@
_objects_name = "_components"
def __init__(self, owner):
+ # type: (Union[GSBackgroundLayer, GSLayer]) -> None
super(LayerComponentsProxy, self).__init__(owner)
@@ -918,6 +1024,7 @@
_objects_name = "_annotations"
def __init__(self, owner):
+ # type: (Union[GSBackgroundLayer, GSLayer]) -> None
super(LayerAnnotationProxy, self).__init__(owner)
@@ -925,6 +1032,7 @@
_objects_name = "_guides"
def __init__(self, owner):
+ # type: (Union[GSBackgroundLayer, GSLayer]) -> None
super(LayerGuideLinesProxy, self).__init__(owner)
@@ -932,11 +1040,13 @@
_objects_name = "_nodes"
def __init__(self, owner):
+ # type: (GSPath) -> None
super(PathNodesProxy, self).__init__(owner)
class CustomParametersProxy(Proxy):
def __getitem__(self, key):
+ # type: (str) -> Any
if isinstance(key, slice):
return self.values().__getitem__(key)
if isinstance(key, int):
@@ -948,11 +1058,13 @@
return None
def _get_parameter_by_key(self, key):
+ # type: (str) -> Optional[GSCustomParameter]
for customParameter in self._owner._customParameters:
if customParameter.name == key:
return customParameter
def __setitem__(self, key, value):
+ # type: (str, Any) -> None
customParameter = self._get_parameter_by_key(key)
if customParameter is not None:
customParameter.value = value
@@ -961,6 +1073,7 @@
self._owner._customParameters.append(parameter)
def __delitem__(self, key):
+ # type: (Union[int, str]) -> None
if isinstance(key, int):
del self._owner._customParameters[key]
elif isinstance(key, basestring):
@@ -971,50 +1084,60 @@
raise KeyError
def __contains__(self, item):
+ # type: (str) -> bool
if isString(item):
return self.__getitem__(item) is not None
return item in self._owner._customParameters
def __iter__(self):
+ # type: () -> Iterator[Union[Iterator, Iterator[GSCustomParameter]]]
for index in range(len(self._owner._customParameters)):
yield self._owner._customParameters[index]
def append(self, parameter):
+ # type: (GSCustomParameter) -> None
parameter.parent = self._owner
self._owner._customParameters.append(parameter)
def extend(self, parameters):
+ # type: (List[GSCustomParameter]) -> None
for parameter in parameters:
parameter.parent = self._owner
self._owner._customParameters.extend(parameters)
def remove(self, parameter):
+ # type: (GSCustomParameter) -> None
if isString(parameter):
parameter = self.__getitem__(parameter)
self._owner._customParameters.remove(parameter)
def insert(self, index, parameter):
+ # type: (int, GSCustomParameter) -> None
parameter.parent = self._owner
self._owner._customParameters.insert(index, parameter)
def __len__(self):
+ # type: () -> int
return len(self._owner._customParameters)
def values(self):
return self._owner._customParameters
def __setter__(self, parameters):
+ # type: (List[GSCustomParameter]) -> None
for parameter in parameters:
parameter.parent = self._owner
self._owner._customParameters = parameters
def setterMethod(self):
+ # type: () -> Callable
return self.__setter__
class UserDataProxy(Proxy):
def __getitem__(self, key):
+ # type: (str) -> Any
if self._owner._userData is None:
return None
# This is not the normal `dict` behaviour, because this does not raise
@@ -1022,16 +1145,19 @@
return self._owner._userData.get(key)
def __setitem__(self, key, value):
+ # type: (str, Any) -> None
if self._owner._userData is not None:
self._owner._userData[key] = value
else:
self._owner._userData = {key: value}
def __delitem__(self, key):
+ # type: (str) -> None
if self._owner._userData is not None and key in self._owner._userData:
del self._owner._userData[key]
def __contains__(self, item):
+ # type: (str) -> bool
if self._owner._userData is None:
return False
return item in self._owner._userData
@@ -1045,21 +1171,25 @@
yield value
def values(self):
+ # type: () -> Union[List, dict_values]
if self._owner._userData is None:
return []
return self._owner._userData.values()
def keys(self):
+ # type: () -> Union[List, dict_keys, odict_keys]
if self._owner._userData is None:
return []
return self._owner._userData.keys()
def get(self, key):
+ # type: (str) -> Optional[Dict[str, List[str]]]
if self._owner._userData is None:
return None
return self._owner._userData.get(key)
def setter(self, values):
+ # type: (Dict[str, Any]) -> None
self._owner._userData = values
@@ -1116,6 +1246,7 @@
'GASP Table'))
def __init__(self, name="New Value", value="New Parameter"):
+ # type: (str, Any) -> None
self.name = name
self.value = value
@@ -1124,15 +1255,18 @@
(self.__class__.__name__, self.name, self._value)
def plistValue(self):
+ # type: () -> str
string = UnicodeIO()
writer = Writer(string)
writer.writeDict({'name': self.name, 'value': self.value})
return string.getvalue()
def getValue(self):
+ # type: () -> Any
return self._value
def setValue(self, value):
+ # type: (Any) -> None
"""Cast some known data in custom parameters."""
if self.name in self._CUSTOM_INT_PARAMS:
value = int(value)
@@ -1154,11 +1288,13 @@
class GSAlignmentZone(GSBase):
def __init__(self, pos=0, size=20):
+ # type: (int, int) -> None
super(GSAlignmentZone, self).__init__()
self.position = pos
self.size = size
def read(self, src):
+ # type: (str) -> GSAlignmentZone
if src is not None:
p = Point(src)
self.position = float(p.value[0])
@@ -1166,13 +1302,16 @@
return self
def __repr__(self):
+ # type: () -> str
return "<%s pos:%g size:%g>" % \
(self.__class__.__name__, self.position, self.size)
def __lt__(self, other):
+ # type: (GSAlignmentZone) -> bool
return (self.position, self.size) < (other.position, other.size)
def plistValue(self):
+ # type: () -> str
return '"{%s, %s}"' % \
(floatToString(self.position), floatToString(self.size))
@@ -1193,15 +1332,18 @@
}
def __init__(self):
+ # type: () -> None
super(GSGuideLine, self).__init__()
def __repr__(self):
+ # type: () -> str
return "<%s x=%.1f y=%.1f angle=%.1f>" % \
(self.__class__.__name__, self.position.x, self.position.y,
self.angle)
@property
def parent(self):
+ # type: () -> GSLayer
return self._parent
@@ -1286,6 +1428,7 @@
)
def __init__(self):
+ # type: () -> None
super(GSFontMaster, self).__init__()
self.id = str(uuid.uuid4())
self.font = None
@@ -1298,10 +1441,12 @@
setattr(self, 'customValue' + number, 0.0)
def __repr__(self):
+ # type: () -> str
return '<GSFontMaster "%s" width %s weight %s>' % \
(self.name, self.widthValue, self.weightValue)
def shouldWriteValueForKey(self, key):
+ # type: (str) -> bool
if key in ("weight", "width"):
return getattr(self, key) != "Regular"
if key in ("xHeight", "capHeight", "ascender", "descender"):
@@ -1314,6 +1459,7 @@
@property
def name(self):
+ # type: () -> str
name = self.customParameters['Master Name']
if name:
return name
@@ -1323,6 +1469,7 @@
@name.setter
def name(self, name):
+ # type: (str) -> None
"""This function will take the given name and split it into components
weight, width, customName, and possibly the full name.
This is what Glyphs 1113 seems to be doing, approximately.
@@ -1330,7 +1477,13 @@
weight, width, custom_name = self._splitName(name)
self.set_all_name_components(name, weight, width, custom_name)
- def set_all_name_components(self, name, weight, width, custom_name):
+ def set_all_name_components(self,
+ name, # type: Optional[str]
+ weight, # type: Optional[str]
+ width, # type: Optional[str]
+ custom_name, # type: Optional[str]
+ ):
+ # type: (...) -> None
"""This function ensures that after being called, the master.name,
master.weight, master.width, and master.customName match the given
values.
@@ -1347,6 +1500,7 @@
self.customParameters['Master Name'] = name
def _joinName(self):
+ # type: () -> str
names = [self.width, self.weight, self.customName]
names = [n for n in names if n] # Remove None and empty string
# Remove all occurences of 'Regular'
@@ -1357,6 +1511,7 @@
return " ".join(list(names))
def _splitName(self, value):
+ # type: (str) -> Tuple[str, str, str]
if value is None:
value = ''
weight = 'Regular'
@@ -1407,6 +1562,7 @@
def __init__(self, position=(0, 0), nodetype=LINE,
smooth=False, name=None):
+ # type: (Union[Tuple[int, int], Point], str, bool, None) -> None
super(GSNode, self).__init__()
self.position = Point(position[0], position[1])
self.type = nodetype
@@ -1416,6 +1572,7 @@
self.name = name
def __repr__(self):
+ # type: () -> str
content = self.type
if self.smooth:
content += " smooth"
@@ -1429,9 +1586,11 @@
@property
def parent(self):
+ # type: () -> GSPath
return self._parent
def plistValue(self):
+ # type: () -> str
content = self.type.upper()
if self.smooth:
content += " SMOOTH"
@@ -1446,6 +1605,7 @@
content)
def read(self, line):
+ # type: (str) -> GSNode
m = self._PLIST_VALUE_RE.match(line).groups()
self.position = Point(float(m[0]), float(m[1]))
self.type = m[2].lower()
@@ -1460,12 +1620,14 @@
@property
def name(self):
+ # type: () -> str
if "name" in self.userData:
return self.userData["name"]
return None
@name.setter
def name(self, value):
+ # type: (Optional[str]) -> None
if value is None:
if "name" in self.userData:
del(self.userData["name"])
@@ -1474,11 +1636,13 @@
@property
def index(self):
+ # type: () -> int
assert self.parent
return self.parent.nodes.index(self)
@property
def nextNode(self):
+ # type: () -> GSNode
assert self.parent
index = self.index
if index == (len(self.parent.nodes) - 1):
@@ -1488,6 +1652,7 @@
@property
def prevNode(self):
+ # type: () -> GSNode
assert self.parent
index = self.index
if index == 0:
@@ -1496,6 +1661,7 @@
return self.parent.nodes[index - 1]
def makeNodeFirst(self):
+ # type: () -> None
assert self.parent
if self.type == 'offcurve':
raise ValueError('Off-curve points cannot become start points.')
@@ -1505,6 +1671,7 @@
self.parent.nodes = newNodes
def toggleConnection(self):
+ # type: () -> None
self.smooth = not self.smooth
# TODO
@@ -1518,6 +1685,7 @@
raise OnlyInGlyphsAppError
def _encode_dict_as_string(self, value):
+ # type: (str) -> str
"""Takes the PLIST string of a dict, and returns the same string
encoded such that it can be included in the string representation
of a GSNode."""
@@ -1534,6 +1702,7 @@
@staticmethod
def _unescape_char(m):
+ # type: (SRE_Match) -> str
char = m.group(1)
if char == '\\':
return '\\'
@@ -1544,10 +1713,12 @@
return m.group(0)
def _decode_dict_as_string(self, value):
+ # type: (str) -> str
"""Reverse function of _encode_string_as_dict"""
return self._ESCAPED_CHAR_RE.sub(self._unescape_char, value)
def _indices(self):
+ # type: () -> Point
"""Find the path_index and node_index that identify the given node."""
path = self.parent
layer = path.parent
@@ -1570,14 +1741,17 @@
_parent = None
def __init__(self):
+ # type: () -> None
super(GSPath, self).__init__()
self.nodes = []
@property
def parent(self):
+ # type: () -> Union[GSBackgroundLayer, GSLayer]
return self._parent
def shouldWriteValueForKey(self, key):
+ # type: (str) -> bool
if key == "closed":
return True
return super(GSPath, self).shouldWriteValueForKey(key)
@@ -1588,6 +1762,7 @@
@property
def segments(self):
+ # type: () -> List[segment]
self._segments = []
self._segmentLength = 0
@@ -1627,6 +1802,7 @@
raise TypeError
def setSegments(self, segments):
+ # type: (List[segment]) -> None
self.nodes = []
for segment in segments:
if len(segment.nodes) == 2 or len(segment.nodes) == 4:
@@ -1636,6 +1812,7 @@
@property
def bounds(self):
+ # type: () -> Rect
left, bottom, right, top = None, None, None, None
for segment in self.segments:
newLeft, newBottom, newRight, newTop = segment.bbox()
@@ -1659,6 +1836,7 @@
@property
def direction(self):
+ # type: () -> int
direction = 0
for i in range(len(self.nodes)):
thisNode = self.nodes[i]
@@ -1678,6 +1856,7 @@
raise OnlyInGlyphsAppError
def reverse(self):
+ # type: () -> None
segments = list(reversed(self.segments))
for s, segment in enumerate(segments):
segment.nodes = list(reversed(segment.nodes))
@@ -1721,6 +1900,7 @@
class segment(list):
def appendNode(self, node):
+ # type: (GSNode) -> None
if not hasattr(self, 'nodes'): # instead of defining this in __init__(), because I hate super()
self.nodes = []
self.nodes.append(node)
@@ -1745,6 +1925,7 @@
return self.parent._segments[index - 1]
def bbox(self):
+ # type: () -> Tuple[float, float, float, float]
if len(self) == 2:
left = min(self[0].x, self[1].x)
bottom = min(self[0].y, self[1].y)
@@ -1757,7 +1938,17 @@
else:
raise ValueError
- def bezierMinMax(self, x0, y0, x1, y1, x2, y2, x3, y3):
+ def bezierMinMax(self,
+ x0, # type: float
+ y0, # type: float
+ x1, # type: float
+ y1, # type: float
+ x2, # type: float
+ y2, # type: float
+ x3, # type: float
+ y3, # type: float
+ ):
+ # type: (...) -> Tuple[float, float, float, float]
tvalues = []
xvalues = []
yvalues = []
@@ -1832,6 +2023,7 @@
# TODO: glyph arg is required
def __init__(self, glyph="", offset=(0, 0), scale=(1, 1), transform=None):
+ # type: (str, Tuple[int, int], Tuple[int, int], None) -> None
super(GSComponent, self).__init__()
if transform is None:
@@ -1848,10 +2040,12 @@
self.name = glyph.name
def __repr__(self):
+ # type: () -> str
return '<GSComponent "%s" x=%.1f y=%.1f>' % \
(self.name, self.transform[4], self.transform[5])
def shouldWriteValueForKey(self, key):
+ # type: (str) -> bool
if key == "piece":
value = self.smartComponentValues
return len(value) > 0
@@ -1859,24 +2053,29 @@
@property
def parent(self):
+ # type: () -> GSLayer
return self._parent
# .position
@property
def position(self):
+ # type: () -> Point
return Point(self.transform[4], self.transform[5])
@position.setter
def position(self, value):
+ # type: (Point) -> None
self.transform[4] = value[0]
self.transform[5] = value[1]
# .scale
@property
def scale(self):
+ # type: () -> Tuple[float, float]
self._sX, self._sY, self._R = transformStructToScaleAndRotation(self.transform.value)
return (self._sX, self._sY)
@scale.setter
def scale(self, value):
+ # type: (float) -> None
self._sX, self._sY, self._R = transformStructToScaleAndRotation(self.transform.value)
if type(value) in [int, float]:
self._sX = value
@@ -1890,20 +2089,24 @@
# .rotation
@property
def rotation(self):
+ # type: () -> float
self._sX, self._sY, self._R = transformStructToScaleAndRotation(self.transform.value)
return self._R
@rotation.setter
def rotation(self, value):
+ # type: (int) -> None
self._sX, self._sY, self._R = transformStructToScaleAndRotation(self.transform.value)
self._R = value
self.updateAffineTransform()
def updateAffineTransform(self):
+ # type: () -> None
affine = list(Affine.translation(self.transform[4], self.transform[5]) * Affine.scale(self._sX, self._sY) * Affine.rotation(self._R))[:6]
self.transform = Transform(affine[0], affine[1], affine[3], affine[4], affine[2], affine[5])
@property
def componentName(self):
+ # type: () -> str
return self.name
@componentName.setter
def componentName(self, value):
@@ -1911,13 +2114,16 @@
@property
def component(self):
+ # type: () -> GSGlyph
return self.parent.parent.parent.glyphs[self.name]
@property
def layer(self):
+ # type: () -> GSLayer
return self.parent.parent.parent.glyphs[self.name].layers[self.parent.layerId]
def applyTransformation(self, x, y):
+ # type: (float, float) -> Tuple[float, float]
x *= self.scale[0]
y *= self.scale[1]
x += self.position.x
@@ -1928,6 +2134,7 @@
@property
def bounds(self):
+ # type: () -> Rect
bounds = self.layer.bounds
if bounds is not None:
left, bottom, width, height = self.layer.bounds
@@ -1962,6 +2169,7 @@
)
def shouldWriteValueForKey(self, key):
+ # type: (str) -> bool
if key in ("bottomValue", "topValue"):
return True
return super(GSSmartComponentAxis, self).shouldWriteValueForKey(key)
@@ -1978,6 +2186,7 @@
}
def __init__(self, name=None, position=None):
+ # type: (Optional[str], Optional[Point]) -> None
super(GSAnchor, self).__init__()
if name is not None:
self.name = name
@@ -1985,17 +2194,20 @@
self.position = position
def __repr__(self):
+ # type: () -> str
return '<%s "%s" x=%.1f y=%.1f>' % \
(self.__class__.__name__, self.name, self.position[0],
self.position[1])
def shouldWriteValueForKey(self, key):
+ # type: (str) -> bool
if key == 'position':
return True
return super(GSAnchor, self).shouldWriteValueForKey(key)
@property
def parent(self):
+ # type: () -> GSLayer
return self._parent
@@ -2039,6 +2251,7 @@
)
def shouldWriteValueForKey(self, key):
+ # type: (str) -> bool
if key == "settings" and (self.settings is None or len(self.settings) == 0):
return None
return super(GSHint, self).shouldWriteValueForKey(key)
@@ -2060,6 +2273,7 @@
return self.width
def __repr__(self):
+ # type: () -> str
if self.horizontal:
direction = "horizontal"
else:
@@ -2076,10 +2290,12 @@
@property
def parent(self):
+ # type: () -> GSLayer
return self._parent
@property
def originNode(self):
+ # type: () -> GSNode
if self._originNode is not None:
return self._originNode
if self._origin is not None:
@@ -2087,11 +2303,13 @@
@originNode.setter
def originNode(self, node):
+ # type: (GSNode) -> None
self._originNode = node
self._origin = None
@property
def origin(self):
+ # type: () -> Optional[Point]
if self._origin is not None:
return self._origin
if self._originNode is not None:
@@ -2099,11 +2317,13 @@
@origin.setter
def origin(self, origin):
+ # type: (Optional[Point]) -> None
self._origin = origin
self._originNode = None
@property
def targetNode(self):
+ # type: () -> GSNode
if self._targetNode is not None:
return self._targetNode
if self._target is not None:
@@ -2111,11 +2331,13 @@
@targetNode.setter
def targetNode(self, node):
+ # type: (GSNode) -> None
self._targetNode = node
self._target = None
@property
def target(self):
+ # type: () -> Optional[Point]
if self._target is not None:
return self._target
if self._targetNode is not None:
@@ -2123,6 +2345,7 @@
@target.setter
def target(self, target):
+ # type: (Optional[Point]) -> None
self._target = target
self._targetNode = None
@@ -2135,11 +2358,13 @@
@otherNode1.setter
def otherNode1(self, node):
+ # type: (GSNode) -> None
self._otherNode1 = node
self._other1 = None
@property
def other1(self):
+ # type: () -> Optional[Point]
if self._other1 is not None:
return self._other1
if self._otherNode1 is not None:
@@ -2147,6 +2372,7 @@
@other1.setter
def other1(self, other1):
+ # type: (None) -> None
self._other1 = other1
self._otherNode1 = None
@@ -2159,11 +2385,13 @@
@otherNode2.setter
def otherNode2(self, node):
+ # type: (GSNode) -> None
self._otherNode2 = node
self._other2 = None
@property
def other2(self):
+ # type: () -> Optional[Point]
if self._other2 is not None:
return self._other2
if self._otherNode2 is not None:
@@ -2171,6 +2399,7 @@
@other2.setter
def other2(self, other2):
+ # type: (None) -> None
self._other2 = other2
self._otherNode2 = None
@@ -2185,19 +2414,23 @@
}
def __init__(self, name="xxxx", code=""):
+ # type: (str, str) -> None
super(GSFeature, self).__init__()
self.name = name
self.code = code
def shouldWriteValueForKey(self, key):
+ # type: (str) -> bool
if key == "code":
return True
return super(GSFeature, self).shouldWriteValueForKey(key)
def getCode(self):
+ # type: () -> str
return self._code
def setCode(self, code):
+ # type: (str) -> None
replacements = (
('\\012', '\n'), ('\\011', '\t'), ('\\U2018', "'"),
('\\U2019', "'"), ('\\U201C', '"'), ('\\U201D', '"'))
@@ -2207,6 +2440,7 @@
code = property(getCode, setCode)
def __repr__(self):
+ # type: () -> str
return '<%s "%s">' % \
(self.__class__.__name__, self.name)
@@ -2312,6 +2546,7 @@
pass
def __init__(self):
+ # type: () -> None
super(GSInstance, self).__init__()
# TODO: (jany) review this and move as much as possible into
# "_defaultsForKey"
@@ -2329,15 +2564,18 @@
@property
def exports(self):
+ # type: () -> bool
"""Deprecated alias for `active`, which is in the documentation."""
return self.active
@exports.setter
def exports(self, value):
+ # type: (bool) -> None
self.active = value
@property
def familyName(self):
+ # type: () -> str
value = self.customParameters["familyName"]
if value:
return value
@@ -2345,10 +2583,12 @@
@familyName.setter
def familyName(self, value):
+ # type: (str) -> None
self.customParameters["familyName"] = value
@property
def preferredFamily(self):
+ # type: () -> str
value = self.customParameters["preferredFamily"]
if value:
return value
@@ -2356,10 +2596,12 @@
@preferredFamily.setter
def preferredFamily(self, value):
+ # type: (str) -> None
self.customParameters["preferredFamily"] = value
@property
def preferredSubfamilyName(self):
+ # type: () -> str
value = self.customParameters["preferredSubfamilyName"]
if value:
return value
@@ -2367,10 +2609,12 @@
@preferredSubfamilyName.setter
def preferredSubfamilyName(self, value):
+ # type: (str) -> None
self.customParameters["preferredSubfamilyName"] = value
@property
def windowsFamily(self):
+ # type: () -> str
value = self.customParameters["styleMapFamilyName"]
if value:
return value
@@ -2381,10 +2625,12 @@
@windowsFamily.setter
def windowsFamily(self, value):
+ # type: (str) -> None
self.customParameters["styleMapFamilyName"] = value
@property
def windowsStyle(self):
+ # type: () -> str
if self.name in ("Regular", "Bold", "Italic", "Bold Italic"):
return self.name
else:
@@ -2392,6 +2638,7 @@
@property
def windowsLinkedToStyle(self):
+ # type: () -> str
value = self.linkStyle
return value
if self.name in ("Regular", "Bold", "Italic", "Bold Italic"):
@@ -2401,6 +2648,7 @@
@property
def fontName(self):
+ # type: () -> str
value = self.customParameters["postscriptFontName"]
if value:
return value
@@ -2409,10 +2657,12 @@
@fontName.setter
def fontName(self, value):
+ # type: (str) -> None
self.customParameters["postscriptFontName"] = value
@property
def fullName(self):
+ # type: () -> str
value = self.customParameters["postscriptFullName"]
if value:
return value
@@ -2420,6 +2670,7 @@
@fullName.setter
def fullName(self, value):
+ # type: (str) -> None
self.customParameters["postscriptFullName"] = value
@@ -2440,6 +2691,7 @@
}
def __init__(self, path=None):
+ # type: (Optional[str]) -> None
super(GSBackgroundImage, self).__init__()
self.imagePath = path
self._sX, self._sY, self._R = transformStructToScaleAndRotation(self.transform.value)
@@ -2450,9 +2702,11 @@
# .path
@property
def path(self):
+ # type: () -> str
return self.imagePath
@path.setter
def path(self, value):
+ # type: (str) -> None
# FIXME: (jany) use posix pathnames here?
# FIXME: (jany) the following code must have never been tested.
# Also it would require to keep track of the parent for background
@@ -2465,18 +2719,22 @@
# .position
@property
def position(self):
+ # type: (Point) -> Optional[Any]
return Point(self.transform[4], self.transform[5])
@position.setter
def position(self, value):
+ # type: (Point) -> None
self.transform[4] = value[0]
self.transform[5] = value[1]
# .scale
@property
def scale(self):
+ # type: (Tuple[float, float]) -> Optional[Any]
return (self._sX, self._sY)
@scale.setter
def scale(self, value):
+ # type: (Tuple[float, float]) -> None
if type(value) in [int, float]:
self._sX = value
self._sY = value
@@ -2489,24 +2747,29 @@
# .rotation
@property
def rotation(self):
+ # type: (float) -> Optional[Any]
return self._R
@rotation.setter
def rotation(self, value):
+ # type: (float) -> None
self._R = value
self.updateAffineTransform()
# .alpha
@property
def alpha(self):
+ # type: () -> int
return self._alpha
@alpha.setter
def alpha(self, value):
+ # type: (int) -> None
if not 10 <= value <= 100:
value = 50
self._alpha = value
def updateAffineTransform(self):
+ # type: () -> None
affine = list(Affine.translation(self.transform[4], self.transform[5]) * Affine.scale(self._sX, self._sY) * Affine.rotation(self._R))[:6]
self.transform = Transform(affine[0], affine[1], affine[3], affine[4], affine[2], affine[5])
@@ -2569,6 +2832,7 @@
)
def __init__(self):
+ # type: () -> None
super(GSLayer, self).__init__()
self.parent = None
self._anchors = []
@@ -2583,6 +2847,7 @@
self.backgroundImage = None
def __repr__(self):
+ # type: () -> str
name = self.name
try:
# assert self.name
@@ -2602,10 +2867,12 @@
@property
def layerId(self):
+ # type: () -> str
return self._layerId
@layerId.setter
def layerId(self, value):
+ # type: (str) -> None
self._layerId = value
# Update the layer map in the parent glyph, if any.
# The "hasattr" is here because this setter is called by the GSBase
@@ -2630,6 +2897,7 @@
return master
def shouldWriteValueForKey(self, key):
+ # type: (str) -> bool
if key == "width":
return True
if key == "associatedMasterId":
@@ -2641,6 +2909,7 @@
@property
def name(self):
+ # type: () -> str
if (self.associatedMasterId and
self.associatedMasterId == self.layerId and self.parent):
master = self.parent.parent.masterForId(self.associatedMasterId)
@@ -2650,6 +2919,7 @@
@name.setter
def name(self, value):
+ # type: (str) -> None
self._name = value
anchors = property(
@@ -2682,16 +2952,19 @@
@property
def smartComponentPoleMapping(self):
+ # type: () -> Union[Dict[str, int], Dict[str, str]]
if "PartSelection" not in self.userData:
self.userData["PartSelection"] = {}
return self.userData["PartSelection"]
@smartComponentPoleMapping.setter
def smartComponentPoleMapping(self, value):
+ # type: (Dict[str, int]) -> None
self.userData["PartSelection"] = value
@property
def bounds(self):
+ # type: () -> Rect
left, bottom, right, top = None, None, None, None
for item in self.paths.values() + self.components.values():
@@ -2721,6 +2994,7 @@
return Rect(Point(left, bottom), Point(right - left, top - bottom))
def _find_node_by_indices(self, point):
+ # type: (Point) -> GSNode
""""Find the GSNode that is refered to by the given indices.
See GSNode::_indices()
@@ -2732,6 +3006,7 @@
@property
def background(self):
+ # type: () -> GSBackgroundLayer
"""Only a getter on purpose. See the tests."""
if self._background is None:
self._background = GSBackgroundLayer()
@@ -2742,26 +3017,31 @@
# ::background?
@property
def hasBackground(self):
+ # type: () -> bool
return bool(self._background)
@property
def foreground(self):
+ # type: () -> NoReturn
"""Forbidden, and also forbidden to set it."""
raise AttributeError
class GSBackgroundLayer(GSLayer):
def shouldWriteValueForKey(self, key):
+ # type: (str) -> bool
if key == 'width':
return False
return super(GSBackgroundLayer, self).shouldWriteValueForKey(key)
@property
def background(self):
+ # type: () -> Optional[Any]
return None
@property
def foreground(self):
+ # type: () -> GSLayer
return self._foreground
# The width property of this class behaves like this in Glyphs:
@@ -2770,6 +3050,7 @@
# Reproduce this behaviour here so that the roundtrip does not rely on it.
@property
def width(self):
+ # type: () -> float
return 600.0
@width.setter
@@ -2856,6 +3137,7 @@
)
def __init__(self, name=None):
+ # type: (Optional[str]) -> None
super(GSGlyph, self).__init__()
self._layers = OrderedDict()
self.name = name
@@ -2869,6 +3151,7 @@
return '<GSGlyph "%s" with %s layers>' % (self.name, len(self.layers))
def shouldWriteValueForKey(self, key):
+ # type: (str) -> bool
if key in ("script", "category", "subCategory"):
return getattr(self, key) is not None
return super(GSGlyph, self).shouldWriteValueForKey(key)
@@ -2877,6 +3160,7 @@
lambda self, value: GlyphLayerProxy(self).setter(value))
def _setupLayer(self, layer, key):
+ # type: (GSLayer, str) -> None
assert isinstance(key, (str, unicode))
layer.parent = self
layer.layerId = key
@@ -2893,12 +3177,14 @@
# self._layers[key] = layer
def removeLayerForKey_(self, key):
+ # type: (str) -> None
for layer in list(self._layers):
if layer == key:
del self._layers[key]
@property
def string(self):
+ # type: () -> str
if self.unicode:
return unichr(int(self.unicode, 16))
@@ -2921,20 +3207,24 @@
@property
def unicode(self):
+ # type: () -> Optional[str]
if self._unicodes:
return self._unicodes[0]
return None
@unicode.setter
def unicode(self, unicode):
+ # type: (str) -> None
self._unicodes = UnicodesList(unicode)
@property
def unicodes(self):
+ # type: () -> UnicodesList
return self._unicodes
@unicodes.setter
def unicodes(self, unicodes):
+ # type: (Union[List[str], UnicodesList]) -> None
self._unicodes = UnicodesList(unicodes)
@@ -2988,6 +3278,7 @@
}
def __init__(self, path=None):
+ # type: (Union[None, WindowsPath, str]) -> None
super(GSFont, self).__init__()
self.familyName = "Unnamed font"
@@ -3021,14 +3312,17 @@
master.font = self
def __repr__(self):
+ # type: () -> str
return "<%s \"%s\">" % (self.__class__.__name__, self.familyName)
def shouldWriteValueForKey(self, key):
+ # type: (str) -> bool
if key in ("unitsPerEm", "versionMajor", "versionMinor"):
return True
return super(GSFont, self).shouldWriteValueForKey(key)
def save(self, path=None):
+ # type: (str) -> None
if path is None:
if self.filepath:
path = self.filepath
@@ -3040,9 +3334,11 @@
w.write(self)
def getVersionMinor(self):
+ # type: () -> int
return self._versionMinor
def setVersionMinor(self, value):
+ # type: (int) -> None
"""Ensure that the minor version number is between 0 and 999."""
assert value >= 0 and value <= 999
self._versionMinor = value
@@ -3053,6 +3349,7 @@
lambda self, value: FontGlyphsProxy(self).setter(value))
def _setupGlyph(self, glyph):
+ # type: (GSGlyph) -> None
glyph.parent = self
for layer in glyph.layers:
if (not hasattr(layer, "associatedMasterId") or
@@ -3062,10 +3359,12 @@
@property
def features(self):
+ # type: () -> List[GSFeature]
return self._features
@features.setter
def features(self, value):
+ # type: (List[GSFeature]) -> None
# FIXME: (jany) why not use Proxy like every other attribute?
# FIXME: (jany) do the same for featurePrefixes?
self._features = value
@@ -3076,6 +3375,7 @@
lambda self, value: FontFontMasterProxy(self).setter(value))
def masterForId(self, key):
+ # type: (str) -> Optional[GSFontMaster]
for master in self._masters:
if master.id == key:
return master
@@ -3084,10 +3384,12 @@
# FIXME: (jany) Why is this not a FontInstanceProxy?
@property
def instances(self):
+ # type: () -> List[GSInstance]
return self._instances
@instances.setter
def instances(self, value):
+ # type: (List[GSInstance]) -> None
self._instances = value
for i in self._instances:
i.parent = self
@@ -3106,10 +3408,12 @@
@property
def kerning(self):
+ # type: () -> Union[Dict[str, OrderedDict], OrderedDict]
return self._kerning
@kerning.setter
def kerning(self, kerning):
+ # type: (Union[Dict[str, OrderedDict], OrderedDict]) -> None
self._kerning = kerning
for master_id, master_map in kerning.items():
for left_glyph, glyph_map in master_map.items():
@@ -3122,6 +3426,7 @@
@property
def note(self):
+ # type: () -> str
value = self.customParameters["note"]
if value:
return value
@@ -3130,10 +3435,12 @@
@note.setter
def note(self, value):
+ # type: (str) -> None
self.customParameters["note"] = value
@property
def gridLength(self):
+ # type: () -> NoReturn
if (self.gridSubDivisions > 0):
return self.grid / self.gridSubDivisions
else:
@@ -3152,6 +3459,7 @@
def setKerningForPair(self, fontMasterId, leftKey, rightKey, value,
direction=LTR):
+ # type: (str, str, str, float, int) -> None
# TODO: (jany) understand and use the direction parameter
if not self._kerning:
self._kerning = {}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment