Reformat with black

This commit is contained in:
Simon Cozens 2020-07-15 14:14:01 +01:00
parent 69a76e9dd8
commit e58fea95ce

View File

@ -72,7 +72,9 @@ def addOpenTypeFeaturesFromString(font, features, filename=None, tables=None):
class Builder(object): class Builder(object):
supportedTables = frozenset(Tag(tag) for tag in [ supportedTables = frozenset(
Tag(tag)
for tag in [
"BASE", "BASE",
"GDEF", "GDEF",
"GPOS", "GPOS",
@ -82,7 +84,8 @@ class Builder(object):
"hhea", "hhea",
"name", "name",
"vhea", "vhea",
]) ]
)
def __init__(self, font, featurefile): def __init__(self, font, featurefile):
self.font = font self.font = font
@ -170,19 +173,20 @@ class Builder(object):
self.build_name() self.build_name()
if "OS/2" in tables: if "OS/2" in tables:
self.build_OS_2() self.build_OS_2()
for tag in ('GPOS', 'GSUB'): for tag in ("GPOS", "GSUB"):
if tag not in tables: if tag not in tables:
continue continue
table = self.makeTable(tag) table = self.makeTable(tag)
if (table.ScriptList.ScriptCount > 0 or if (
table.FeatureList.FeatureCount > 0 or table.ScriptList.ScriptCount > 0
table.LookupList.LookupCount > 0): or table.FeatureList.FeatureCount > 0
or table.LookupList.LookupCount > 0
):
fontTable = self.font[tag] = newTable(tag) fontTable = self.font[tag] = newTable(tag)
fontTable.table = table fontTable.table = table
elif tag in self.font: elif tag in self.font:
del self.font[tag] del self.font[tag]
if (any(tag in self.font for tag in ("GPOS", "GSUB")) and if any(tag in self.font for tag in ("GPOS", "GSUB")) and "OS/2" in self.font:
"OS/2" in self.font):
self.font["OS/2"].usMaxContext = maxCtxFont(self.font) self.font["OS/2"].usMaxContext = maxCtxFont(self.font)
if "GDEF" in tables: if "GDEF" in tables:
gdef = self.buildGDEF() gdef = self.buildGDEF()
@ -210,16 +214,19 @@ class Builder(object):
self.features_.setdefault(key, []).append(lookup) self.features_.setdefault(key, []).append(lookup)
def get_lookup_(self, location, builder_class): def get_lookup_(self, location, builder_class):
if (self.cur_lookup_ and if (
type(self.cur_lookup_) == builder_class and self.cur_lookup_
self.cur_lookup_.lookupflag == self.lookupflag_ and and type(self.cur_lookup_) == builder_class
self.cur_lookup_.markFilterSet == and self.cur_lookup_.lookupflag == self.lookupflag_
self.lookupflag_markFilterSet_): and self.cur_lookup_.markFilterSet == self.lookupflag_markFilterSet_
):
return self.cur_lookup_ return self.cur_lookup_
if self.cur_lookup_name_ and self.cur_lookup_: if self.cur_lookup_name_ and self.cur_lookup_:
raise FeatureLibError( raise FeatureLibError(
"Within a named lookup block, all rules must be of " "Within a named lookup block, all rules must be of "
"the same lookup type and flag", location) "the same lookup type and flag",
location,
)
self.cur_lookup_ = builder_class(self.font, location) self.cur_lookup_ = builder_class(self.font, location)
self.cur_lookup_.lookupflag = self.lookupflag_ self.cur_lookup_.lookupflag = self.lookupflag_
self.cur_lookup_.markFilterSet = self.lookupflag_markFilterSet_ self.cur_lookup_.markFilterSet = self.lookupflag_markFilterSet_
@ -230,8 +237,7 @@ class Builder(object):
if self.cur_feature_name_: if self.cur_feature_name_:
# We are starting a lookup rule inside a feature. This includes # We are starting a lookup rule inside a feature. This includes
# lookup rules inside named lookups inside features. # lookup rules inside named lookups inside features.
self.add_lookup_to_feature_(self.cur_lookup_, self.add_lookup_to_feature_(self.cur_lookup_, self.cur_feature_name_)
self.cur_feature_name_)
return self.cur_lookup_ return self.cur_lookup_
def build_feature_aalt_(self): def build_feature_aalt_(self):
@ -239,14 +245,16 @@ class Builder(object):
return return
alternates = {g: set(a) for g, a in self.aalt_alternates_.items()} alternates = {g: set(a) for g, a in self.aalt_alternates_.items()}
for location, name in self.aalt_features_ + [(None, "aalt")]: for location, name in self.aalt_features_ + [(None, "aalt")]:
feature = [(script, lang, feature, lookups) feature = [
for (script, lang, feature), lookups (script, lang, feature, lookups)
in self.features_.items() for (script, lang, feature), lookups in self.features_.items()
if feature == name] if feature == name
]
# "aalt" does not have to specify its own lookups, but it might. # "aalt" does not have to specify its own lookups, but it might.
if not feature and name != "aalt": if not feature and name != "aalt":
raise FeatureLibError("Feature %s has not been defined" % name, raise FeatureLibError(
location) "Feature %s has not been defined" % name, location
)
for script, lang, feature, lookups in feature: for script, lang, feature, lookups in feature:
for lookuplist in lookups: for lookuplist in lookups:
if not isinstance(lookuplist, list): if not isinstance(lookuplist, list):
@ -254,19 +262,23 @@ class Builder(object):
for lookup in lookuplist: for lookup in lookuplist:
for glyph, alts in lookup.getAlternateGlyphs().items(): for glyph, alts in lookup.getAlternateGlyphs().items():
alternates.setdefault(glyph, set()).update(alts) alternates.setdefault(glyph, set()).update(alts)
single = {glyph: list(repl)[0] for glyph, repl in alternates.items() single = {
if len(repl) == 1} glyph: list(repl)[0] for glyph, repl in alternates.items() if len(repl) == 1
}
# TODO: Figure out the glyph alternate ordering used by makeotf. # TODO: Figure out the glyph alternate ordering used by makeotf.
# https://github.com/fonttools/fonttools/issues/836 # https://github.com/fonttools/fonttools/issues/836
multi = {glyph: sorted(repl, key=self.font.getGlyphID) multi = {
glyph: sorted(repl, key=self.font.getGlyphID)
for glyph, repl in alternates.items() for glyph, repl in alternates.items()
if len(repl) > 1} if len(repl) > 1
}
if not single and not multi: if not single and not multi:
return return
self.features_ = {(script, lang, feature): lookups self.features_ = {
for (script, lang, feature), lookups (script, lang, feature): lookups
in self.features_.items() for (script, lang, feature), lookups in self.features_.items()
if feature != "aalt"} if feature != "aalt"
}
old_lookups = self.lookups_ old_lookups = self.lookups_
self.lookups_ = [] self.lookups_ = []
self.start_feature(self.aalt_location_, "aalt") self.start_feature(self.aalt_location_, "aalt")
@ -333,8 +345,12 @@ class Builder(object):
params = None params = None
if tag == "size": if tag == "size":
params = otTables.FeatureParamsSize() params = otTables.FeatureParamsSize()
params.DesignSize, params.SubfamilyID, params.RangeStart, \ (
params.RangeEnd = self.size_parameters_ params.DesignSize,
params.SubfamilyID,
params.RangeStart,
params.RangeEnd,
) = self.size_parameters_
if tag in self.featureNames_ids_: if tag in self.featureNames_ids_:
params.SubfamilyNameID = self.featureNames_ids_[tag] params.SubfamilyNameID = self.featureNames_ids_[tag]
else: else:
@ -352,14 +368,18 @@ class Builder(object):
params = otTables.FeatureParamsCharacterVariants() params = otTables.FeatureParamsCharacterVariants()
params.Format = 0 params.Format = 0
params.FeatUILabelNameID = self.cv_parameters_ids_.get( params.FeatUILabelNameID = self.cv_parameters_ids_.get(
(tag, 'FeatUILabelNameID'), 0) (tag, "FeatUILabelNameID"), 0
)
params.FeatUITooltipTextNameID = self.cv_parameters_ids_.get( params.FeatUITooltipTextNameID = self.cv_parameters_ids_.get(
(tag, 'FeatUITooltipTextNameID'), 0) (tag, "FeatUITooltipTextNameID"), 0
)
params.SampleTextNameID = self.cv_parameters_ids_.get( params.SampleTextNameID = self.cv_parameters_ids_.get(
(tag, 'SampleTextNameID'), 0) (tag, "SampleTextNameID"), 0
)
params.NumNamedParameters = self.cv_num_named_params_.get(tag, 0) params.NumNamedParameters = self.cv_num_named_params_.get(tag, 0)
params.FirstParamUILabelNameID = self.cv_parameters_ids_.get( params.FirstParamUILabelNameID = self.cv_parameters_ids_.get(
(tag, 'ParamUILabelNameID_0'), 0) (tag, "ParamUILabelNameID_0"), 0
)
params.CharCount = len(self.cv_characters_[tag]) params.CharCount = len(self.cv_characters_[tag])
params.Character = self.cv_characters_[tag] params.Character = self.cv_characters_[tag]
return params return params
@ -402,10 +422,18 @@ class Builder(object):
table.fsType = self.os2_["fstype"] table.fsType = self.os2_["fstype"]
if "panose" in self.os2_: if "panose" in self.os2_:
panose = getTableModule("OS/2").Panose() panose = getTableModule("OS/2").Panose()
panose.bFamilyType, panose.bSerifStyle, panose.bWeight,\ (
panose.bProportion, panose.bContrast, panose.bStrokeVariation,\ panose.bFamilyType,
panose.bArmStyle, panose.bLetterForm, panose.bMidline, \ panose.bSerifStyle,
panose.bXHeight = self.os2_["panose"] panose.bWeight,
panose.bProportion,
panose.bContrast,
panose.bStrokeVariation,
panose.bArmStyle,
panose.bLetterForm,
panose.bMidline,
panose.bXHeight,
) = self.os2_["panose"]
table.panose = panose table.panose = panose
if "typoascender" in self.os2_: if "typoascender" in self.os2_:
table.sTypoAscender = self.os2_["typoascender"] table.sTypoAscender = self.os2_["typoascender"]
@ -441,28 +469,63 @@ class Builder(object):
if "upperopsize" in self.os2_: if "upperopsize" in self.os2_:
table.usUpperOpticalPointSize = self.os2_["upperopsize"] table.usUpperOpticalPointSize = self.os2_["upperopsize"]
version = 5 version = 5
def checkattr(table, attrs): def checkattr(table, attrs):
for attr in attrs: for attr in attrs:
if not hasattr(table, attr): if not hasattr(table, attr):
setattr(table, attr, 0) setattr(table, attr, 0)
table.version = max(version, table.version) table.version = max(version, table.version)
# this only happens for unit tests # this only happens for unit tests
if version >= 1: if version >= 1:
checkattr(table, ("ulCodePageRange1", "ulCodePageRange2")) checkattr(table, ("ulCodePageRange1", "ulCodePageRange2"))
if version >= 2: if version >= 2:
checkattr(table, ("sxHeight", "sCapHeight", "usDefaultChar", checkattr(
"usBreakChar", "usMaxContext")) table,
(
"sxHeight",
"sCapHeight",
"usDefaultChar",
"usBreakChar",
"usMaxContext",
),
)
if version >= 5: if version >= 5:
checkattr(table, ("usLowerOpticalPointSize", checkattr(table, ("usLowerOpticalPointSize", "usUpperOpticalPointSize"))
"usUpperOpticalPointSize"))
def build_codepages_(self, pages): def build_codepages_(self, pages):
pages2bits = { pages2bits = {
1252: 0, 1250: 1, 1251: 2, 1253: 3, 1254: 4, 1255: 5, 1256: 6, 1252: 0,
1257: 7, 1258: 8, 874: 16, 932: 17, 936: 18, 949: 19, 950: 20, 1250: 1,
1361: 21, 869: 48, 866: 49, 865: 50, 864: 51, 863: 52, 862: 53, 1251: 2,
861: 54, 860: 55, 857: 56, 855: 57, 852: 58, 775: 59, 737: 60, 1253: 3,
708: 61, 850: 62, 437: 63, 1254: 4,
1255: 5,
1256: 6,
1257: 7,
1258: 8,
874: 16,
932: 17,
936: 18,
949: 19,
950: 20,
1361: 21,
869: 48,
866: 49,
865: 50,
864: 51,
863: 52,
862: 53,
861: 54,
860: 55,
857: 56,
855: 57,
852: 58,
775: 59,
737: 60,
708: 61,
850: 62,
437: 63,
} }
bits = [pages2bits[p] for p in pages if p in pages2bits] bits = [pages2bits[p] for p in pages if p in pages2bits]
pages = [] pages = []
@ -518,16 +581,22 @@ class Builder(object):
def buildGDEF(self): def buildGDEF(self):
gdef = otTables.GDEF() gdef = otTables.GDEF()
gdef.GlyphClassDef = self.buildGDEFGlyphClassDef_() gdef.GlyphClassDef = self.buildGDEFGlyphClassDef_()
gdef.AttachList = \ gdef.AttachList = otl.buildAttachList(self.attachPoints_, self.glyphMap)
otl.buildAttachList(self.attachPoints_, self.glyphMap) gdef.LigCaretList = otl.buildLigCaretList(
gdef.LigCaretList = \ self.ligCaretCoords_, self.ligCaretPoints_, self.glyphMap
otl.buildLigCaretList(self.ligCaretCoords_, self.ligCaretPoints_, )
self.glyphMap)
gdef.MarkAttachClassDef = self.buildGDEFMarkAttachClassDef_() gdef.MarkAttachClassDef = self.buildGDEFMarkAttachClassDef_()
gdef.MarkGlyphSetsDef = self.buildGDEFMarkGlyphSetsDef_() gdef.MarkGlyphSetsDef = self.buildGDEFMarkGlyphSetsDef_()
gdef.Version = 0x00010002 if gdef.MarkGlyphSetsDef else 0x00010000 gdef.Version = 0x00010002 if gdef.MarkGlyphSetsDef else 0x00010000
if any((gdef.GlyphClassDef, gdef.AttachList, gdef.LigCaretList, if any(
gdef.MarkAttachClassDef, gdef.MarkGlyphSetsDef)): (
gdef.GlyphClassDef,
gdef.AttachList,
gdef.LigCaretList,
gdef.MarkAttachClassDef,
gdef.MarkGlyphSetsDef,
)
):
result = newTable("GDEF") result = newTable("GDEF")
result.table = gdef result.table = gdef
return result return result
@ -562,13 +631,14 @@ class Builder(object):
def buildGDEFMarkGlyphSetsDef_(self): def buildGDEFMarkGlyphSetsDef_(self):
sets = [] sets = []
for glyphs, id_ in sorted(self.markFilterSets_.items(), for glyphs, id_ in sorted(
key=lambda item: item[1]): self.markFilterSets_.items(), key=lambda item: item[1]
):
sets.append(glyphs) sets.append(glyphs)
return otl.buildMarkGlyphSetsDef(sets, self.glyphMap) return otl.buildMarkGlyphSetsDef(sets, self.glyphMap)
def buildLookups_(self, tag): def buildLookups_(self, tag):
assert tag in ('GPOS', 'GSUB'), tag assert tag in ("GPOS", "GSUB"), tag
for lookup in self.lookups_: for lookup in self.lookups_:
lookup.lookup_index = None lookup.lookup_index = None
lookups = [] lookups = []
@ -606,10 +676,11 @@ class Builder(object):
# l.lookup_index will be None when a lookup is not needed # l.lookup_index will be None when a lookup is not needed
# for the table under construction. For example, substitution # for the table under construction. For example, substitution
# rules will have no lookup_index while building GPOS tables. # rules will have no lookup_index while building GPOS tables.
lookup_indices = tuple([l.lookup_index for l in lookups lookup_indices = tuple(
if l.lookup_index is not None]) [l.lookup_index for l in lookups if l.lookup_index is not None]
)
size_feature = (tag == "GPOS" and feature_tag == "size") size_feature = tag == "GPOS" and feature_tag == "size"
if len(lookup_indices) == 0 and not size_feature: if len(lookup_indices) == 0 and not size_feature:
continue continue
@ -620,14 +691,12 @@ class Builder(object):
frec = otTables.FeatureRecord() frec = otTables.FeatureRecord()
frec.FeatureTag = feature_tag frec.FeatureTag = feature_tag
frec.Feature = otTables.Feature() frec.Feature = otTables.Feature()
frec.Feature.FeatureParams = self.buildFeatureParams( frec.Feature.FeatureParams = self.buildFeatureParams(feature_tag)
feature_tag)
frec.Feature.LookupListIndex = list(lookup_indices) frec.Feature.LookupListIndex = list(lookup_indices)
frec.Feature.LookupCount = len(lookup_indices) frec.Feature.LookupCount = len(lookup_indices)
table.FeatureList.FeatureRecord.append(frec) table.FeatureList.FeatureRecord.append(frec)
feature_indices[feature_key] = feature_index feature_indices[feature_key] = feature_index
scripts.setdefault(script, {}).setdefault(lang, []).append( scripts.setdefault(script, {}).setdefault(lang, []).append(feature_index)
feature_index)
if self.required_features_.get((script, lang)) == feature_tag: if self.required_features_.get((script, lang)) == feature_tag:
required_feature_indices[(script, lang)] = feature_index required_feature_indices[(script, lang)] = feature_index
@ -643,17 +712,16 @@ class Builder(object):
langrec.LangSys = otTables.LangSys() langrec.LangSys = otTables.LangSys()
langrec.LangSys.LookupOrder = None langrec.LangSys.LookupOrder = None
req_feature_index = \ req_feature_index = required_feature_indices.get((script, lang))
required_feature_indices.get((script, lang))
if req_feature_index is None: if req_feature_index is None:
langrec.LangSys.ReqFeatureIndex = 0xFFFF langrec.LangSys.ReqFeatureIndex = 0xFFFF
else: else:
langrec.LangSys.ReqFeatureIndex = req_feature_index langrec.LangSys.ReqFeatureIndex = req_feature_index
langrec.LangSys.FeatureIndex = [i for i in feature_indices langrec.LangSys.FeatureIndex = [
if i != req_feature_index] i for i in feature_indices if i != req_feature_index
langrec.LangSys.FeatureCount = \ ]
len(langrec.LangSys.FeatureIndex) langrec.LangSys.FeatureCount = len(langrec.LangSys.FeatureIndex)
if lang == "dflt": if lang == "dflt":
srec.Script.DefaultLangSys = langrec.LangSys srec.Script.DefaultLangSys = langrec.LangSys
@ -670,24 +738,27 @@ class Builder(object):
def add_language_system(self, location, script, language): def add_language_system(self, location, script, language):
# OpenType Feature File Specification, section 4.b.i # OpenType Feature File Specification, section 4.b.i
if (script == "DFLT" and language == "dflt" and if script == "DFLT" and language == "dflt" and self.default_language_systems_:
self.default_language_systems_):
raise FeatureLibError( raise FeatureLibError(
'If "languagesystem DFLT dflt" is present, it must be ' 'If "languagesystem DFLT dflt" is present, it must be '
'the first of the languagesystem statements', location) "the first of the languagesystem statements",
location,
)
if script == "DFLT": if script == "DFLT":
if self.seen_non_DFLT_script_: if self.seen_non_DFLT_script_:
raise FeatureLibError( raise FeatureLibError(
'languagesystems using the "DFLT" script tag must ' 'languagesystems using the "DFLT" script tag must '
"precede all other languagesystems", "precede all other languagesystems",
location location,
) )
else: else:
self.seen_non_DFLT_script_ = True self.seen_non_DFLT_script_ = True
if (script, language) in self.default_language_systems_: if (script, language) in self.default_language_systems_:
raise FeatureLibError( raise FeatureLibError(
'"languagesystem %s %s" has already been specified' % '"languagesystem %s %s" has already been specified'
(script.strip(), language.strip()), location) % (script.strip(), language.strip()),
location,
)
self.default_language_systems_.add((script, language)) self.default_language_systems_.add((script, language))
def get_default_language_systems_(self): def get_default_language_systems_(self):
@ -699,11 +770,11 @@ class Builder(object):
if self.default_language_systems_: if self.default_language_systems_:
return frozenset(self.default_language_systems_) return frozenset(self.default_language_systems_)
else: else:
return frozenset({('DFLT', 'dflt')}) return frozenset({("DFLT", "dflt")})
def start_feature(self, location, name): def start_feature(self, location, name):
self.language_systems = self.get_default_language_systems_() self.language_systems = self.get_default_language_systems_()
self.script_ = 'DFLT' self.script_ = "DFLT"
self.cur_lookup_ = None self.cur_lookup_ = None
self.cur_feature_name_ = name self.cur_feature_name_ = name
self.lookupflag_ = 0 self.lookupflag_ = 0
@ -722,12 +793,14 @@ class Builder(object):
def start_lookup_block(self, location, name): def start_lookup_block(self, location, name):
if name in self.named_lookups_: if name in self.named_lookups_:
raise FeatureLibError( raise FeatureLibError(
'Lookup "%s" has already been defined' % name, location) 'Lookup "%s" has already been defined' % name, location
)
if self.cur_feature_name_ == "aalt": if self.cur_feature_name_ == "aalt":
raise FeatureLibError( raise FeatureLibError(
"Lookup blocks cannot be placed inside 'aalt' features; " "Lookup blocks cannot be placed inside 'aalt' features; "
"move it out, and then refer to it with a lookup statement", "move it out, and then refer to it with a lookup statement",
location) location,
)
self.cur_lookup_name_ = name self.cur_lookup_name_ = name
self.named_lookups_[name] = None self.named_lookups_[name] = None
self.cur_lookup_ = None self.cur_lookup_ = None
@ -753,20 +826,24 @@ class Builder(object):
self.fontRevision_ = revision self.fontRevision_ = revision
def set_language(self, location, language, include_default, required): def set_language(self, location, language, include_default, required):
assert(len(language) == 4) assert len(language) == 4
if self.cur_feature_name_ in ('aalt', 'size'): if self.cur_feature_name_ in ("aalt", "size"):
raise FeatureLibError( raise FeatureLibError(
"Language statements are not allowed " "Language statements are not allowed "
"within \"feature %s\"" % self.cur_feature_name_, location) 'within "feature %s"' % self.cur_feature_name_,
location,
)
if self.cur_feature_name_ is None: if self.cur_feature_name_ is None:
raise FeatureLibError( raise FeatureLibError(
"Language statements are not allowed " "Language statements are not allowed "
"within standalone lookup blocks", location) "within standalone lookup blocks",
location,
)
self.cur_lookup_ = None self.cur_lookup_ = None
key = (self.script_, language, self.cur_feature_name_) key = (self.script_, language, self.cur_feature_name_)
lookups = self.features_.get((key[0], 'dflt', key[2])) lookups = self.features_.get((key[0], "dflt", key[2]))
if (language == 'dflt' or include_default) and lookups: if (language == "dflt" or include_default) and lookups:
self.features_[key] = lookups[:] self.features_[key] = lookups[:]
else: else:
self.features_[key] = [] self.features_[key] = []
@ -777,10 +854,14 @@ class Builder(object):
if key in self.required_features_: if key in self.required_features_:
raise FeatureLibError( raise FeatureLibError(
"Language %s (script %s) has already " "Language %s (script %s) has already "
"specified feature %s as its required feature" % ( "specified feature %s as its required feature"
language.strip(), self.script_.strip(), % (
self.required_features_[key].strip()), language.strip(),
location) self.script_.strip(),
self.required_features_[key].strip(),
),
location,
)
self.required_features_[key] = self.cur_feature_name_ self.required_features_[key] = self.cur_feature_name_
def getMarkAttachClass_(self, location, glyphs): def getMarkAttachClass_(self, location, glyphs):
@ -796,7 +877,8 @@ class Builder(object):
raise FeatureLibError( raise FeatureLibError(
"Glyph %s already has been assigned " "Glyph %s already has been assigned "
"a MarkAttachmentType at %s" % (glyph, loc), "a MarkAttachmentType at %s" % (glyph, loc),
location) location,
)
self.markAttach_[glyph] = (id_, location) self.markAttach_[glyph] = (id_, location)
return id_ return id_
@ -823,23 +905,25 @@ class Builder(object):
self.lookupflag_ = value self.lookupflag_ = value
def set_script(self, location, script): def set_script(self, location, script):
if self.cur_feature_name_ in ('aalt', 'size'): if self.cur_feature_name_ in ("aalt", "size"):
raise FeatureLibError( raise FeatureLibError(
"Script statements are not allowed " "Script statements are not allowed "
"within \"feature %s\"" % self.cur_feature_name_, location) 'within "feature %s"' % self.cur_feature_name_,
location,
)
if self.cur_feature_name_ is None: if self.cur_feature_name_ is None:
raise FeatureLibError( raise FeatureLibError(
"Script statements are not allowed " "Script statements are not allowed " "within standalone lookup blocks",
"within standalone lookup blocks", location) location,
if self.language_systems == {(script, 'dflt')}: )
if self.language_systems == {(script, "dflt")}:
# Nothing to do. # Nothing to do.
return return
self.cur_lookup_ = None self.cur_lookup_ = None
self.script_ = script self.script_ = script
self.lookupflag_ = 0 self.lookupflag_ = 0
self.lookupflag_markFilterSet_ = None self.lookupflag_markFilterSet_ = None
self.set_language(location, "dflt", self.set_language(location, "dflt", include_default=True, required=False)
include_default=True, required=False)
def find_lookup_builders_(self, lookups): def find_lookup_builders_(self, lookups):
"""Helper for building chain contextual substitutions """Helper for building chain contextual substitutions
@ -850,8 +934,9 @@ class Builder(object):
lookup_builders = [] lookup_builders = []
for lookuplist in lookups: for lookuplist in lookups:
if lookuplist is not None: if lookuplist is not None:
lookup_builders.append([self.named_lookups_.get(l.name) lookup_builders.append(
for l in lookuplist]) [self.named_lookups_.get(l.name) for l in lookuplist]
)
else: else:
lookup_builders.append(None) lookup_builders.append(None)
return lookup_builders return lookup_builders
@ -862,17 +947,17 @@ class Builder(object):
def add_chain_context_pos(self, location, prefix, glyphs, suffix, lookups): def add_chain_context_pos(self, location, prefix, glyphs, suffix, lookups):
lookup = self.get_lookup_(location, ChainContextPosBuilder) lookup = self.get_lookup_(location, ChainContextPosBuilder)
lookup.rules.append((prefix, glyphs, suffix, lookup.rules.append(
self.find_lookup_builders_(lookups))) (prefix, glyphs, suffix, self.find_lookup_builders_(lookups))
)
def add_chain_context_subst(self, location, def add_chain_context_subst(self, location, prefix, glyphs, suffix, lookups):
prefix, glyphs, suffix, lookups):
lookup = self.get_lookup_(location, ChainContextSubstBuilder) lookup = self.get_lookup_(location, ChainContextSubstBuilder)
lookup.rules.append((prefix, glyphs, suffix, lookup.rules.append(
self.find_lookup_builders_(lookups))) (prefix, glyphs, suffix, self.find_lookup_builders_(lookups))
)
def add_alternate_subst(self, location, def add_alternate_subst(self, location, prefix, glyph, suffix, replacement):
prefix, glyph, suffix, replacement):
if self.cur_feature_name_ == "aalt": if self.cur_feature_name_ == "aalt":
alts = self.aalt_alternates_.setdefault(glyph, set()) alts = self.aalt_alternates_.setdefault(glyph, set())
alts.update(replacement) alts.update(replacement)
@ -885,15 +970,15 @@ class Builder(object):
lookup = self.get_lookup_(location, AlternateSubstBuilder) lookup = self.get_lookup_(location, AlternateSubstBuilder)
if glyph in lookup.alternates: if glyph in lookup.alternates:
raise FeatureLibError( raise FeatureLibError(
'Already defined alternates for glyph "%s"' % glyph, 'Already defined alternates for glyph "%s"' % glyph, location
location) )
lookup.alternates[glyph] = replacement lookup.alternates[glyph] = replacement
def add_feature_reference(self, location, featureName): def add_feature_reference(self, location, featureName):
if self.cur_feature_name_ != "aalt": if self.cur_feature_name_ != "aalt":
raise FeatureLibError( raise FeatureLibError(
'Feature references are only allowed inside "feature aalt"', 'Feature references are only allowed inside "feature aalt"', location
location) )
self.aalt_features_.append((location, featureName)) self.aalt_features_.append((location, featureName))
def add_featureName(self, tag): def add_featureName(self, tag):
@ -919,19 +1004,23 @@ class Builder(object):
else: else:
self.base_horiz_axis_ = (bases, scripts) self.base_horiz_axis_ = (bases, scripts)
def set_size_parameters(self, location, DesignSize, SubfamilyID, def set_size_parameters(
RangeStart, RangeEnd): self, location, DesignSize, SubfamilyID, RangeStart, RangeEnd
if self.cur_feature_name_ != 'size': ):
if self.cur_feature_name_ != "size":
raise FeatureLibError( raise FeatureLibError(
"Parameters statements are not allowed " "Parameters statements are not allowed "
"within \"feature %s\"" % self.cur_feature_name_, location) 'within "feature %s"' % self.cur_feature_name_,
location,
)
self.size_parameters_ = [DesignSize, SubfamilyID, RangeStart, RangeEnd] self.size_parameters_ = [DesignSize, SubfamilyID, RangeStart, RangeEnd]
for script, lang in self.language_systems: for script, lang in self.language_systems:
key = (script, lang, self.cur_feature_name_) key = (script, lang, self.cur_feature_name_)
self.features_.setdefault(key, []) self.features_.setdefault(key, [])
def add_ligature_subst(self, location, def add_ligature_subst(
prefix, glyphs, suffix, replacement, forceChain): self, location, prefix, glyphs, suffix, replacement, forceChain
):
if prefix or suffix or forceChain: if prefix or suffix or forceChain:
chain = self.get_lookup_(location, ChainContextSubstBuilder) chain = self.get_lookup_(location, ChainContextSubstBuilder)
lookup = self.get_chained_lookup_(location, LigatureSubstBuilder) lookup = self.get_chained_lookup_(location, LigatureSubstBuilder)
@ -947,8 +1036,9 @@ class Builder(object):
for g in sorted(itertools.product(*glyphs)): for g in sorted(itertools.product(*glyphs)):
lookup.ligatures[g] = replacement lookup.ligatures[g] = replacement
def add_multiple_subst(self, location, def add_multiple_subst(
prefix, glyph, suffix, replacements, forceChain=False): self, location, prefix, glyph, suffix, replacements, forceChain=False
):
if prefix or suffix or forceChain: if prefix or suffix or forceChain:
chain = self.get_lookup_(location, ChainContextSubstBuilder) chain = self.get_lookup_(location, ChainContextSubstBuilder)
sub = self.get_chained_lookup_(location, MultipleSubstBuilder) sub = self.get_chained_lookup_(location, MultipleSubstBuilder)
@ -959,19 +1049,19 @@ class Builder(object):
if glyph in lookup.mapping: if glyph in lookup.mapping:
if replacements == lookup.mapping[glyph]: if replacements == lookup.mapping[glyph]:
log.info( log.info(
'Removing duplicate multiple substitution from glyph' "Removing duplicate multiple substitution from glyph"
' "%s" to %s%s', ' "%s" to %s%s',
glyph, replacements, glyph,
f' at {location}' if location else '', replacements,
f" at {location}" if location else "",
) )
else: else:
raise FeatureLibError( raise FeatureLibError(
'Already defined substitution for glyph "%s"' % glyph, 'Already defined substitution for glyph "%s"' % glyph, location
location) )
lookup.mapping[glyph] = replacements lookup.mapping[glyph] = replacements
def add_reverse_chain_single_subst(self, location, old_prefix, def add_reverse_chain_single_subst(self, location, old_prefix, old_suffix, mapping):
old_suffix, mapping):
lookup = self.get_lookup_(location, ReverseChainSingleSubstBuilder) lookup = self.get_lookup_(location, ReverseChainSingleSubstBuilder)
lookup.rules.append((old_prefix, old_suffix, mapping)) lookup.rules.append((old_prefix, old_suffix, mapping))
@ -989,15 +1079,18 @@ class Builder(object):
if from_glyph in lookup.mapping: if from_glyph in lookup.mapping:
if to_glyph == lookup.mapping[from_glyph]: if to_glyph == lookup.mapping[from_glyph]:
log.info( log.info(
'Removing duplicate single substitution from glyph' "Removing duplicate single substitution from glyph"
' "%s" to "%s" at %s', ' "%s" to "%s" at %s',
from_glyph, to_glyph, location, from_glyph,
to_glyph,
location,
) )
else: else:
raise FeatureLibError( raise FeatureLibError(
'Already defined rule for replacing glyph "%s" by "%s"' % 'Already defined rule for replacing glyph "%s" by "%s"'
(from_glyph, lookup.mapping[from_glyph]), % (from_glyph, lookup.mapping[from_glyph]),
location) location,
)
lookup.mapping[from_glyph] = to_glyph lookup.mapping[from_glyph] = to_glyph
def add_single_subst_chained_(self, location, prefix, suffix, mapping): def add_single_subst_chained_(self, location, prefix, suffix, mapping):
@ -1012,9 +1105,11 @@ class Builder(object):
def add_cursive_pos(self, location, glyphclass, entryAnchor, exitAnchor): def add_cursive_pos(self, location, glyphclass, entryAnchor, exitAnchor):
lookup = self.get_lookup_(location, CursivePosBuilder) lookup = self.get_lookup_(location, CursivePosBuilder)
lookup.add_attachment( lookup.add_attachment(
location, glyphclass, location,
glyphclass,
makeOpenTypeAnchor(entryAnchor), makeOpenTypeAnchor(entryAnchor),
makeOpenTypeAnchor(exitAnchor)) makeOpenTypeAnchor(exitAnchor),
)
def add_marks_(self, location, lookupBuilder, marks): def add_marks_(self, location, lookupBuilder, marks):
"""Helper for add_mark_{base,liga,mark}_pos.""" """Helper for add_mark_{base,liga,mark}_pos."""
@ -1023,15 +1118,15 @@ class Builder(object):
for mark in markClassDef.glyphs.glyphSet(): for mark in markClassDef.glyphs.glyphSet():
if mark not in lookupBuilder.marks: if mark not in lookupBuilder.marks:
otMarkAnchor = makeOpenTypeAnchor(markClassDef.anchor) otMarkAnchor = makeOpenTypeAnchor(markClassDef.anchor)
lookupBuilder.marks[mark] = ( lookupBuilder.marks[mark] = (markClass.name, otMarkAnchor)
markClass.name, otMarkAnchor)
else: else:
existingMarkClass = lookupBuilder.marks[mark][0] existingMarkClass = lookupBuilder.marks[mark][0]
if markClass.name != existingMarkClass: if markClass.name != existingMarkClass:
raise FeatureLibError( raise FeatureLibError(
"Glyph %s cannot be in both @%s and @%s" % ( "Glyph %s cannot be in both @%s and @%s"
mark, existingMarkClass, markClass.name), % (mark, existingMarkClass, markClass.name),
location) location,
)
def add_mark_base_pos(self, location, bases, marks): def add_mark_base_pos(self, location, bases, marks):
builder = self.get_lookup_(location, MarkBasePosBuilder) builder = self.get_lookup_(location, MarkBasePosBuilder)
@ -1039,8 +1134,7 @@ class Builder(object):
for baseAnchor, markClass in marks: for baseAnchor, markClass in marks:
otBaseAnchor = makeOpenTypeAnchor(baseAnchor) otBaseAnchor = makeOpenTypeAnchor(baseAnchor)
for base in bases: for base in bases:
builder.bases.setdefault(base, {})[markClass.name] = ( builder.bases.setdefault(base, {})[markClass.name] = otBaseAnchor
otBaseAnchor)
def add_mark_lig_pos(self, location, ligatures, components): def add_mark_lig_pos(self, location, ligatures, components):
builder = self.get_lookup_(location, MarkLigPosBuilder) builder = self.get_lookup_(location, MarkLigPosBuilder)
@ -1060,11 +1154,11 @@ class Builder(object):
for baseAnchor, markClass in marks: for baseAnchor, markClass in marks:
otBaseAnchor = makeOpenTypeAnchor(baseAnchor) otBaseAnchor = makeOpenTypeAnchor(baseAnchor)
for baseMark in baseMarks: for baseMark in baseMarks:
builder.baseMarks.setdefault(baseMark, {})[markClass.name] = ( builder.baseMarks.setdefault(baseMark, {})[
otBaseAnchor) markClass.name
] = otBaseAnchor
def add_class_pair_pos(self, location, glyphclass1, value1, def add_class_pair_pos(self, location, glyphclass1, value1, glyphclass2, value2):
glyphclass2, value2):
lookup = self.get_lookup_(location, PairPosBuilder) lookup = self.get_lookup_(location, PairPosBuilder)
v1 = makeOpenTypeValueRecord(value1, pairPosContext=True) v1 = makeOpenTypeValueRecord(value1, pairPosContext=True)
v2 = makeOpenTypeValueRecord(value2, pairPosContext=True) v2 = makeOpenTypeValueRecord(value2, pairPosContext=True)
@ -1112,20 +1206,21 @@ class Builder(object):
sub.add_pos(location, glyph, otValue) sub.add_pos(location, glyph, otValue)
subs.append(sub) subs.append(sub)
assert len(pos) == len(subs), (pos, subs) assert len(pos) == len(subs), (pos, subs)
chain.rules.append( chain.rules.append((prefix, [g for g, v in pos], suffix, subs))
(prefix, [g for g, v in pos], suffix, subs))
def setGlyphClass_(self, location, glyph, glyphClass): def setGlyphClass_(self, location, glyph, glyphClass):
oldClass, oldLocation = self.glyphClassDefs_.get(glyph, (None, None)) oldClass, oldLocation = self.glyphClassDefs_.get(glyph, (None, None))
if oldClass and oldClass != glyphClass: if oldClass and oldClass != glyphClass:
raise FeatureLibError( raise FeatureLibError(
"Glyph %s was assigned to a different class at %s" % "Glyph %s was assigned to a different class at %s"
(glyph, oldLocation), % (glyph, oldLocation),
location) location,
)
self.glyphClassDefs_[glyph] = (glyphClass, location) self.glyphClassDefs_[glyph] = (glyphClass, location)
def add_glyphClassDef(self, location, baseGlyphs, ligatureGlyphs, def add_glyphClassDef(
markGlyphs, componentGlyphs): self, location, baseGlyphs, ligatureGlyphs, markGlyphs, componentGlyphs
):
for glyph in baseGlyphs: for glyph in baseGlyphs:
self.setGlyphClass_(location, glyph, 1) self.setGlyphClass_(location, glyph, 1)
for glyph in ligatureGlyphs: for glyph in ligatureGlyphs:
@ -1145,8 +1240,7 @@ class Builder(object):
if glyph not in self.ligCaretCoords_: if glyph not in self.ligCaretCoords_:
self.ligCaretCoords_[glyph] = carets self.ligCaretCoords_[glyph] = carets
def add_name_record(self, location, nameID, platformID, platEncID, def add_name_record(self, location, nameID, platformID, platEncID, langID, string):
langID, string):
self.names_.append([nameID, platformID, platEncID, langID, string]) self.names_.append([nameID, platformID, platEncID, langID, string])
def add_os2_field(self, key, value): def add_os2_field(self, key, value):
@ -1168,8 +1262,7 @@ def makeOpenTypeAnchor(anchor):
deviceX = otl.buildDevice(dict(anchor.xDeviceTable)) deviceX = otl.buildDevice(dict(anchor.xDeviceTable))
if anchor.yDeviceTable is not None: if anchor.yDeviceTable is not None:
deviceY = otl.buildDevice(dict(anchor.yDeviceTable)) deviceY = otl.buildDevice(dict(anchor.yDeviceTable))
return otl.buildAnchor(anchor.x, anchor.y, anchor.contourpoint, return otl.buildAnchor(anchor.x, anchor.y, anchor.contourpoint, deviceX, deviceY)
deviceX, deviceY)
_VALUEREC_ATTRS = { _VALUEREC_ATTRS = {
@ -1193,6 +1286,3 @@ def makeOpenTypeValueRecord(v, pairPosContext):
vr = {"YAdvance": 0} if v.vertical else {"XAdvance": 0} vr = {"YAdvance": 0} if v.vertical else {"XAdvance": 0}
valRec = otl.buildValue(vr) valRec = otl.buildValue(vr)
return valRec return valRec