diff --git a/Lib/fontTools/feaLib/ast.py b/Lib/fontTools/feaLib/ast.py index 53b100ec7..5bf5bbcfa 100644 --- a/Lib/fontTools/feaLib/ast.py +++ b/Lib/fontTools/feaLib/ast.py @@ -84,7 +84,6 @@ def deviceToString(device): return "" % ", ".join("%d %d" % t for t in device) - fea_keywords = set( [ "anchor", @@ -260,7 +259,7 @@ class GlyphClass(Expression): def add_range(self, start, end, glyphs): """Add a range (e.g. ``A-Z``) to the class. ``start`` and ``end`` - are either :class:`GlyphName` objects or strings representing the + are either :class:`GlyphName` objects or strings representing the start and end glyphs in the class, and ``glyphs`` is the full list of :class:`GlyphName` objects in the range.""" if self.curr < len(self.glyphs): @@ -555,7 +554,7 @@ class MarkClass(object): class MarkClassDefinition(Statement): - """A single ``markClass`` statement. The ``markClass`` should be a + """A single ``markClass`` statement. The ``markClass`` should be a :class:`MarkClass` object, the ``anchor`` an :class:`Anchor` object, and the ``glyphs`` parameter should be a `glyph-containing object`_ . @@ -857,7 +856,7 @@ class IgnorePosStatement(Statement): """An ``ignore pos`` statement, containing `one or more` contexts to ignore. ``chainContexts`` should be a list of ``(prefix, glyphs, suffix)`` tuples, - with each of ``prefix``, ``glyphs`` and ``suffix`` being + with each of ``prefix``, ``glyphs`` and ``suffix`` being `glyph-containing objects`_ .""" def __init__(self, chainContexts, location=None): @@ -1173,7 +1172,7 @@ class MarkLigPosStatement(Statement): # ... add definitions to mark classes... glyph = GlyphName("lam_meem_jeem") - marks = [ + marks = [ [ (Anchor(625,1800), m1) ], # Attachments on 1st component (lam) [ (Anchor(376,-378), m2) ], # Attachments on 2nd component (meem) [ ] # No attachments on the jeem @@ -1910,6 +1909,7 @@ class STATDesignAxisStatement(Statement): axisOrder (int): an int names (list): a list of :class:`STATNameStatement` objects """ + def __init__(self, tag, axisOrder, names, location=None): Statement.__init__(self, location) self.tag = tag @@ -1923,8 +1923,7 @@ class STATDesignAxisStatement(Statement): def asFea(self, indent=""): indent += SHIFT res = f"DesignAxis {self.tag} {self.axisOrder} {{ \n" - res += ("\n" + indent).join([s.asFea(indent=indent) for s in - self.names]) + "\n" + res += ("\n" + indent).join([s.asFea(indent=indent) for s in self.names]) + "\n" res += "};" return res @@ -1935,6 +1934,7 @@ class ElidedFallbackName(Statement): Args: names: a list of :class:`STATNameStatement` objects """ + def __init__(self, names, location=None): Statement.__init__(self, location) self.names = names @@ -1946,8 +1946,7 @@ class ElidedFallbackName(Statement): def asFea(self, indent=""): indent += SHIFT res = "ElidedFallbackName { \n" - res += ("\n" + indent).join([s.asFea(indent=indent) for s in - self.names]) + "\n" + res += ("\n" + indent).join([s.asFea(indent=indent) for s in self.names]) + "\n" res += "};" return res @@ -1958,6 +1957,7 @@ class ElidedFallbackNameID(Statement): Args: value: an int pointing to an existing name table name ID """ + def __init__(self, value, location=None): Statement.__init__(self, location) self.value = value @@ -1978,6 +1978,7 @@ class STATAxisValueStatement(Statement): locations (list): a list of :class:`AxisValueLocationStatement` objects flags (int): an int """ + def __init__(self, names, locations, flags, location=None): Statement.__init__(self, location) self.names = names @@ -2017,6 +2018,7 @@ class AxisValueLocationStatement(Statement): tag (str): a 4 letter axis tag values (list): a list of ints and/or floats """ + def __init__(self, tag, values, location=None): Statement.__init__(self, location) self.tag = tag diff --git a/Lib/fontTools/feaLib/builder.py b/Lib/fontTools/feaLib/builder.py index ff03b79ab..ae81c9ff9 100644 --- a/Lib/fontTools/feaLib/builder.py +++ b/Lib/fontTools/feaLib/builder.py @@ -551,11 +551,13 @@ class Builder(object): self.stat_["AxisValueRecords"] = [] # Check for duplicate AxisValueRecords for record_ in self.stat_["AxisValueRecords"]: - if ({n.asFea() for n in record_.names} == - {n.asFea() for n in axisValueRecord.names} and - {n.asFea() for n in record_.locations} == - {n.asFea() for n in axisValueRecord.locations} - and record_.flags == axisValueRecord.flags): + if ( + {n.asFea() for n in record_.names} + == {n.asFea() for n in axisValueRecord.names} + and {n.asFea() for n in record_.locations} + == {n.asFea() for n in axisValueRecord.locations} + and record_.flags == axisValueRecord.flags + ): raise FeatureLibError( "An AxisValueRecord with these values is already defined.", location, @@ -568,7 +570,7 @@ class Builder(object): axes = self.stat_.get("DesignAxes") if not axes: - raise FeatureLibError('DesignAxes not defined', None) + raise FeatureLibError("DesignAxes not defined", None) axisValueRecords = self.stat_.get("AxisValueRecords") axisValues = {} format4_locations = [] @@ -578,52 +580,74 @@ class Builder(object): for avr in axisValueRecords: valuesDict = {} if avr.flags > 0: - valuesDict['flags'] = avr.flags + valuesDict["flags"] = avr.flags if len(avr.locations) == 1: location = avr.locations[0] values = location.values - if len(values) == 1: #format1 - valuesDict.update({'value': values[0],'name': avr.names}) - if len(values) == 2: #format3 - valuesDict.update({ 'value': values[0], - 'linkedValue': values[1], - 'name': avr.names}) - if len(values) == 3: #format2 + if len(values) == 1: # format1 + valuesDict.update({"value": values[0], "name": avr.names}) + if len(values) == 2: # format3 + valuesDict.update( + { + "value": values[0], + "linkedValue": values[1], + "name": avr.names, + } + ) + if len(values) == 3: # format2 nominal, minVal, maxVal = values - valuesDict.update({ 'nominalValue': nominal, - 'rangeMinValue': minVal, - 'rangeMaxValue': maxVal, - 'name': avr.names}) + valuesDict.update( + { + "nominalValue": nominal, + "rangeMinValue": minVal, + "rangeMaxValue": maxVal, + "name": avr.names, + } + ) axisValues[location.tag].append(valuesDict) else: - valuesDict.update({"location": {i.tag: i.values[0] - for i in avr.locations}, - "name": avr.names}) + valuesDict.update( + { + "location": {i.tag: i.values[0] for i in avr.locations}, + "name": avr.names, + } + ) format4_locations.append(valuesDict) - designAxes = [{"ordering": a.axisOrder, - "tag": a.tag, - "name": a.names, - 'values': axisValues[a.tag]} for a in axes] - + designAxes = [ + { + "ordering": a.axisOrder, + "tag": a.tag, + "name": a.names, + "values": axisValues[a.tag], + } + for a in axes + ] + nameTable = self.font.get("name") if not nameTable: # this only happens for unit tests nameTable = self.font["name"] = newTable("name") nameTable.names = [] if "ElidedFallbackNameID" in self.stat_: - nameID = self.stat_["ElidedFallbackNameID"] - name = nameTable.getDebugName(nameID) + nameID = self.stat_["ElidedFallbackNameID"] + name = nameTable.getDebugName(nameID) if not name: - raise FeatureLibError(f'ElidedFallbackNameID {nameID} points ' - 'to a nameID that does not exist in the ' - '"name" table', None) + raise FeatureLibError( + f"ElidedFallbackNameID {nameID} points " + "to a nameID that does not exist in the " + '"name" table', + None, + ) elif "ElidedFallbackName" in self.stat_: - nameID = self.stat_["ElidedFallbackName"] - - otl.buildStatTable(self.font, designAxes, locations=format4_locations, - elidedFallbackName=nameID) + nameID = self.stat_["ElidedFallbackName"] + otl.buildStatTable( + self.font, + designAxes, + locations=format4_locations, + elidedFallbackName=nameID, + ) def build_codepages_(self, pages): pages2bits = { @@ -833,8 +857,10 @@ class Builder(object): str(ix) ]._replace(feature=key) except KeyError: - warnings.warn("feaLib.Builder subclass needs upgrading to " - "stash debug information. See fonttools#2065.") + warnings.warn( + "feaLib.Builder subclass needs upgrading to " + "stash debug information. See fonttools#2065." + ) feature_key = (feature_tag, lookup_indices) feature_index = feature_indices.get(feature_key) diff --git a/Lib/fontTools/feaLib/parser.py b/Lib/fontTools/feaLib/parser.py index f638c68c4..c248c3409 100644 --- a/Lib/fontTools/feaLib/parser.py +++ b/Lib/fontTools/feaLib/parser.py @@ -1322,32 +1322,36 @@ class Parser(object): if self.is_cur_keyword_("name"): platformID, platEncID, langID, string = self.parse_stat_name_() nameRecord = self.ast.STATNameStatement( - "stat", platformID, platEncID, langID, string, - location=self.cur_token_location_ + "stat", + platformID, + platEncID, + langID, + string, + location=self.cur_token_location_, ) names.append(nameRecord) else: if self.cur_token_ != ";": - raise FeatureLibError(f"Unexpected token {self.cur_token_} " - f"in ElidedFallbackName", - self.cur_token_location_) + raise FeatureLibError( + f"Unexpected token {self.cur_token_} " f"in ElidedFallbackName", + self.cur_token_location_, + ) self.expect_symbol_("}") if not names: - raise FeatureLibError('Expected "name"', - self.cur_token_location_) + raise FeatureLibError('Expected "name"', self.cur_token_location_) return names def parse_STAT_design_axis(self): - assert self.is_cur_keyword_('DesignAxis') + assert self.is_cur_keyword_("DesignAxis") names = [] axisTag = self.expect_tag_() - if (axisTag not in ('ital', 'opsz', 'slnt', 'wdth', 'wght') - and not axisTag.isupper()): - log.warning( - f'Unregistered axis tag {axisTag} should be uppercase.' - ) + if ( + axisTag not in ("ital", "opsz", "slnt", "wdth", "wght") + and not axisTag.isupper() + ): + log.warning(f"Unregistered axis tag {axisTag} should be uppercase.") axisOrder = self.expect_number_() - self.expect_symbol_('{') + self.expect_symbol_("{") while self.next_token_ != "}" or self.cur_comments_: self.advance_lexer_() if self.cur_token_type_ is Lexer.COMMENT: @@ -1362,11 +1366,14 @@ class Parser(object): elif self.cur_token_ == ";": continue else: - raise FeatureLibError(f'Expected "name", got {self.cur_token_}', - self.cur_token_location_) + raise FeatureLibError( + f'Expected "name", got {self.cur_token_}', self.cur_token_location_ + ) self.expect_symbol_("}") - return self.ast.STATDesignAxisStatement(axisTag, axisOrder, names, self.cur_token_location_) + return self.ast.STATDesignAxisStatement( + axisTag, axisOrder, names, self.cur_token_location_ + ) def parse_STAT_axis_value_(self): assert self.is_cur_keyword_("AxisValue") @@ -1393,39 +1400,45 @@ class Parser(object): elif self.cur_token_ == ";": continue else: - raise FeatureLibError(f"Unexpected token {self.cur_token_} " - f"in AxisValue", self.cur_token_location_) + raise FeatureLibError( + f"Unexpected token {self.cur_token_} " f"in AxisValue", + self.cur_token_location_, + ) self.expect_symbol_("}") if not names: - raise FeatureLibError('Expected "Axis Name"', - self.cur_token_location_) + raise FeatureLibError('Expected "Axis Name"', self.cur_token_location_) if not locations: - raise FeatureLibError('Expected "Axis location"', - self.cur_token_location_) + raise FeatureLibError('Expected "Axis location"', self.cur_token_location_) if len(locations) > 1: for location in locations: if len(location.values) > 1: - raise FeatureLibError("Only one value is allowed in a " - "Format 4 Axis Value Record, but " - f"{len(location.values)} were found.", - self.cur_token_location_) + raise FeatureLibError( + "Only one value is allowed in a " + "Format 4 Axis Value Record, but " + f"{len(location.values)} were found.", + self.cur_token_location_, + ) format4_tags = [] for location in locations: tag = location.tag if tag in format4_tags: - raise FeatureLibError(f"Axis tag {tag} already " - "defined.", - self.cur_token_location_) + raise FeatureLibError( + f"Axis tag {tag} already " "defined.", self.cur_token_location_ + ) format4_tags.append(tag) - return self.ast.STATAxisValueStatement(names, locations, flags, self.cur_token_location_) + return self.ast.STATAxisValueStatement( + names, locations, flags, self.cur_token_location_ + ) def parse_STAT_location(self): values = [] tag = self.expect_tag_() if len(tag.strip()) != 4: - raise FeatureLibError(f"Axis tag {self.cur_token_} must be 4 " - "characters", self.cur_token_location_) + raise FeatureLibError( + f"Axis tag {self.cur_token_} must be 4 " "characters", + self.cur_token_location_, + ) while self.next_token_ != ";": if self.next_token_type_ is Lexer.FLOAT: @@ -1435,16 +1448,20 @@ class Parser(object): value = self.expect_number_() values.append(value) else: - raise FeatureLibError(f'Unexpected value "{self.next_token_}". ' - 'Expected integer or float.', - self.next_token_location_) + raise FeatureLibError( + f'Unexpected value "{self.next_token_}". ' + "Expected integer or float.", + self.next_token_location_, + ) if len(values) == 3: nominal, min_val, max_val = values if nominal < min_val or nominal > max_val: - raise FeatureLibError(f'Default value {nominal} is outside ' - f'of specified range ' - f'{min_val}-{max_val}.', - self.next_token_location_) + raise FeatureLibError( + f"Default value {nominal} is outside " + f"of specified range " + f"{min_val}-{max_val}.", + self.next_token_location_, + ) return self.ast.AxisValueLocationStatement(tag, values) def parse_table_STAT_(self, table): @@ -1475,14 +1492,16 @@ class Parser(object): if location.tag not in design_axes: # Tag must be defined in a DesignAxis before it # can be referenced - raise FeatureLibError("DesignAxis not defined for " - f"{location.tag}.", - self.cur_token_location_) + raise FeatureLibError( + "DesignAxis not defined for " f"{location.tag}.", + self.cur_token_location_, + ) statements.append(axisValueRecord) self.expect_symbol_(";") else: - raise FeatureLibError(f"Unexpected token {self.cur_token_}", - self.cur_token_location_) + raise FeatureLibError( + f"Unexpected token {self.cur_token_}", self.cur_token_location_ + ) elif self.cur_token_ == ";": continue @@ -2056,8 +2075,7 @@ class Parser(object): return self.expect_number_() / 10 else: raise FeatureLibError( - "Expected an integer or floating-point number", - self.cur_token_location_ + "Expected an integer or floating-point number", self.cur_token_location_ ) def expect_stat_flags(self): @@ -2072,8 +2090,7 @@ class Parser(object): value = value | flags[name] else: raise FeatureLibError( - f"Unexpected STAT flag {self.cur_token_}", - self.cur_token_location_ + f"Unexpected STAT flag {self.cur_token_}", self.cur_token_location_ ) return value @@ -2084,8 +2101,7 @@ class Parser(object): return self.expect_number_() else: raise FeatureLibError( - "Expected an integer or floating-point number", - self.cur_token_location_ + "Expected an integer or floating-point number", self.cur_token_location_ ) def expect_string_(self): diff --git a/Lib/fontTools/otlLib/builder.py b/Lib/fontTools/otlLib/builder.py index ab27463a6..ca9e936d6 100644 --- a/Lib/fontTools/otlLib/builder.py +++ b/Lib/fontTools/otlLib/builder.py @@ -95,9 +95,10 @@ def buildLookup(subtables, flags=0, markFilterSet=None): subtables = [st for st in subtables if st is not None] if not subtables: return None - assert all(t.LookupType == subtables[0].LookupType for t in subtables), ( - "all subtables must have the same LookupType; got %s" - % repr([t.LookupType for t in subtables]) + assert all( + t.LookupType == subtables[0].LookupType for t in subtables + ), "all subtables must have the same LookupType; got %s" % repr( + [t.LookupType for t in subtables] ) self = ot.Lookup() self.LookupType = subtables[0].LookupType @@ -2576,7 +2577,9 @@ class ClassDefBuilder(object): self.classes_.add(glyphs) for glyph in glyphs: if glyph in self.glyphs_: - raise OpenTypeLibError(f"Glyph {glyph} is already present in class.", None) + raise OpenTypeLibError( + f"Glyph {glyph} is already present in class.", None + ) self.glyphs_[glyph] = glyphs def classes(self): @@ -2688,7 +2691,7 @@ def buildStatTable(ttFont, axes, locations=None, elidedFallbackName=2): ] The optional 'elidedFallbackName' argument can be a name ID (int), - a string, a dictionary containing multilingual names, or a list of + a string, a dictionary containing multilingual names, or a list of STATNameStatements. It translates to the ElidedFallbackNameID field. The 'ttFont' argument must be a TTFont instance that already has a @@ -2802,9 +2805,13 @@ def _addName(nameTable, value, minNameID=0): nameID = nameTable._findUnusedNameID() for nameRecord in value: if isinstance(nameRecord, STATNameStatement): - nameTable.setName(nameRecord.string, - nameID,nameRecord.platformID, - nameRecord.platEncID,nameRecord.langID) + nameTable.setName( + nameRecord.string, + nameID, + nameRecord.platformID, + nameRecord.platEncID, + nameRecord.langID, + ) else: raise TypeError("value must be a list of STATNameStatements") return nameID