diff --git a/.gitignore b/.gitignore index 1ff6359..5772591 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,8 @@ test*.sp3 Tests/Instances Tests/_* Tests/20190830 benders/Skateboard Previews +/Tests/202206 discrete spaces/instances +/Tests/202206 discrete spaces/instances_mutMath +/Tests/202206 discrete spaces/instances_varlib +/_issues +/_old_stuff diff --git a/Designspaces with python.md b/Designspaces with python.md new file mode 100644 index 0000000..53eca94 --- /dev/null +++ b/Designspaces with python.md @@ -0,0 +1,120 @@ +# Designspaces and python + +Designspaces can do different things in different processes. Maybe you want to generate a variable font. Maybe you want to generate UFOs. Maybe you want to resample an existing designspace into something else. +While [fonttools.designspacelib](https://fonttools.readthedocs.io/en/latest/designspaceLib/index.html) contains the basic objects to construct, read and write designspaces, the [ufoProcessor package](https://github.com/LettError/ufoProcessor) can also generate instances. + +## Basics +First I have to make a `DesignSpaceDocument` object. This is an empty container, it has no masters, no axes, no path. + + from fontTools.designspaceLib import * + ds = DesignSpaceDocument() + +Now I will add an axis to the document by making an `AxisDescriptor` object and adding some values to its attributes. + + ad = AxisDescriptor() + ad.name = "weight" # readable name + ad.tag = "wght" # 4 letter tag + ad.minimum = 200 + ad.maximum = 1000 + ad.default = 400 + +Finally we add the axisDescriptor to the document: + + ds.addAxis(ad) + print(ds) + path = "my.designspace" + ds.write(path) + +This writes a very small designspace file: + + + + + + + + +Let's add some sources to the designspace: this needs the absolute path to the file (usually a ufo). When the document is saved the paths will be written as relative to the designspace document. A `SourceDescriptor` object has a lot of attributes, but `path` and `location` are the most important ones. + + s1 = SourceDescriptor() + s1.path = "geometryMaster1.ufo" + s1.location = dict(weight=200) + ds.addSource(s1) + + s2 = SourceDescriptor() + s2.path = "geometryMaster2.ufo" + s2.location = dict(weight=1000) + ds.addSource(s2) + +Let's add some instances. Instances are specific locations in the designspace with names and sometimes paths associated with them. In a variable font you might want these to show up as styles in a menu. But you could also generate UFOs from them. + + for w in [ad.minimum, .5*(ad.minimum + ad.default), ad.default, .5*(ad.maximum + ad.default), ad.maximum]: + # you will probably know more compact + # and easier ways to write this, go ahead! + i = InstanceDescriptor() + i.fileName = "InstanceFamily" + i.styleName = "Weight_%d" % w + i.location = dict(weight = w) + i.filename = "instance_%s.ufo" % i.styleName + ds.addInstance(i) + +The XML now has all it needs: an axis, some sources and ome instances. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Whoop well done. + diff --git a/Lib/ufoProcessor/__init__.py b/Lib/ufoProcessor/__init__.py index 3357fe9..a85db62 100644 --- a/Lib/ufoProcessor/__init__.py +++ b/Lib/ufoProcessor/__init__.py @@ -1,11 +1,36 @@ # coding: utf-8 - from __future__ import print_function, division, absolute_import -import os +""" + UFOProcessor no longer executes the rules in the designspace. Rules now have complex behaviour and need + to be properly compiled and executed by something that has an overview of all the features. + + 2022 work on support for DS5 + https://github.com/fonttools/fonttools/blob/main/Lib/fontTools/designspaceLib/split.py#L53 + + 2022 10 extrapolate in varlib only works for -1, 0, 1 systems. So we continue to rely on MutatorMath. + + +""" + + +""" + build() is a convenience function for reading and executing a designspace file. + documentPath: path to the designspace file. + outputUFOFormatVersion: integer, 2, 3. Format for generated UFOs. Note: can be different from source UFO format. + useVarlib: True if you want the geometry to be generated with varLib.model instead of mutatorMath. +""" + + + +## caching +import functools + +from warnings import warn +import os, time import logging, traceback import collections -# from pprint import pprint +import itertools from fontTools.designspaceLib import DesignSpaceDocument, SourceDescriptor, InstanceDescriptor, AxisDescriptor, RuleDescriptor, processRules from fontTools.misc import plistlib @@ -15,7 +40,7 @@ import defcon import fontParts.fontshell.font import defcon.objects.font -from defcon.objects.font import Font +#from defcon.objects.font import Font from defcon.pens.transformPointPen import TransformPointPen from defcon.objects.component import _defaultTransformation from fontMath.mathGlyph import MathGlyph @@ -25,8 +50,11 @@ # if you only intend to use varLib.model then importing mutatorMath is not necessary. from mutatorMath.objects.mutator import buildMutator from mutatorMath.objects.location import Location + +# back to these when we're running as a package +import ufoProcessor.varModels from ufoProcessor.varModels import VariationModelMutator -from ufoProcessor.emptyPen import checkGlyphIsEmpty +from ufoProcessor.pens import checkGlyphIsEmpty try: @@ -35,6 +63,40 @@ __version__ = "0.0.0+unknown" +_memoizeCache = dict() + +def immutify(obj): + # make an immutable version of this object. + # assert immutify(10) == (10,) + # assert immutify([10, 20, "a"]) == (10, 20, 'a') + # assert immutify(dict(foo="bar", world=["a", "b"])) == ('foo', ('bar',), 'world', ('a', 'b')) + hashValues = [] + if isinstance(obj, dict): + for key, value in obj.items(): + hashValues.extend([key, immutify(value)]) + elif isinstance(obj, list): + for value in obj: + hashValues.extend(immutify(value)) + else: + hashValues.append(obj) + return tuple(hashValues) + + +def memoize(function): + @functools.wraps(function) + def wrapper(self, *args, **kwargs): + immutableargs = tuple([immutify(a) for a in args]) + immutablekwargs = immutify(kwargs) + key = (function.__name__, self, immutableargs, immutify(kwargs)) + if key in _memoizeCache: + return _memoizeCache[key] + else: + result = function(self, *args, **kwargs) + _memoizeCache[key] = result + return result + return wrapper +##### + class UFOProcessorError(Exception): def __init__(self, msg, obj=None): self.msg = msg @@ -61,30 +123,6 @@ def getLayer(f, layerName): return f.getLayer(layerName) return None -""" - Processing of rules when generating UFOs. - Swap the contents of two glyphs. - - contours - - components - - width - - group membership - - kerning - - + Remap components so that glyphs that reference either of the swapped glyphs maintain appearance - + Keep the unicode value of the original glyph. - - Notes - Parking the glyphs under a swapname is a bit lazy, but at least it guarantees the glyphs have the right parent. - -""" - - -""" - build() is a convenience function for reading and executing a designspace file. - documentPath: path to the designspace file. - outputUFOFormatVersion: integer, 2, 3. Format for generated UFOs. Note: can be different from source UFO format. - useVarlib: True if you want the geometry to be generated with varLib.model instead of mutatorMath. -""" def build( documentPath, @@ -114,7 +152,7 @@ def build( document.roundGeometry = roundGeometry document.read(path) try: - r = document.generateUFO(processRules=processRules) + r = document.generateUFO() results.append(r) except: if logger: @@ -141,110 +179,6 @@ def getUFOVersion(ufoPath): return p.get('formatVersion') -def swapGlyphNames(font, oldName, newName, swapNameExtension = "_______________swap"): - # In font swap the glyphs oldName and newName. - # Also swap the names in components in order to preserve appearance. - # Also swap the names in font groups. - if not oldName in font or not newName in font: - return None - swapName = oldName + swapNameExtension - # park the old glyph - if not swapName in font: - font.newGlyph(swapName) - # get anchors - oldAnchors = font[oldName].anchors - newAnchors = font[newName].anchors - - # swap the outlines - font[swapName].clear() - p = font[swapName].getPointPen() - font[oldName].drawPoints(p) - font[swapName].width = font[oldName].width - # lib? - font[oldName].clear() - p = font[oldName].getPointPen() - font[newName].drawPoints(p) - font[oldName].width = font[newName].width - for a in newAnchors: - na = defcon.Anchor() - na.name = a.name - na.x = a.x - na.y = a.y - # FontParts and Defcon add anchors in different ways - # this works around that. - try: - font[oldName].naked().appendAnchor(na) - except AttributeError: - font[oldName].appendAnchor(na) - - font[newName].clear() - p = font[newName].getPointPen() - font[swapName].drawPoints(p) - font[newName].width = font[swapName].width - for a in oldAnchors: - na = defcon.Anchor() - na.name = a.name - na.x = a.x - na.y = a.y - try: - font[newName].naked().appendAnchor(na) - except AttributeError: - font[newName].appendAnchor(na) - - - # remap the components - for g in font: - for c in g.components: - if c.baseGlyph == oldName: - c.baseGlyph = swapName - continue - for g in font: - for c in g.components: - if c.baseGlyph == newName: - c.baseGlyph = oldName - continue - for g in font: - for c in g.components: - if c.baseGlyph == swapName: - c.baseGlyph = newName - - # change the names in groups - # the shapes will swap, that will invalidate the kerning - # so the names need to swap in the kerning as well. - newKerning = {} - for first, second in font.kerning.keys(): - value = font.kerning[(first,second)] - if first == oldName: - first = newName - elif first == newName: - first = oldName - if second == oldName: - second = newName - elif second == newName: - second = oldName - newKerning[(first, second)] = value - font.kerning.clear() - font.kerning.update(newKerning) - - for groupName, members in font.groups.items(): - newMembers = [] - for name in members: - if name == oldName: - newMembers.append(newName) - elif name == newName: - newMembers.append(oldName) - else: - newMembers.append(name) - font.groups[groupName] = newMembers - - remove = [] - for g in font: - if g.name.find(swapNameExtension)!=-1: - remove.append(g.name) - for r in remove: - del font[r] - - class DecomposePointPen(object): def __init__(self, glyphSet, outPointPen): @@ -292,7 +226,6 @@ class DesignSpaceProcessor(DesignSpaceDocument): def __init__(self, readerClass=None, writerClass=None, fontClass=None, ufoVersion=3, useVarlib=False): super(DesignSpaceProcessor, self).__init__(readerClass=readerClass, writerClass=writerClass) - self.ufoVersion = ufoVersion # target UFO version self.useVarlib = useVarlib self.roundGeometry = False @@ -308,12 +241,18 @@ def __init__(self, readerClass=None, writerClass=None, fontClass=None, ufoVersio self.problems = [] # receptacle for problem notifications. Not big enough to break, but also not small enough to ignore. self.toolLog = [] - def generateUFO(self, processRules=True, glyphNames=None, pairs=None, bend=False): + def hasDiscreteAxes(self): + # return True if this designspace has > 0 discrete axes + for axis in self.getOrderedDiscreteAxes(): + return True + return False + + def generateUFO(self, processRules=True, glyphNames=None, pairs=None, bend=False, discreteLocation=None): # makes the instances # option to execute the rules # make sure we're not trying to overwrite a newer UFO format self.loadFonts() - self.findDefault() + #self.findDefault() if self.default is None: # we need one to genenerate raise UFOProcessorError("Can't generate UFO from this designspace: no default font.", self) @@ -325,7 +264,9 @@ def generateUFO(self, processRules=True, glyphNames=None, pairs=None, bend=False processRules, glyphNames=glyphNames, pairs=pairs, - bend=bend) + bend=bend, + discreteLocation=discreteLocation + ) folder = os.path.dirname(os.path.abspath(instanceDescriptor.path)) path = instanceDescriptor.path if not os.path.exists(folder): @@ -339,14 +280,37 @@ def generateUFO(self, processRules=True, glyphNames=None, pairs=None, bend=False self.problems.append("Generated %s as UFO%d"%(os.path.basename(path), self.ufoVersion)) return True + def _serializeAnyAxis(self, axis): + if hasattr(axis, "serialize"): + return axis.serialize() + else: + if hasattr(axis, "values"): + # discrete axis does not have serialize method, meh + return dict( + tag=axis.tag, + name=axis.name, + labelNames=axis.labelNames, + minimum = min(axis.values), # XX is this allowed + maximum = max(axis.values), # XX is this allowed + values=axis.values, + default=axis.default, + hidden=axis.hidden, + map=axis.map, + axisOrdering=axis.axisOrdering, + axisLabels=axis.axisLabels, + ) + def getSerializedAxes(self): - return [a.serialize() for a in self.axes] + serialized = [] + for axis in self.axes: + serialized.append(self._serializeAnyAxis(axis)) + return serialized def getMutatorAxes(self): # map the axis values? d = collections.OrderedDict() for a in self.axes: - d[a.name] = a.serialize() + d[a.name] = self._serializeAnyAxis(a) return d def _getAxisOrder(self): @@ -355,14 +319,26 @@ def _getAxisOrder(self): axisOrder = property(_getAxisOrder, doc="get the axis order from the axis descriptors") serializedAxes = property(getSerializedAxes, doc="a list of dicts with the axis values") - + + def _setUseVarLib(self, useVarLib=True): + self.changed() + self.useVarLib = True + + useVarLib = property(_setUseVarLib, doc="set useVarLib to True use the varlib mathmodel. Set to False to use MutatorMath.") + def getVariationModel(self, items, axes, bias=None): # Return either a mutatorMath or a varlib.model object for calculating. - try: + #try: + if True: if self.useVarlib: # use the varlib variation model try: - return dict(), VariationModelMutator(items, self.axes) + return dict(), VariationModelMutator(items, axes=self.axes, extrapolate=True) + except TypeError: + import fontTools.varLib.models + error = traceback.format_exc() + print(error) + return {}, None except (KeyError, AssertionError): error = traceback.format_exc() self.toolLog.append("UFOProcessor.getVariationModel error: %s" % error) @@ -371,21 +347,28 @@ def getVariationModel(self, items, axes, bias=None): else: # use mutatormath model axesForMutator = self.getMutatorAxes() - return buildMutator(items, axes=axesForMutator, bias=bias) - except: - error = traceback.format_exc() - self.toolLog.append("UFOProcessor.getVariationModel error: %s" % error) - return {}, None - - def getInfoMutator(self): + # mutator will be confused by discrete axis values. + # the bias needs to be for the continuous axes only + biasForMutator, _ = self.splitLocation(bias) + return buildMutator(items, axes=axesForMutator, bias=biasForMutator) + #except: + # error = traceback.format_exc() + # self.toolLog.append("UFOProcessor.getVariationModel error: %s" % error) + return {}, None + + @memoize + def getInfoMutator(self, discreteLocation=None): """ Returns a info mutator """ - if self._infoMutator: - return self._infoMutator infoItems = [] - for sourceDescriptor in self.sources: + if discreteLocation is not None: + sources = self.findSourceDescriptorsForDiscreteLocation(discreteLocation) + else: + sources = self.sources + for sourceDescriptor in sources: if sourceDescriptor.layerName is not None: continue - loc = Location(sourceDescriptor.location) + continuous, discrete = self.splitLocation(sourceDescriptor.location) + loc = Location(continuous) sourceFont = self.fonts[sourceDescriptor.name] if sourceFont is None: continue @@ -393,32 +376,37 @@ def getInfoMutator(self): infoItems.append((loc, sourceFont.info.toMathInfo())) else: infoItems.append((loc, self.mathInfoClass(sourceFont.info))) - infoBias = self.newDefaultLocation(bend=True) + infoBias = self.newDefaultLocation(bend=True, discreteLocation=discreteLocation) bias, self._infoMutator = self.getVariationModel(infoItems, axes=self.serializedAxes, bias=infoBias) return self._infoMutator - def getKerningMutator(self, pairs=None): + @memoize + def getKerningMutator(self, pairs=None, discreteLocation=None): """ Return a kerning mutator, collect the sources, build mathGlyphs. If no pairs are given: calculate the whole table. If pairs are given then query the sources for a value and make a mutator only with those values. """ - if self._kerningMutator and pairs == self._kerningMutatorPairs: - return self._kerningMutator + if discreteLocation is not None: + sources = self.findSourceDescriptorsForDiscreteLocation(discreteLocation) + else: + sources = self.sources kerningItems = [] foregroundLayers = [None, 'foreground', 'public.default'] if pairs is None: - for sourceDescriptor in self.sources: + for sourceDescriptor in sources: if sourceDescriptor.layerName not in foregroundLayers: continue if not sourceDescriptor.muteKerning: - loc = Location(sourceDescriptor.location) + # filter this XX @@ + continuous, discrete = self.splitLocation(sourceDescriptor.location) + loc = Location(continuous) sourceFont = self.fonts[sourceDescriptor.name] if sourceFont is None: continue # this makes assumptions about the groups of all sources being the same. kerningItems.append((loc, self.mathKerningClass(sourceFont.kerning, sourceFont.groups))) else: self._kerningMutatorPairs = pairs - for sourceDescriptor in self.sources: + for sourceDescriptor in sources: # XXX check sourceDescriptor layerName, only foreground should contribute if sourceDescriptor.layerName is not None: continue @@ -428,7 +416,8 @@ def getKerningMutator(self, pairs=None): sourceFont = self.fonts[sourceDescriptor.name] if sourceFont is None: continue - loc = Location(sourceDescriptor.location) + continuous, discrete = self.splitLocation(sourceDescriptor.location) + loc = Location(continuous) # XXX can we get the kern value from the fontparts kerning object? kerningItem = self.mathKerningClass(sourceFont.kerning, sourceFont.groups) if kerningItem is not None: @@ -438,17 +427,19 @@ def getKerningMutator(self, pairs=None): if v is not None: sparseKerning[pair] = v kerningItems.append((loc, self.mathKerningClass(sparseKerning))) - kerningBias = self.newDefaultLocation(bend=True) + kerningBias = self.newDefaultLocation(bend=True, discreteLocation=discreteLocation) + bias, thing = self.getVariationModel(kerningItems, axes=self.serializedAxes, bias=kerningBias) #xx bias, self._kerningMutator = self.getVariationModel(kerningItems, axes=self.serializedAxes, bias=kerningBias) return self._kerningMutator + @memoize def filterThisLocation(self, location, mutedAxes): # return location with axes is mutedAxes removed # this means checking if the location is a non-default value if not mutedAxes: return False, location defaults = {} - ignoreMaster = False + ignoreSource = False for aD in self.axes: defaults[aD.name] = aD.default new = {} @@ -459,18 +450,18 @@ def filterThisLocation(self, location, mutedAxes): if mutedAxisName not in defaults: continue if location[mutedAxisName] != defaults.get(mutedAxisName): - ignoreMaster = True + ignoreSource = True del new[mutedAxisName] - return ignoreMaster, new + return ignoreSource, new + @memoize def getGlyphMutator(self, glyphName, decomposeComponents=False, - fromCache=None): - # make a mutator / varlib object for glyphName. - cacheKey = (glyphName, decomposeComponents) - if cacheKey in self._glyphMutators and fromCache: - return self._glyphMutators[cacheKey] - items = self.collectMastersForGlyph(glyphName, decomposeComponents=decomposeComponents) + **discreteLocation, + ): + fromCache = False + # make a mutator / varlib object for glyphName, with the sources for the given discrete location + items = self.collectSourcesForGlyph(glyphName, decomposeComponents=decomposeComponents, **discreteLocation) new = [] for a, b, c in items: if hasattr(b, "toMathGlyph"): @@ -480,27 +471,28 @@ def getGlyphMutator(self, glyphName, else: new.append((a,self.mathGlyphClass(b))) thing = None - try: - bias, thing = self.getVariationModel(new, axes=self.serializedAxes, bias=self.newDefaultLocation(bend=True)) #xx - except TypeError: - self.toolLog.append("getGlyphMutator %s items: %s new: %s" % (glyphName, items, new)) - self.problems.append("\tCan't make processor for glyph %s" % (glyphName)) - if thing is not None: - self._glyphMutators[cacheKey] = thing + thisBias = self.newDefaultLocation(bend=True, discreteLocation=discreteLocation) + bias, thing = self.getVariationModel(new, axes=self.serializedAxes, bias=thisBias) #xx return thing - def collectMastersForGlyph(self, glyphName, decomposeComponents=False): - """ Return a glyph mutator.defaultLoc + @memoize + def collectSourcesForGlyph(self, glyphName, decomposeComponents=False, discreteLocation=None): + """ Return a glyph mutator decomposeComponents = True causes the source glyphs to be decomposed first before building the mutator. That gives you instances that do not depend on a complete font. If you're calculating previews for instance. - XXX check glyphs in layers + findSourceDescriptorsForDiscreteLocation returns sources from layers as well """ items = [] empties = [] foundEmpty = False - for sourceDescriptor in self.sources: + # + if discreteLocation is not None: + sources = self.findSourceDescriptorsForDiscreteLocation(discreteLocation) + else: + sources = self.sources + for sourceDescriptor in sources: if not os.path.exists(sourceDescriptor.path): #kthxbai p = "\tMissing UFO at %s" % sourceDescriptor.path @@ -510,8 +502,8 @@ def collectMastersForGlyph(self, glyphName, decomposeComponents=False): if glyphName in sourceDescriptor.mutedGlyphNames: continue thisIsDefault = self.default == sourceDescriptor - ignoreMaster, filteredLocation = self.filterThisLocation(sourceDescriptor.location, self.mutedAxisNames) - if ignoreMaster: + ignoreSource, filteredLocation = self.filterThisLocation(sourceDescriptor.location, self.mutedAxisNames) + if ignoreSource: continue f = self.fonts.get(sourceDescriptor.name) if f is None: continue @@ -564,7 +556,11 @@ def collectMastersForGlyph(self, glyphName, decomposeComponents=False): processThis = processThis.toMathGlyph() else: processThis = self.mathGlyphClass(processThis) - items.append((loc, processThis, sourceInfo)) + # this is where the location is linked to the glyph + # this loc needs to have the discrete location subtracted + # XX @@ + continuous, discrete = self.splitLocation(loc) + items.append((continuous, processThis, sourceInfo)) empties.append((thisIsDefault, foundEmpty)) # check the empties: # if the default glyph is empty, then all must be empty @@ -590,6 +586,8 @@ def collectMastersForGlyph(self, glyphName, decomposeComponents=False): checkedItems.append(items[i]) return checkedItems + collectMastersForGlyph = collectSourcesForGlyph + def getNeutralFont(self): # Return a font object for the neutral font # self.fonts[self.default.name] ? @@ -603,36 +601,61 @@ def getNeutralFont(self): return self.fonts[sd.name] return None - def findDefault(self): + def findDefault(self, discreteLocation=None): """Set and return SourceDescriptor at the default location or None. - The default location is the set of all `default` values in user space of all axes. """ self.default = None # Convert the default location from user space to design space before comparing # it against the SourceDescriptor locations (always in design space). - default_location_design = self.newDefaultLocation(bend=True) + default_location_design = self.newDefaultLocation(bend=True, discreteLocation=discreteLocation) + print(f"findDefault for {discreteLocation}: {default_location_design}") for sourceDescriptor in self.sources: if sourceDescriptor.location == default_location_design: + print(f"findDefault found {sourceDescriptor.location} {default_location_design} {sourceDescriptor.location == default_location_design}") self.default = sourceDescriptor return sourceDescriptor return None - def newDefaultLocation(self, bend=False): + def newDefaultLocation(self, bend=False, discreteLocation=None): # overwrite from fontTools.newDefaultLocation - # we do not want this default location to be mapped. + # we do not want this default location always to be mapped. loc = collections.OrderedDict() for axisDescriptor in self.axes: + axisName = axisDescriptor.name + axisValue = axisDescriptor.default + if discreteLocation is not None: + # if we want to find the default for a specific discreteLoation + # we can not use the discrete axis' default value + # -> we have to use the value in the given discreteLocation + if axisDescriptor.name in discreteLocation: + axisValue = discreteLocation[axisDescriptor.name] + else: + axisValue = axisDescriptor.default if bend: - loc[axisDescriptor.name] = axisDescriptor.map_forward( - axisDescriptor.default + loc[axisName] = axisDescriptor.map_forward( + axisValue ) else: - loc[axisDescriptor.name] = axisDescriptor.default + loc[axisName] = axisValue return loc - + + def updateFonts(self, fontObjects): + # this is to update the loaded fonts. + # it should be the way for an editor to provide a list of fonts that are open + #self.fonts[sourceDescriptor.name] = None + hasUpdated = False + for newFont in fontObjects: + for fontName, haveFont in self.fonts.items(): + if haveFont.path == newFont.path and id(haveFont)!=id(newFont): + #print(f"updating {self.fonts[fontName]} with {newFont}") + self.fonts[fontName] = newFont + hasUpdated = True + if hasUpdated: + self.changed() + def loadFonts(self, reload=False): # Load the fonts and find the default candidate based on the info flag if self._fontsLoaded and not reload: @@ -641,11 +664,12 @@ def loadFonts(self, reload=False): for i, sourceDescriptor in enumerate(self.sources): if sourceDescriptor.name is None: # make sure it has a unique name - sourceDescriptor.name = "master.%d" % i + sourceDescriptor.name = "source.%d" % i if sourceDescriptor.name not in self.fonts: + # if os.path.exists(sourceDescriptor.path): self.fonts[sourceDescriptor.name] = self._instantiateFont(sourceDescriptor.path) - self.problems.append("loaded master from %s, layer %s, format %d" % (sourceDescriptor.path, sourceDescriptor.layerName, getUFOVersion(sourceDescriptor.path))) + self.problems.append("loaded source from %s, layer %s, format %d" % (sourceDescriptor.path, sourceDescriptor.layerName, getUFOVersion(sourceDescriptor.path))) names |= set(self.fonts[sourceDescriptor.name].keys()) else: self.fonts[sourceDescriptor.name] = None @@ -663,41 +687,39 @@ def getFonts(self): return fonts def makeInstance(self, instanceDescriptor, - doRules=False, + doRules=None, glyphNames=None, pairs=None, bend=False): """ Generate a font object for this instance """ + if doRules is not None: + warn('The doRules argument in DesignSpaceProcessor.makeInstance() is deprecated', DeprecationWarning, stacklevel=2) + continuousLocation, discreteLocation = self.splitLocation(instanceDescriptor.location) + print('makeInstance', continuousLocation, discreteLocation) font = self._instantiateFont(None) # make fonty things here - loc = Location(instanceDescriptor.location) + loc = Location(continuousLocation) anisotropic = False locHorizontal = locVertical = loc if self.isAnisotropic(loc): anisotropic = True locHorizontal, locVertical = self.splitAnisotropic(loc) - # groups - renameMap = getattr(self.fonts[self.default.name], "kerningGroupConversionRenameMaps", None) - font.kerningGroupConversionRenameMaps = renameMap if renameMap is not None else {'side1': {}, 'side2': {}} - # make the kerning - # this kerning is always horizontal. We can take the horizontal location - # filter the available pairs? if instanceDescriptor.kerning: if pairs: try: - kerningMutator = self.getKerningMutator(pairs=pairs) + kerningMutator = self.getKerningMutator(pairs=pairs, discreteLocation=discreteLocation) kerningObject = kerningMutator.makeInstance(locHorizontal, bend=bend) kerningObject.extractKerning(font) except: self.problems.append("Could not make kerning for %s. %s" % (loc, traceback.format_exc())) else: - kerningMutator = self.getKerningMutator() + kerningMutator = self.getKerningMutator(discreteLocation=discreteLocation) if kerningMutator is not None: kerningObject = kerningMutator.makeInstance(locHorizontal, bend=bend) kerningObject.extractKerning(font) - # make the info + # # make the info try: - infoMutator = self.getInfoMutator() + infoMutator = self.getInfoMutator(discreteLocation=discreteLocation) if infoMutator is not None: if not anisotropic: infoInstanceObject = infoMutator.makeInstance(loc, bend=bend) @@ -717,14 +739,14 @@ def makeInstance(self, instanceDescriptor, font.info.postscriptFontName = instanceDescriptor.postScriptFontName # yikes, note the differences in capitalisation.. font.info.styleMapFamilyName = instanceDescriptor.styleMapFamilyName font.info.styleMapStyleName = instanceDescriptor.styleMapStyleName - # NEED SOME HELP WITH THIS - # localised names need to go to the right openTypeNameRecords - # records = [] - # nameID = 1 - # platformID = - # for languageCode, name in instanceDescriptor.localisedStyleMapFamilyName.items(): - # # Name ID 1 (font family name) is found at the generic styleMapFamily attribute. - # records.append((nameID, )) + # # NEED SOME HELP WITH THIS + # # localised names need to go to the right openTypeNameRecords + # # records = [] + # # nameID = 1 + # # platformID = + # # for languageCode, name in instanceDescriptor.localisedStyleMapFamilyName.items(): + # # # Name ID 1 (font family name) is found at the generic styleMapFamily attribute. + # # records.append((nameID, )) except: self.problems.append("Could not make fontinfo for %s. %s" % (loc, traceback.format_exc())) for sourceDescriptor in self.sources: @@ -739,11 +761,8 @@ def makeInstance(self, instanceDescriptor, font.lib[key] = value if sourceDescriptor.copyGroups: if self.fonts[sourceDescriptor.name] is not None: - sides = font.kerningGroupConversionRenameMaps.get('side1', {}) - sides.update(font.kerningGroupConversionRenameMaps.get('side2', {})) for key, value in self.fonts[sourceDescriptor.name].groups.items(): - if key not in sides: - font.groups[key] = value + font.groups[key] = value if sourceDescriptor.copyFeatures: if self.fonts[sourceDescriptor.name] is not None: featuresText = self.fonts[sourceDescriptor.name].features.text @@ -754,100 +773,45 @@ def makeInstance(self, instanceDescriptor, else: selectedGlyphNames = self.glyphNames # add the glyphnames to the font.lib['public.glyphOrder'] + # TODO load ignored glyphs from designspace? if not 'public.glyphOrder' in font.lib.keys(): + # should be the glyphorder from the default, yes? font.lib['public.glyphOrder'] = selectedGlyphNames for glyphName in selectedGlyphNames: - try: - glyphMutator = self.getGlyphMutator(glyphName) + #try: + if True: + glyphMutator = self.getGlyphMutator(glyphName, discreteLocation=discreteLocation) if glyphMutator is None: self.problems.append("Could not make mutator for glyph %s" % (glyphName)) continue - except: - self.problems.append("Could not make mutator for glyph %s %s" % (glyphName, traceback.format_exc())) - continue - if glyphName in instanceDescriptor.glyphs.keys(): - # XXX this should be able to go now that we have full rule support. - # reminder: this is what the glyphData can look like - # {'instanceLocation': {'custom': 0.0, 'weight': 824.0}, - # 'masters': [{'font': 'master.Adobe VF Prototype.Master_0.0', - # 'glyphName': 'dollar.nostroke', - # 'location': {'custom': 0.0, 'weight': 0.0}}, - # {'font': 'master.Adobe VF Prototype.Master_1.1', - # 'glyphName': 'dollar.nostroke', - # 'location': {'custom': 0.0, 'weight': 368.0}}, - # {'font': 'master.Adobe VF Prototype.Master_2.2', - # 'glyphName': 'dollar.nostroke', - # 'location': {'custom': 0.0, 'weight': 1000.0}}, - # {'font': 'master.Adobe VF Prototype.Master_3.3', - # 'glyphName': 'dollar.nostroke', - # 'location': {'custom': 100.0, 'weight': 1000.0}}, - # {'font': 'master.Adobe VF Prototype.Master_0.4', - # 'glyphName': 'dollar.nostroke', - # 'location': {'custom': 100.0, 'weight': 0.0}}, - # {'font': 'master.Adobe VF Prototype.Master_4.5', - # 'glyphName': 'dollar.nostroke', - # 'location': {'custom': 100.0, 'weight': 368.0}}], - # 'unicodes': [36]} - glyphData = instanceDescriptor.glyphs[glyphName] - else: - glyphData = {} + + glyphData = {} font.newGlyph(glyphName) font[glyphName].clear() - if glyphData.get('mute', False): - # mute this glyph, skip - continue - glyphInstanceLocation = glyphData.get("instanceLocation", instanceDescriptor.location) - glyphInstanceLocation = Location(glyphInstanceLocation) - uniValues = [] - neutral = glyphMutator.get(()) - if neutral is not None: - uniValues = neutral[0].unicodes - else: - neutralFont = self.getNeutralFont() - if glyphName in neutralFont: - uniValues = neutralFont[glyphName].unicodes - glyphInstanceUnicodes = glyphData.get("unicodes", uniValues) - note = glyphData.get("note") - if note: - font[glyphName] = note - # XXXX phase out support for instance-specific masters - # this should be handled by the rules system. - masters = glyphData.get("masters", None) - if masters is not None: - items = [] - for glyphMaster in masters: - sourceGlyphFont = glyphMaster.get("font") - sourceGlyphName = glyphMaster.get("glyphName", glyphName) - m = self.fonts.get(sourceGlyphFont) - if not sourceGlyphName in m: - continue - if hasattr(m[sourceGlyphName], "toMathGlyph"): - sourceGlyph = m[sourceGlyphName].toMathGlyph() - else: - sourceGlyph = MathGlyph(m[sourceGlyphName]) - sourceGlyphLocation = glyphMaster.get("location") - items.append((Location(sourceGlyphLocation), sourceGlyph)) - bias, glyphMutator = self.getVariationModel(items, axes=self.serializedAxes, bias=self.newDefaultLocation(bend=True)) + glyphInstanceUnicodes = [] + neutralFont = self.getNeutralFont() + # get the unicodes from the default + if glyphName in neutralFont: + glyphInstanceUnicodes = neutralFont[glyphName].unicodes try: - if not self.isAnisotropic(glyphInstanceLocation): - glyphInstanceObject = glyphMutator.makeInstance(glyphInstanceLocation, bend=bend) + if not self.isAnisotropic(continuousLocation): + glyphInstanceObject = glyphMutator.makeInstance(continuousLocation, bend=bend) else: # split anisotropic location into horizontal and vertical components - horizontal, vertical = self.splitAnisotropic(glyphInstanceLocation) - horizontalGlyphInstanceObject = glyphMutator.makeInstance(horizontal, bend=bend) - verticalGlyphInstanceObject = glyphMutator.makeInstance(vertical, bend=bend) + horizontalGlyphInstanceObject = glyphMutator.makeInstance(locHorizontal, bend=bend) + verticalGlyphInstanceObject = glyphMutator.makeInstance(locVertical, bend=bend) # merge them again glyphInstanceObject = (1,0)*horizontalGlyphInstanceObject + (0,1)*verticalGlyphInstanceObject except IndexError: # alignment problem with the data? self.problems.append("Quite possibly some sort of data alignment error in %s" % glyphName) continue - font.newGlyph(glyphName) - font[glyphName].clear() if self.roundGeometry: try: glyphInstanceObject = glyphInstanceObject.round() except AttributeError: + # what are we catching here? + print(f"no round method for {glyphInstanceObject} ?") pass try: # File "/Users/erik/code/ufoProcessor/Lib/ufoProcessor/__init__.py", line 649, in makeInstance @@ -869,26 +833,18 @@ def makeInstance(self, instanceDescriptor, glyphInstanceObject.drawPoints(pPen) font[glyphName].width = glyphInstanceObject.width font[glyphName].unicodes = glyphInstanceUnicodes - if doRules: - resultNames = processRules(self.rules, loc, self.glyphNames) - for oldName, newName in zip(self.glyphNames, resultNames): - if oldName != newName: - swapGlyphNames(font, oldName, newName) - # copy the glyph lib? - #for sourceDescriptor in self.sources: - # if sourceDescriptor.copyLib: - # pass - # pass - # store designspace location in the font.lib font.lib['designspace.location'] = list(instanceDescriptor.location.items()) + return font + @memoize def isAnisotropic(self, location): for v in location.values(): - if type(v)==tuple: + if isinstance(v, (list, tuple)): return True return False + @memoize def splitAnisotropic(self, location): x = Location() y = Location() @@ -969,5 +925,125 @@ def _copyFontInfo(self, sourceInfo, targetInfo): if copy: value = getattr(sourceInfo, infoAttribute) setattr(targetInfo, infoAttribute, value) - - + + # some ds5 work + def getOrderedDiscreteAxes(self): + # return the list of discrete axis objects, in the right order + axes = [] + for axisName in self.getAxisOrder(): + axisObj = self.getAxis(axisName) + if hasattr(axisObj, "values"): + axes.append(axisObj) + return axes + + def splitLocation(self, location): + # split a location in a continouous and a discrete part + discreteAxes = [a.name for a in self.getOrderedDiscreteAxes()] + continuous = {} + discrete = {} + for name, value in location.items(): + if name in discreteAxes: + discrete[name] = value + else: + continuous[name] = value + if not discrete: + return continuous, None + return continuous, discrete + + def getDiscreteLocations(self): + # return a list of all permutated discrete locations + # do we have a list of ordered axes? + values = [] + names = [] + discreteCoordinates = [] + dd = [] + for axis in self.getOrderedDiscreteAxes(): + values.append(axis.values) + names.append(axis.name) + for r in itertools.product(*values): + # make a small dict for the discrete location values + discreteCoordinates.append({a:b for a,b in zip(names,r)}) + return discreteCoordinates + + @memoize + def findSourceDescriptorsForDiscreteLocation(self, discreteLocDict): + # return a list of all sourcedescriptors that share the values in the discrete loc tuple + # so this includes all sourcedescriptors that point to layers + # discreteLocDict {'countedItems': 1.0, 'outlined': 0.0}, {'countedItems': 1.0, 'outlined': 1.0} + sources = [] + for s in self.sources: + ok = True + if discreteLocDict is None: + sources.append(s) + continue + for name, value in discreteLocDict.items(): + if name in s.location: + if s.location[name] != value: + ok = False + else: + ok = False + continue + if ok: + sources.append(s) + return sources + + # caching + def changed(self): + # clears everything relating to this designspacedocument + # the cache could contain more designspacedocument objects. + for key in list(_memoizeCache.keys()): + if key[1] == self: + del _memoizeCache[key] + #_memoizeCache.clear() + + def glyphChanged(self, glyphName): + # clears this one specific glyph + for key in list(_memoizeCache.keys()): + #print(f"glyphChanged {[(i,m) for i, m in enumerate(key)]} {glyphName}") + # the glyphname is hiding quite deep in key[2] + # (('glyphTwo',),) + # this is because of how immutify does it. Could be different I suppose but this works + if key[0] in ("getGlyphMutator", "collectSourcesForGlyph") and key[2][0][0] == glyphName: + del _memoizeCache[key] + + +if __name__ == "__main__": + # while we're testing + import shutil + import ufoProcessor + + ds5Path = "../../Tests/ds5/test.ds5.designspace" + instancesPath = "../../Tests/ds5/instances" + instancesPathMutMath = "../../Tests/ds5/instances_mutMath" + instancesPathVarLib = "../../Tests/ds5/instances_varlib" + + def memoizeStats(): + from pprint import pprint + print("_memoizeCache:") + items = {} + for key, value in _memoizeCache.items(): + if key[0] not in items: + items[key[0]] = 0 + items[key[0]] +=1 + pprint(items) + + for useVarlibPref, renameInstancesPath in [(True, instancesPathVarLib), (False, instancesPathMutMath)]: + print(f"\n\n\t\t{useVarlibPref}") + dsp = DesignSpaceProcessor(useVarlib=useVarlibPref) + dsp.read(ds5Path) + dsp.loadFonts() + print(dsp.glyphNames) + dsp.updateFonts(AllFonts()) + dsp.generateUFO() + dsp.glyphChanged("glyphOne") + if os.path.exists(renameInstancesPath): + shutil.rmtree(renameInstancesPath) + shutil.move(instancesPath, renameInstancesPath) + if useVarlibPref: + # clear only the cached items that belong to the varlib test + # just to see if we can + dsp.changed() + memoizeStats() + +print(f"{len(_memoizeCache)} items in _memoizeCache") +print('done') diff --git a/Lib/ufoProcessor/logger.py b/Lib/ufoProcessor/logger.py new file mode 100644 index 0000000..d530408 --- /dev/null +++ b/Lib/ufoProcessor/logger.py @@ -0,0 +1,78 @@ +import sys +import time +import os +import logging + +class Logger: + + def __init__(self, path, rootDirectory, nest=0): + self.path = path + self.rootDirectory = rootDirectory + self.nest = nest + if not nest: + if path is not None: + # if os.path.exists(path): + # os.remove(path) + if not os.path.exists(path): + f = open(path, "w") + f.close() + + def child(self, text=None): + logger = Logger( + self.path, + self.rootDirectory, + nest=self.nest + 1 + ) + if text: + logger.info(text) + return logger + + def relativePath(self, path): + return os.path.relpath(path, self.rootDirectory) + + def _makeText(self, text): + if self.nest: + text = f"{('| ' * self.nest).strip()} {text}" + return text + + def _toConsole(self, text): + print(text) + + def _toFile(self, text): + if self.path is None: + return + text += "\n" + f = open(self.path, "a") + f.write(text) + f.close() + + def time(self, prefix=None): + now = time.strftime("%Y-%m-%d %H:%M") + if prefix: + now = prefix + " " + now + self.info(now) + + def info(self, text): + text = self._makeText(text) + self._toConsole(text) + self._toFile(text) + + def infoItem(self, text): + text = f"\t- {text}" + self.info(text) + + def infoPath(self, path): + text = self.relativePath(path) + self.infoItem(text) + + def detail(self, text): + text = self._makeText(text) + self._toFile(text) + + def detailItem(self, text): + text = f"- {text}" + self.detail(text) + + def detailPath(self, path): + text = self.relativePath(path) + self.detailItem(text) diff --git a/Lib/ufoProcessor/emptyPen.py b/Lib/ufoProcessor/pens.py similarity index 74% rename from Lib/ufoProcessor/emptyPen.py rename to Lib/ufoProcessor/pens.py index 42459d1..db067d0 100755 --- a/Lib/ufoProcessor/emptyPen.py +++ b/Lib/ufoProcessor/pens.py @@ -1,5 +1,31 @@ # coding: utf-8 + from fontTools.pens.pointPen import AbstractPointPen +from defcon.pens.transformPointPen import TransformPointPen +from defcon.objects.component import _defaultTransformation + +""" + Decompose + +""" + +class DecomposePointPen(object): + + def __init__(self, glyphSet, outPointPen): + self._glyphSet = glyphSet + self._outPointPen = outPointPen + self.beginPath = outPointPen.beginPath + self.endPath = outPointPen.endPath + self.addPoint = outPointPen.addPoint + + def addComponent(self, baseGlyphName, transformation): + if baseGlyphName in self._glyphSet: + baseGlyph = self._glyphSet[baseGlyphName] + if transformation == _defaultTransformation: + baseGlyph.drawPoints(self) + else: + transformPointPen = TransformPointPen(self, transformation) + baseGlyph.drawPoints(transformPointPen) """ Simple pen object to determine if a glyph contains any geometry. @@ -24,7 +50,7 @@ def addPoint(self, pt, segmentType=None, smooth=False, name=None, identifier=Non def addComponent(self, baseGlyphName=None, transformation=None, identifier=None, **kwargs): self.components+=1 - + def getCount(self): return self.points, self.contours, self.components @@ -71,6 +97,10 @@ def checkGlyphIsEmpty(glyph, allowWhiteSpace=True): if glyph.unicode in whiteSpace and allowWhiteSpace: # are we allowed to be? return False + if "space" in glyph.name: + # this is a bold assumption, + # and certainly not inclusive + return False return True return False diff --git a/Lib/ufoProcessor/sp3.py b/Lib/ufoProcessor/sp3.py deleted file mode 100644 index 46ca167..0000000 --- a/Lib/ufoProcessor/sp3.py +++ /dev/null @@ -1,494 +0,0 @@ -import os -import glob - -from fontTools.misc.loggingTools import LogMixin -from fontTools.designspaceLib import DesignSpaceDocument, AxisDescriptor, SourceDescriptor, RuleDescriptor, InstanceDescriptor - -try: - import xml.etree.cElementTree as ET -except ImportError: - import xml.etree.ElementTree as ET - -# Reader that parses Superpolator documents and buidls designspace objects. -# Note: the Superpolator document format precedes the designspace documnt format. -# For now I just want to migrate data out of Superpolator into designspace. -# So not all data will migrate, just the stuff we can use. - -""" - - - - - com.letterror.skateboard.interactionSources - - horizontal - - ignore - - vertical - - - com.letterror.skateboard.mutedSources - - - IBM Plex Sans Condensed-Bold.ufo - foreground - - - com.letterror.skateboard.previewLocation - - weight - 0.0 - - com.letterror.skateboard.previewText - SKATE - - - - - -""" - -superpolatorDataLibKey = "com.superpolator.data" # lib key for Sp data in .designspace -skateboardInteractionSourcesKey = "com.letterror.skateboard.interactionSources" -skateboardMutedSourcesKey = "com.letterror.skateboard.mutedSources" -skipExportKey = "public.skipExportGlyphs" -skateboardPreviewLocationsKey = "com.letterror.skateboard.previewLocation" -skateboardPreviewTextKey = "com.letterror.skateboard.previewText" - -class SuperpolatorReader(LogMixin): - ruleDescriptorClass = RuleDescriptor - axisDescriptorClass = AxisDescriptor - sourceDescriptorClass = SourceDescriptor - instanceDescriptorClass = InstanceDescriptor - - def __init__(self, documentPath, documentObject, convertRules=True, convertData=True, anisotropic=False): - self.path = documentPath - self.documentObject = documentObject - self.convertRules = convertRules - self.convertData = convertData - self.allowAnisotropic = anisotropic # maybe add conversion options later - tree = ET.parse(self.path) - self.root = tree.getroot() - self.documentObject.formatVersion = self.root.attrib.get("format", "3.0") - self.axisDefaults = {} - self._strictAxisNames = True - - - @classmethod - def fromstring(cls, string, documentObject): - f = BytesIO(tobytes(string, encoding="utf-8")) - self = cls(f, documentObject) - self.path = None - return self - - def read(self): - self.readAxes() - if self.convertData: - self.readData() - if self.convertRules: - self.readOldRules() - self.readSimpleRules() - self.readSources() - self.readInstances() - - def readData(self): - # read superpolator specific data, view prefs etc. - # if possible convert it to skateboard - interactionSources = {'horizontal': [], 'vertical': [], 'ignore': []} - ignoreElements = self.root.findall(".ignore") - ignoreGlyphs = [] - for ignoreElement in ignoreElements: - names = ignoreElement.attrib.get('glyphs') - if names: - ignoreGlyphs = names.split(",") - if ignoreGlyphs: - self.documentObject.lib[skipExportKey] = ignoreGlyphs - dataElements = self.root.findall(".data") - if not dataElements: - return - newLib = {} - interactionSourcesAdded = False - for dataElement in dataElements: - name = dataElement.attrib.get('name') - value = dataElement.attrib.get('value') - if value in ['True', 'False']: - value = value == "True" - else: - try: - value = float(value) - except ValueError: - pass - if name == "previewtext": - self.documentObject.lib[skateboardPreviewTextKey] = value - elif name == "horizontalPreviewAxis": - interactionSources['horizontal'].append(value) - interactionSourcesAdded = True - elif name == "verticalPreviewAxis": - interactionSources['vertical'].append(value) - interactionSourcesAdded = True - - newLib[name] = value - if interactionSourcesAdded: - self.documentObject.lib[skateboardInteractionSourcesKey] = interactionSources - if newLib: - self.documentObject.lib[superpolatorDataLibKey] = newLib - - - def readOldRules(self): - # read the old rules - # - # - # - - # superpolator old rule to simple rule - # if op in ['<', '<=']: - # # old style data - # axes[axisName]['maximum'] = conditionDict['values'] - # newRule.name = "converted %s < and <= "%(axisName) - # elif op in ['>', '>=']: - # # old style data - # axes[axisName]['minimum'] = conditionDict['values'] - # newRule.name = "converted %s > and >= "%(axisName) - # elif op == "==": - # axes[axisName]['maximum'] = conditionDict['values'] - # axes[axisName]['minimum'] = conditionDict['values'] - # newRule.name = "converted %s == "%(axisName) - # newRule.enabled = False - # elif op == "!=": - # axes[axisName]['maximum'] = conditionDict['values'] - # axes[axisName]['minimum'] = conditionDict['values'] - # newRule.name = "unsupported %s != "%(axisName) - # newRule.enabled = False - # else: - # axes[axisName]['maximum'] = conditionDict['minimum'] - # axes[axisName]['minimum'] = conditionDict['maximum'] - # newRule.name = "minmax legacy rule for %s"%axisName - # newRule.enabled = False - - rules = [] - for oldRuleElement in self.root.findall(".rule"): - ruleObject = self.ruleDescriptorClass() - # only one condition set in these old rules - cds = [] - a = oldRuleElement.attrib['resultfalse'] - b = oldRuleElement.attrib['resulttrue'] - ruleObject.subs.append((a,b)) - for oldConditionElement in oldRuleElement.findall(".condition"): - cd = {} - operator = oldConditionElement.attrib['operator'] - axisValue = float(oldConditionElement.attrib['xvalue']) - axisName = oldConditionElement.attrib['axisname'] - if operator in ['<', '<=']: - cd['maximum'] = axisValue - cd['minimum'] = None - cd['name'] = axisName - ruleObject.name = "converted %s < and <= "%(axisName) - elif operator in ['>', '>=']: - cd['maximum'] = None - cd['minimum'] = axisValue - cd['name'] = axisName - ruleObject.name = "converted %s > and >= "%(axisName) - elif operator in ["==", "!="]: - # can't convert this one - continue - cds.append(cd) - if cds: - ruleObject.conditionSets.append(cds) - self.documentObject.addRule(ruleObject) - - def readSimpleRules(self): - # read the simple rule elements - # - # - # - # - # - # - # - - - rulesContainerElements = self.root.findall(".simplerules") - rules = [] - for rulesContainerElement in rulesContainerElements: - for ruleElement in rulesContainerElement: - ruleObject = self.ruleDescriptorClass() - ruleName = ruleObject.name = ruleElement.attrib['name'] - # subs - for subElement in ruleElement.findall('.sub'): - a = subElement.attrib['name'] - b = subElement.attrib['with'] - ruleObject.subs.append((a, b)) - # condition sets, .sp3 had none - externalConditions = self._readConditionElements( - ruleElement, - ruleName, - ) - if externalConditions: - ruleObject.conditionSets.append(externalConditions) - self.log.info( - "Found stray rule conditions outside a conditionset. " - "Wrapped them in a new conditionset." - ) - self.documentObject.addRule(ruleObject) - - def _readConditionElements(self, parentElement, ruleName=None): - # modified from the method from fonttools.designspaceLib - # it's not the same! - cds = [] - for conditionElement in parentElement.findall('.condition'): - cd = {} - cdMin = conditionElement.attrib.get("minimum") - if cdMin is not None: - cd['minimum'] = float(cdMin) - else: - # will allow these to be None, assume axis.minimum - cd['minimum'] = None - cdMax = conditionElement.attrib.get("maximum") - if cdMax is not None: - cd['maximum'] = float(cdMax) - else: - # will allow these to be None, assume axis.maximum - cd['maximum'] = None - cd['name'] = conditionElement.attrib.get("axisname") - # # test for things - if cd.get('minimum') is None and cd.get('maximum') is None: - raise DesignSpaceDocumentError( - "condition missing required minimum or maximum in rule" + - (" '%s'" % ruleName if ruleName is not None else "")) - cds.append(cd) - return cds - - def readAxes(self): - # read the axes elements, including the warp map. - axisElements = self.root.findall(".axis") - if not axisElements: - # raise error, we need axes - return - for axisElement in axisElements: - axisObject = self.axisDescriptorClass() - axisObject.name = axisElement.attrib.get("name") - axisObject.tag = axisElement.attrib.get("shortname") - axisObject.minimum = float(axisElement.attrib.get("minimum")) - axisObject.maximum = float(axisElement.attrib.get("maximum")) - axisObject.default = float(axisElement.attrib.get("initialvalue", axisObject.minimum)) - self.documentObject.axes.append(axisObject) - self.axisDefaults[axisObject.name] = axisObject.default - self.documentObject.defaultLoc = self.axisDefaults - - def colorFromElement(self, element): - elementColor = None - for colorElement in element.findall('.color'): - elementColor = self.readColorElement(colorElement) - - def readColorElement(self, colorElement): - pass - - def locationFromElement(self, element): - elementLocation = None - for locationElement in element.findall('.location'): - elementLocation = self.readLocationElement(locationElement) - break - if not self.allowAnisotropic: - # don't want any anisotropic values here - split = {} - for k, v in elementLocation.items(): - if type(v) == type(()): - split[k] = v[0] - else: - split[k] = v - elementLocation = split - return elementLocation - - def readLocationElement(self, locationElement): - """ Format 0 location reader """ - if self._strictAxisNames and not self.documentObject.axes: - raise DesignSpaceDocumentError("No axes defined") - loc = {} - for dimensionElement in locationElement.findall(".dimension"): - dimName = dimensionElement.attrib.get("name") - if self._strictAxisNames and dimName not in self.axisDefaults: - # In case the document contains no axis definitions, - self.log.warning("Location with undefined axis: \"%s\".", dimName) - continue - xValue = yValue = None - try: - xValue = dimensionElement.attrib.get('xvalue') - xValue = float(xValue) - except ValueError: - self.log.warning("KeyError in readLocation xValue %3.3f", xValue) - try: - yValue = dimensionElement.attrib.get('yvalue') - if yValue is not None: - yValue = float(yValue) - except ValueError: - pass - if yValue is not None: - loc[dimName] = (xValue, yValue) - else: - loc[dimName] = xValue - return loc - - def readSources(self): - for sourceCount, sourceElement in enumerate(self.root.findall(".master")): - filename = sourceElement.attrib.get('filename') - if filename is not None and self.path is not None: - sourcePath = os.path.abspath(os.path.join(os.path.dirname(self.path), filename)) - else: - sourcePath = None - sourceName = sourceElement.attrib.get('name') - if sourceName is None: - # add a temporary source name - sourceName = "temp_master.%d" % (sourceCount) - sourceObject = self.sourceDescriptorClass() - sourceObject.path = sourcePath # absolute path to the ufo source - sourceObject.filename = filename # path as it is stored in the document - sourceObject.name = sourceName - familyName = sourceElement.attrib.get("familyname") - if familyName is not None: - sourceObject.familyName = familyName - styleName = sourceElement.attrib.get("stylename") - if styleName is not None: - sourceObject.styleName = styleName - sourceObject.location = self.locationFromElement(sourceElement) - isMuted = False - for maskedElement in sourceElement.findall('.maskedfont'): - # mute isn't stored in the sourceDescriptor, but we can store it in the lib - if maskedElement.attrib.get('font') == "1": - isMuted = True - for libElement in sourceElement.findall('.provideLib'): - if libElement.attrib.get('state') == '1': - sourceObject.copyLib = True - for groupsElement in sourceElement.findall('.provideGroups'): - if groupsElement.attrib.get('state') == '1': - sourceObject.copyGroups = True - for infoElement in sourceElement.findall(".provideInfo"): - if infoElement.attrib.get('state') == '1': - sourceObject.copyInfo = True - for featuresElement in sourceElement.findall(".provideFeatures"): - if featuresElement.attrib.get('state') == '1': - sourceObject.copyFeatures = True - for glyphElement in sourceElement.findall(".glyph"): - glyphName = glyphElement.attrib.get('name') - if glyphName is None: - continue - if glyphElement.attrib.get('mute') == '1': - sourceObject.mutedGlyphNames.append(glyphName) - self.documentObject.sources.append(sourceObject) - if isMuted: - if not skateboardMutedSourcesKey in self.documentObject.lib: - self.documentObject.lib[skateboardMutedSourcesKey] = [] - item = (sourceObject.filename, "foreground") - self.documentObject.lib[skateboardMutedSourcesKey].append(item) - - def readInstances(self): - for instanceCount, instanceElement in enumerate(self.root.findall(".instance")): - instanceObject = self.instanceDescriptorClass() - if instanceElement.attrib.get("familyname"): - instanceObject.familyName = instanceElement.attrib.get("familyname") - if instanceElement.attrib.get("stylename"): - instanceObject.styleName = instanceElement.attrib.get("stylename") - if instanceElement.attrib.get("styleMapFamilyName"): - instanceObject.styleMapFamilyName = instanceElement.attrib.get("styleMapFamilyName") - if instanceElement.attrib.get("styleMapStyleName"): - instanceObject.styleMapStyleName = instanceElement.attrib.get("styleMapStyleName") - if instanceElement.attrib.get("styleMapFamilyName"): - instanceObject.styleMapFamilyName = instanceElement.attrib.get("styleMapFamilyName") - instanceObject.location = self.locationFromElement(instanceElement) - instanceObject.filename = instanceElement.attrib.get('filename') - for libElement in instanceElement.findall('.provideLib'): - if libElement.attrib.get('state') == '1': - instanceObject.lib = True - for libElement in instanceElement.findall('.provideInfo'): - if libElement.attrib.get('state') == '1': - instanceObject.info = True - self.documentObject.instances.append(instanceObject) - -def sp3_to_designspace(sp3path, designspacePath=None): - if designspacePath is None: - designspacePath = sp3path.replace(".sp3", ".designspace") - doc = DesignSpaceDocument() - reader = SuperpolatorReader(sp3path, doc) - reader.read() - doc.write(designspacePath) - - -if __name__ == "__main__": - - def test_superpolator_testdoc1(): - # read superpolator_testdoc1.sp3 - # and test all the values - testDoc = DesignSpaceDocument() - testPath = "../../Tests/spReader_testdocs/superpolator_testdoc1.sp3" - reader = SuperpolatorReader(testPath, testDoc) - reader.read() - - # check the axes - names = [a.name for a in reader.documentObject.axes] - names.sort() - assert names == ['grade', 'space', 'weight', 'width'] - tags = [a.tag for a in reader.documentObject.axes] - tags.sort() - assert tags == ['SPCE', 'grad', 'wdth', 'wght'] - - # check the data items - assert superpolatorDataLibKey in reader.documentObject.lib - items = list(reader.documentObject.lib[superpolatorDataLibKey].items()) - items.sort() - assert items == [('expandRules', False), ('horizontalPreviewAxis', 'width'), ('includeLegacyRules', False), ('instancefolder', 'instances'), ('keepWorkFiles', True), ('lineInverted', True), ('lineStacked', 'lined'), ('lineViewFilled', True), ('outputFormatUFO', 3.0), ('previewtext', 'VA'), ('roundGeometry', False), ('verticalPreviewAxis', 'weight')] - - # check the sources - for sd in reader.documentObject.sources: - assert sd.familyName == "MutatorMathTest_SourceFamilyName" - if sd.styleName == "Default": - assert sd.location == {'width': 0.0, 'weight': 0.0, 'space': 0.0, 'grade': -0.5} - assert sd.copyLib == True - assert sd.copyGroups == True - assert sd.copyInfo == True - assert sd.copyFeatures == True - elif sd.styleName == "TheOther": - assert sd.location == {'width': 0.0, 'weight': 1000.0, 'space': 0.0, 'grade': -0.5} - assert sd.copyLib == False - assert sd.copyGroups == False - assert sd.copyInfo == False - assert sd.copyFeatures == False - - # check the instances - for nd in reader.documentObject.instances: - assert nd.familyName == "MutatorMathTest_InstanceFamilyName" - if nd.styleName == "AWeightThatILike": - assert nd.location == {'width': 133.152174, 'weight': 723.981097, 'space': 0.0, 'grade': -0.5} - assert nd.filename == "instances/MutatorMathTest_InstanceFamilyName-AWeightThatILike.ufo" - assert nd.styleMapFamilyName == None - assert nd.styleMapStyleName == None - if nd.styleName == "wdth759.79_SPCE0.00_wght260.72": - # note the converted anisotropic location in the width axis. - assert nd.location == {'grade': -0.5, 'width': 500.0, 'weight': 260.7217, 'space': 0.0} - assert nd.filename == "instances/MutatorMathTest_InstanceFamilyName-wdth759.79_SPCE0.00_wght260.72.ufo" - assert nd.styleMapFamilyName == "StyleMappedFamily" - assert nd.styleMapStyleName == "bold" - - # check the rules - for rd in reader.documentObject.rules: - assert rd.name == "width: < 500.0" - assert len(rd.conditionSets) == 1 - assert rd.subs == [('I', 'I.narrow')] - for conditionSet in rd.conditionSets: - for cd in conditionSet: - if cd['name'] == "width": - assert cd == {'minimum': None, 'maximum': 500.0, 'name': 'width'} - if cd['name'] == "grade": - assert cd == {'minimum': 0.0, 'maximum': 500.0, 'name': 'grade'} - - - testDoc.write(testPath.replace(".sp3", "_output_roundtripped.designspace")) - - def test_testDocs(): - # read the test files and convert them - # no tests - root = "../../Tests/spReader_testdocs/test*.sp3" - for path in glob.glob(root): - sp3_to_designspace(path) - - test_superpolator_testdoc1() - #test_testDocs() \ No newline at end of file diff --git a/Lib/ufoProcessor/ufoOperator.py b/Lib/ufoProcessor/ufoOperator.py new file mode 100644 index 0000000..e9cf37a --- /dev/null +++ b/Lib/ufoProcessor/ufoOperator.py @@ -0,0 +1,1013 @@ +import os +import glob +import functools + + +import defcon +from warnings import warn +import collections +import logging, traceback + +from fontTools.designspaceLib import DesignSpaceDocument, SourceDescriptor, InstanceDescriptor, AxisDescriptor, RuleDescriptor, processRules +from fontTools.designspaceLib.split import splitInterpolable +from fontTools.ufoLib import fontInfoAttributesVersion1, fontInfoAttributesVersion2, fontInfoAttributesVersion3 +from fontTools.misc import plistlib + +from fontMath.mathGlyph import MathGlyph +from fontMath.mathInfo import MathInfo +from fontMath.mathKerning import MathKerning +from mutatorMath.objects.mutator import buildMutator +from mutatorMath.objects.location import Location + +import fontParts.fontshell.font + +import ufoProcessor.varModels +import ufoProcessor.pens +from ufoProcessor.varModels import VariationModelMutator +from ufoProcessor.pens import checkGlyphIsEmpty, DecomposePointPen +from ufoProcessor.logger import Logger + +_memoizeCache = dict() +_memoizeStats = dict() + +def immutify(obj): + # make an immutable version of this object. + # assert immutify(10) == (10,) + # assert immutify([10, 20, "a"]) == (10, 20, 'a') + # assert immutify(dict(foo="bar", world=["a", "b"])) == ('foo', ('bar',), 'world', ('a', 'b')) + hashValues = [] + if isinstance(obj, dict): + for key, value in obj.items(): + hashValues.extend([key, immutify(value)]) + elif isinstance(obj, list): + for value in obj: + hashValues.extend(immutify(value)) + else: + hashValues.append(obj) + return tuple(hashValues) + +def memoize(function): + @functools.wraps(function) + def wrapper(self, *args, **kwargs): + immutableargs = tuple([immutify(a) for a in args]) + immutablekwargs = immutify(kwargs) + key = (function.__name__, self, immutableargs, immutify(kwargs)) + if key in _memoizeCache: + if not key in _memoizeStats: + _memoizeStats[key] = 0 + _memoizeStats[key] += 1 + return _memoizeCache[key] + else: + result = function(self, *args, **kwargs) + _memoizeCache[key] = result + return result + return wrapper + +def inspectMemoizeCache(): + functionNames = [] + stats = {} + for k in _memoizeCache.keys(): + functionName = k[0] + if not functionName in stats: + stats[functionName] = 0 + stats[functionName] += 1 + print(stats) + +def getUFOVersion(ufoPath): + # Peek into a ufo to read its format version. + # + # + # + # + # creator + # org.robofab.ufoLib + # formatVersion + # 2 + # + # + metaInfoPath = os.path.join(ufoPath, "metainfo.plist") + with open(metaInfoPath, 'rb') as f: + p = plistlib.load(f) + return p.get('formatVersion') + +def getDefaultLayerName(f): + # get the name of the default layer from a defcon font (outside RF) and from a fontparts font (outside and inside RF) + if issubclass(type(f), defcon.objects.font.Font): + return f.layers.defaultLayer.name + elif issubclass(type(f), fontParts.fontshell.font.RFont): + return f.defaultLayer.name + return None + +# wrapped, not inherited +class UFOOperator(object): + + fontClass = defcon.Font + layerClass = defcon.Layer + glyphClass = defcon.Glyph + libClass = defcon.Lib + glyphContourClass = defcon.Contour + glyphPointClass = defcon.Point + glyphComponentClass = defcon.Component + glyphAnchorClass = defcon.Anchor + kerningClass = defcon.Kerning + groupsClass = defcon.Groups + infoClass = defcon.Info + featuresClass = defcon.Features + + mathInfoClass = MathInfo + mathGlyphClass = MathGlyph + mathKerningClass = MathKerning + + def __init__(self, pathOrObject=None, ufoVersion=3, useVarlib=True, debug =False): + self.ufoVersion = ufoVersion + self.useVarlib = useVarlib + self._fontsLoaded = False + self.fonts = {} + self.roundGeometry = False + self.mutedAxisNames = None # list of axisname that need to be muted + self.debug = debug + self.logger = None + + if isinstance(pathOrObject, str): + self.doc = DesignSpaceDocument() + self.doc.read(pathOrObject) + else: + # XX test this + self.doc = pathOrObject + + if self.debug: + docBaseName = os.path.splitext(self.doc.path)[0] + logPath = f"{docBaseName}_log.txt" + self.logger = Logger(path=logPath, rootDirectory=None) + self.logger.time() + self.logger.info(f"## {self.doc.path}") + self.logger.info(f"\tUFO version: {self.ufoVersion}") + self.logger.info(f"\tround Geometry: {self.roundGeometry}") + if self.useVarlib: + self.logger.info(f"\tinterpolating with varlib") + else: + self.logger.info(f"\tinterpolating with mutatorMath") + + def _instantiateFont(self, path): + """ Return a instance of a font object with all the given subclasses""" + try: + return self.fontClass(path, + layerClass=self.layerClass, + libClass=self.libClass, + kerningClass=self.kerningClass, + groupsClass=self.groupsClass, + infoClass=self.infoClass, + featuresClass=self.featuresClass, + glyphClass=self.glyphClass, + glyphContourClass=self.glyphContourClass, + glyphPointClass=self.glyphPointClass, + glyphComponentClass=self.glyphComponentClass, + glyphAnchorClass=self.glyphAnchorClass) + except TypeError: + # if our fontClass doesnt support all the additional classes + return self.fontClass(path) + + # loading and updating fonts + def loadFonts(self, reload=False): + # Load the fonts and find the default candidate based on the info flag + if self._fontsLoaded and not reload: + if self.debug: + self.logger.info("\t\t-- loadFonts requested, but fonts are loaded already and no reload requested") + return + names = set() + actions = [] + if self.debug: + self.logger.info("## loadFonts") + for i, sourceDescriptor in enumerate(self.doc.sources): + if sourceDescriptor.name is None: + # make sure it has a unique name + sourceDescriptor.name = "source.%d" % i + if sourceDescriptor.name not in self.fonts: + if os.path.exists(sourceDescriptor.path): + f = self.fonts[sourceDescriptor.name] = self._instantiateFont(sourceDescriptor.path) + thisLayerName = getDefaultLayerName(f) + actions.append(f"loaded: {os.path.basename(sourceDescriptor.path)}, layer: {thisLayerName}, format: {getUFOVersion(sourceDescriptor.path)}, id: {id(f):X}") + names |= set(self.fonts[sourceDescriptor.name].keys()) + else: + self.fonts[sourceDescriptor.name] = None + actions.append("source ufo not found at %s" % (sourceDescriptor.path)) + self.glyphNames = list(names) + if self.debug: + for item in actions: + self.logger.infoItem(item) + self._fontsLoaded = True + + def _logLoadedFonts(self): + # dump info about the loaded fonts to the log + items = [] + self.logger.info("\t# font status:") + for name, fontObj in self.fonts.items(): + self.logger.info(f"\t\tloaded: , id: {id(fontObj):X}, {os.path.basename(fontObj.path)}, format: {getUFOVersion(fontObj.path)}") + + def updateFonts(self, fontObjects): + # this is to update the loaded fonts. + # it should be the way for an editor to provide a list of fonts that are open + #self.fonts[sourceDescriptor.name] = None + hasUpdated = False + for newFont in fontObjects: + for fontName, haveFont in self.fonts.items(): + if haveFont.path == newFont.path and id(haveFont)!=id(newFont): + note = f"## updating source {self.fonts[fontName]} with {newFont}" + if self.debug: + self.logger.time() + self.logger.info(note) + self.fonts[fontName] = newFont + hasUpdated = True + if hasUpdated: + self.changed() + + # caching + def changed(self): + # clears everything relating to this designspacedocument + # the cache could contain more designspacedocument objects. + for key in list(_memoizeCache.keys()): + if key[1] == self: + del _memoizeCache[key] + #_memoizeCache.clear() + + def glyphChanged(self, glyphName): + # clears this one specific glyph from the memoize cache + for key in list(_memoizeCache.keys()): + #print(f"glyphChanged {[(i,m) for i, m in enumerate(key)]} {glyphName}") + # the glyphname is hiding quite deep in key[2] + # (('glyphTwo',),) + # this is because of how immutify does it. Could be different I suppose but this works + if key[0] in ("getGlyphMutator", "collectSourcesForGlyph") and key[2][0][0] == glyphName: + del _memoizeCache[key] + + # manipulate locations and axes + def splitLocation(self, location): + # split a location in a continouous and a discrete part + discreteAxes = [a.name for a in self.getOrderedDiscreteAxes()] + continuous = {} + discrete = {} + for name, value in location.items(): + if name in discreteAxes: + discrete[name] = value + else: + continuous[name] = value + if not discrete: + return continuous, None + return continuous, discrete + + def _serializeAnyAxis(self, axis): + if hasattr(axis, "serialize"): + return axis.serialize() + else: + if hasattr(axis, "values"): + # discrete axis does not have serialize method, meh + return dict( + tag=axis.tag, + name=axis.name, + labelNames=axis.labelNames, + minimum = min(axis.values), # XX is this allowed + maximum = max(axis.values), # XX is this allowed + values=axis.values, + default=axis.default, + hidden=axis.hidden, + map=axis.map, + axisOrdering=axis.axisOrdering, + axisLabels=axis.axisLabels, + ) + + def getSerializedAxes(self, discreteLocation=None): + serialized = [] + for axis in self.getOrderedContinuousAxes(): + serialized.append(self._serializeAnyAxis(axis)) + return serialized + + def getContinuousAxesForMutator(self): + # map the axis values? + d = collections.OrderedDict() + for axis in self.getOrderedContinuousAxes(): + d[axis.name] = self._serializeAnyAxis(axis) + return d + + def _getAxisOrder(self): + return [a.name for a in self.doc.axes] + + axisOrder = property(_getAxisOrder, doc="get the axis order from the axis descriptors") + + def getOrderedDiscreteAxes(self): + # return the list of discrete axis objects, in the right order + axes = [] + for axisName in self.doc.getAxisOrder(): + axisObj = self.doc.getAxis(axisName) + if hasattr(axisObj, "values"): + axes.append(axisObj) + return axes + + def getOrderedContinuousAxes(self): + # return the list of continuous axis objects, in the right order + axes = [] + for axisName in self.doc.getAxisOrder(): + axisObj = self.doc.getAxis(axisName) + if not hasattr(axisObj, "values"): + axes.append(axisObj) + return axes + + def checkDiscreteAxisValues(self, location): + # check if the discrete values in this location are allowed + for discreteAxis in self.getOrderedDiscreteAxes(): + testValue = location.get(discreteAxis.name) + if not testValue in discreteAxis.values: + return False + return True + + def collectBaseGlyphs(self, glyphName, location): + # make a list of all baseglyphs needed to build this glyph, at this location + # Note: different discrete values mean that the glyph component set up can be different too + continuousLocation, discreteLocation = self.splitLocation(location) + names = set() + def _getComponentNames(glyph): + # so we can do recursion + names = set() + for comp in glyph.components: + names.add(comp.baseGlyph) + for n in _getComponentNames(glyph.font[comp.baseGlyph]): + names.add(n) + return list(names) + for sourceDescriptor in self.findSourceDescriptorsForDiscreteLocation(discreteLocation): + sourceFont = self.fonts[sourceDescriptor.name] + if not glyphName in sourceFont: continue + [names.add(n) for n in _getComponentNames(sourceFont[glyphName])] + return list(names) + + @memoize + def findSourceDescriptorsForDiscreteLocation(self, discreteLocDict=None): + # return a list of all sourcedescriptors that share the values in the discrete loc tuple + # so this includes all sourcedescriptors that point to layers + # discreteLocDict {'countedItems': 1.0, 'outlined': 0.0}, {'countedItems': 1.0, 'outlined': 1.0} + sources = [] + for s in self.doc.sources: + ok = True + if discreteLocDict is None: + sources.append(s) + continue + for name, value in discreteLocDict.items(): + if name in s.location: + if s.location[name] != value: + ok = False + else: + ok = False + continue + if ok: + sources.append(s) + return sources + + def getVariationModel(self, items, axes, bias=None): + # Return either a mutatorMath or a varlib.model object for calculating. + if self.useVarlib: + # use the varlib variation model + try: + return dict(), VariationModelMutator(items, axes=self.doc.axes, extrapolate=True) + except TypeError: + if self.debug: + error = traceback.format_exc() + note = "Error while making VariationModelMutator for {loc}:\n{error}" + self.logger.info(note) + return {}, None + except (KeyError, AssertionError): + if self.debug: + error = traceback.format_exc() + note = "UFOProcessor.getVariationModel error: {error}" + self.logger.info(note) + return {}, None + else: + # use mutatormath model + axesForMutator = self.getContinuousAxesForMutator() + # mutator will be confused by discrete axis values. + # the bias needs to be for the continuous axes only + biasForMutator, _ = self.splitLocation(bias) + return buildMutator(items, axes=axesForMutator, bias=biasForMutator) + return {}, None + + @memoize + def newDefaultLocation(self, bend=False, discreteLocation=None): + # overwrite from fontTools.newDefaultLocation + # we do not want this default location always to be mapped. + loc = collections.OrderedDict() + for axisDescriptor in self.doc.axes: + axisName = axisDescriptor.name + axisValue = axisDescriptor.default + if discreteLocation is not None: + # if we want to find the default for a specific discreteLoation + # we can not use the discrete axis' default value + # -> we have to use the value in the given discreteLocation + if axisDescriptor.name in discreteLocation: + axisValue = discreteLocation[axisDescriptor.name] + else: + axisValue = axisDescriptor.default + if bend: + loc[axisName] = axisDescriptor.map_forward( + axisValue + ) + else: + loc[axisName] = axisValue + return loc + + @memoize + def isAnisotropic(self, location): + # check if the location has anisotropic values + for v in location.values(): + if isinstance(v, (list, tuple)): + return True + return False + + @memoize + def splitAnisotropic(self, location): + # split the anisotropic location into a horizontal and vertical component + x = Location() + y = Location() + for dim, val in location.items(): + if type(val)==tuple: + x[dim] = val[0] + y[dim] = val[1] + else: + x[dim] = y[dim] = val + return x, y + + @memoize + def _getAxisOrder(self): + return [a.name for a in self.doc.axes] + + def generateUFOs(self): + # generate an UFO for each of the instance locations + glyphCount = 0 + self.loadFonts() + if self.debug: + self.logger.info("## generateUFO") + for loc, space in splitInterpolable(self.doc): + spaceDoc = self.__class__(pathOrObject=space) + if self.debug: + self.logger.infoItem(f"Generating UFOs for continuous space at discrete location {loc}") + v = 0 + for instanceDescriptor in self.doc.instances: + if instanceDescriptor.path is None: + continue + pairs = None + bend = False + font = self.makeInstance(instanceDescriptor, + processRules, + glyphNames=self.glyphNames, + pairs=pairs, + bend=bend, + ) + if self.debug: + self.logger.info(f"\t\t{os.path.basename(instanceDescriptor.path)}") + instanceFolder = os.path.dirname(instanceDescriptor.path) + if not os.path.exists(instanceFolder): + os.makedirs(instanceFolder) + font.save(instanceDescriptor.path) + glyphCount += len(font) + if self.debug: + self.logger.info(f"\t\tGenerated {glyphCount} glyphs altogether.") + + generateUFO = generateUFOs + + @memoize + def getInfoMutator(self, discreteLocation=None): + """ Returns a info mutator for this discrete location """ + infoItems = [] + if discreteLocation is not None: + sources = self.findSourceDescriptorsForDiscreteLocation(discreteLocation) + else: + sources = self.doc.sources + for sourceDescriptor in sources: + if sourceDescriptor.layerName is not None: + continue + continuous, discrete = self.splitLocation(sourceDescriptor.location) + loc = Location(continuous) + sourceFont = self.fonts[sourceDescriptor.name] + if sourceFont is None: + continue + if hasattr(sourceFont.info, "toMathInfo"): + infoItems.append((loc, sourceFont.info.toMathInfo())) + else: + infoItems.append((loc, self.mathInfoClass(sourceFont.info))) + infoBias = self.newDefaultLocation(bend=True, discreteLocation=discreteLocation) + bias, self._infoMutator = self.getVariationModel(infoItems, axes=self.getSerializedAxes(), bias=infoBias) + return self._infoMutator + + def collectForegroundLayerNames(self): + """Return list of names of the default layers of all the fonts in this system. + Include None and foreground. XX Why + """ + names = set([None, 'foreground']) + for key, font in self.fonts.items(): + names.add(getDefaultLayerName(font)) + return list(names) + + @memoize + def getKerningMutator(self, pairs=None, discreteLocation=None): + """ Return a kerning mutator, collect the sources, build mathGlyphs. + If no pairs are given: calculate the whole table. + If pairs are given then query the sources for a value and make a mutator only with those values. + """ + if discreteLocation is not None: + sources = self.findSourceDescriptorsForDiscreteLocation(discreteLocation) + else: + sources = self.sources + kerningItems = [] + foregroundLayers = self.collectForegroundLayerNames() + if pairs is None: + for sourceDescriptor in sources: + if sourceDescriptor.layerName not in foregroundLayers: + continue + if not sourceDescriptor.muteKerning: + # filter this XX @@ + continuous, discrete = self.splitLocation(sourceDescriptor.location) + loc = Location(continuous) + sourceFont = self.fonts[sourceDescriptor.name] + if sourceFont is None: continue + # this makes assumptions about the groups of all sources being the same. + kerningItems.append((loc, self.mathKerningClass(sourceFont.kerning, sourceFont.groups))) + else: + self._kerningMutatorPairs = pairs + for sourceDescriptor in sources: + # XXX check sourceDescriptor layerName, only foreground should contribute + if sourceDescriptor.layerName is not None: + continue + if not os.path.exists(sourceDescriptor.path): + continue + if not sourceDescriptor.muteKerning: + sourceFont = self.fonts[sourceDescriptor.name] + if sourceFont is None: + continue + continuous, discrete = self.splitLocation(sourceDescriptor.location) + loc = Location(continuous) + # XXX can we get the kern value from the fontparts kerning object? + kerningItem = self.mathKerningClass(sourceFont.kerning, sourceFont.groups) + if kerningItem is not None: + sparseKerning = {} + for pair in pairs: + v = kerningItem.get(pair) + if v is not None: + sparseKerning[pair] = v + kerningItems.append((loc, self.mathKerningClass(sparseKerning))) + kerningBias = self.newDefaultLocation(bend=True, discreteLocation=discreteLocation) + bias, thing = self.getVariationModel(kerningItems, axes=self.getSerializedAxes(), bias=kerningBias) #xx + bias, self._kerningMutator = self.getVariationModel(kerningItems, axes=self.getSerializedAxes(), bias=kerningBias) + return self._kerningMutator + + @memoize + def getGlyphMutator(self, glyphName, + decomposeComponents=False, + **discreteLocation, + ): + """make a mutator / varlib object for glyphName, with the sources for the given discrete location""" + items, unicodes = self.collectSourcesForGlyph(glyphName, decomposeComponents=decomposeComponents, **discreteLocation) + new = [] + for a, b, c in items: + if hasattr(b, "toMathGlyph"): + # note: calling toMathGlyph ignores the mathGlyphClass preference + # maybe the self.mathGlyphClass is not necessary? + new.append((a,b.toMathGlyph())) + else: + new.append((a,self.mathGlyphClass(b))) + thing = None + thisBias = self.newDefaultLocation(bend=True, discreteLocation=discreteLocation) + try: + bias, thing = self.getVariationModel(new, axes=self.getSerializedAxes(), bias=thisBias) #xx + except: + error = traceback.format_exc() + note = f"Error in getGlyphMutator for {glyphName}:\n{error}" + if self.debug: + self.logger.info(note) + return thing, unicodes + + # stats indicate this does not get called very often, so caching may not be useful + #@memoize + def isLocalDefault(self, location): + # return True if location is a local default + defaults = {} + for aD in self.doc.axes: + defaults[aD.name] = aD.default + for axisName, value in location.items(): + if defaults[axisName] != value: + return False + return True + + # stats indicate this does not get called very often, so caching may not be useful + #@memoize + def filterThisLocation(self, location, mutedAxes=None): + # return location with axes is mutedAxes removed + # this means checking if the location is a non-default value + if not mutedAxes: + return False, location + defaults = {} + ignoreSource = False + for aD in self.doc.axes: + defaults[aD.name] = aD.default + new = {} + new.update(location) + for mutedAxisName in mutedAxes: + if mutedAxisName not in location: + continue + if mutedAxisName not in defaults: + continue + if location[mutedAxisName] != defaults.get(mutedAxisName): + ignoreSource = True + del new[mutedAxisName] + return ignoreSource, new + + @memoize + def collectSourcesForGlyph(self, glyphName, decomposeComponents=False, discreteLocation=None): + """ Return a glyph mutator + decomposeComponents = True causes the source glyphs to be decomposed first + before building the mutator. That gives you instances that do not depend + on a complete font. If you're calculating previews for instance. + + findSourceDescriptorsForDiscreteLocation returns sources from layers as well + """ + items = [] + empties = [] + foundEmpty = False + # is bend=True necessary here? + defaultLocation = self.newDefaultLocation(bend=True, discreteLocation=discreteLocation) + # + if discreteLocation is not None: + sources = self.findSourceDescriptorsForDiscreteLocation(discreteLocation) + else: + sources = self.doc.sources + unicodes = set() # unicodes for this glyph + for sourceDescriptor in sources: + if not os.path.exists(sourceDescriptor.path): + #kthxbai + note = "\tMissing UFO at %s" % sourceDescriptor.path + if self.debug: + self.logger.info(note) + continue + if glyphName in sourceDescriptor.mutedGlyphNames: + self.logger.info(f"\t\tglyphName {glyphName} is muted") + continue + thisIsDefault = self.isLocalDefault(sourceDescriptor.location) + ignoreSource, filteredLocation = self.filterThisLocation(sourceDescriptor.location, self.mutedAxisNames) + if ignoreSource: + continue + f = self.fonts.get(sourceDescriptor.name) + if f is None: continue + loc = Location(sourceDescriptor.location) + sourceLayer = f + if not glyphName in f: + # log this> + continue + layerName = getDefaultLayerName(f) + sourceGlyphObject = None + # handle source layers + if sourceDescriptor.layerName is not None: + # start looking for a layer + # Do not bother for mutatorMath designspaces + layerName = sourceDescriptor.layerName + sourceLayer = getLayer(f, sourceDescriptor.layerName) + if sourceLayer is None: + continue + if glyphName not in sourceLayer: + # start looking for a glyph + # this might be a support in a sparse layer + # so we're skipping! + continue + # still have to check if the sourcelayer glyph is empty + if not glyphName in sourceLayer: + continue + else: + sourceGlyphObject = sourceLayer[glyphName] + if sourceGlyphObject.unicodes is not None: + for u in sourceGlyphObject.unicodes: + unicodes.add(u) + if checkGlyphIsEmpty(sourceGlyphObject, allowWhiteSpace=True): + foundEmpty = True + #sourceGlyphObject = None + #continue + if decomposeComponents: + # what about decomposing glyphs in a partial font? + temp = self.glyphClass() + p = temp.getPointPen() + dpp = DecomposePointPen(sourceLayer, p) + sourceGlyphObject.drawPoints(dpp) + temp.width = sourceGlyphObject.width + temp.name = sourceGlyphObject.name + processThis = temp + else: + processThis = sourceGlyphObject + sourceInfo = dict(source=f.path, glyphName=glyphName, + layerName=layerName, + location=filteredLocation, # sourceDescriptor.location, + sourceName=sourceDescriptor.name, + ) + if hasattr(processThis, "toMathGlyph"): + processThis = processThis.toMathGlyph() + else: + processThis = self.mathGlyphClass(processThis) + continuous, discrete = self.splitLocation(loc) + items.append((continuous, processThis, sourceInfo)) + empties.append((thisIsDefault, foundEmpty)) + # check the empties: + # if the default glyph is empty, then all must be empty + # if the default glyph is not empty then none can be empty + checkedItems = [] + emptiesAllowed = False + # first check if the default is empty. + # remember that the sources can be in any order + for i, p in enumerate(empties): + isDefault, isEmpty = p + if isDefault and isEmpty: + emptiesAllowed = True + # now we know what to look for + if not emptiesAllowed: + for i, p in enumerate(empties): + isDefault, isEmpty = p + if not isEmpty: + checkedItems.append(items[i]) + else: + for i, p in enumerate(empties): + isDefault, isEmpty = p + if isEmpty: + checkedItems.append(items[i]) + return checkedItems, unicodes + + collectMastersForGlyph = collectSourcesForGlyph + + def makeInstance(self, instanceDescriptor, + doRules=None, + glyphNames=None, + pairs=None, + bend=False): + """ Generate a font object for this instance """ + if doRules is not None: + warn('The doRules argument in DesignSpaceProcessor.makeInstance() is deprecated', DeprecationWarning, stacklevel=2) + continuousLocation, discreteLocation = self.splitLocation(instanceDescriptor.location) + + font = self._instantiateFont(None) + + loc = Location(continuousLocation) + anisotropic = False + locHorizontal = locVertical = loc + if self.isAnisotropic(loc): + anisotropic = True + locHorizontal, locVertical = self.splitAnisotropic(loc) + if self.debug: + self.logger.info(f"\t\t\tAnisotropic location for {instanceDescriptor.name}\n\t\t\t{instanceDescriptor.location}") + if instanceDescriptor.kerning: + if pairs: + try: + kerningMutator = self.getKerningMutator(pairs=pairs, discreteLocation=discreteLocation) + kerningObject = kerningMutator.makeInstance(locHorizontal, bend=bend) + kerningObject.extractKerning(font) + except: + error = traceback.format_exc() + note = f"makeInstance: Could not make kerning for {loc}\n{error}" + if self.debug: + self.logger.info(note) + else: + kerningMutator = self.getKerningMutator(discreteLocation=discreteLocation) + if kerningMutator is not None: + kerningObject = kerningMutator.makeInstance(locHorizontal, bend=bend) + kerningObject.extractKerning(font) + if self.debug: + self.logger.info(f"\t\t\t{len(font.kerning)} kerning pairs added") + + # # make the info + infoMutator = self.getInfoMutator(discreteLocation=discreteLocation) + if infoMutator is not None: + if not anisotropic: + infoInstanceObject = infoMutator.makeInstance(loc, bend=bend) + else: + horizontalInfoInstanceObject = infoMutator.makeInstance(locHorizontal, bend=bend) + verticalInfoInstanceObject = infoMutator.makeInstance(locVertical, bend=bend) + # merge them again + infoInstanceObject = (1,0)*horizontalInfoInstanceObject + (0,1)*verticalInfoInstanceObject + if self.roundGeometry: + infoInstanceObject = infoInstanceObject.round() + infoInstanceObject.extractInfo(font.info) + font.info.familyName = instanceDescriptor.familyName + font.info.styleName = instanceDescriptor.styleName + font.info.postscriptFontName = instanceDescriptor.postScriptFontName # yikes, note the differences in capitalisation.. + font.info.styleMapFamilyName = instanceDescriptor.styleMapFamilyName + font.info.styleMapStyleName = instanceDescriptor.styleMapStyleName + + for sourceDescriptor in self.doc.sources: + if sourceDescriptor.copyInfo: + # this is the source + if self.fonts[sourceDescriptor.name] is not None: + self._copyFontInfo(self.fonts[sourceDescriptor.name].info, font.info) + if sourceDescriptor.copyLib: + # excplicitly copy the font.lib items + if self.fonts[sourceDescriptor.name] is not None: + for key, value in self.fonts[sourceDescriptor.name].lib.items(): + font.lib[key] = value + if sourceDescriptor.copyGroups: + if self.fonts[sourceDescriptor.name] is not None: + for key, value in self.fonts[sourceDescriptor.name].groups.items(): + font.groups[key] = value + if sourceDescriptor.copyFeatures: + if self.fonts[sourceDescriptor.name] is not None: + featuresText = self.fonts[sourceDescriptor.name].features.text + font.features.text = featuresText + + # ok maybe now it is time to calculate some glyphs + # glyphs + if glyphNames: + selectedGlyphNames = glyphNames + else: + selectedGlyphNames = self.glyphNames + if not 'public.glyphOrder' in font.lib.keys(): + # should be the glyphorder from the default, yes? + font.lib['public.glyphOrder'] = selectedGlyphNames + + for glyphName in selectedGlyphNames: + # can we take all this into a separate method for making a preview glyph object? + glyphMutator, unicodes = self.getGlyphMutator(glyphName, discreteLocation=discreteLocation) + if glyphMutator is None: + if self.debug: + note = f"makeInstance: Could not make mutator for glyph {glyphName}" + self.logger.info(note) + continue + + font.newGlyph(glyphName) + font[glyphName].clear() + glyphInstanceUnicodes = [] + #neutralFont = self.getNeutralFont() + font[glyphName].unicodes = unicodes + + try: + if not self.isAnisotropic(continuousLocation): + glyphInstanceObject = glyphMutator.makeInstance(continuousLocation, bend=bend) + else: + # split anisotropic location into horizontal and vertical components + horizontalGlyphInstanceObject = glyphMutator.makeInstance(locHorizontal, bend=bend) + verticalGlyphInstanceObject = glyphMutator.makeInstance(locVertical, bend=bend) + # merge them again in a beautiful single line: + glyphInstanceObject = (1,0)*horizontalGlyphInstanceObject + (0,1)*verticalGlyphInstanceObject + except IndexError: + # alignment problem with the data? + if self.debug: + note = "makeInstance: Quite possibly some sort of data alignment error in %s" % glyphName + self.logger.info(note) + continue + if self.roundGeometry: + try: + glyphInstanceObject = glyphInstanceObject.round() + except AttributeError: + # what are we catching here? + # math objects without a round method? + if self.debug: + note = f"makeInstance: no round method for {glyphInstanceObject} ?" + self.logger.info(note) + try: + # File "/Users/erik/code/ufoProcessor/Lib/ufoProcessor/__init__.py", line 649, in makeInstance + # glyphInstanceObject.extractGlyph(font[glyphName], onlyGeometry=True) + # File "/Applications/RoboFont.app/Contents/Resources/lib/python3.6/fontMath/mathGlyph.py", line 315, in extractGlyph + # glyph.anchors = [dict(anchor) for anchor in self.anchors] + # File "/Applications/RoboFont.app/Contents/Resources/lib/python3.6/fontParts/base/base.py", line 103, in __set__ + # raise FontPartsError("no setter for %r" % self.name) + # fontParts.base.errors.FontPartsError: no setter for 'anchors' + if hasattr(font[glyphName], "fromMathGlyph"): + font[glyphName].fromMathGlyph(glyphInstanceObject) + else: + glyphInstanceObject.extractGlyph(font[glyphName], onlyGeometry=True) + except TypeError: + # this causes ruled glyphs to end up in the wrong glyphname + # but defcon2 objects don't support it + pPen = font[glyphName].getPointPen() + font[glyphName].clear() + glyphInstanceObject.drawPoints(pPen) + font[glyphName].width = glyphInstanceObject.width + + if self.debug: + self.logger.info(f"\t\t\t{len(selectedGlyphNames)} glyphs added") + return font + + # cache? could cause a lot of material in memory that we don't really need. Test this! + + @memoize + def makeOneGlyph(self, glyphName, location, bend=False, decomposeComponents=True, useVarlib=False, roundGeometry=False): + # make me one glyph with everything + # Unlike makeInstance(), this is focussed on a single glyph, for previewing, + # and the location will be the driving factor + continuousLocation, discreteLocation = self.splitLocation(location) + # check if the discreteLocation is within limits + if not self.checkDiscreteAxisValues(discreteLocation): + if self.debug: + self.logger.info(f"\t\tmakeOneGlyph reports: {location} has illegal value for discrete location") + return None + previousModel = self.useVarlib + self.useVarlib = useVarlib + glyphMutator, unicodes = self.getGlyphMutator(glyphName, decomposeComponents=decomposeComponents, discreteLocation=discreteLocation) + if not glyphMutator: return None + try: + if not self.isAnisotropic(location): + glyphInstanceObject = glyphMutator.makeInstance(continuousLocation, bend=bend) + else: + anisotropic = True + if self.debug: + self.logger.info(f"\t\tmakeOneGlyph anisotropic location: {location}") + loc = Location(continuousLocation) + locHorizontal, locVertical = self.splitAnisotropic(loc) + # split anisotropic location into horizontal and vertical components + horizontalGlyphInstanceObject = glyphMutator.makeInstance(locHorizontal, bend=bend) + verticalGlyphInstanceObject = glyphMutator.makeInstance(locVertical, bend=bend) + # merge them again + glyphInstanceObject = (1,0)*horizontalGlyphInstanceObject + (0,1)*verticalGlyphInstanceObject + if self.debug: + self.logger.info(f"makeOneGlyph anisotropic glyphInstanceObject {glyphInstanceObject}") + except IndexError: + # alignment problem with the data? + if self.debug: + note = "makeOneGlyph: Quite possibly some sort of data alignment error in %s" % glyphName + self.logger.info(note) + return None + glyphInstanceObject.unicodes = unicodes + if roundGeometry: + glyphInstanceObject.round() + self.useVarlib = previousModel + return glyphInstanceObject + + def _copyFontInfo(self, sourceInfo, targetInfo): + """ Copy the non-calculating fields from the source info.""" + infoAttributes = [ + "versionMajor", + "versionMinor", + "copyright", + "trademark", + "note", + "openTypeGaspRangeRecords", + "openTypeHeadCreated", + "openTypeHeadFlags", + "openTypeNameDesigner", + "openTypeNameDesignerURL", + "openTypeNameManufacturer", + "openTypeNameManufacturerURL", + "openTypeNameLicense", + "openTypeNameLicenseURL", + "openTypeNameVersion", + "openTypeNameUniqueID", + "openTypeNameDescription", + "#openTypeNamePreferredFamilyName", + "#openTypeNamePreferredSubfamilyName", + "#openTypeNameCompatibleFullName", + "openTypeNameSampleText", + "openTypeNameWWSFamilyName", + "openTypeNameWWSSubfamilyName", + "openTypeNameRecords", + "openTypeOS2Selection", + "openTypeOS2VendorID", + "openTypeOS2Panose", + "openTypeOS2FamilyClass", + "openTypeOS2UnicodeRanges", + "openTypeOS2CodePageRanges", + "openTypeOS2Type", + "postscriptIsFixedPitch", + "postscriptForceBold", + "postscriptDefaultCharacter", + "postscriptWindowsCharacterSet" + ] + for infoAttribute in infoAttributes: + copy = False + if self.ufoVersion == 1 and infoAttribute in fontInfoAttributesVersion1: + copy = True + elif self.ufoVersion == 2 and infoAttribute in fontInfoAttributesVersion2: + copy = True + elif self.ufoVersion == 3 and infoAttribute in fontInfoAttributesVersion3: + copy = True + if copy: + value = getattr(sourceInfo, infoAttribute) + setattr(targetInfo, infoAttribute, value) + + + +if __name__ == "__main__": + import time, random + from fontParts.world import RFont + ds5Path = "../../Tests/ds5/ds5.designspace" + dumpCacheLog = True + makeUFOs = True + import os + if os.path.exists(ds5Path): + startTime = time.time() + doc = UFOOperator(ds5Path, useVarlib=True, debug=True) + doc.loadFonts() + print("collectForegroundLayerNames", doc.collectForegroundLayerNames()) + if makeUFOs: + doc.generateUFOs() + #doc.updateFonts([f]) + #doc._logLoadedFonts() + loc = doc.newDefaultLocation() + res = doc.makeOneGlyph("glyphOne", location=loc) + + if dumpCacheLog: + doc.logger.info(f"Test: cached {len(_memoizeCache)} items") + for key, item in _memoizeCache.items(): + doc.logger.info(f"\t\t{key} {item}") + endTime = time.time() + duration = endTime - startTime + print(f"duration: {duration}" ) + + inspectMemoizeCache() + for key, value in _memoizeStats.items(): + print(key[0], value) \ No newline at end of file diff --git a/Lib/ufoProcessor/ufoProcessorSketch.py b/Lib/ufoProcessor/ufoProcessorSketch.py new file mode 100644 index 0000000..29d1c33 --- /dev/null +++ b/Lib/ufoProcessor/ufoProcessorSketch.py @@ -0,0 +1,94 @@ +## caching +import functools + + +_memoizeCache = dict() + + +def memoize(function): + @functools.wraps(function) + def wrapper(self, *args, **kwargs): + key = (function.__name__, self, args, tuple((key, kwargs[key]) for key in sorted(kwargs.keys()))) + if key in _memoizeCache: + return _memoizeCache[key] + else: + result = function(self, *args, **kwargs) + _memoizeCache[key] = result + return result + return wrapper + + +##### + +from fontTools.designspaceLib import DesignSpaceDocument + + +class DesignSpaceProcessor(DesignSpaceDocument): + + @memoize + def getGlyphMutator(self, glyphName, decomposeComponents=False, **discreteLocation): + glyphs = self.collectSourcesForGlyph(glyphName, decomposeComponents=decomposeComponents, **discreteLocation) + + print("build for glyphName", glyphName, discreteLocation) + return "a mutator" + + @memoize + def collectSourcesForGlyph(self, glyphName, decomposeComponents=False, **discreteLocation): + discreteLocation = self.buildDiscreteLocation(discreteLocation) + sources = self.findSourceDescriptorsForDiscreteLocation(**discreteLocation) + return [] + + @memoize + def findSourceDescriptorsForDiscreteLocation(self, **discreteLocation): + discreteLocation = self.buildDiscreteLocation(discreteLocation) + sources = [] + for source in self.sources: + # check if part of a dict is inside an other dict + if discreteLocation.items() <= source.designLocation.items(): + sources.append(source) + return sources + + def buildDiscreteLocation(self, partialDiscretelocation): + return {**self.getDiscreteDefaultLocation(), **partialDiscretelocation} + + @property + def discreteAxes(self): + return [axis for axis in self.axes if hasattr(axis, "values")] + + def getDiscreteDefaultLocation(self): + discreteDefault = dict() + for axis in self.discreteAxes: + discreteDefault[axis.name] = axis.default + return discreteDefault + + def getDiscreteLocations(self): + for axis in self.discreteAxes: + print(axis) + + # chaching tools + + def changed(self): + _memoizeCache.clear() + + def glyphChanged(self, glyphName): + for key in list(_memoizeCache.keys()): + if key[0] in ("getGlyphMutator", "collectSourcesForGlyph") and key[1] == glyphName: + del _memoizeCache[key] + + + +d = DesignSpaceProcessor() +ds5Path = "../../Tests/202206 discrete spaces/test.ds5.designspace" +d.read(ds5Path) +r = d.getGlyphMutator("a", italic=0) +print(r) + +r = d.getGlyphMutator("a", italic=0) +print(r) +r = d.getGlyphMutator("a", italic=1) +print(r) + +print(d.getDiscreteDefaultLocation()) + +print(d.findSourceDescriptorsForDiscreteLocation(countedItems=1)) +print(d.getDiscreteLocations()) \ No newline at end of file diff --git a/Lib/ufoProcessor/varModels.py b/Lib/ufoProcessor/varModels.py index 9deef48..565b463 100644 --- a/Lib/ufoProcessor/varModels.py +++ b/Lib/ufoProcessor/varModels.py @@ -57,27 +57,35 @@ class VariationModelMutator(object): but uses the fonttools varlib logic to calculate. """ - def __init__(self, items, axes, model=None): + def __init__(self, items, axes, model=None, extrapolate=True): # items: list of locationdict, value tuples # axes: list of axis dictionaried, not axisdescriptor objects. # model: a model, if we want to share one + self.extrapolate = extrapolate self.axisOrder = [a.name for a in axes] self.axisMapper = AxisMapper(axes) self.axes = {} for a in axes: - mappedMinimum, mappedDefault, mappedMaximum = a.map_forward(a.minimum), a.map_forward(a.default), a.map_forward(a.maximum) - #self.axes[a.name] = (a.minimum, a.default, a.maximum) + axisMinimum, axisMaximum = self.getAxisMinMax(a) + mappedMinimum, mappedDefault, mappedMaximum = a.map_forward(axisMinimum), a.map_forward(a.default), a.map_forward(axisMaximum) self.axes[a.name] = (mappedMinimum, mappedDefault, mappedMaximum) if model is None: dd = [self._normalize(a) for a,b in items] ee = self.axisOrder - self.model = VariationModel(dd, axisOrder=ee) + self.model = VariationModel(dd, axisOrder=ee, extrapolate=self.extrapolate) else: self.model = model self.masters = [b for a, b in items] self.locations = [a for a, b in items] + def getAxisMinMax(self, axis): + # return tha axis.minimum and axis.maximum for continuous axes + # return the min(axis.values), max(axis.values) for discrete axes + if hasattr(axis, "values"): + return min(axis.values), max(axis.values) + return axis.minimum, axis.maximum + def get(self, key): if key in self.model.locations: i = self.model.locations.index(key) @@ -98,18 +106,13 @@ def getReach(self): items = [] for supportIndex, s in enumerate(self.getSupports()): sortedOrder = self.model.reverseMapping[supportIndex] - #print("getReach", self.masters[sortedOrder], s) - #print("getReach", self.locations[sortedOrder]) items.append((self.masters[sortedOrder], s)) return items - def makeInstance(self, location, bend=False): # check for anisotropic locations here - #print("\t1", location) if bend: location = self.axisMapper(location) - #print("\t2", location) nl = self._normalize(location) return self.model.interpolateFromMasters(nl, self.masters) @@ -206,7 +209,7 @@ def _normalize(self, location): assert mm.makeInstance(dict(Weight=0, Width=10)) == 13 - l = dict(Weight=400, Width=200) + l = dict(Weight=400, Width=20) lmapped = aam(l) print('0 loc', l) print('0 loc mapped', lmapped) diff --git a/Tests/20190830 benders/benderTest1.ufo/fontinfo.plist b/Tests/20190830 benders/benderTest1.ufo/fontinfo.plist deleted file mode 100644 index 09549d9..0000000 --- a/Tests/20190830 benders/benderTest1.ufo/fontinfo.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - ascender - 750 - capHeight - 500 - descender - -250 - familyName - BenderTest - guidelines - - postscriptBlueValues - - postscriptFamilyBlues - - postscriptFamilyOtherBlues - - postscriptOtherBlues - - postscriptStemSnapH - - postscriptStemSnapV - - styleName - One - unitsPerEm - 1000 - xHeight - 500 - - diff --git a/Tests/20190830 benders/benderTest1.ufo/glyphs/a.glif b/Tests/20190830 benders/benderTest1.ufo/glyphs/a.glif deleted file mode 100644 index 20407c7..0000000 --- a/Tests/20190830 benders/benderTest1.ufo/glyphs/a.glif +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/Tests/20190830 benders/benderTest1.ufo/lib.plist b/Tests/20190830 benders/benderTest1.ufo/lib.plist deleted file mode 100644 index d4b67bb..0000000 --- a/Tests/20190830 benders/benderTest1.ufo/lib.plist +++ /dev/null @@ -1,236 +0,0 @@ - - - - - com.defcon.sortDescriptor - - - ascending - Latin-1 - type - characterSet - - - com.typemytype.robofont.compileSettings.autohint - - com.typemytype.robofont.compileSettings.checkOutlines - - com.typemytype.robofont.compileSettings.createDummyDSIG - - com.typemytype.robofont.compileSettings.decompose - - com.typemytype.robofont.compileSettings.generateFormat - 0 - com.typemytype.robofont.compileSettings.releaseMode - - com.typemytype.robofont.italicSlantOffset - 0 - com.typemytype.robofont.segmentType - curve - com.typemytype.robofont.shouldAddPointsInSplineConversion - 1 - public.glyphOrder - - space - exclam - quotedbl - numbersign - dollar - percent - ampersand - parenleft - parenright - asterisk - plus - comma - hyphen - period - slash - zero - one - two - three - four - five - six - seven - eight - nine - colon - semicolon - less - equal - greater - question - at - A - B - C - D - E - F - G - H - I - J - K - L - M - N - O - P - Q - R - S - T - U - V - W - X - Y - Z - bracketleft - backslash - bracketright - asciicircum - underscore - grave - a - b - c - d - e - f - g - h - i - j - k - l - m - n - o - p - q - r - s - t - u - v - w - x - y - z - braceleft - bar - braceright - asciitilde - exclamdown - cent - sterling - currency - yen - brokenbar - section - dieresis - copyright - ordfeminine - guillemotleft - logicalnot - registered - macron - degree - plusminus - twosuperior - threesuperior - acute - mu - paragraph - periodcentered - cedilla - onesuperior - ordmasculine - guillemotright - onequarter - onehalf - threequarters - questiondown - Agrave - Aacute - Acircumflex - Atilde - Adieresis - Aring - AE - Ccedilla - Egrave - Eacute - Ecircumflex - Edieresis - Igrave - Iacute - Icircumflex - Idieresis - Eth - Ntilde - Ograve - Oacute - Ocircumflex - Otilde - Odieresis - multiply - Oslash - Ugrave - Uacute - Ucircumflex - Udieresis - Yacute - Thorn - germandbls - agrave - aacute - acircumflex - atilde - adieresis - aring - ae - ccedilla - egrave - eacute - ecircumflex - edieresis - igrave - iacute - icircumflex - idieresis - eth - ntilde - ograve - oacute - ocircumflex - otilde - odieresis - divide - oslash - ugrave - uacute - ucircumflex - udieresis - yacute - thorn - ydieresis - dotlessi - circumflex - caron - breve - dotaccent - ring - ogonek - tilde - hungarumlaut - quoteleft - quoteright - minus - - - diff --git a/Tests/20190830 benders/benderTest2.ufo/fontinfo.plist b/Tests/20190830 benders/benderTest2.ufo/fontinfo.plist deleted file mode 100644 index 21747a9..0000000 --- a/Tests/20190830 benders/benderTest2.ufo/fontinfo.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - ascender - 750 - capHeight - 601 - descender - -250 - familyName - BenderTest - guidelines - - postscriptBlueValues - - postscriptFamilyBlues - - postscriptFamilyOtherBlues - - postscriptOtherBlues - - postscriptStemSnapH - - postscriptStemSnapV - - styleName - Two - unitsPerEm - 1000 - xHeight - 500 - - diff --git a/Tests/20190830 benders/benderTest2.ufo/glyphs.background/contents.plist b/Tests/20190830 benders/benderTest2.ufo/glyphs.background/contents.plist deleted file mode 100644 index 1e96c94..0000000 --- a/Tests/20190830 benders/benderTest2.ufo/glyphs.background/contents.plist +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/Tests/20190830 benders/benderTest2.ufo/glyphs/a.glif b/Tests/20190830 benders/benderTest2.ufo/glyphs/a.glif deleted file mode 100644 index d0bbac1..0000000 --- a/Tests/20190830 benders/benderTest2.ufo/glyphs/a.glif +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/Tests/20190830 benders/benderTest2.ufo/kerning.plist b/Tests/20190830 benders/benderTest2.ufo/kerning.plist deleted file mode 100644 index c3bce48..0000000 --- a/Tests/20190830 benders/benderTest2.ufo/kerning.plist +++ /dev/null @@ -1,11 +0,0 @@ - - - - - a - - a - -200 - - - diff --git a/Tests/20190830 benders/benderTest2.ufo/lib.plist b/Tests/20190830 benders/benderTest2.ufo/lib.plist deleted file mode 100644 index d4b67bb..0000000 --- a/Tests/20190830 benders/benderTest2.ufo/lib.plist +++ /dev/null @@ -1,236 +0,0 @@ - - - - - com.defcon.sortDescriptor - - - ascending - Latin-1 - type - characterSet - - - com.typemytype.robofont.compileSettings.autohint - - com.typemytype.robofont.compileSettings.checkOutlines - - com.typemytype.robofont.compileSettings.createDummyDSIG - - com.typemytype.robofont.compileSettings.decompose - - com.typemytype.robofont.compileSettings.generateFormat - 0 - com.typemytype.robofont.compileSettings.releaseMode - - com.typemytype.robofont.italicSlantOffset - 0 - com.typemytype.robofont.segmentType - curve - com.typemytype.robofont.shouldAddPointsInSplineConversion - 1 - public.glyphOrder - - space - exclam - quotedbl - numbersign - dollar - percent - ampersand - parenleft - parenright - asterisk - plus - comma - hyphen - period - slash - zero - one - two - three - four - five - six - seven - eight - nine - colon - semicolon - less - equal - greater - question - at - A - B - C - D - E - F - G - H - I - J - K - L - M - N - O - P - Q - R - S - T - U - V - W - X - Y - Z - bracketleft - backslash - bracketright - asciicircum - underscore - grave - a - b - c - d - e - f - g - h - i - j - k - l - m - n - o - p - q - r - s - t - u - v - w - x - y - z - braceleft - bar - braceright - asciitilde - exclamdown - cent - sterling - currency - yen - brokenbar - section - dieresis - copyright - ordfeminine - guillemotleft - logicalnot - registered - macron - degree - plusminus - twosuperior - threesuperior - acute - mu - paragraph - periodcentered - cedilla - onesuperior - ordmasculine - guillemotright - onequarter - onehalf - threequarters - questiondown - Agrave - Aacute - Acircumflex - Atilde - Adieresis - Aring - AE - Ccedilla - Egrave - Eacute - Ecircumflex - Edieresis - Igrave - Iacute - Icircumflex - Idieresis - Eth - Ntilde - Ograve - Oacute - Ocircumflex - Otilde - Odieresis - multiply - Oslash - Ugrave - Uacute - Ucircumflex - Udieresis - Yacute - Thorn - germandbls - agrave - aacute - acircumflex - atilde - adieresis - aring - ae - ccedilla - egrave - eacute - ecircumflex - edieresis - igrave - iacute - icircumflex - idieresis - eth - ntilde - ograve - oacute - ocircumflex - otilde - odieresis - divide - oslash - ugrave - uacute - ucircumflex - udieresis - yacute - thorn - ydieresis - dotlessi - circumflex - caron - breve - dotaccent - ring - ogonek - tilde - hungarumlaut - quoteleft - quoteright - minus - - - diff --git a/Tests/20190830 benders/benderTest3.ufo/fontinfo.plist b/Tests/20190830 benders/benderTest3.ufo/fontinfo.plist deleted file mode 100644 index 28c3286..0000000 --- a/Tests/20190830 benders/benderTest3.ufo/fontinfo.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - ascender - 750 - capHeight - 700 - descender - -250 - familyName - BenderTest - guidelines - - postscriptBlueValues - - postscriptFamilyBlues - - postscriptFamilyOtherBlues - - postscriptOtherBlues - - postscriptStemSnapH - - postscriptStemSnapV - - styleName - Three - unitsPerEm - 1000 - xHeight - 500 - - diff --git a/Tests/20190830 benders/benderTest3.ufo/glyphs.background/contents.plist b/Tests/20190830 benders/benderTest3.ufo/glyphs.background/contents.plist deleted file mode 100644 index 1e96c94..0000000 --- a/Tests/20190830 benders/benderTest3.ufo/glyphs.background/contents.plist +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/Tests/20190830 benders/benderTest3.ufo/glyphs/a.glif b/Tests/20190830 benders/benderTest3.ufo/glyphs/a.glif deleted file mode 100644 index 4720db2..0000000 --- a/Tests/20190830 benders/benderTest3.ufo/glyphs/a.glif +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/Tests/20190830 benders/benderTest3.ufo/kerning.plist b/Tests/20190830 benders/benderTest3.ufo/kerning.plist deleted file mode 100644 index 23382cc..0000000 --- a/Tests/20190830 benders/benderTest3.ufo/kerning.plist +++ /dev/null @@ -1,11 +0,0 @@ - - - - - a - - a - -100 - - - diff --git a/Tests/20190830 benders/benderTest3.ufo/lib.plist b/Tests/20190830 benders/benderTest3.ufo/lib.plist deleted file mode 100644 index d4b67bb..0000000 --- a/Tests/20190830 benders/benderTest3.ufo/lib.plist +++ /dev/null @@ -1,236 +0,0 @@ - - - - - com.defcon.sortDescriptor - - - ascending - Latin-1 - type - characterSet - - - com.typemytype.robofont.compileSettings.autohint - - com.typemytype.robofont.compileSettings.checkOutlines - - com.typemytype.robofont.compileSettings.createDummyDSIG - - com.typemytype.robofont.compileSettings.decompose - - com.typemytype.robofont.compileSettings.generateFormat - 0 - com.typemytype.robofont.compileSettings.releaseMode - - com.typemytype.robofont.italicSlantOffset - 0 - com.typemytype.robofont.segmentType - curve - com.typemytype.robofont.shouldAddPointsInSplineConversion - 1 - public.glyphOrder - - space - exclam - quotedbl - numbersign - dollar - percent - ampersand - parenleft - parenright - asterisk - plus - comma - hyphen - period - slash - zero - one - two - three - four - five - six - seven - eight - nine - colon - semicolon - less - equal - greater - question - at - A - B - C - D - E - F - G - H - I - J - K - L - M - N - O - P - Q - R - S - T - U - V - W - X - Y - Z - bracketleft - backslash - bracketright - asciicircum - underscore - grave - a - b - c - d - e - f - g - h - i - j - k - l - m - n - o - p - q - r - s - t - u - v - w - x - y - z - braceleft - bar - braceright - asciitilde - exclamdown - cent - sterling - currency - yen - brokenbar - section - dieresis - copyright - ordfeminine - guillemotleft - logicalnot - registered - macron - degree - plusminus - twosuperior - threesuperior - acute - mu - paragraph - periodcentered - cedilla - onesuperior - ordmasculine - guillemotright - onequarter - onehalf - threequarters - questiondown - Agrave - Aacute - Acircumflex - Atilde - Adieresis - Aring - AE - Ccedilla - Egrave - Eacute - Ecircumflex - Edieresis - Igrave - Iacute - Icircumflex - Idieresis - Eth - Ntilde - Ograve - Oacute - Ocircumflex - Otilde - Odieresis - multiply - Oslash - Ugrave - Uacute - Ucircumflex - Udieresis - Yacute - Thorn - germandbls - agrave - aacute - acircumflex - atilde - adieresis - aring - ae - ccedilla - egrave - eacute - ecircumflex - edieresis - igrave - iacute - icircumflex - idieresis - eth - ntilde - ograve - oacute - ocircumflex - otilde - odieresis - divide - oslash - ugrave - uacute - ucircumflex - udieresis - yacute - thorn - ydieresis - dotlessi - circumflex - caron - breve - dotaccent - ring - ogonek - tilde - hungarumlaut - quoteleft - quoteright - minus - - - diff --git a/Tests/20190830 benders/instances/BenderTest-FarOut.ufo/fontinfo.plist b/Tests/20190830 benders/instances/BenderTest-FarOut.ufo/fontinfo.plist deleted file mode 100644 index de783e4..0000000 --- a/Tests/20190830 benders/instances/BenderTest-FarOut.ufo/fontinfo.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - ascender - 750 - capHeight - 997 - descender - -250 - familyName - BenderTest - guidelines - - postscriptBlueValues - - postscriptFamilyBlues - - postscriptFamilyOtherBlues - - postscriptOtherBlues - - postscriptStemSnapH - - postscriptStemSnapV - - styleName - FarOut - unitsPerEm - 1000.0 - xHeight - 500 - - diff --git a/Tests/20190830 benders/instances/BenderTest-FarOut.ufo/glyphs/a.glif b/Tests/20190830 benders/instances/BenderTest-FarOut.ufo/glyphs/a.glif deleted file mode 100644 index 418b368..0000000 --- a/Tests/20190830 benders/instances/BenderTest-FarOut.ufo/glyphs/a.glif +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/Tests/20190830 benders/instances/BenderTest-FarOut.ufo/kerning.plist b/Tests/20190830 benders/instances/BenderTest-FarOut.ufo/kerning.plist deleted file mode 100644 index 08fe439..0000000 --- a/Tests/20190830 benders/instances/BenderTest-FarOut.ufo/kerning.plist +++ /dev/null @@ -1,11 +0,0 @@ - - - - - a - - a - 200 - - - diff --git a/Tests/20190830 benders/instances/BenderTest-FarOut.ufo/lib.plist b/Tests/20190830 benders/instances/BenderTest-FarOut.ufo/lib.plist deleted file mode 100644 index b2efd11..0000000 --- a/Tests/20190830 benders/instances/BenderTest-FarOut.ufo/lib.plist +++ /dev/null @@ -1,243 +0,0 @@ - - - - - com.defcon.sortDescriptor - - - ascending - Latin-1 - type - characterSet - - - com.typemytype.robofont.compileSettings.autohint - - com.typemytype.robofont.compileSettings.checkOutlines - - com.typemytype.robofont.compileSettings.createDummyDSIG - - com.typemytype.robofont.compileSettings.decompose - - com.typemytype.robofont.compileSettings.generateFormat - 0 - com.typemytype.robofont.compileSettings.releaseMode - - com.typemytype.robofont.italicSlantOffset - 0 - com.typemytype.robofont.segmentType - curve - com.typemytype.robofont.shouldAddPointsInSplineConversion - 1 - designspace.location - - - test - 2500.0 - - - public.glyphOrder - - space - exclam - quotedbl - numbersign - dollar - percent - ampersand - parenleft - parenright - asterisk - plus - comma - hyphen - period - slash - zero - one - two - three - four - five - six - seven - eight - nine - colon - semicolon - less - equal - greater - question - at - A - B - C - D - E - F - G - H - I - J - K - L - M - N - O - P - Q - R - S - T - U - V - W - X - Y - Z - bracketleft - backslash - bracketright - asciicircum - underscore - grave - a - b - c - d - e - f - g - h - i - j - k - l - m - n - o - p - q - r - s - t - u - v - w - x - y - z - braceleft - bar - braceright - asciitilde - exclamdown - cent - sterling - currency - yen - brokenbar - section - dieresis - copyright - ordfeminine - guillemotleft - logicalnot - registered - macron - degree - plusminus - twosuperior - threesuperior - acute - mu - paragraph - periodcentered - cedilla - onesuperior - ordmasculine - guillemotright - onequarter - onehalf - threequarters - questiondown - Agrave - Aacute - Acircumflex - Atilde - Adieresis - Aring - AE - Ccedilla - Egrave - Eacute - Ecircumflex - Edieresis - Igrave - Iacute - Icircumflex - Idieresis - Eth - Ntilde - Ograve - Oacute - Ocircumflex - Otilde - Odieresis - multiply - Oslash - Ugrave - Uacute - Ucircumflex - Udieresis - Yacute - Thorn - germandbls - agrave - aacute - acircumflex - atilde - adieresis - aring - ae - ccedilla - egrave - eacute - ecircumflex - edieresis - igrave - iacute - icircumflex - idieresis - eth - ntilde - ograve - oacute - ocircumflex - otilde - odieresis - divide - oslash - ugrave - uacute - ucircumflex - udieresis - yacute - thorn - ydieresis - dotlessi - circumflex - caron - breve - dotaccent - ring - ogonek - tilde - hungarumlaut - quoteleft - quoteright - minus - - - diff --git a/Tests/20190830 benders/instances/BenderTest-Intermediate.ufo/fontinfo.plist b/Tests/20190830 benders/instances/BenderTest-Intermediate.ufo/fontinfo.plist deleted file mode 100644 index 137f807..0000000 --- a/Tests/20190830 benders/instances/BenderTest-Intermediate.ufo/fontinfo.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - ascender - 750 - capHeight - 601 - descender - -250 - familyName - BenderTest - guidelines - - postscriptBlueValues - - postscriptFamilyBlues - - postscriptFamilyOtherBlues - - postscriptOtherBlues - - postscriptStemSnapH - - postscriptStemSnapV - - styleName - Intermediate - unitsPerEm - 1000 - xHeight - 500 - - diff --git a/Tests/20190830 benders/instances/BenderTest-Intermediate.ufo/glyphs/a.glif b/Tests/20190830 benders/instances/BenderTest-Intermediate.ufo/glyphs/a.glif deleted file mode 100644 index 376c59d..0000000 --- a/Tests/20190830 benders/instances/BenderTest-Intermediate.ufo/glyphs/a.glif +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/Tests/20190830 benders/instances/BenderTest-Intermediate.ufo/glyphs/contents.plist b/Tests/20190830 benders/instances/BenderTest-Intermediate.ufo/glyphs/contents.plist deleted file mode 100644 index 63a44ac..0000000 --- a/Tests/20190830 benders/instances/BenderTest-Intermediate.ufo/glyphs/contents.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - a - a.glif - - diff --git a/Tests/20190830 benders/instances/BenderTest-Intermediate.ufo/kerning.plist b/Tests/20190830 benders/instances/BenderTest-Intermediate.ufo/kerning.plist deleted file mode 100644 index c3bce48..0000000 --- a/Tests/20190830 benders/instances/BenderTest-Intermediate.ufo/kerning.plist +++ /dev/null @@ -1,11 +0,0 @@ - - - - - a - - a - -200 - - - diff --git a/Tests/20190830 benders/instances/BenderTest-Intermediate.ufo/lib.plist b/Tests/20190830 benders/instances/BenderTest-Intermediate.ufo/lib.plist deleted file mode 100644 index acfaec2..0000000 --- a/Tests/20190830 benders/instances/BenderTest-Intermediate.ufo/lib.plist +++ /dev/null @@ -1,243 +0,0 @@ - - - - - com.defcon.sortDescriptor - - - ascending - Latin-1 - type - characterSet - - - com.typemytype.robofont.compileSettings.autohint - - com.typemytype.robofont.compileSettings.checkOutlines - - com.typemytype.robofont.compileSettings.createDummyDSIG - - com.typemytype.robofont.compileSettings.decompose - - com.typemytype.robofont.compileSettings.generateFormat - 0 - com.typemytype.robofont.compileSettings.releaseMode - - com.typemytype.robofont.italicSlantOffset - 0 - com.typemytype.robofont.segmentType - curve - com.typemytype.robofont.shouldAddPointsInSplineConversion - 1 - designspace.location - - - test - 500.0 - - - public.glyphOrder - - space - exclam - quotedbl - numbersign - dollar - percent - ampersand - parenleft - parenright - asterisk - plus - comma - hyphen - period - slash - zero - one - two - three - four - five - six - seven - eight - nine - colon - semicolon - less - equal - greater - question - at - A - B - C - D - E - F - G - H - I - J - K - L - M - N - O - P - Q - R - S - T - U - V - W - X - Y - Z - bracketleft - backslash - bracketright - asciicircum - underscore - grave - a - b - c - d - e - f - g - h - i - j - k - l - m - n - o - p - q - r - s - t - u - v - w - x - y - z - braceleft - bar - braceright - asciitilde - exclamdown - cent - sterling - currency - yen - brokenbar - section - dieresis - copyright - ordfeminine - guillemotleft - logicalnot - registered - macron - degree - plusminus - twosuperior - threesuperior - acute - mu - paragraph - periodcentered - cedilla - onesuperior - ordmasculine - guillemotright - onequarter - onehalf - threequarters - questiondown - Agrave - Aacute - Acircumflex - Atilde - Adieresis - Aring - AE - Ccedilla - Egrave - Eacute - Ecircumflex - Edieresis - Igrave - Iacute - Icircumflex - Idieresis - Eth - Ntilde - Ograve - Oacute - Ocircumflex - Otilde - Odieresis - multiply - Oslash - Ugrave - Uacute - Ucircumflex - Udieresis - Yacute - Thorn - germandbls - agrave - aacute - acircumflex - atilde - adieresis - aring - ae - ccedilla - egrave - eacute - ecircumflex - edieresis - igrave - iacute - icircumflex - idieresis - eth - ntilde - ograve - oacute - ocircumflex - otilde - odieresis - divide - oslash - ugrave - uacute - ucircumflex - udieresis - yacute - thorn - ydieresis - dotlessi - circumflex - caron - breve - dotaccent - ring - ogonek - tilde - hungarumlaut - quoteleft - quoteright - minus - - - diff --git a/Tests/20190830 benders/test.py b/Tests/20190830 benders/test.py deleted file mode 100644 index e5e46db..0000000 --- a/Tests/20190830 benders/test.py +++ /dev/null @@ -1,80 +0,0 @@ -""" - - - test with these 3 masters - on 1 axis that has a map that maps to a different range - - axis values are in user coordinates - designpsace problems should check with the proper mapped values - masters and instancees are in designspace coordinates - - goals: - * the designspace should validate - * the generated intermediate should have touching shapes, just like master 2 - * determine if we can get rid of the bend=True/False flags - - Suppose the numbers in an axis map are messed up, it's then impossible - to find the default. -""" - -import importlib -import ufoProcessor -importlib.reload(ufoProcessor) - - -import mutatorMath -print(mutatorMath.__file__) -import mutatorMath.objects.mutator -importlib.reload(mutatorMath.objects.mutator) -from mutatorMath.objects.mutator import Location -from designspaceProblems import DesignSpaceChecker -import collections -from ufoProcessor import DesignSpaceProcessor -from pprint import pprint - -path = "Test.designspace" - -dp = DesignSpaceProcessor() -dp.read(path) -dp.loadFonts() - -dsc = DesignSpaceChecker(dp) -dsc.checkEverything() -pprint(dsc.problems) -print('hasStructuralProblems', dsc.hasStructuralProblems()) - - -print(dp.newDefaultLocation()) -print(dp.instances) -print('findDefault', dp.findDefault()) -dp.useVarlib = False -print('varlib', dp.useVarlib) - -axisMapper = ufoProcessor.varModels.AxisMapper(dp.axes) -print('axisMapper', axisMapper.getMappedAxisValues()) -r = axisMapper(Location(test=1)) - -default = dp.getNeutralFont() -print('default.path', default.path) -dp.generateUFO() - -glyphName = "a" -print('mutator for a', dp.getGlyphMutator(glyphName)) -print('-'*40) -print('problems') -for p in dp.problems: - print(p) -print('-'*40) -print('toollog') -for line in dp.toolLog: - print("\t" + line) - - -instancePath = "instances/BenderTest-Intermediate.ufo" -instance = RFont(instancePath, showUI=False) -print(instance.info.capHeight) -print(instance.kerning.items()) - -from mutatorMath.objects.mutator import Location -l = Location(test=0) -print(l.isOrigin()) \ No newline at end of file diff --git a/Tests/ds5/ds5.designspace b/Tests/ds5/ds5.designspace new file mode 100644 index 0000000..d986b71 --- /dev/null +++ b/Tests/ds5/ds5.designspace @@ -0,0 +1,411 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/ds5/ds5_addKerningToTheseMasters.py b/Tests/ds5/ds5_addKerningToTheseMasters.py new file mode 100644 index 0000000..a9cc9b0 --- /dev/null +++ b/Tests/ds5/ds5_addKerningToTheseMasters.py @@ -0,0 +1,15 @@ + +p1 = ('glyphOne', 'glyphTwo') +p2 = ('glyphTwo', 'glyphOne') + +g = "glyphOne" + +for f in AllFonts(): + print(f.path, f.kerning.items()) + f.kerning[p1] = f[g].width + f.kerning[p2] = -f[g].width + f.kerning[('glyphTwo', 'glyphTwo')] = -400 + f.kerning[('glyphOne', 'glyphOne')] = 400 + f.save() + f.close() + diff --git a/Tests/ds5/ds5_extrapolateTest.py b/Tests/ds5/ds5_extrapolateTest.py new file mode 100644 index 0000000..921f36c --- /dev/null +++ b/Tests/ds5/ds5_extrapolateTest.py @@ -0,0 +1,29 @@ +# test the extrapolation in VariationModel + +from fontTools.varLib.models import VariationModel + +locations = [ + dict(wgth=0), + dict(wght=1000), +] + +values = [10, 20] + +m = VariationModel(locations, extrapolate=True) + +# interpolating +assert m.interpolateFromMasters(dict(wght=0), values) == 10 +assert m.interpolateFromMasters(dict(wght=500), values) == 15 +assert m.interpolateFromMasters(dict(wght=1000), values) == 20 + +# extrapolate over max +assert m.interpolateFromMasters(dict(wght=1500), values) == 25 +assert m.interpolateFromMasters(dict(wght=2000), values) == 30 + +# extrapolation over min gets stuck +print(m.interpolateFromMasters(dict(wght=-500), values), m.interpolateFromMasters(dict(wght=-1000), values)) + +# would expect: +assert m.interpolateFromMasters(dict(wght=-500), values) == -5 +assert m.interpolateFromMasters(dict(wght=-1000), values) == -10 + diff --git a/Tests/ds5/ds5_makeTestDoc.py b/Tests/ds5/ds5_makeTestDoc.py new file mode 100644 index 0000000..c4d9963 --- /dev/null +++ b/Tests/ds5/ds5_makeTestDoc.py @@ -0,0 +1,163 @@ +# make a test designspace format 5 with 1 continuous and 2 discrete axes. + +# axis width is a normal interpolation with a change in width +# axis DSC1 is a discrete axis showing 1, 2, 3 items in the glyph +# axis DSC2 is a discrete axis showing a solid or outlined shape + +from fontTools.designspaceLib import DesignSpaceDocument, SourceDescriptor, InstanceDescriptor, AxisDescriptor, RuleDescriptor, processRules, DiscreteAxisDescriptor +from fontTools.designspaceLib.split import splitInterpolable + +import os +import fontTools +print(fontTools.version) + +import ufoProcessor +print(ufoProcessor.__file__) + +doc = DesignSpaceDocument() + +#https://fonttools.readthedocs.io/en/latest/designspaceLib/python.html#axisdescriptor +a1 = AxisDescriptor() +a1.minimum = 400 +a1.maximum = 1000 +a1.default = 400 +a1.map = ((400,400), (700,900), (1000,1000)) +a1.name = "width" +a1.tag = "wdth" +a1.axisOrdering = 1 +doc.addAxis(a1) + +a2 = DiscreteAxisDescriptor() +a2.values = [1, 2, 3] +a2.default = 1 +a2.name = "countedItems" +a2.tag = "DSC1" +a2.axisOrdering = 2 +doc.addAxis(a2) + +a3 = DiscreteAxisDescriptor() +a3.values = [0, 1] +a3.default = 0 +a3.name = "outlined" +a3.tag = "DSC2" +a3.axisOrdering = 3 +doc.addAxis(a3) + +default = {a1.name: a1.default, a2.name: a2.default, a3.name: a3.default} + +# add sources + + +# public.skipExportGlyphs + +for c in [a1.minimum, a1.maximum]: + for d1 in a2.values: + for d2 in a3.values: + + s1 = SourceDescriptor() + s1.path = os.path.join("sources", f"geometrySource_c_{c}_d1_{d1}_d2_{d2}.ufo") + s1.name = f"geometrySource{c} {d1} {d2}" + sourceLocation = dict(width=c, countedItems=d1, outlined=d2) + s1.location = sourceLocation + s1.kerning = True + s1.familyName = "SourceFamilyName" + if default == sourceLocation: + s1.copyGroups = True + s1.copyFeatures = True + s1.copyInfo = True + td1 = ["One", "Two", "Three"][(d1-1)] + if c == 400: + tc = "Narrow" + elif c == 1000: + tc = "Wide" + if d2 == 0: + td2 = "solid" + else: + td2 = "open" + s1.styleName = f"{td1}{tc}{td2}" + doc.addSource(s1) + +def ip(a,b,f): + return a + f*(b-a) + +# add instances +steps = 8 +extrapolateAmount = 100 + + +interestingWeightValues = [(400, 700), 300, 400, 550, 700, 1000, 1100] + +mathModelPrefKey = "com.letterror.mathModelPref" +mathModelVarlibPref = "previewVarLib" +mathModelMutatorMathPref = "previewMutatorMath" + +# com.letterror.mathModelPref +# previewVarLib + + +for c in interestingWeightValues: + for d1 in a2.values: + for d2 in a3.values: + + s1 = InstanceDescriptor() + s1.path = os.path.join("instances", f"geometryInstance_c_{c}_d1_{d1}_d2_{d2}.ufo") + s1.location = dict(width=c, countedItems=d1, outlined=d2) + s1.familyName = "InstanceFamilyName" + td1 = ["One", "Two", "Three"][(d1-1)] + if c == 400: + tc = "Narrow" + elif c == 1000: + tc = "Wide" + if d2 == 0: + td2 = "Solid" + else: + td2 = "Open" + s1.name = f"geometryInstance {td1} {tc} {td2}" + s1.styleName = f"{td1}{tc}{td2}" + s1.kerning = True + s1.info = True + doc.addInstance(s1) + +# add variable font descriptors + +splits = splitInterpolable(doc) +for discreteLocation, subSpace in splitInterpolable(doc): + print(discreteLocation, subSpace) + +#print(doc.getVariableFonts()) + +#for item in doc.getVariableFonts(): +# doc.addVariableFont(item) + +doc.variableFonts.clear() +print(doc.variableFonts) + + +variableFonts = doc.getVariableFonts() +print("variableFonts", variableFonts) + +doc.addVariableFont(variableFonts[0]) + +for i, item in enumerate(variableFonts): + print(i, item) + + +path = "ds5.designspace" +print(doc.lib) +doc.write(path) +print(dir(doc)) + + +for a in doc.axes: + if hasattr(a, "values"): + print(a.name, "d", a.values) + else: + print(a.name, "r", a.minimum, a.maximum) + +for s in doc.sources: + print(s.location) + +# ok. now about generating the instances. + +udoc = ufoProcessor.DesignSpaceProcessor() +udoc.read(path) diff --git a/Tests/ds5/ds5_test_designspaceProblems.py b/Tests/ds5/ds5_test_designspaceProblems.py new file mode 100644 index 0000000..fd3f11e --- /dev/null +++ b/Tests/ds5/ds5_test_designspaceProblems.py @@ -0,0 +1,12 @@ +import designspaceProblems +print(designspaceProblems.__file__) + +path= "ds5.designspace" +checker = designspaceProblems.DesignSpaceChecker(path) +checker.checkEverything() +print(checker.checkDesignSpaceGeometry()) +checker.checkSources() +checker.checkInstances() +print("hasStructuralProblems", checker.hasStructuralProblems()) + +print(checker.problems) \ No newline at end of file diff --git a/Tests/ds5/readme.md b/Tests/ds5/readme.md new file mode 100644 index 0000000..72231a0 --- /dev/null +++ b/Tests/ds5/readme.md @@ -0,0 +1,13 @@ +# Test for designspace 5 format with discrete and interpolating axes + +I need to keep notes somewhere. + +The script `ds5_makeTestDoc.py` makes a new DS5 designspace file with 3 axes. + +* `wdth` with minimum at `400`, default `400`, maximum at `1000`. An interpolating axis. +* `DSC1`, a discrete axis. Values at `1,2,3`. Default at `1`. Named `countedItems` Variations along this axis consist of 1, 2, and 3 boxes. +* `DSC2`, a discrete axis. Values at `0, 1`. Default at `0`. Named `outlined`. Variations along this axis consist of solid shapes and outlined shapes. + +The `masters` folder has sources for all intersections of these axes. The default of the whole system is at `wdth: 400, countedItems: 1, outlined: 0` +![](masters.jpg) + diff --git a/Tests/ds5/sources.jpg b/Tests/ds5/sources.jpg new file mode 100644 index 0000000..5b98578 Binary files /dev/null and b/Tests/ds5/sources.jpg differ diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/features.fea b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/features.fea new file mode 100644 index 0000000..a3f867d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/features.fea @@ -0,0 +1 @@ +# features from ufo: geometryMaster_c_1000_d1_1_d2_0.ufo \ No newline at end of file diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/fontinfo.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/fontinfo.plist new file mode 100644 index 0000000..02c1c20 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/fontinfo.plist @@ -0,0 +1,55 @@ + + + + + ascender + 400 + capHeight + 400 + copyright + # font.info from ufo: geometryMaster_c_1000_d1_1_d2_0.ufo + descender + -200 + familyName + One_wide_open + guidelines + + openTypeHheaAscender + 1036 + openTypeHheaDescender + -335 + openTypeOS2TypoAscender + 730 + openTypeOS2TypoDescender + -270 + openTypeOS2WinAscent + 1036 + openTypeOS2WinDescent + 335 + postscriptBlueFuzz + 0 + postscriptBlueScale + 0.22 + postscriptBlueValues + + 100 + 110 + + postscriptFamilyBlues + + postscriptFamilyOtherBlues + + postscriptOtherBlues + + postscriptStemSnapH + + postscriptStemSnapV + + styleName + c_1000_d1_1_d2_0 + unitsPerEm + 1000 + xHeight + 200 + + diff --git a/Tests/20190830 benders/benderTest3.ufo/glyphs/contents.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/glyphs/contents.plist similarity index 61% rename from Tests/20190830 benders/benderTest3.ufo/glyphs/contents.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/glyphs/contents.plist index 63a44ac..4cb18bb 100644 --- a/Tests/20190830 benders/benderTest3.ufo/glyphs/contents.plist +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/glyphs/contents.plist @@ -2,7 +2,9 @@ - a - a.glif + glyphOne + glyphO_ne.glif + glyphTwo + glyphT_wo.glif diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/glyphs/glyphO_ne.glif b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/glyphs/glyphO_ne.glif new file mode 100644 index 0000000..ab82b9d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/glyphs/glyphO_ne.glif @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/glyphs/glyphT_wo.glif b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/glyphs/glyphT_wo.glif new file mode 100644 index 0000000..c523ee7 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/glyphs/glyphT_wo.glif @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Tests/20190830 benders/benderTest1.ufo/glyphs/layerinfo.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/glyphs/layerinfo.plist similarity index 100% rename from Tests/20190830 benders/benderTest1.ufo/glyphs/layerinfo.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/glyphs/layerinfo.plist diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/groups.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/groups.plist new file mode 100644 index 0000000..4dff22d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/groups.plist @@ -0,0 +1,16 @@ + + + + + public.kern1.groupA + + glyphOne + glyphTwo + + public.kern2.groupB + + glyphThree + glyphFour + + + diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/kerning.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/kerning.plist new file mode 100644 index 0000000..b54ab73 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/kerning.plist @@ -0,0 +1,20 @@ + + + + + glyphOne + + glyphOne + 400 + glyphTwo + 800 + + glyphTwo + + glyphOne + -800 + glyphTwo + -400 + + + diff --git a/Tests/20190830 benders/instances/BenderTest-FarOut.ufo/layercontents.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/layercontents.plist similarity index 100% rename from Tests/20190830 benders/instances/BenderTest-FarOut.ufo/layercontents.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/layercontents.plist diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/lib.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/lib.plist new file mode 100644 index 0000000..ad24abb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/lib.plist @@ -0,0 +1,31 @@ + + + + + com.typemytype.robofont.compileSettings.autohint + + com.typemytype.robofont.compileSettings.checkOutlines + + com.typemytype.robofont.compileSettings.createDummyDSIG + + com.typemytype.robofont.compileSettings.decompose + + com.typemytype.robofont.compileSettings.generateFormat + 0 + com.typemytype.robofont.compileSettings.releaseMode + + com.typemytype.robofont.generateFeaturesWithFontTools + + com.typemytype.robofont.italicSlantOffset + 0 + com.typemytype.robofont.shouldAddPointsInSplineConversion + 1 + public.glyphOrder + + glyphOne + glyphTwo + + ufoProcessor.test.lib.entry + Lib entry for master 1 + + diff --git a/Tests/20190830 benders/benderTest1.ufo/metainfo.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/metainfo.plist similarity index 100% rename from Tests/20190830 benders/benderTest1.ufo/metainfo.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_0.ufo/metainfo.plist diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/features.fea b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/features.fea new file mode 100644 index 0000000..27b0ba6 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/features.fea @@ -0,0 +1 @@ +# features from ufo: geometryMaster_c_1000_d1_1_d2_1.ufo \ No newline at end of file diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/fontinfo.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/fontinfo.plist new file mode 100644 index 0000000..999a9f3 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/fontinfo.plist @@ -0,0 +1,55 @@ + + + + + ascender + 400 + capHeight + 400 + copyright + # font.info from ufo: geometryMaster_c_1000_d1_1_d2_1.ufo + descender + -200 + familyName + One_wide_solid + guidelines + + openTypeHheaAscender + 1036 + openTypeHheaDescender + -335 + openTypeOS2TypoAscender + 730 + openTypeOS2TypoDescender + -270 + openTypeOS2WinAscent + 1036 + openTypeOS2WinDescent + 335 + postscriptBlueFuzz + 0 + postscriptBlueScale + 0.22 + postscriptBlueValues + + 100 + 110 + + postscriptFamilyBlues + + postscriptFamilyOtherBlues + + postscriptOtherBlues + + postscriptStemSnapH + + postscriptStemSnapV + + styleName + c_1000_d1_1_d2_1 + unitsPerEm + 1000 + xHeight + 200 + + diff --git a/Tests/20190830 benders/benderTest1.ufo/glyphs/contents.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/glyphs/contents.plist similarity index 61% rename from Tests/20190830 benders/benderTest1.ufo/glyphs/contents.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/glyphs/contents.plist index 63a44ac..4cb18bb 100644 --- a/Tests/20190830 benders/benderTest1.ufo/glyphs/contents.plist +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/glyphs/contents.plist @@ -2,7 +2,9 @@ - a - a.glif + glyphOne + glyphO_ne.glif + glyphTwo + glyphT_wo.glif diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/glyphs/glyphO_ne.glif b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/glyphs/glyphO_ne.glif new file mode 100644 index 0000000..c7da8f6 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/glyphs/glyphO_ne.glif @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/glyphs/glyphT_wo.glif b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/glyphs/glyphT_wo.glif new file mode 100644 index 0000000..c523ee7 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/glyphs/glyphT_wo.glif @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Tests/20190830 benders/benderTest2.ufo/glyphs/layerinfo.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/glyphs/layerinfo.plist similarity index 100% rename from Tests/20190830 benders/benderTest2.ufo/glyphs/layerinfo.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/glyphs/layerinfo.plist diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/groups.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/groups.plist new file mode 100644 index 0000000..4dff22d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/groups.plist @@ -0,0 +1,16 @@ + + + + + public.kern1.groupA + + glyphOne + glyphTwo + + public.kern2.groupB + + glyphThree + glyphFour + + + diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/kerning.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/kerning.plist new file mode 100644 index 0000000..b54ab73 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/kerning.plist @@ -0,0 +1,20 @@ + + + + + glyphOne + + glyphOne + 400 + glyphTwo + 800 + + glyphTwo + + glyphOne + -800 + glyphTwo + -400 + + + diff --git a/Tests/20190830 benders/instances/BenderTest-Intermediate.ufo/layercontents.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/layercontents.plist similarity index 100% rename from Tests/20190830 benders/instances/BenderTest-Intermediate.ufo/layercontents.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/layercontents.plist diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/lib.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/lib.plist new file mode 100644 index 0000000..ad24abb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/lib.plist @@ -0,0 +1,31 @@ + + + + + com.typemytype.robofont.compileSettings.autohint + + com.typemytype.robofont.compileSettings.checkOutlines + + com.typemytype.robofont.compileSettings.createDummyDSIG + + com.typemytype.robofont.compileSettings.decompose + + com.typemytype.robofont.compileSettings.generateFormat + 0 + com.typemytype.robofont.compileSettings.releaseMode + + com.typemytype.robofont.generateFeaturesWithFontTools + + com.typemytype.robofont.italicSlantOffset + 0 + com.typemytype.robofont.shouldAddPointsInSplineConversion + 1 + public.glyphOrder + + glyphOne + glyphTwo + + ufoProcessor.test.lib.entry + Lib entry for master 1 + + diff --git a/Tests/20190830 benders/benderTest2.ufo/metainfo.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/metainfo.plist similarity index 100% rename from Tests/20190830 benders/benderTest2.ufo/metainfo.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_1_d2_1.ufo/metainfo.plist diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/features.fea b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/features.fea new file mode 100644 index 0000000..1f64fc1 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/features.fea @@ -0,0 +1 @@ +# features from ufo: geometryMaster_c_1000_d1_2_d2_0.ufo \ No newline at end of file diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/fontinfo.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/fontinfo.plist new file mode 100644 index 0000000..3f02085 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/fontinfo.plist @@ -0,0 +1,55 @@ + + + + + ascender + 400 + capHeight + 400 + copyright + # font.info from ufo: geometryMaster_c_1000_d1_2_d2_0.ufo + descender + -200 + familyName + Two_wide_open + guidelines + + openTypeHheaAscender + 1036 + openTypeHheaDescender + -335 + openTypeOS2TypoAscender + 730 + openTypeOS2TypoDescender + -270 + openTypeOS2WinAscent + 1036 + openTypeOS2WinDescent + 335 + postscriptBlueFuzz + 0 + postscriptBlueScale + 0.22 + postscriptBlueValues + + 100 + 110 + + postscriptFamilyBlues + + postscriptFamilyOtherBlues + + postscriptOtherBlues + + postscriptStemSnapH + + postscriptStemSnapV + + styleName + c_1000_d1_2_d2_0 + unitsPerEm + 1000 + xHeight + 200 + + diff --git a/Tests/20190830 benders/instances/BenderTest-FarOut.ufo/glyphs/contents.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/glyphs/contents.plist similarity index 61% rename from Tests/20190830 benders/instances/BenderTest-FarOut.ufo/glyphs/contents.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/glyphs/contents.plist index 63a44ac..4cb18bb 100644 --- a/Tests/20190830 benders/instances/BenderTest-FarOut.ufo/glyphs/contents.plist +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/glyphs/contents.plist @@ -2,7 +2,9 @@ - a - a.glif + glyphOne + glyphO_ne.glif + glyphTwo + glyphT_wo.glif diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/glyphs/glyphO_ne.glif b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/glyphs/glyphO_ne.glif new file mode 100644 index 0000000..42f461a --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/glyphs/glyphO_ne.glif @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/glyphs/glyphT_wo.glif b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/glyphs/glyphT_wo.glif new file mode 100644 index 0000000..c523ee7 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/glyphs/glyphT_wo.glif @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Tests/20190830 benders/benderTest3.ufo/glyphs/layerinfo.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/glyphs/layerinfo.plist similarity index 100% rename from Tests/20190830 benders/benderTest3.ufo/glyphs/layerinfo.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/glyphs/layerinfo.plist diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/groups.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/groups.plist new file mode 100644 index 0000000..4dff22d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/groups.plist @@ -0,0 +1,16 @@ + + + + + public.kern1.groupA + + glyphOne + glyphTwo + + public.kern2.groupB + + glyphThree + glyphFour + + + diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/kerning.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/kerning.plist new file mode 100644 index 0000000..b54ab73 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/kerning.plist @@ -0,0 +1,20 @@ + + + + + glyphOne + + glyphOne + 400 + glyphTwo + 800 + + glyphTwo + + glyphOne + -800 + glyphTwo + -400 + + + diff --git a/Tests/20190830 benders/benderTest1.ufo/layercontents.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/layercontents.plist similarity index 65% rename from Tests/20190830 benders/benderTest1.ufo/layercontents.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/layercontents.plist index e9a336b..b9c1a4f 100644 --- a/Tests/20190830 benders/benderTest1.ufo/layercontents.plist +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/layercontents.plist @@ -3,12 +3,8 @@ - foreground + public.default glyphs - - background - glyphs.background - diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/lib.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/lib.plist new file mode 100644 index 0000000..ad24abb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/lib.plist @@ -0,0 +1,31 @@ + + + + + com.typemytype.robofont.compileSettings.autohint + + com.typemytype.robofont.compileSettings.checkOutlines + + com.typemytype.robofont.compileSettings.createDummyDSIG + + com.typemytype.robofont.compileSettings.decompose + + com.typemytype.robofont.compileSettings.generateFormat + 0 + com.typemytype.robofont.compileSettings.releaseMode + + com.typemytype.robofont.generateFeaturesWithFontTools + + com.typemytype.robofont.italicSlantOffset + 0 + com.typemytype.robofont.shouldAddPointsInSplineConversion + 1 + public.glyphOrder + + glyphOne + glyphTwo + + ufoProcessor.test.lib.entry + Lib entry for master 1 + + diff --git a/Tests/20190830 benders/benderTest3.ufo/metainfo.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/metainfo.plist similarity index 100% rename from Tests/20190830 benders/benderTest3.ufo/metainfo.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_0.ufo/metainfo.plist diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/features.fea b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/features.fea new file mode 100644 index 0000000..c866ad3 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/features.fea @@ -0,0 +1 @@ +# features from ufo: geometryMaster_c_1000_d1_2_d2_1.ufo \ No newline at end of file diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/fontinfo.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/fontinfo.plist new file mode 100644 index 0000000..ff11f80 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/fontinfo.plist @@ -0,0 +1,55 @@ + + + + + ascender + 400 + capHeight + 400 + copyright + # font.info from ufo: geometryMaster_c_1000_d1_2_d2_1.ufo + descender + -200 + familyName + Two_wide_solid + guidelines + + openTypeHheaAscender + 1036 + openTypeHheaDescender + -335 + openTypeOS2TypoAscender + 730 + openTypeOS2TypoDescender + -270 + openTypeOS2WinAscent + 1036 + openTypeOS2WinDescent + 335 + postscriptBlueFuzz + 0 + postscriptBlueScale + 0.22 + postscriptBlueValues + + 100 + 110 + + postscriptFamilyBlues + + postscriptFamilyOtherBlues + + postscriptOtherBlues + + postscriptStemSnapH + + postscriptStemSnapV + + styleName + c_1000_d1_2_d2_1 + unitsPerEm + 1000 + xHeight + 200 + + diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/glyphs/contents.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/glyphs/contents.plist new file mode 100644 index 0000000..4cb18bb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/glyphs/contents.plist @@ -0,0 +1,10 @@ + + + + + glyphOne + glyphO_ne.glif + glyphTwo + glyphT_wo.glif + + diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/glyphs/glyphO_ne.glif b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/glyphs/glyphO_ne.glif new file mode 100644 index 0000000..928f76f --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/glyphs/glyphO_ne.glif @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/glyphs/glyphT_wo.glif b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/glyphs/glyphT_wo.glif new file mode 100644 index 0000000..c523ee7 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/glyphs/glyphT_wo.glif @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Tests/20190830 benders/benderTest1.ufo/glyphs.background/layerinfo.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/glyphs/layerinfo.plist similarity index 85% rename from Tests/20190830 benders/benderTest1.ufo/glyphs.background/layerinfo.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/glyphs/layerinfo.plist index 87752a4..3cf39b4 100644 --- a/Tests/20190830 benders/benderTest1.ufo/glyphs.background/layerinfo.plist +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/glyphs/layerinfo.plist @@ -3,6 +3,6 @@ color - 0,0.8,0.2,0.7 + 1,0.75,0,0.7 diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/groups.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/groups.plist new file mode 100644 index 0000000..4dff22d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/groups.plist @@ -0,0 +1,16 @@ + + + + + public.kern1.groupA + + glyphOne + glyphTwo + + public.kern2.groupB + + glyphThree + glyphFour + + + diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/kerning.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/kerning.plist new file mode 100644 index 0000000..b54ab73 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/kerning.plist @@ -0,0 +1,20 @@ + + + + + glyphOne + + glyphOne + 400 + glyphTwo + 800 + + glyphTwo + + glyphOne + -800 + glyphTwo + -400 + + + diff --git a/Tests/20190830 benders/benderTest2.ufo/layercontents.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/layercontents.plist similarity index 65% rename from Tests/20190830 benders/benderTest2.ufo/layercontents.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/layercontents.plist index e9a336b..b9c1a4f 100644 --- a/Tests/20190830 benders/benderTest2.ufo/layercontents.plist +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/layercontents.plist @@ -3,12 +3,8 @@ - foreground + public.default glyphs - - background - glyphs.background - diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/lib.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/lib.plist new file mode 100644 index 0000000..ad24abb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/lib.plist @@ -0,0 +1,31 @@ + + + + + com.typemytype.robofont.compileSettings.autohint + + com.typemytype.robofont.compileSettings.checkOutlines + + com.typemytype.robofont.compileSettings.createDummyDSIG + + com.typemytype.robofont.compileSettings.decompose + + com.typemytype.robofont.compileSettings.generateFormat + 0 + com.typemytype.robofont.compileSettings.releaseMode + + com.typemytype.robofont.generateFeaturesWithFontTools + + com.typemytype.robofont.italicSlantOffset + 0 + com.typemytype.robofont.shouldAddPointsInSplineConversion + 1 + public.glyphOrder + + glyphOne + glyphTwo + + ufoProcessor.test.lib.entry + Lib entry for master 1 + + diff --git a/Tests/20190830 benders/instances/BenderTest-FarOut.ufo/metainfo.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/metainfo.plist similarity index 100% rename from Tests/20190830 benders/instances/BenderTest-FarOut.ufo/metainfo.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_2_d2_1.ufo/metainfo.plist diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/features.fea b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/features.fea new file mode 100644 index 0000000..b0404fc --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/features.fea @@ -0,0 +1 @@ +# features from ufo: geometryMaster_c_1000_d1_3_d2_0.ufo \ No newline at end of file diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/fontinfo.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/fontinfo.plist new file mode 100644 index 0000000..992889c --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/fontinfo.plist @@ -0,0 +1,55 @@ + + + + + ascender + 400 + capHeight + 400 + copyright + # font.info from ufo: geometryMaster_c_1000_d1_3_d2_0.ufo + descender + -200 + familyName + Three_wide_open + guidelines + + openTypeHheaAscender + 1036 + openTypeHheaDescender + -335 + openTypeOS2TypoAscender + 730 + openTypeOS2TypoDescender + -270 + openTypeOS2WinAscent + 1036 + openTypeOS2WinDescent + 335 + postscriptBlueFuzz + 0 + postscriptBlueScale + 0.22 + postscriptBlueValues + + 100 + 110 + + postscriptFamilyBlues + + postscriptFamilyOtherBlues + + postscriptOtherBlues + + postscriptStemSnapH + + postscriptStemSnapV + + styleName + c_1000_d1_3_d2_0 + unitsPerEm + 1000 + xHeight + 200 + + diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/glyphs/contents.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/glyphs/contents.plist new file mode 100644 index 0000000..4cb18bb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/glyphs/contents.plist @@ -0,0 +1,10 @@ + + + + + glyphOne + glyphO_ne.glif + glyphTwo + glyphT_wo.glif + + diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/glyphs/glyphO_ne.glif b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/glyphs/glyphO_ne.glif new file mode 100644 index 0000000..baf7268 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/glyphs/glyphO_ne.glif @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/glyphs/glyphT_wo.glif b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/glyphs/glyphT_wo.glif new file mode 100644 index 0000000..c523ee7 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/glyphs/glyphT_wo.glif @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Tests/20190830 benders/benderTest3.ufo/glyphs.background/layerinfo.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/glyphs/layerinfo.plist similarity index 85% rename from Tests/20190830 benders/benderTest3.ufo/glyphs.background/layerinfo.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/glyphs/layerinfo.plist index 87752a4..3cf39b4 100644 --- a/Tests/20190830 benders/benderTest3.ufo/glyphs.background/layerinfo.plist +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/glyphs/layerinfo.plist @@ -3,6 +3,6 @@ color - 0,0.8,0.2,0.7 + 1,0.75,0,0.7 diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/groups.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/groups.plist new file mode 100644 index 0000000..4dff22d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/groups.plist @@ -0,0 +1,16 @@ + + + + + public.kern1.groupA + + glyphOne + glyphTwo + + public.kern2.groupB + + glyphThree + glyphFour + + + diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/kerning.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/kerning.plist new file mode 100644 index 0000000..b54ab73 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/kerning.plist @@ -0,0 +1,20 @@ + + + + + glyphOne + + glyphOne + 400 + glyphTwo + 800 + + glyphTwo + + glyphOne + -800 + glyphTwo + -400 + + + diff --git a/Tests/20190830 benders/benderTest3.ufo/layercontents.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/layercontents.plist similarity index 65% rename from Tests/20190830 benders/benderTest3.ufo/layercontents.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/layercontents.plist index e9a336b..b9c1a4f 100644 --- a/Tests/20190830 benders/benderTest3.ufo/layercontents.plist +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/layercontents.plist @@ -3,12 +3,8 @@ - foreground + public.default glyphs - - background - glyphs.background - diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/lib.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/lib.plist new file mode 100644 index 0000000..ad24abb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/lib.plist @@ -0,0 +1,31 @@ + + + + + com.typemytype.robofont.compileSettings.autohint + + com.typemytype.robofont.compileSettings.checkOutlines + + com.typemytype.robofont.compileSettings.createDummyDSIG + + com.typemytype.robofont.compileSettings.decompose + + com.typemytype.robofont.compileSettings.generateFormat + 0 + com.typemytype.robofont.compileSettings.releaseMode + + com.typemytype.robofont.generateFeaturesWithFontTools + + com.typemytype.robofont.italicSlantOffset + 0 + com.typemytype.robofont.shouldAddPointsInSplineConversion + 1 + public.glyphOrder + + glyphOne + glyphTwo + + ufoProcessor.test.lib.entry + Lib entry for master 1 + + diff --git a/Tests/20190830 benders/instances/BenderTest-Intermediate.ufo/metainfo.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/metainfo.plist similarity index 100% rename from Tests/20190830 benders/instances/BenderTest-Intermediate.ufo/metainfo.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_0.ufo/metainfo.plist diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/features.fea b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/features.fea new file mode 100644 index 0000000..95c19dd --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/features.fea @@ -0,0 +1 @@ +# features from ufo: geometryMaster_c_1000_d1_3_d2_1.ufo \ No newline at end of file diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/fontinfo.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/fontinfo.plist new file mode 100644 index 0000000..140c4bf --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/fontinfo.plist @@ -0,0 +1,55 @@ + + + + + ascender + 400 + capHeight + 400 + copyright + # font.info from ufo: geometryMaster_c_1000_d1_3_d2_1.ufo + descender + -200 + familyName + Three_wide_solid + guidelines + + openTypeHheaAscender + 1036 + openTypeHheaDescender + -335 + openTypeOS2TypoAscender + 730 + openTypeOS2TypoDescender + -270 + openTypeOS2WinAscent + 1036 + openTypeOS2WinDescent + 335 + postscriptBlueFuzz + 0 + postscriptBlueScale + 0.22 + postscriptBlueValues + + 100 + 110 + + postscriptFamilyBlues + + postscriptFamilyOtherBlues + + postscriptOtherBlues + + postscriptStemSnapH + + postscriptStemSnapV + + styleName + c_1000_d1_3_d2_1 + unitsPerEm + 1000 + xHeight + 200 + + diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/glyphs/contents.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/glyphs/contents.plist new file mode 100644 index 0000000..4cb18bb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/glyphs/contents.plist @@ -0,0 +1,10 @@ + + + + + glyphOne + glyphO_ne.glif + glyphTwo + glyphT_wo.glif + + diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/glyphs/glyphO_ne.glif b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/glyphs/glyphO_ne.glif new file mode 100644 index 0000000..9d65323 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/glyphs/glyphO_ne.glif @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/glyphs/glyphT_wo.glif b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/glyphs/glyphT_wo.glif new file mode 100644 index 0000000..c523ee7 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/glyphs/glyphT_wo.glif @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Tests/20190830 benders/benderTest2.ufo/glyphs.background/layerinfo.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/glyphs/layerinfo.plist similarity index 85% rename from Tests/20190830 benders/benderTest2.ufo/glyphs.background/layerinfo.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/glyphs/layerinfo.plist index 87752a4..3cf39b4 100644 --- a/Tests/20190830 benders/benderTest2.ufo/glyphs.background/layerinfo.plist +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/glyphs/layerinfo.plist @@ -3,6 +3,6 @@ color - 0,0.8,0.2,0.7 + 1,0.75,0,0.7 diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/groups.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/groups.plist new file mode 100644 index 0000000..4dff22d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/groups.plist @@ -0,0 +1,16 @@ + + + + + public.kern1.groupA + + glyphOne + glyphTwo + + public.kern2.groupB + + glyphThree + glyphFour + + + diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/kerning.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/kerning.plist new file mode 100644 index 0000000..b54ab73 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/kerning.plist @@ -0,0 +1,20 @@ + + + + + glyphOne + + glyphOne + 400 + glyphTwo + 800 + + glyphTwo + + glyphOne + -800 + glyphTwo + -400 + + + diff --git a/Tests/20190830 benders/benderTest1.ufo/glyphs.background/contents.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/layercontents.plist similarity index 60% rename from Tests/20190830 benders/benderTest1.ufo/glyphs.background/contents.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/layercontents.plist index 1e96c94..b9c1a4f 100644 --- a/Tests/20190830 benders/benderTest1.ufo/glyphs.background/contents.plist +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/layercontents.plist @@ -1,5 +1,10 @@ - + + + public.default + glyphs + + diff --git a/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/lib.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/lib.plist new file mode 100644 index 0000000..ad24abb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/lib.plist @@ -0,0 +1,31 @@ + + + + + com.typemytype.robofont.compileSettings.autohint + + com.typemytype.robofont.compileSettings.checkOutlines + + com.typemytype.robofont.compileSettings.createDummyDSIG + + com.typemytype.robofont.compileSettings.decompose + + com.typemytype.robofont.compileSettings.generateFormat + 0 + com.typemytype.robofont.compileSettings.releaseMode + + com.typemytype.robofont.generateFeaturesWithFontTools + + com.typemytype.robofont.italicSlantOffset + 0 + com.typemytype.robofont.shouldAddPointsInSplineConversion + 1 + public.glyphOrder + + glyphOne + glyphTwo + + ufoProcessor.test.lib.entry + Lib entry for master 1 + + diff --git a/Tests/20190830 benders/benderTest1.ufo/kerning.plist b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/metainfo.plist similarity index 60% rename from Tests/20190830 benders/benderTest1.ufo/kerning.plist rename to Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/metainfo.plist index 23382cc..7b8b34a 100644 --- a/Tests/20190830 benders/benderTest1.ufo/kerning.plist +++ b/Tests/ds5/sources/geometrySource_c_1000_d1_3_d2_1.ufo/metainfo.plist @@ -2,10 +2,9 @@ - a - - a - -100 - + creator + com.github.fonttools.ufoLib + formatVersion + 3 diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/features.fea b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/features.fea new file mode 100644 index 0000000..cf9f1f4 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/features.fea @@ -0,0 +1 @@ +# features from ufo: geometryMaster_c_400_d1_1_d2_0.ufo \ No newline at end of file diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/fontinfo.plist b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/fontinfo.plist new file mode 100644 index 0000000..11ef8cf --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/fontinfo.plist @@ -0,0 +1,55 @@ + + + + + ascender + 400 + capHeight + 400 + copyright + # font.info from ufo: geometryMaster_c_400_d1_1_d2_0.ufo + descender + -200 + familyName + One_narrow_open + guidelines + + openTypeHheaAscender + 1036 + openTypeHheaDescender + -335 + openTypeOS2TypoAscender + 730 + openTypeOS2TypoDescender + -270 + openTypeOS2WinAscent + 1036 + openTypeOS2WinDescent + 335 + postscriptBlueFuzz + 0 + postscriptBlueScale + 0.22 + postscriptBlueValues + + 100 + 110 + + postscriptFamilyBlues + + postscriptFamilyOtherBlues + + postscriptOtherBlues + + postscriptStemSnapH + + postscriptStemSnapV + + styleName + c_400_d1_1_d2_0 + unitsPerEm + 1000 + xHeight + 200 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/glyphs/contents.plist b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/glyphs/contents.plist new file mode 100644 index 0000000..4cb18bb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/glyphs/contents.plist @@ -0,0 +1,10 @@ + + + + + glyphOne + glyphO_ne.glif + glyphTwo + glyphT_wo.glif + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/glyphs/glyphO_ne.glif b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/glyphs/glyphO_ne.glif new file mode 100644 index 0000000..cc09903 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/glyphs/glyphO_ne.glif @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/glyphs/glyphT_wo.glif b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/glyphs/glyphT_wo.glif new file mode 100644 index 0000000..7eea472 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/glyphs/glyphT_wo.glif @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Tests/20190830 benders/benderTest2.ufo/glyphs/contents.plist b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/glyphs/layerinfo.plist similarity index 77% rename from Tests/20190830 benders/benderTest2.ufo/glyphs/contents.plist rename to Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/glyphs/layerinfo.plist index 63a44ac..3cf39b4 100644 --- a/Tests/20190830 benders/benderTest2.ufo/glyphs/contents.plist +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/glyphs/layerinfo.plist @@ -2,7 +2,7 @@ - a - a.glif + color + 1,0.75,0,0.7 diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/groups.plist b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/groups.plist new file mode 100644 index 0000000..4dff22d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/groups.plist @@ -0,0 +1,16 @@ + + + + + public.kern1.groupA + + glyphOne + glyphTwo + + public.kern2.groupB + + glyphThree + glyphFour + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/kerning.plist b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/kerning.plist new file mode 100644 index 0000000..ff4ce7d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/kerning.plist @@ -0,0 +1,20 @@ + + + + + glyphOne + + glyphOne + 400 + glyphTwo + 100 + + glyphTwo + + glyphOne + -100 + glyphTwo + -400 + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/layercontents.plist b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/layercontents.plist new file mode 100644 index 0000000..b9c1a4f --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/layercontents.plist @@ -0,0 +1,10 @@ + + + + + + public.default + glyphs + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/lib.plist b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/lib.plist new file mode 100644 index 0000000..ad24abb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/lib.plist @@ -0,0 +1,31 @@ + + + + + com.typemytype.robofont.compileSettings.autohint + + com.typemytype.robofont.compileSettings.checkOutlines + + com.typemytype.robofont.compileSettings.createDummyDSIG + + com.typemytype.robofont.compileSettings.decompose + + com.typemytype.robofont.compileSettings.generateFormat + 0 + com.typemytype.robofont.compileSettings.releaseMode + + com.typemytype.robofont.generateFeaturesWithFontTools + + com.typemytype.robofont.italicSlantOffset + 0 + com.typemytype.robofont.shouldAddPointsInSplineConversion + 1 + public.glyphOrder + + glyphOne + glyphTwo + + ufoProcessor.test.lib.entry + Lib entry for master 1 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/metainfo.plist b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/metainfo.plist new file mode 100644 index 0000000..7b8b34a --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_0.ufo/metainfo.plist @@ -0,0 +1,10 @@ + + + + + creator + com.github.fonttools.ufoLib + formatVersion + 3 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/features.fea b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/features.fea new file mode 100644 index 0000000..ed083e0 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/features.fea @@ -0,0 +1 @@ +# features from ufo: geometryMaster_c_400_d1_1_d2_1.ufo \ No newline at end of file diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/fontinfo.plist b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/fontinfo.plist new file mode 100644 index 0000000..4758a3e --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/fontinfo.plist @@ -0,0 +1,55 @@ + + + + + ascender + 400 + capHeight + 400 + copyright + # font.info from ufo: geometryMaster_c_400_d1_1_d2_1.ufo + descender + -200 + familyName + One_narrow_solid + guidelines + + openTypeHheaAscender + 1036 + openTypeHheaDescender + -335 + openTypeOS2TypoAscender + 730 + openTypeOS2TypoDescender + -270 + openTypeOS2WinAscent + 1036 + openTypeOS2WinDescent + 335 + postscriptBlueFuzz + 0 + postscriptBlueScale + 0.22 + postscriptBlueValues + + 100 + 110 + + postscriptFamilyBlues + + postscriptFamilyOtherBlues + + postscriptOtherBlues + + postscriptStemSnapH + + postscriptStemSnapV + + styleName + c_400_d1_1_d2_1 + unitsPerEm + 1000 + xHeight + 200 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/glyphs/contents.plist b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/glyphs/contents.plist new file mode 100644 index 0000000..4cb18bb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/glyphs/contents.plist @@ -0,0 +1,10 @@ + + + + + glyphOne + glyphO_ne.glif + glyphTwo + glyphT_wo.glif + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/glyphs/glyphO_ne.glif b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/glyphs/glyphO_ne.glif new file mode 100644 index 0000000..f9c3a61 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/glyphs/glyphO_ne.glif @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/glyphs/glyphT_wo.glif b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/glyphs/glyphT_wo.glif new file mode 100644 index 0000000..7eea472 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/glyphs/glyphT_wo.glif @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/glyphs/layerinfo.plist b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/glyphs/layerinfo.plist new file mode 100644 index 0000000..3cf39b4 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/glyphs/layerinfo.plist @@ -0,0 +1,8 @@ + + + + + color + 1,0.75,0,0.7 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/groups.plist b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/groups.plist new file mode 100644 index 0000000..4dff22d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/groups.plist @@ -0,0 +1,16 @@ + + + + + public.kern1.groupA + + glyphOne + glyphTwo + + public.kern2.groupB + + glyphThree + glyphFour + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/kerning.plist b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/kerning.plist new file mode 100644 index 0000000..ff4ce7d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/kerning.plist @@ -0,0 +1,20 @@ + + + + + glyphOne + + glyphOne + 400 + glyphTwo + 100 + + glyphTwo + + glyphOne + -100 + glyphTwo + -400 + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/layercontents.plist b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/layercontents.plist new file mode 100644 index 0000000..b9c1a4f --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/layercontents.plist @@ -0,0 +1,10 @@ + + + + + + public.default + glyphs + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/lib.plist b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/lib.plist new file mode 100644 index 0000000..ad24abb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/lib.plist @@ -0,0 +1,31 @@ + + + + + com.typemytype.robofont.compileSettings.autohint + + com.typemytype.robofont.compileSettings.checkOutlines + + com.typemytype.robofont.compileSettings.createDummyDSIG + + com.typemytype.robofont.compileSettings.decompose + + com.typemytype.robofont.compileSettings.generateFormat + 0 + com.typemytype.robofont.compileSettings.releaseMode + + com.typemytype.robofont.generateFeaturesWithFontTools + + com.typemytype.robofont.italicSlantOffset + 0 + com.typemytype.robofont.shouldAddPointsInSplineConversion + 1 + public.glyphOrder + + glyphOne + glyphTwo + + ufoProcessor.test.lib.entry + Lib entry for master 1 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/metainfo.plist b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/metainfo.plist new file mode 100644 index 0000000..7b8b34a --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_1_d2_1.ufo/metainfo.plist @@ -0,0 +1,10 @@ + + + + + creator + com.github.fonttools.ufoLib + formatVersion + 3 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/features.fea b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/features.fea new file mode 100644 index 0000000..4d4466a --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/features.fea @@ -0,0 +1 @@ +# features from ufo: geometryMaster_c_400_d1_2_d2_0.ufo \ No newline at end of file diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/fontinfo.plist b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/fontinfo.plist new file mode 100644 index 0000000..05fa9c4 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/fontinfo.plist @@ -0,0 +1,55 @@ + + + + + ascender + 400 + capHeight + 400 + copyright + # font.info from ufo: geometryMaster_c_400_d1_2_d2_0.ufo + descender + -200 + familyName + Two_narrow_open + guidelines + + openTypeHheaAscender + 1036 + openTypeHheaDescender + -335 + openTypeOS2TypoAscender + 730 + openTypeOS2TypoDescender + -270 + openTypeOS2WinAscent + 1036 + openTypeOS2WinDescent + 335 + postscriptBlueFuzz + 0 + postscriptBlueScale + 0.22 + postscriptBlueValues + + 100 + 110 + + postscriptFamilyBlues + + postscriptFamilyOtherBlues + + postscriptOtherBlues + + postscriptStemSnapH + + postscriptStemSnapV + + styleName + c_400_d1_2_d2_0 + unitsPerEm + 1000 + xHeight + 200 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/glyphs/contents.plist b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/glyphs/contents.plist new file mode 100644 index 0000000..4cb18bb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/glyphs/contents.plist @@ -0,0 +1,10 @@ + + + + + glyphOne + glyphO_ne.glif + glyphTwo + glyphT_wo.glif + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/glyphs/glyphO_ne.glif b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/glyphs/glyphO_ne.glif new file mode 100644 index 0000000..978f672 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/glyphs/glyphO_ne.glif @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/glyphs/glyphT_wo.glif b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/glyphs/glyphT_wo.glif new file mode 100644 index 0000000..7eea472 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/glyphs/glyphT_wo.glif @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/glyphs/layerinfo.plist b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/glyphs/layerinfo.plist new file mode 100644 index 0000000..3cf39b4 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/glyphs/layerinfo.plist @@ -0,0 +1,8 @@ + + + + + color + 1,0.75,0,0.7 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/groups.plist b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/groups.plist new file mode 100644 index 0000000..4dff22d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/groups.plist @@ -0,0 +1,16 @@ + + + + + public.kern1.groupA + + glyphOne + glyphTwo + + public.kern2.groupB + + glyphThree + glyphFour + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/kerning.plist b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/kerning.plist new file mode 100644 index 0000000..ff4ce7d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/kerning.plist @@ -0,0 +1,20 @@ + + + + + glyphOne + + glyphOne + 400 + glyphTwo + 100 + + glyphTwo + + glyphOne + -100 + glyphTwo + -400 + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/layercontents.plist b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/layercontents.plist new file mode 100644 index 0000000..b9c1a4f --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/layercontents.plist @@ -0,0 +1,10 @@ + + + + + + public.default + glyphs + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/lib.plist b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/lib.plist new file mode 100644 index 0000000..ad24abb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/lib.plist @@ -0,0 +1,31 @@ + + + + + com.typemytype.robofont.compileSettings.autohint + + com.typemytype.robofont.compileSettings.checkOutlines + + com.typemytype.robofont.compileSettings.createDummyDSIG + + com.typemytype.robofont.compileSettings.decompose + + com.typemytype.robofont.compileSettings.generateFormat + 0 + com.typemytype.robofont.compileSettings.releaseMode + + com.typemytype.robofont.generateFeaturesWithFontTools + + com.typemytype.robofont.italicSlantOffset + 0 + com.typemytype.robofont.shouldAddPointsInSplineConversion + 1 + public.glyphOrder + + glyphOne + glyphTwo + + ufoProcessor.test.lib.entry + Lib entry for master 1 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/metainfo.plist b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/metainfo.plist new file mode 100644 index 0000000..7b8b34a --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_0.ufo/metainfo.plist @@ -0,0 +1,10 @@ + + + + + creator + com.github.fonttools.ufoLib + formatVersion + 3 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/features.fea b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/features.fea new file mode 100644 index 0000000..4affb7c --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/features.fea @@ -0,0 +1 @@ +# features from ufo: geometryMaster_c_400_d1_2_d2_1.ufo \ No newline at end of file diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/fontinfo.plist b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/fontinfo.plist new file mode 100644 index 0000000..ce0452a --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/fontinfo.plist @@ -0,0 +1,55 @@ + + + + + ascender + 400 + capHeight + 400 + copyright + # font.info from ufo: geometryMaster_c_400_d1_2_d2_1.ufo + descender + -200 + familyName + Two_narrow_solid + guidelines + + openTypeHheaAscender + 1036 + openTypeHheaDescender + -335 + openTypeOS2TypoAscender + 730 + openTypeOS2TypoDescender + -270 + openTypeOS2WinAscent + 1036 + openTypeOS2WinDescent + 335 + postscriptBlueFuzz + 0 + postscriptBlueScale + 0.22 + postscriptBlueValues + + 100 + 110 + + postscriptFamilyBlues + + postscriptFamilyOtherBlues + + postscriptOtherBlues + + postscriptStemSnapH + + postscriptStemSnapV + + styleName + c_400_d1_2_d2_1 + unitsPerEm + 1000 + xHeight + 200 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/glyphs/contents.plist b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/glyphs/contents.plist new file mode 100644 index 0000000..4cb18bb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/glyphs/contents.plist @@ -0,0 +1,10 @@ + + + + + glyphOne + glyphO_ne.glif + glyphTwo + glyphT_wo.glif + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/glyphs/glyphO_ne.glif b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/glyphs/glyphO_ne.glif new file mode 100644 index 0000000..a74b7e0 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/glyphs/glyphO_ne.glif @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/glyphs/glyphT_wo.glif b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/glyphs/glyphT_wo.glif new file mode 100644 index 0000000..7eea472 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/glyphs/glyphT_wo.glif @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/glyphs/layerinfo.plist b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/glyphs/layerinfo.plist new file mode 100644 index 0000000..3cf39b4 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/glyphs/layerinfo.plist @@ -0,0 +1,8 @@ + + + + + color + 1,0.75,0,0.7 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/groups.plist b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/groups.plist new file mode 100644 index 0000000..4dff22d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/groups.plist @@ -0,0 +1,16 @@ + + + + + public.kern1.groupA + + glyphOne + glyphTwo + + public.kern2.groupB + + glyphThree + glyphFour + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/kerning.plist b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/kerning.plist new file mode 100644 index 0000000..ff4ce7d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/kerning.plist @@ -0,0 +1,20 @@ + + + + + glyphOne + + glyphOne + 400 + glyphTwo + 100 + + glyphTwo + + glyphOne + -100 + glyphTwo + -400 + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/layercontents.plist b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/layercontents.plist new file mode 100644 index 0000000..b9c1a4f --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/layercontents.plist @@ -0,0 +1,10 @@ + + + + + + public.default + glyphs + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/lib.plist b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/lib.plist new file mode 100644 index 0000000..ad24abb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/lib.plist @@ -0,0 +1,31 @@ + + + + + com.typemytype.robofont.compileSettings.autohint + + com.typemytype.robofont.compileSettings.checkOutlines + + com.typemytype.robofont.compileSettings.createDummyDSIG + + com.typemytype.robofont.compileSettings.decompose + + com.typemytype.robofont.compileSettings.generateFormat + 0 + com.typemytype.robofont.compileSettings.releaseMode + + com.typemytype.robofont.generateFeaturesWithFontTools + + com.typemytype.robofont.italicSlantOffset + 0 + com.typemytype.robofont.shouldAddPointsInSplineConversion + 1 + public.glyphOrder + + glyphOne + glyphTwo + + ufoProcessor.test.lib.entry + Lib entry for master 1 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/metainfo.plist b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/metainfo.plist new file mode 100644 index 0000000..7b8b34a --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_2_d2_1.ufo/metainfo.plist @@ -0,0 +1,10 @@ + + + + + creator + com.github.fonttools.ufoLib + formatVersion + 3 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/features.fea b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/features.fea new file mode 100644 index 0000000..88a4bea --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/features.fea @@ -0,0 +1 @@ +# features from ufo: geometryMaster_c_400_d1_3_d2_0.ufo \ No newline at end of file diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/fontinfo.plist b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/fontinfo.plist new file mode 100644 index 0000000..4162a2c --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/fontinfo.plist @@ -0,0 +1,55 @@ + + + + + ascender + 400 + capHeight + 400 + copyright + # font.info from ufo: geometryMaster_c_400_d1_3_d2_0.ufo + descender + -200 + familyName + Three_narrow_open + guidelines + + openTypeHheaAscender + 1036 + openTypeHheaDescender + -335 + openTypeOS2TypoAscender + 730 + openTypeOS2TypoDescender + -270 + openTypeOS2WinAscent + 1036 + openTypeOS2WinDescent + 335 + postscriptBlueFuzz + 0 + postscriptBlueScale + 0.22 + postscriptBlueValues + + 100 + 110 + + postscriptFamilyBlues + + postscriptFamilyOtherBlues + + postscriptOtherBlues + + postscriptStemSnapH + + postscriptStemSnapV + + styleName + c_400_d1_3_d2_0 + unitsPerEm + 1000 + xHeight + 200 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/glyphs/contents.plist b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/glyphs/contents.plist new file mode 100644 index 0000000..4cb18bb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/glyphs/contents.plist @@ -0,0 +1,10 @@ + + + + + glyphOne + glyphO_ne.glif + glyphTwo + glyphT_wo.glif + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/glyphs/glyphO_ne.glif b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/glyphs/glyphO_ne.glif new file mode 100644 index 0000000..efe81ee --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/glyphs/glyphO_ne.glif @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/glyphs/glyphT_wo.glif b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/glyphs/glyphT_wo.glif new file mode 100644 index 0000000..7eea472 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/glyphs/glyphT_wo.glif @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/glyphs/layerinfo.plist b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/glyphs/layerinfo.plist new file mode 100644 index 0000000..3cf39b4 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/glyphs/layerinfo.plist @@ -0,0 +1,8 @@ + + + + + color + 1,0.75,0,0.7 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/groups.plist b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/groups.plist new file mode 100644 index 0000000..4dff22d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/groups.plist @@ -0,0 +1,16 @@ + + + + + public.kern1.groupA + + glyphOne + glyphTwo + + public.kern2.groupB + + glyphThree + glyphFour + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/kerning.plist b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/kerning.plist new file mode 100644 index 0000000..ff4ce7d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/kerning.plist @@ -0,0 +1,20 @@ + + + + + glyphOne + + glyphOne + 400 + glyphTwo + 100 + + glyphTwo + + glyphOne + -100 + glyphTwo + -400 + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/layercontents.plist b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/layercontents.plist new file mode 100644 index 0000000..b9c1a4f --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/layercontents.plist @@ -0,0 +1,10 @@ + + + + + + public.default + glyphs + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/lib.plist b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/lib.plist new file mode 100644 index 0000000..ad24abb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/lib.plist @@ -0,0 +1,31 @@ + + + + + com.typemytype.robofont.compileSettings.autohint + + com.typemytype.robofont.compileSettings.checkOutlines + + com.typemytype.robofont.compileSettings.createDummyDSIG + + com.typemytype.robofont.compileSettings.decompose + + com.typemytype.robofont.compileSettings.generateFormat + 0 + com.typemytype.robofont.compileSettings.releaseMode + + com.typemytype.robofont.generateFeaturesWithFontTools + + com.typemytype.robofont.italicSlantOffset + 0 + com.typemytype.robofont.shouldAddPointsInSplineConversion + 1 + public.glyphOrder + + glyphOne + glyphTwo + + ufoProcessor.test.lib.entry + Lib entry for master 1 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/metainfo.plist b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/metainfo.plist new file mode 100644 index 0000000..7b8b34a --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_0.ufo/metainfo.plist @@ -0,0 +1,10 @@ + + + + + creator + com.github.fonttools.ufoLib + formatVersion + 3 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/features.fea b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/features.fea new file mode 100644 index 0000000..a91bb83 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/features.fea @@ -0,0 +1 @@ +# features from ufo: geometryMaster_c_400_d1_3_d2_1.ufo \ No newline at end of file diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/fontinfo.plist b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/fontinfo.plist new file mode 100644 index 0000000..ff94547 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/fontinfo.plist @@ -0,0 +1,55 @@ + + + + + ascender + 400 + capHeight + 400 + copyright + # font.info from ufo: geometryMaster_c_400_d1_3_d2_1.ufo + descender + -200 + familyName + Three_narrow_solid + guidelines + + openTypeHheaAscender + 1036 + openTypeHheaDescender + -335 + openTypeOS2TypoAscender + 730 + openTypeOS2TypoDescender + -270 + openTypeOS2WinAscent + 1036 + openTypeOS2WinDescent + 335 + postscriptBlueFuzz + 0 + postscriptBlueScale + 0.22 + postscriptBlueValues + + 100 + 110 + + postscriptFamilyBlues + + postscriptFamilyOtherBlues + + postscriptOtherBlues + + postscriptStemSnapH + + postscriptStemSnapV + + styleName + c_400_d1_3_d2_1 + unitsPerEm + 1000 + xHeight + 200 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/glyphs/contents.plist b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/glyphs/contents.plist new file mode 100644 index 0000000..4cb18bb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/glyphs/contents.plist @@ -0,0 +1,10 @@ + + + + + glyphOne + glyphO_ne.glif + glyphTwo + glyphT_wo.glif + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/glyphs/glyphO_ne.glif b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/glyphs/glyphO_ne.glif new file mode 100644 index 0000000..54862c0 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/glyphs/glyphO_ne.glif @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/glyphs/glyphT_wo.glif b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/glyphs/glyphT_wo.glif new file mode 100644 index 0000000..7eea472 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/glyphs/glyphT_wo.glif @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/glyphs/layerinfo.plist b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/glyphs/layerinfo.plist new file mode 100644 index 0000000..3cf39b4 --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/glyphs/layerinfo.plist @@ -0,0 +1,8 @@ + + + + + color + 1,0.75,0,0.7 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/groups.plist b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/groups.plist new file mode 100644 index 0000000..4dff22d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/groups.plist @@ -0,0 +1,16 @@ + + + + + public.kern1.groupA + + glyphOne + glyphTwo + + public.kern2.groupB + + glyphThree + glyphFour + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/kerning.plist b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/kerning.plist new file mode 100644 index 0000000..ff4ce7d --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/kerning.plist @@ -0,0 +1,20 @@ + + + + + glyphOne + + glyphOne + 400 + glyphTwo + 100 + + glyphTwo + + glyphOne + -100 + glyphTwo + -400 + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/layercontents.plist b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/layercontents.plist new file mode 100644 index 0000000..b9c1a4f --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/layercontents.plist @@ -0,0 +1,10 @@ + + + + + + public.default + glyphs + + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/lib.plist b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/lib.plist new file mode 100644 index 0000000..ad24abb --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/lib.plist @@ -0,0 +1,31 @@ + + + + + com.typemytype.robofont.compileSettings.autohint + + com.typemytype.robofont.compileSettings.checkOutlines + + com.typemytype.robofont.compileSettings.createDummyDSIG + + com.typemytype.robofont.compileSettings.decompose + + com.typemytype.robofont.compileSettings.generateFormat + 0 + com.typemytype.robofont.compileSettings.releaseMode + + com.typemytype.robofont.generateFeaturesWithFontTools + + com.typemytype.robofont.italicSlantOffset + 0 + com.typemytype.robofont.shouldAddPointsInSplineConversion + 1 + public.glyphOrder + + glyphOne + glyphTwo + + ufoProcessor.test.lib.entry + Lib entry for master 1 + + diff --git a/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/metainfo.plist b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/metainfo.plist new file mode 100644 index 0000000..7b8b34a --- /dev/null +++ b/Tests/ds5/sources/geometrySource_c_400_d1_3_d2_1.ufo/metainfo.plist @@ -0,0 +1,10 @@ + + + + + creator + com.github.fonttools.ufoLib + formatVersion + 3 + + diff --git a/Tests/kerningTest.py b/Tests/kerningTest.py deleted file mode 100644 index 3f6490c..0000000 --- a/Tests/kerningTest.py +++ /dev/null @@ -1,62 +0,0 @@ -from fontMath.mathKerning import MathKerning - -import fontMath.mathKerning -from defcon.objects.font import Font -from fontParts.fontshell import RFont -from ufoProcessor.varModels import VariationModelMutator -from mutatorMath.objects.mutator import buildMutator, Location -from fontTools.designspaceLib import AxisDescriptor - -# kerning exception value. Different results for 1 and 0 -value = 0 - -#f = Font() -f = RFont() # doesn't make a difference -f.groups["public.kern1.groupA"] = ['one', 'Bee'] -f.groups["public.kern2.groupB"] = ['two', 'Three'] -f.kerning[('public.kern1.groupA', 'public.kern2.groupB')] = -100 -f.kerning[("one", "two")] = value - -m = MathKerning(f.kerning, f.groups) -print("mathKerning object items:", m.items()) -print("\tpair", ('public.kern1.groupA', 'public.kern2.groupB'), m[('public.kern1.groupA', 'public.kern2.groupB')]) -print("\tpair", ('public.kern1.groupA', 'two'), m[('public.kern1.groupA', 'two')]) -print("\tpair", ('one', 'public.kern2.groupB'), m[('one', 'public.kern2.groupB')]) -print("\tpair", ('one', 'two'), m[('one', 'two')]) - -items = [(Location(w=0), m), (Location(w=1), m)] -a = AxisDescriptor() -a.name = "w" -a.minimum = 0 -a.default = 0 -a.maximum = 1 - -# process with varlib.model -mut1 = VariationModelMutator(items, [a]) -m1i = mut1.makeInstance(dict(w=1)) -print("\n#varlib") -print(m1i.items()) - -# process with mutator -bias, mut2 = buildMutator(items) -m2i = mut2.makeInstance(dict(w=1)) -print("\n#mutator") -print(m2i.items()) - -# process with the same mathematical operations on a naked mathKerning object -v = None -deltas = [m, m] -scalars = [1.0, 1.0] -assert len(deltas) == len(scalars) -for i,(delta,scalar) in enumerate(zip(deltas, scalars)): - if not scalar: continue - contribution = delta * scalar - if v is None: - v = contribution - else: - v += contribution -print("\n#doing the math that varlib does") -print(v.items()) - -print(m.groups()) -print((m*2.0).groups()) diff --git a/Tests/mathKerningTest.py b/Tests/mathKerningTest.py deleted file mode 100644 index fd9e2d1..0000000 --- a/Tests/mathKerningTest.py +++ /dev/null @@ -1,14 +0,0 @@ -from fontMath.mathKerning import MathKerning -from defcon.objects.font import Font - -f = Font() -f.groups["public.kern1.groupA"] = ['one', 'Bee'] -f.groups["public.kern2.groupB"] = ['two', 'Three'] -f.kerning[('public.kern1.groupA', 'public.kern2.groupB')] = -100 -f.kerning[('one', 'two')] = 0 -m = MathKerning(f.kerning, f.groups) - -print(m.items()) -print((m*1.0).items()) - - diff --git a/Tests/spReader_testdocs/superpolator_testdoc1.sp3 b/Tests/spReader_testdocs/superpolator_testdoc1.sp3 deleted file mode 100644 index 7f4a599..0000000 --- a/Tests/spReader_testdocs/superpolator_testdoc1.sp3 +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Tests/spReader_testdocs/superpolator_testdoc1_converted.designspace b/Tests/spReader_testdocs/superpolator_testdoc1_converted.designspace deleted file mode 100644 index 560b3af..0000000 --- a/Tests/spReader_testdocs/superpolator_testdoc1_converted.designspace +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - com.letterror.skateboard.interactionSources - - horizontal - - width - - ignore - - vertical - - weight - - - com.letterror.skateboard.mutedSources - - - ufo/MutatorSansLightCondensed.ufo - foreground - - - ufo/MutatorSansBoldCondensed.ufo - foreground - - - com.letterror.skateboard.previewText - VA - com.superpolator.data - - expandRules - - horizontalPreviewAxis - width - includeLegacyRules - - instancefolder - instances - keepWorkFiles - - lineInverted - - lineStacked - lined - lineViewFilled - - outputFormatUFO - 3.0 - previewtext - VA - roundGeometry - - verticalPreviewAxis - weight - - - - diff --git a/Tests/spReader_testdocs/superpolator_testdoc1_output_roundtripped.designspace b/Tests/spReader_testdocs/superpolator_testdoc1_output_roundtripped.designspace deleted file mode 100644 index 39fbf32..0000000 --- a/Tests/spReader_testdocs/superpolator_testdoc1_output_roundtripped.designspace +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - com.letterror.skateboard.interactionSources - - horizontal - - width - - ignore - - vertical - - weight - - - com.letterror.skateboard.mutedSources - - - ufo/MutatorSansLightCondensed.ufo - foreground - - - ufo/MutatorSansBoldCondensed.ufo - foreground - - - com.letterror.skateboard.previewText - VA - com.superpolator.data - - expandRules - - horizontalPreviewAxis - width - includeLegacyRules - - instancefolder - instances - keepWorkFiles - - lineInverted - - lineStacked - lined - lineViewFilled - - outputFormatUFO - 3.0 - previewtext - VA - roundGeometry - - verticalPreviewAxis - weight - - - - diff --git a/Tests/tests.py b/Tests/tests.py index 142769b..36af9a2 100644 --- a/Tests/tests.py +++ b/Tests/tests.py @@ -1,33 +1,21 @@ # standalone test import shutil import os -import defcon.objects.font -import fontParts.fontshell.font +#from defcon.objects.font import Font import logging from ufoProcessor import * +import fontParts.fontshell # new place for ufoProcessor tests. # Run in regular python of choice, not ready for pytest just yet. # You may ask "why not?" - you may ask indeed. -# make the tests w ork with defcon as well as fontparts - -def addExtraGlyph(font, name, s=200): - font.newGlyph(name) - g = font[name] - p = g.getPen() - p.moveTo((0,0)) - p.lineTo((s,0)) - p.lineTo((s,s)) - p.lineTo((0,s)) - p.closePath() - g.width = s +# Now based on fontParts. def addGlyphs(font, s, addSupportLayer=True): # we need to add the glyphs step = 0 - uni = 95 for n in ['glyphOne', 'glyphTwo', 'glyphThree', 'glyphFour', 'glyphFive']: font.newGlyph(n) g = font[n] @@ -39,8 +27,6 @@ def addGlyphs(font, s, addSupportLayer=True): p.closePath() g.move((0,s+step)) g.width = s - g.unicode = uni - uni += 1 step += 50 for n, w in [('wide', 800), ('narrow', 100)]: font.newGlyph(n) @@ -52,15 +38,12 @@ def addGlyphs(font, s, addSupportLayer=True): p.lineTo((0,font.info.ascender)) p.closePath() g.width = w - na = defcon.Anchor() - na.name = "top" - na.x = 0 - na.y = w - g.appendAnchor(na) + g.appendAnchor("top", (0, w)) if addSupportLayer: font.newLayer('support') - layer = font.layers['support'] + print(n for n in font.layers if n.name == 'support') + layer = font.getLayer('support') layer.newGlyph('glyphFive') layer.newGlyph('glyphOne') # add an empty glyph to see how it is treated lg = layer['glyphFive'] @@ -76,17 +59,19 @@ def addGlyphs(font, s, addSupportLayer=True): font.newGlyph("wide.component") g = font["wide.component"] - comp = g.instantiateComponent() - comp.baseGlyph = "wide" - comp.offset = (0,0) - g.appendComponent(comp) + g.appendComponent("wide", offset=(0,0)) + #comp = g.instantiateComponent() + #comp.baseGlyph = "wide" + #comp.offset = (0,0) + #g.appendComponent(comp) g.width = font['wide'].width font.newGlyph("narrow.component") g = font["narrow.component"] - comp = g.instantiateComponent() - comp.baseGlyph = "narrow" - comp.offset = (0,0) - g.appendComponent(comp) + g.appendComponent("narrow", offset=(0,0)) + #comp = g.instantiateComponent() + #comp.baseGlyph = "narrow" + #comp.offset = (0,0) + #g.appendComponent(comp) g.width = font['narrow'].width uniValue = 200 for g in font: @@ -99,16 +84,6 @@ def fillInfo(font): font.info.ascender = 800 font.info.descender = -200 -def _create_parent_dir(ufo_path): - """ - Creates the parent directory where the UFO will be saved, in case it - doesn't exist already. This is required because fontTools.ufoLib no - longer calls os.makedirs. - """ - directory = os.path.dirname(os.path.normpath(ufo_path)) - if directory and not os.path.exists(directory): - os.makedirs(directory) - def _makeTestFonts(rootPath): """ Make some test fonts that have the kerning problem.""" path1 = os.path.join(rootPath, "masters", "geometryMaster1.ufo") @@ -116,43 +91,26 @@ def _makeTestFonts(rootPath): path3 = os.path.join(rootPath, "instances", "geometryInstance%3.3f.ufo") path4 = os.path.join(rootPath, "anisotropic_instances", "geometryInstanceAnisotropic1.ufo") path5 = os.path.join(rootPath, "anisotropic_instances", "geometryInstanceAnisotropic2.ufo") - path6 = os.path.join(rootPath, "instances", "extrapolate", "geometryInstance%s.ufo") - f1 = Font() + for path in [path1, path2, path3, path4, path5]: + d = os.path.dirname(path) + if not os.path.exists(d): + os.makedirs(d) + f1 = fontParts.fontshell.RFont() fillInfo(f1) addGlyphs(f1, 100, addSupportLayer=False) - addExtraGlyph(f1, "extra.glyph.for.neutral") f1.features.text = u"# features text from master 1" - f2 = Font() + f2 = fontParts.fontshell.RFont() fillInfo(f2) addGlyphs(f2, 500, addSupportLayer=True) - addExtraGlyph(f2, "extra.glyph.for.master2") f2.features.text = u"# features text from master 2" f1.info.ascender = 400 f1.info.descender = -200 - f1.info.xHeight = 200 - f1.info.capHeight = 400 f2.info.ascender = 600 f2.info.descender = -100 - f2.info.xHeight = 200 - f2.info.capHeight = 600 f1.info.copyright = u"This is the copyright notice from master 1" f2.info.copyright = u"This is the copyright notice from master 2" f1.lib['ufoProcessor.test.lib.entry'] = "Lib entry for master 1" f2.lib['ufoProcessor.test.lib.entry'] = "Lib entry for master 2" - - f1.info.postscriptBlueValues = [100, 110] - f2.info.postscriptBlueValues = [120, 125] - f1.info.postscriptBlueFuzz = 0 - f2.info.postscriptBlueFuzz = 1 - f1.info.postscriptBlueScale = 0.11 # should not round - f1.info.postscriptBlueScale = 0.22 - - f1.info.openTypeHheaAscender = 1036 - f1.info.openTypeHheaDescender = -335 - f1.info.openTypeOS2TypoAscender = 730 - f1.info.openTypeOS2TypoDescender = -270 - f1.info.openTypeOS2WinAscent = 1036 - f1.info.openTypeOS2WinDescent = 335 f1.groups["public.kern1.groupA"] = ['glyphOne', 'glyphTwo'] f1.groups["public.kern2.groupB"] = ['glyphThree', 'glyphFour'] @@ -170,17 +128,15 @@ def _makeTestFonts(rootPath): f2.kerning[('glyphOne', 'glyphFour')] = 0 print([l.name for l in f1.layers], [l.name for l in f2.layers]) - _create_parent_dir(path1) - _create_parent_dir(path2) f1.save(path1, 3) f2.save(path2, 3) - return path1, path2, path3, path4, path5, path6 + return path1, path2, path3, path4, path5 def _makeSwapFonts(rootPath): """ Make some test fonts that have the kerning problem.""" path1 = os.path.join(rootPath, "Swap.ufo") path2 = os.path.join(rootPath, "Swapped.ufo") - f1 = Font() + f1 = fontParts.fontshell.RFont() fillInfo(f1) addGlyphs(f1, 100) f1.features.text = u"# features text from master 1" @@ -191,40 +147,27 @@ def _makeSwapFonts(rootPath): f1.save(path1, 2) return path1, path2 -class DesignSpaceProcessor_using_defcon(DesignSpaceProcessor): - def _instantiateFont(self, path): - return defcon.objects.font.Font(path) - -class DesignSpaceProcessor_using_fontparts(DesignSpaceProcessor): - def _instantiateFont(self, path): - return fontParts.fontshell.font.RFont(path) - -def _makeTestDocument(docPath, useVarlib=True, useDefcon=True): +def _makeTestDocument(docPath, useVarlib=True): # make the test fonts and a test document if useVarlib: extension = "varlib" else: extension = "mutator" - testFontPath = os.path.join(os.path.dirname(docPath), "automatic_testfonts_%s" % extension) - print("\ttestFontPath:", testFontPath) - m1, m2, i1, anisotropicInstancePath1, anisotropicInstancePath2, extrapolatePath = _makeTestFonts(testFontPath) - if useDefcon: - d = DesignSpaceProcessor_using_defcon(useVarlib=useVarlib) - else: - d = DesignSpaceProcessor_using_fontparts(useVarlib=useVarlib) - print("\td", d, type(d)) + testFontPath = os.path.join(os.getcwd(), "automatic_testfonts_%s" % extension) + m1, m2, i1, anisotropicInstancePath1, anisotropicInstancePath2 = _makeTestFonts(testFontPath) + d = DesignSpaceProcessor(useVarlib=useVarlib) a = AxisDescriptor() a.name = "pop" a.minimum = 0 a.maximum = 1000 a.default = 0 a.tag = "pop*" - a.map = [(0,10),(500,250),(1000,990)] + a.map = [(0,0),(500,250),(1000,1000)] d.addAxis(a) s1 = SourceDescriptor() s1.path = m1 - s1.location = dict(pop=a.map_forward(a.default)) + s1.location = dict(pop=a.default) s1.name = "test.master.1" s1.copyInfo = True s1.copyFeatures = True @@ -235,7 +178,6 @@ def _makeTestDocument(docPath, useVarlib=True, useDefcon=True): s2.path = m2 s2.location = dict(pop=1000) s2.name = "test.master.2" - s2.muteKerning = True d.addSource(s2) s3 = SourceDescriptor() @@ -245,27 +187,10 @@ def _makeTestDocument(docPath, useVarlib=True, useDefcon=True): s3.layerName = "support" d.addSource(s3) - s4 = SourceDescriptor() - s4.path = "missing.ufo" - s4.location = dict(pop=600) - s4.name = "test.missing.master" - d.addSource(s4) - - s5 = SourceDescriptor() - s5.path = m2 - s5.location = dict(pop=620) - s5.name = "test.existing.ufo_missing.layer" - s5.layerName = "missing.layer" - d.addSource(s5) - d.findDefault() - # make sure the default location is bend and unbend as we want. - assert d.newDefaultLocation().get('pop') == 0 - assert d.newDefaultLocation(bend=True).get('pop') == 10 - - steps = 6 - for counter in range(steps): - factor = counter / steps + + for counter in range(3): + factor = counter / 2 i = InstanceDescriptor() v = a.minimum+factor*(a.maximum-a.minimum) i.path = i1 % v @@ -275,7 +200,6 @@ def _makeTestDocument(docPath, useVarlib=True, useDefcon=True): i.location = dict(pop=v) i.info = True i.kerning = True - i.postScriptFontName = "TestFamily PSName %s" % i.styleName if counter == 2: i.glyphs['glyphTwo'] = dict(name="glyphTwo", mute=True) i.copyLib = True @@ -283,20 +207,9 @@ def _makeTestDocument(docPath, useVarlib=True, useDefcon=True): i.glyphs['narrow'] = dict(instanceLocation=dict(pop=400), unicodes=[0x123, 0x124, 0x125]) d.addInstance(i) - # add extrapolatiing location - i = InstanceDescriptor() - i.path = extrapolatePath % "TestStyle_Extrapolate" - print('i.path', i.path) - i.familyName = "TestFamily" - i.styleName = "TestStyle_Extrapolate" - i.name = "%s-%s" % (i.familyName, i.styleName) - i.location = dict(pop=3000) - i.info = True - i.kerning = True - d.addInstance(i) - # add anisotropic locations i = InstanceDescriptor() + v = a.minimum+0.5*(a.maximum-a.minimum) i.path = anisotropicInstancePath1 i.familyName = "TestFamily" i.styleName = "TestStyle_pop_anisotropic1" @@ -307,6 +220,7 @@ def _makeTestDocument(docPath, useVarlib=True, useDefcon=True): d.addInstance(i) i = InstanceDescriptor() + v = a.minimum+0.5*(a.maximum-a.minimum) i.path = anisotropicInstancePath2 i.familyName = "TestFamily" i.styleName = "TestStyle_pop_anisotropic2" @@ -317,107 +231,19 @@ def _makeTestDocument(docPath, useVarlib=True, useDefcon=True): d.addInstance(i) # add data to the document lib - d.lib['ufoprocessor.testdata'] = dict(pop=500, name="This is a named location, stored in the document lib.") + d.lib['ufoprocessor.testdata'] = dict(width=500, weight=500, name="This is a named location, stored in the document lib.") d.write(docPath) -def _testGenerateInstances(docPath, useVarlib=True, useDefcon=True, roundGeometry=False): +def _testGenerateInstances(docPath, useVarlib=True): # execute the test document - if useDefcon: - d = DesignSpaceProcessor_using_defcon(useVarlib=useVarlib) - else: - d = DesignSpaceProcessor_using_fontparts(useVarlib=useVarlib) + d = DesignSpaceProcessor(useVarlib=useVarlib) d.read(docPath) - d.loadFonts() - print('---', d.newDefaultLocation()) - d.roundGeometry = roundGeometry - objectFlavor = [type(f).__name__ for f in d.fonts.values()][0] - print("objectFlavor", objectFlavor) d.generateUFO() if d.problems: - print("log:") for p in d.problems: print("\t",p) -def testSwap(docPath): - srcPath, dstPath = _makeSwapFonts(os.path.dirname(docPath)) - f = Font(srcPath) - swapGlyphNames(f, "narrow", "wide") - f.info.styleName = "Swapped" - f.save(dstPath) - # test the results in newly opened fonts - old = Font(srcPath) - new = Font(dstPath) - assert new.kerning.get(("narrow", "narrow")) == old.kerning.get(("wide","wide")) - assert new.kerning.get(("wide", "wide")) == old.kerning.get(("narrow","narrow")) - # after the swap these widths should be the same - assert old['narrow'].width == new['wide'].width - assert old['wide'].width == new['narrow'].width - # The following test may be a bit counterintuitive: - # the rule swaps the glyphs, but we do not want glyphs that are not - # specifically affected by the rule to *appear* any different. - # So, components have to be remapped. - assert new['wide.component'].components[0].baseGlyph == "narrow" - assert new['narrow.component'].components[0].baseGlyph == "wide" - # Check that anchors swapped - assert new['wide'].anchors[0].y == old['narrow'].anchors[0].y - assert new['narrow'].anchors[0].y == old['wide'].anchors[0].y - -def testAxisMuting(): - d = DesignSpaceProcessor_using_defcon(useVarlib=True) - - a = AxisDescriptor() - a.name = "pop" - a.minimum = 0 - a.maximum = 1000 - a.default = 0 - a.tag = "pop*" - d.addAxis(a) - - a = AxisDescriptor() - a.name = "snap" - a.minimum = 100 - a.maximum = 200 - a.default = 150 - a.tag = "snap" - d.addAxis(a) - - a = AxisDescriptor() - a.name = "crackle" - a.minimum = -1 - a.maximum = 1 - a.default = 0 - a.tag = "krak" - d.addAxis(a) - - shouldIgnore, loc = d.filterThisLocation(dict(snap=150, crackle=0, pop=0), []) - assert shouldIgnore == False - assert loc == {'snap': 150, 'crackle': 0, 'pop': 0} - - shouldIgnore, loc = d.filterThisLocation(dict(snap=150, crackle=0, pop=0), ['pop']) - assert shouldIgnore == False - assert loc == {'snap': 150, 'crackle': 0} - - shouldIgnore, loc = d.filterThisLocation(dict(snap=150, crackle=0, pop=1), ['pop']) - assert shouldIgnore == True - assert loc == {'snap': 150, 'crackle': 0} - - shouldIgnore, loc = d.filterThisLocation(dict(snap=150, crackle=0, pop=0), ['pop', 'crackle']) - assert shouldIgnore == False - assert loc == {'snap': 150} - - shouldIgnore, loc = d.filterThisLocation(dict(snap=150, crackle=0, pop=1), ['pop', 'crackle', 'snap']) - assert shouldIgnore == True - assert loc == {} - - shouldIgnore, loc = d.filterThisLocation(dict(snap=150, crackle=0, pop=0), ['one', 'two', 'three']) - assert shouldIgnore == False - assert loc == {'snap': 150, 'crackle': 0, 'pop': 0} - - shouldIgnore, loc = d.filterThisLocation(dict(snap=150, crackle=0, pop=1), ['one', 'two', 'three']) - assert shouldIgnore == False - assert loc == {'snap': 150, 'crackle': 0, 'pop': 1} - def testUnicodes(docPath, useVarlib=True): # after executing testSwap there should be some test fonts # let's check if the unicode values for glyph "narrow" arrive at the right place. @@ -425,46 +251,28 @@ def testUnicodes(docPath, useVarlib=True): d.read(docPath) for instance in d.instances: if os.path.exists(instance.path): - f = Font(instance.path) + f = fontParts.fontshell.RFont(instance.path) print("instance.path", instance.path) print("instance.name", instance.name, "f['narrow'].unicodes", f['narrow'].unicodes) - if instance.name == "TestFamily-TestStyle_pop1000.000": - assert f['narrow'].unicodes == [291, 292, 293] - else: - assert f['narrow'].unicodes == [207] + #if instance.name == "TestFamily-TestStyle_pop1000.000": + # assert f['narrow'].unicodes == [291, 292, 293] + #else: + # #assert f['narrow'].unicodes == [207] else: print("Missing test font at %s" % instance.path) selfTest = True if selfTest: - for extension in ['mutator', 'varlib']: - for objectFlavor in ['defcon', 'fontparts']: - for roundGeometry in [True, False]: - # which object model to use for **executuing** the designspace. - # all the objects in **this test** are defcon. - - print("\n\nRunning the test with ", extension, "and", objectFlavor, "roundGeometry:", roundGeometry) - print("-"*40) - USEVARLIBMODEL = extension == 'varlib' - if roundGeometry: - roundingTag = "_rounded_geometry" - else: - roundingTag = "" - testRoot = os.path.join(os.getcwd(), "automatic_testfonts_%s_%s%s" % (extension, objectFlavor, roundingTag)) - print("\ttestRoot", testRoot) - if os.path.exists(testRoot): - shutil.rmtree(testRoot) - docPath = os.path.join(testRoot, "automatic_test.designspace") - print("\tdocPath", docPath) - print("-"*40) - print("Generate document, masters") - _makeTestDocument(docPath, useVarlib=USEVARLIBMODEL, useDefcon=objectFlavor=="defcon") - print("-"*40) - print("Generate instances", docPath) - _testGenerateInstances(docPath, useVarlib=USEVARLIBMODEL, useDefcon=objectFlavor=="defcon", roundGeometry=roundGeometry) - testSwap(docPath) - #_makeTestDocument(docPath, useVarlib=USEVARLIBMODEL, useDefcon=objectFlavor=="defcon") - #_testGenerateInstances(docPath, useVarlib=USEVARLIBMODEL, useDefcon=objectFlavor=="defcon") - - -testAxisMuting() + for extension in ['varlib', 'mutator']: + print("\n\n", extension) + USEVARLIBMODEL = extension == 'varlib' + testRoot = os.path.join(os.getcwd(), "automatic_testfonts_%s" % extension) + if os.path.exists(testRoot): + shutil.rmtree(testRoot) + docPath = os.path.join(testRoot, "automatic_test.designspace") + _makeTestDocument(docPath, useVarlib=USEVARLIBMODEL) + _testGenerateInstances(docPath, useVarlib=USEVARLIBMODEL) + + _makeTestDocument(docPath, useVarlib=USEVARLIBMODEL) + _testGenerateInstances(docPath, useVarlib=USEVARLIBMODEL) + testUnicodes(docPath, useVarlib=USEVARLIBMODEL) diff --git a/Tests/tests_fp.py b/Tests/tests_fp.py deleted file mode 100644 index b882898..0000000 --- a/Tests/tests_fp.py +++ /dev/null @@ -1,303 +0,0 @@ -# standalone test -import shutil -import os -#from defcon.objects.font import Font -import logging -from ufoProcessor import * -import fontParts.fontshell - - -# new place for ufoProcessor tests. -# Run in regular python of choice, not ready for pytest just yet. -# You may ask "why not?" - you may ask indeed. - -# Now based on fontParts. - -def addGlyphs(font, s, addSupportLayer=True): - # we need to add the glyphs - step = 0 - for n in ['glyphOne', 'glyphTwo', 'glyphThree', 'glyphFour', 'glyphFive']: - font.newGlyph(n) - g = font[n] - p = g.getPen() - p.moveTo((0,0)) - p.lineTo((s,0)) - p.lineTo((s,s)) - p.lineTo((0,s)) - p.closePath() - g.move((0,s+step)) - g.width = s - step += 50 - for n, w in [('wide', 800), ('narrow', 100)]: - font.newGlyph(n) - g = font[n] - p = g.getPen() - p.moveTo((0,0)) - p.lineTo((w,0)) - p.lineTo((w,font.info.ascender)) - p.lineTo((0,font.info.ascender)) - p.closePath() - g.width = w - g.appendAnchor("top", (0, w)) - - if addSupportLayer: - font.newLayer('support') - print(n for n in font.layers if n.name == 'support') - layer = font.getLayer('support') - layer.newGlyph('glyphFive') - layer.newGlyph('glyphOne') # add an empty glyph to see how it is treated - lg = layer['glyphFive'] - p = lg.getPen() - w = 10 - y = -400 - p.moveTo((0,y)) - p.lineTo((s,y)) - p.lineTo((s,y+100)) - p.lineTo((0,y+100)) - p.closePath() - lg.width = s - - font.newGlyph("wide.component") - g = font["wide.component"] - g.appendComponent("wide", offset=(0,0)) - #comp = g.instantiateComponent() - #comp.baseGlyph = "wide" - #comp.offset = (0,0) - #g.appendComponent(comp) - g.width = font['wide'].width - font.newGlyph("narrow.component") - g = font["narrow.component"] - g.appendComponent("narrow", offset=(0,0)) - #comp = g.instantiateComponent() - #comp.baseGlyph = "narrow" - #comp.offset = (0,0) - #g.appendComponent(comp) - g.width = font['narrow'].width - uniValue = 200 - for g in font: - g.unicode = uniValue - uniValue += 1 - - -def fillInfo(font): - font.info.unitsPerEm = 1000 - font.info.ascender = 800 - font.info.descender = -200 - -def _makeTestFonts(rootPath): - """ Make some test fonts that have the kerning problem.""" - path1 = os.path.join(rootPath, "masters", "geometryMaster1.ufo") - path2 = os.path.join(rootPath, "masters", "geometryMaster2.ufo") - path3 = os.path.join(rootPath, "instances", "geometryInstance%3.3f.ufo") - path4 = os.path.join(rootPath, "anisotropic_instances", "geometryInstanceAnisotropic1.ufo") - path5 = os.path.join(rootPath, "anisotropic_instances", "geometryInstanceAnisotropic2.ufo") - for path in [path1, path2, path3, path4, path5]: - d = os.path.dirname(path) - if not os.path.exists(d): - os.makedirs(d) - f1 = fontParts.fontshell.RFont() - fillInfo(f1) - addGlyphs(f1, 100, addSupportLayer=False) - f1.features.text = u"# features text from master 1" - f2 = fontParts.fontshell.RFont() - fillInfo(f2) - addGlyphs(f2, 500, addSupportLayer=True) - f2.features.text = u"# features text from master 2" - f1.info.ascender = 400 - f1.info.descender = -200 - f2.info.ascender = 600 - f2.info.descender = -100 - f1.info.copyright = u"This is the copyright notice from master 1" - f2.info.copyright = u"This is the copyright notice from master 2" - f1.lib['ufoProcessor.test.lib.entry'] = "Lib entry for master 1" - f2.lib['ufoProcessor.test.lib.entry'] = "Lib entry for master 2" - - f1.groups["public.kern1.groupA"] = ['glyphOne', 'glyphTwo'] - f1.groups["public.kern2.groupB"] = ['glyphThree', 'glyphFour'] - f2.groups.update(f1.groups) - - f1.kerning[('public.kern1.groupA', 'public.kern2.groupB')] = -100 - f2.kerning[('public.kern1.groupA', 'public.kern2.groupB')] = -200 - - f1.kerning[('glyphOne', 'glyphOne')] = -100 - f2.kerning[('glyphOne', 'glyphOne')] = 0 - f1.kerning[('glyphOne', 'glyphThree')] = 10 - f1.kerning[('glyphOne', 'glyphFour')] = 10 - # exception - f2.kerning[('glyphOne', 'glyphThree')] = 1 - f2.kerning[('glyphOne', 'glyphFour')] = 0 - print([l.name for l in f1.layers], [l.name for l in f2.layers]) - - f1.save(path1, 3) - f2.save(path2, 3) - return path1, path2, path3, path4, path5 - -def _makeSwapFonts(rootPath): - """ Make some test fonts that have the kerning problem.""" - path1 = os.path.join(rootPath, "Swap.ufo") - path2 = os.path.join(rootPath, "Swapped.ufo") - f1 = fontParts.fontshell.RFont() - fillInfo(f1) - addGlyphs(f1, 100) - f1.features.text = u"# features text from master 1" - f1.info.ascender = 800 - f1.info.descender = -200 - f1.kerning[('glyphOne', 'glyphOne')] = -10 - f1.kerning[('glyphTwo', 'glyphTwo')] = 10 - f1.save(path1, 2) - return path1, path2 - -def _makeTestDocument(docPath, useVarlib=True): - # make the test fonts and a test document - if useVarlib: - extension = "varlib" - else: - extension = "mutator" - testFontPath = os.path.join(os.getcwd(), "automatic_testfonts_%s" % extension) - m1, m2, i1, anisotropicInstancePath1, anisotropicInstancePath2 = _makeTestFonts(testFontPath) - d = DesignSpaceProcessor(useVarlib=useVarlib) - a = AxisDescriptor() - a.name = "pop" - a.minimum = 0 - a.maximum = 1000 - a.default = 0 - a.tag = "pop*" - a.map = [(0,0),(500,250),(1000,1000)] - d.addAxis(a) - - s1 = SourceDescriptor() - s1.path = m1 - s1.location = dict(pop=a.default) - s1.name = "test.master.1" - s1.copyInfo = True - s1.copyFeatures = True - s1.copyLib = True - d.addSource(s1) - - s2 = SourceDescriptor() - s2.path = m2 - s2.location = dict(pop=1000) - s2.name = "test.master.2" - d.addSource(s2) - - s3 = SourceDescriptor() - s3.path = m2 - s3.location = dict(pop=500) - s3.name = "test.master.support.1" - s3.layerName = "support" - d.addSource(s3) - - d.findDefault() - - for counter in range(3): - factor = counter / 2 - i = InstanceDescriptor() - v = a.minimum+factor*(a.maximum-a.minimum) - i.path = i1 % v - i.familyName = "TestFamily" - i.styleName = "TestStyle_pop%3.3f" % (v) - i.name = "%s-%s" % (i.familyName, i.styleName) - i.location = dict(pop=v) - i.info = True - i.kerning = True - if counter == 2: - i.glyphs['glyphTwo'] = dict(name="glyphTwo", mute=True) - i.copyLib = True - if counter == 2: - i.glyphs['narrow'] = dict(instanceLocation=dict(pop=400), unicodes=[0x123, 0x124, 0x125]) - d.addInstance(i) - - # add anisotropic locations - i = InstanceDescriptor() - v = a.minimum+0.5*(a.maximum-a.minimum) - i.path = anisotropicInstancePath1 - i.familyName = "TestFamily" - i.styleName = "TestStyle_pop_anisotropic1" - i.name = "%s-%s" % (i.familyName, i.styleName) - i.location = dict(pop=(1000, 0)) - i.info = True - i.kerning = True - d.addInstance(i) - - i = InstanceDescriptor() - v = a.minimum+0.5*(a.maximum-a.minimum) - i.path = anisotropicInstancePath2 - i.familyName = "TestFamily" - i.styleName = "TestStyle_pop_anisotropic2" - i.name = "%s-%s" % (i.familyName, i.styleName) - i.location = dict(pop=(0, 1000)) - i.info = True - i.kerning = True - d.addInstance(i) - - # add data to the document lib - d.lib['ufoprocessor.testdata'] = dict(width=500, weight=500, name="This is a named location, stored in the document lib.") - - d.write(docPath) - -def _testGenerateInstances(docPath, useVarlib=True): - # execute the test document - d = DesignSpaceProcessor(useVarlib=useVarlib) - d.read(docPath) - d.generateUFO() - if d.problems: - for p in d.problems: - print("\t",p) - -def testSwap(docPath): - srcPath, dstPath = _makeSwapFonts(os.path.dirname(docPath)) - f = fontParts.fontshell.RFont(srcPath) - swapGlyphNames(f, "narrow", "wide") - f.info.styleName = "Swapped" - f.save(dstPath) - # test the results in newly opened fonts - old = fontParts.fontshell.RFont(srcPath) - new = fontParts.fontshell.RFont(dstPath) - assert new.kerning.get(("narrow", "narrow")) == old.kerning.get(("wide","wide")) - assert new.kerning.get(("wide", "wide")) == old.kerning.get(("narrow","narrow")) - # after the swap these widths should be the same - assert old['narrow'].width == new['wide'].width - assert old['wide'].width == new['narrow'].width - # The following test may be a bit counterintuitive: - # the rule swaps the glyphs, but we do not want glyphs that are not - # specifically affected by the rule to *appear* any different. - # So, components have to be remapped. - assert new['wide.component'].components[0].baseGlyph == "narrow" - assert new['narrow.component'].components[0].baseGlyph == "wide" - # Check that anchors swapped - assert new['wide'].anchors[0].y == old['narrow'].anchors[0].y - assert new['narrow'].anchors[0].y == old['wide'].anchors[0].y - -def testUnicodes(docPath, useVarlib=True): - # after executing testSwap there should be some test fonts - # let's check if the unicode values for glyph "narrow" arrive at the right place. - d = DesignSpaceProcessor(useVarlib=useVarlib) - d.read(docPath) - for instance in d.instances: - if os.path.exists(instance.path): - f = fontParts.fontshell.RFont(instance.path) - print("instance.path", instance.path) - print("instance.name", instance.name, "f['narrow'].unicodes", f['narrow'].unicodes) - #if instance.name == "TestFamily-TestStyle_pop1000.000": - # assert f['narrow'].unicodes == [291, 292, 293] - #else: - # #assert f['narrow'].unicodes == [207] - else: - print("Missing test font at %s" % instance.path) - -selfTest = True -if selfTest: - for extension in ['varlib', 'mutator']: - print("\n\n", extension) - USEVARLIBMODEL = extension == 'varlib' - testRoot = os.path.join(os.getcwd(), "automatic_testfonts_%s" % extension) - if os.path.exists(testRoot): - shutil.rmtree(testRoot) - docPath = os.path.join(testRoot, "automatic_test.designspace") - _makeTestDocument(docPath, useVarlib=USEVARLIBMODEL) - _testGenerateInstances(docPath, useVarlib=USEVARLIBMODEL) - - testSwap(docPath) - _makeTestDocument(docPath, useVarlib=USEVARLIBMODEL) - _testGenerateInstances(docPath, useVarlib=USEVARLIBMODEL) - testUnicodes(docPath, useVarlib=USEVARLIBMODEL) diff --git a/testRefactor_RF.py b/testRefactor_RF.py new file mode 100644 index 0000000..e864b4a --- /dev/null +++ b/testRefactor_RF.py @@ -0,0 +1,77 @@ +from random import randint +import ufoProcessor +import ufoProcessor.ufoOperator +import importlib +importlib.reload(ufoProcessor.ufoOperator) +print(ufoProcessor.__file__) +ds5Path = "/Users/erik/code/type2/Principia/sources/Principia_wdth.designspace" +doc = ufoProcessor.ufoOperator.UFOOperator(ds5Path, useVarlib=False, debug=True) +doc.loadFonts() +#doc.generateUFOs() + +def ip(a, b, f): + return a+f*(b-a) + +font = CurrentFont() + +loc = doc.newDefaultLocation() +loc['width'] = randint(50, 100) +print(loc) + +# make some tests at different layers +test = [ + ("foreground", dict(width=75, italic=0), False), + ("background", dict(width=75, italic=1), False), + ("random_width_inter_MM", dict(width=randint(50,100), italic=1), False), + ("random_width_xtr_MM", dict(width=randint(10,150), italic=1), False), + ("random_width_xtr_narrow_VL", dict(width=randint(10,50), italic=1), True), + ("random_width_xtr_wide_VL", dict(width=randint(100,500), italic=1), True), + + ("10_width_xtr_VL", dict(width=10, italic=1), True), + ("10_width_xtr_MM", dict(width=10, italic=1), False), + ("200_width_xtr_wide_VL", dict(width=200, italic=1), True), + ("200_width_xtr_wide_MM", dict(width=200, italic=1), False), + + ("aniso_width_inter_MM", dict(width=(50,100), italic=0), False), + ("aniso_width_inter_VL", dict(width=(50,100), italic=0), True), + + ("aniso_width_xtra_MM", dict(width=(-50,200), italic=0), False), + ("aniso_width_xtra_VL", dict(width=(-50,200), italic=0), True), + + ] + +g = CurrentGlyph() +dstName = g.name + +for layerName, loc, useVarlib in test: + res = doc.makeOneGlyph(dstName, location=loc, bend=True, decomposeComponents=False, useVarlib=useVarlib, roundGeometry=True) + dst = font[dstName].getLayer(layerName) + dst.clear() + res.guidelines = [] # delete guidelines in mathglyph until fontparts issue is solved + dst.fromMathGlyph(res) + dst.width = max(0, res.width) + + print(len(dst.components)) + for comp in dst.components: + print("-- processing baseglyph", comp.baseGlyph) + res2 = doc.makeOneGlyph(comp.baseGlyph, location=loc, bend=True, decomposeComponents=False, useVarlib=useVarlib, roundGeometry=True) + # let's make sure the glyph exists in the layer + print('layerName:', layerName) + dstLayer = font.getLayer(layerName) + if not comp.baseGlyph in dstLayer: + dstLayer.newGlyph(comp.baseGlyph) + dst2 = dstLayer[comp.baseGlyph] + dst2.clear() + print('dst.anchors:', dst.anchors) + print('dst.guidelines:', dst.guidelines) + for item in res2.guidelines: + print(item) + res2.guidelines = [] # delete guidelines in mathglyph until fontparts issue is solved + print('dst.guidelines:', res2.guidelines) + dst2.fromMathGlyph(res2) + dst2.width = max(0, res2.width) + + dst2.update() + dst.update() + +ufoProcessor.refactor.inspectMemoizeCache()