def normalizeLocation(location, axes): """This behaves different from varLib.models.normalizeLocation in that it won't add missing axes values and doesn't filter values that aren't in the axes dict. """ return { axisName: normalizeValue(v, axes.get(axisName, (-1, 0, 1))) for axisName, v in location.items() }
def _normalized_location(self, location): location = self.fix_location(location) normalized_location = {} for axtag in location.keys(): if axtag not in self.axes_dict: raise ValueError("Unknown axis %s in %s" % (axtag, location)) axis = self.axes_dict[axtag] normalized_location[axtag] = normalizeValue( location[axtag], (axis.minValue, axis.defaultValue, axis.maxValue) ) return Location(normalized_location)
def _normalized_location(self, location): normalized_location = {} for axtag in location.keys(): if axtag not in self.axes_dict: raise ValueError("Unknown axis %s in %s" % axtag, location) axis = self.axes_dict[axtag] normalized_location[axtag] = normalizeValue( location[axtag], (axis.minimum, axis.default, axis.maximum)) for ax in self.axes: if ax.tag not in normalized_location: normalized_location[ax.tag] = 0 return NormalizedLocation(normalized_location)
def normalizeLocation(doc, location): # Adapted from DesignSpaceDocument.normalizeLocation(), which takes axis # names, yet we need to work with tags here. # Also, the original takes design space coordinates, whereas I have user # space coordinates here. new = {} for axis in doc.axes: if axis.tag not in location: # skipping this dimension it seems continue value = axis.map_forward(location[axis.tag]) triple = [ axis.map_forward(v) for v in (axis.minimum, axis.default, axis.maximum) ] new[axis.tag] = normalizeValue(value, triple) return new
def _add_avar(font, axes): """ Add 'avar' table to font. axes is an ordered dictionary of DesignspaceAxis objects. """ assert axes assert isinstance(axes, OrderedDict) log.info("Generating avar") avar = newTable('avar') interesting = False for axis in axes.values(): # Currently, some rasterizers require that the default value maps # (-1 to -1, 0 to 0, and 1 to 1) be present for all the segment # maps, even when the default normalization mapping for the axis # was not modified. # https://github.com/googlei18n/fontmake/issues/295 # https://github.com/fonttools/fonttools/issues/1011 # TODO(anthrotype) revert this (and 19c4b37) when issue is fixed curve = avar.segments[axis.tag] = {-1.0: -1.0, 0.0: 0.0, 1.0: 1.0} if not axis.map: continue items = sorted(axis.map.items()) keys = [item[0] for item in items] vals = [item[1] for item in items] # Current avar requirements. We don't have to enforce # these on the designer and can deduce some ourselves, # but for now just enforce them. assert axis.minimum == min(keys) assert axis.maximum == max(keys) assert axis.default in keys # No duplicates assert len(set(keys)) == len(keys) assert len(set(vals)) == len(vals) # Ascending values assert sorted(vals) == vals keys_triple = (axis.minimum, axis.default, axis.maximum) vals_triple = tuple(axis.map_forward(v) for v in keys_triple) keys = [models.normalizeValue(v, keys_triple) for v in keys] vals = [models.normalizeValue(v, vals_triple) for v in vals] if all(k == v for k, v in zip(keys, vals)): continue interesting = True curve.update(zip(keys, vals)) assert 0.0 in curve and curve[0.0] == 0.0 assert -1.0 not in curve or curve[-1.0] == -1.0 assert +1.0 not in curve or curve[+1.0] == +1.0 # curve.update({-1.0: -1.0, 0.0: 0.0, 1.0: 1.0}) assert "avar" not in font if not interesting: log.info("No need for avar") avar = None else: font['avar'] = avar return avar
def _add_avar(font, axes): """ Add 'avar' table to font. axes is an ordered dictionary of AxisDescriptor objects. """ assert axes assert isinstance(axes, OrderedDict) log.info("Generating avar") avar = newTable('avar') interesting = False for axis in axes.values(): # Currently, some rasterizers require that the default value maps # (-1 to -1, 0 to 0, and 1 to 1) be present for all the segment # maps, even when the default normalization mapping for the axis # was not modified. # https://github.com/googlei18n/fontmake/issues/295 # https://github.com/fonttools/fonttools/issues/1011 # TODO(anthrotype) revert this (and 19c4b37) when issue is fixed curve = avar.segments[axis.tag] = {-1.0: -1.0, 0.0: 0.0, 1.0: 1.0} if not axis.map: continue items = sorted(axis.map) keys = [item[0] for item in items] vals = [item[1] for item in items] # Current avar requirements. We don't have to enforce # these on the designer and can deduce some ourselves, # but for now just enforce them. if axis.minimum != min(keys): raise VarLibValidationError( f"Axis '{axis.name}': there must be a mapping for the axis minimum " f"value {axis.minimum} and it must be the lowest input mapping value." ) if axis.maximum != max(keys): raise VarLibValidationError( f"Axis '{axis.name}': there must be a mapping for the axis maximum " f"value {axis.maximum} and it must be the highest input mapping value." ) if axis.default not in keys: raise VarLibValidationError( f"Axis '{axis.name}': there must be a mapping for the axis default " f"value {axis.default}.") # No duplicate input values (output values can be >= their preceeding value). if len(set(keys)) != len(keys): raise VarLibValidationError( f"Axis '{axis.name}': All axis mapping input='...' values must be " "unique, but we found duplicates.") # Ascending values if sorted(vals) != vals: raise VarLibValidationError( f"Axis '{axis.name}': mapping output values must be in ascending order." ) keys_triple = (axis.minimum, axis.default, axis.maximum) vals_triple = tuple(axis.map_forward(v) for v in keys_triple) keys = [models.normalizeValue(v, keys_triple) for v in keys] vals = [models.normalizeValue(v, vals_triple) for v in vals] if all(k == v for k, v in zip(keys, vals)): continue interesting = True curve.update(zip(keys, vals)) assert 0.0 in curve and curve[0.0] == 0.0 assert -1.0 not in curve or curve[-1.0] == -1.0 assert +1.0 not in curve or curve[+1.0] == +1.0 # curve.update({-1.0: -1.0, 0.0: 0.0, 1.0: 1.0}) assert "avar" not in font if not interesting: log.info("No need for avar") avar = None else: font['avar'] = avar return avar
def normalize(value, triple, avarMapping): value = normalizeValue(value, triple) if avarMapping: value = piecewiseLinearMap(value, avarMapping) # Quantize to F2Dot14, to avoid surprise interpolations. return floatToFixedToFloat(value, 14)
def _add_avar(font, axes): """ Add 'avar' table to font. axes is an ordered dictionary of AxisDescriptor objects. """ assert axes assert isinstance(axes, OrderedDict) log.info("Generating avar") avar = newTable('avar') interesting = False for axis in axes.values(): # Currently, some rasterizers require that the default value maps # (-1 to -1, 0 to 0, and 1 to 1) be present for all the segment # maps, even when the default normalization mapping for the axis # was not modified. # https://github.com/googlei18n/fontmake/issues/295 # https://github.com/fonttools/fonttools/issues/1011 # TODO(anthrotype) revert this (and 19c4b37) when issue is fixed curve = avar.segments[axis.tag] = {-1.0: -1.0, 0.0: 0.0, 1.0: 1.0} if not axis.map: continue items = sorted(axis.map) keys = [item[0] for item in items] vals = [item[1] for item in items] # Current avar requirements. We don't have to enforce # these on the designer and can deduce some ourselves, # but for now just enforce them. assert axis.minimum == min(keys) assert axis.maximum == max(keys) assert axis.default in keys # No duplicates assert len(set(keys)) == len(keys) assert len(set(vals)) == len(vals) # Ascending values assert sorted(vals) == vals keys_triple = (axis.minimum, axis.default, axis.maximum) vals_triple = tuple(axis.map_forward(v) for v in keys_triple) keys = [models.normalizeValue(v, keys_triple) for v in keys] vals = [models.normalizeValue(v, vals_triple) for v in vals] if all(k == v for k, v in zip(keys, vals)): continue interesting = True curve.update(zip(keys, vals)) assert 0.0 in curve and curve[0.0] == 0.0 assert -1.0 not in curve or curve[-1.0] == -1.0 assert +1.0 not in curve or curve[+1.0] == +1.0 # curve.update({-1.0: -1.0, 0.0: 0.0, 1.0: 1.0}) assert "avar" not in font if not interesting: log.info("No need for avar") avar = None else: font['avar'] = avar return avar
def _add_fvar_avar(font, axes, instances): """ Add 'fvar' table to font. axes is an ordered dictionary of DesignspaceAxis objects. instances is list of dictionary objects with 'location', 'stylename', and possibly 'postscriptfontname' entries. """ assert axes assert isinstance(axes, OrderedDict) log.info("Generating fvar / avar") fvar = newTable('fvar') nameTable = font['name'] for a in axes.values(): axis = Axis() axis.axisTag = Tag(a.tag) axis.minValue, axis.defaultValue, axis.maxValue = a.minimum, a.default, a.maximum axis.axisNameID = nameTable.addName(tounicode(a.labelname['en'])) # TODO: # Replace previous line with the following when the following issues are resolved: # https://github.com/fonttools/fonttools/issues/930 # https://github.com/fonttools/fonttools/issues/931 # axis.axisNameID = nameTable.addMultilingualName(a.labelname, font) fvar.axes.append(axis) for instance in instances: coordinates = instance['location'] name = tounicode(instance['stylename']) psname = instance.get('postscriptfontname') inst = NamedInstance() inst.subfamilyNameID = nameTable.addName(name) if psname is not None: psname = tounicode(psname) inst.postscriptNameID = nameTable.addName(psname) inst.coordinates = { axes[k].tag: axes[k].map_backward(v) for k, v in coordinates.items() } fvar.instances.append(inst) avar = newTable('avar') interesting = False for axis in axes.values(): curve = avar.segments[axis.tag] = {} if not axis.map or all(k == v for k, v in axis.map.items()): continue interesting = True items = sorted(axis.map.items()) keys = [item[0] for item in items] vals = [item[1] for item in items] # Current avar requirements. We don't have to enforce # these on the designer and can deduce some ourselves, # but for now just enforce them. assert axis.minimum == min(keys) assert axis.maximum == max(keys) assert axis.default in keys # No duplicates assert len(set(keys)) == len(keys) assert len(set(vals)) == len(vals) # Ascending values assert sorted(vals) == vals keys_triple = (axis.minimum, axis.default, axis.maximum) vals_triple = tuple(axis.map_forward(v) for v in keys_triple) keys = [models.normalizeValue(v, keys_triple) for v in keys] vals = [models.normalizeValue(v, vals_triple) for v in vals] curve.update(zip(keys, vals)) if not interesting: log.info("No need for avar") avar = None assert "fvar" not in font font['fvar'] = fvar assert "avar" not in font if avar: font['avar'] = avar return fvar, avar
def _add_fvar_avar(font, axes, instances): """ Add 'fvar' table to font. axes is an ordered dictionary of DesignspaceAxis objects. instances is list of dictionary objects with 'location', 'stylename', and possibly 'postscriptfontname' entries. """ assert axes assert isinstance(axes, OrderedDict) log.info("Generating fvar / avar") fvar = newTable('fvar') nameTable = font['name'] for a in axes.values(): axis = Axis() axis.axisTag = Tag(a.tag) axis.minValue, axis.defaultValue, axis.maxValue = a.minimum, a.default, a.maximum axis.axisNameID = nameTable.addName(tounicode(a.labelname['en'])) # TODO: # Replace previous line with the following when the following issues are resolved: # https://github.com/fonttools/fonttools/issues/930 # https://github.com/fonttools/fonttools/issues/931 # axis.axisNameID = nameTable.addMultilingualName(a.labelname, font) fvar.axes.append(axis) for instance in instances: coordinates = instance['location'] name = tounicode(instance['stylename']) psname = instance.get('postscriptfontname') inst = NamedInstance() inst.subfamilyNameID = nameTable.addName(name) if psname is not None: psname = tounicode(psname) inst.postscriptNameID = nameTable.addName(psname) inst.coordinates = {axes[k].tag:axes[k].map_backward(v) for k,v in coordinates.items()} fvar.instances.append(inst) avar = newTable('avar') interesting = False for axis in axes.values(): curve = avar.segments[axis.tag] = {} if not axis.map or all(k==v for k,v in axis.map.items()): continue interesting = True items = sorted(axis.map.items()) keys = [item[0] for item in items] vals = [item[1] for item in items] # Current avar requirements. We don't have to enforce # these on the designer and can deduce some ourselves, # but for now just enforce them. assert axis.minimum == min(keys) assert axis.maximum == max(keys) assert axis.default in keys # No duplicates assert len(set(keys)) == len(keys) assert len(set(vals)) == len(vals) # Ascending values assert sorted(vals) == vals keys_triple = (axis.minimum, axis.default, axis.maximum) vals_triple = tuple(axis.map_forward(v) for v in keys_triple) keys = [models.normalizeValue(v, keys_triple) for v in keys] vals = [models.normalizeValue(v, vals_triple) for v in vals] curve.update(zip(keys, vals)) if not interesting: log.info("No need for avar") avar = None assert "fvar" not in font font['fvar'] = fvar assert "avar" not in font if avar: font['avar'] = avar return fvar,avar