def _write(self): lookups = self._makeKerningLookups() if not lookups: self.log.debug("kerning lookups empty; skipped") return False features = self._makeFeatureBlocks(lookups) if not features: self.log.debug("kerning features empty; skipped") return False # extend feature file with the new generated statements statements = self.context.feaFile.statements # first add the glyph class definitions side1Classes = self.context.kerning.side1Classes side2Classes = self.context.kerning.side2Classes for classes in (side1Classes, side2Classes): statements.extend([c for _, c in sorted(classes.items())]) # add empty line to separate classes from following statements if statements: statements.append(ast.Comment("")) # finally add the lookup and feature blocks for _, lookup in sorted(lookups.items()): statements.append(lookup) if "kern" in features: statements.append(features["kern"]) if "dist" in features: statements.append(features["dist"]) return True
def _write(self): self._pruneUnusedAnchors() newClassDefs = self._makeMarkClassDefinitions() self._setBaseAnchorMarkClasses() features = self._makeFeatures() if not features: return False feaFile = self.context.feaFile feaFile.statements.extend(newClassDefs) # add empty line to separate classes from following statements feaFile.statements.append(ast.Comment("")) for _, feature in sorted(features.items()): feaFile.statements.append(feature) return True
def _insert( self, feaFile, classDefs=None, anchorDefs=None, markClassDefs=None, lookups=None, features=None, ): """ Insert feature, its classDefs or markClassDefs and lookups at insert marker comment. If the insert marker is at the top of a feature block, the feature is inserted before that block, and after if the insert marker is at the bottom. """ statements = feaFile.statements inserted = {} # First handle those with a known location, i.e. insert markers insertComments = self.context.insertComments indices = [] for ix, feature in enumerate(features): if insertComments and feature.name in insertComments: block, comment = insertComments[feature.name] markerIndex = block.statements.index(comment) onlyCommentsBefore = all( isinstance(s, ast.Comment) for s in block.statements[:markerIndex] ) onlyCommentsAfter = all( isinstance(s, ast.Comment) for s in block.statements[markerIndex:] ) # Remove insert marker(s) from feature block. del block.statements[markerIndex] # insertFeatureMarker is in a block with only comments. # Replace that block with new feature block. if onlyCommentsBefore and onlyCommentsAfter: index = statements.index(block) statements.remove(block) # insertFeatureMarker is at the top of a feature block # or only preceded by other comments. elif onlyCommentsBefore: index = statements.index(block) # insertFeatureMarker is at the bottom of a feature block # or only followed by other comments elif onlyCommentsAfter: index = statements.index(block) + 1 # insertFeatureMarker is in the middle of a feature block # preceded and followed by statements that are not comments # # Glyphs3 can insert a feature block when rules are before # and after the insert marker. # See # https://github.com/googlefonts/ufo2ft/issues/351#issuecomment-765294436 # This is currently not supported. else: raise InvalidFeaturesData( "Insert marker has rules before and after, feature " f"{block.name} cannot be inserted. This is not supported." ) statements.insert(index, feature) indices.append(index) inserted[id(feature)] = True # Now walk feature list backwards and insert any dependent features for i in range(ix - 1, -1, -1): if id(features[i]) in inserted: break # Insert this before the current one i.e. at same array index statements.insert(index, features[i]) # All the indices recorded previously have now shifted up by one indices = [index] + [j + 1 for j in indices] inserted[id(features[i])] = True # Finally, deal with any remaining features for feature in features: if id(feature) in inserted: continue index = len(statements) statements.insert(index, feature) indices.append(index) # Write classDefs, anchorsDefs, markClassDefs, lookups at earliest # opportunity. others = [] minindex = min(indices) for defs in [classDefs, anchorDefs, markClassDefs]: if defs: others.extend(defs) others.append(ast.Comment("")) # Insert lookups if lookups: if minindex > 0 and not others: others.append(ast.Comment("")) others.extend(lookups) if others: feaFile.statements = statements = ( statements[:minindex] + others + statements[minindex:] )
def _insert( self, feaFile, classDefs=None, anchorDefs=None, markClassDefs=None, lookups=None, features=None, ): """ Insert feature, its classDefs or markClassDefs and lookups at insert marker comment. If the insert marker is at the top of a feature block, the feature is inserted before that block, and after if the insert marker is at the bottom. """ statements = feaFile.statements # Collect insert markers in blocks insertComments = self.context.insertComments wroteOthers = False for feature in features: if insertComments and feature.name in insertComments: block, comment = insertComments[feature.name] markerIndex = block.statements.index(comment) onlyCommentsBefore = all( isinstance(s, ast.Comment) for s in block.statements[:markerIndex]) onlyCommentsAfter = all( isinstance(s, ast.Comment) for s in block.statements[markerIndex:]) # Remove insert marker(s) from feature block. del block.statements[markerIndex] # insertFeatureMarker is in a block with only comments. # Replace that block with new feature block. if onlyCommentsBefore and onlyCommentsAfter: index = statements.index(block) statements.remove(block) # insertFeatureMarker is at the top of a feature block # or only preceded by other comments. elif onlyCommentsBefore: index = statements.index(block) # insertFeatureMarker is at the bottom of a feature block # or only followed by other comments elif onlyCommentsAfter: index = statements.index(block) + 1 # insertFeatureMarker is in the middle of a feature block # preceded and followed by statements that are not comments # # Glyphs3 can insert a feature block when rules are before # and after the insert marker. # See # https://github.com/googlefonts/ufo2ft/issues/351#issuecomment-765294436 # This is currently not supported. else: raise InvalidFeaturesData( "Insert marker has rules before and after, feature " f"{block.name} cannot be inserted. This is not supported." ) else: index = len(statements) # Write classDefs, anchorsDefs, markClassDefs, lookups on the first # iteration. if not wroteOthers: others = [] # Insert classDefs, anchorsDefs, markClassDefs for defs in [classDefs, anchorDefs, markClassDefs]: if defs: others.extend(defs) others.append(ast.Comment("")) # Insert lookups if lookups: if index > 0 and not others: others.append(ast.Comment("")) others.extend(lookups) if others: feaFile.statements = statements = (statements[:index] + others + statements[index:]) index = index + len(others) wroteOthers = True statements.insert(index, feature)