I have a font file with script latin and script hebrew. Now I want to export one .otf for latin and hebrew together and one .otf for latin only.
But I have a lot of feature code and classes in this font file and managing the export with the custom parameters is very painful. (expecially as the font file will be in development for a longer time range…)
I think, it would be much easier, if feature code within script tags could be omitted on export. Or a mechanism like the conditional feature code for variables. Or if I could write special “versions” of feature tags for the scripts. (ss01#latin, ss01#hebrew e.g.)
Also classes, that are not needed for the not exported scripts, should be simply ignored.
Are there any possibilities yes, that I couldn’t find? Or would this be a feature request?
Ralf has a good point; adding a feature similar to “conditional statements” to other OpenType features could be really beneficial. Instead of using scripts, which can be limiting, we could implement a tagging system. This way, we could tag certain parts of the code and then specify, through a Custom Parameter in the font’s settings, which tags to ignore during export.
Right now, exporting fonts that are subset to specific scripts is quite complicated. It requires manually removing classes and updating features, which is done by using a “Replace Features” CP.
Here’s how it works currently with an example of a stylistic set:
sub a by a.ss01
sub alpha by alpha.ss01
sub a-cy by a-cy.ss01
Exporting a font where Latin and Greek glyphs are removed using this setup would result in an error.
However, if we used conditional code, it would look something like this:
#if LAT_tag
sub a by a.ss01
#end LAT_tag
#if CYR_tag
sub a-cy by a-cy.ss01
#end CYR_tag
#if GRK_tag
sub alpha by alpha.ss01
#end GRK_tag
This way, when exporting a font with only Cyrillic, I could apply a Custom Parameter called “OpenType Ignore Tags” and specify GRK_tag and LAT_tag to be ignored.
I wrote a script to parse OpenType feature code, and keep only code between specific tags.
import re
def keep_code_between_tags(FEATURE_CODE, TAGS):
results_str = ""
for tag in TAGS:
pattern = rf"(?<=#ifdef {tag}\n).*?(?=#endif)"
match = re.search(pattern, FEATURE_CODE, flags=re.DOTALL)
if match:
updated_code += match.group(0) + "\n"
if results_str == "":
return FEATURE_CODE
else:
return updated_code.strip()
TAGS = ["LATIN", "CYRILLIC"]
for feature in Font.features:
if feature.automatic:
print(feature.name, "is autogenerated; no need to update the code manually.")
else:
print(feature.name)
feature_code = feature.code
updated_code = extract_texts_for_tags(feature_code, TAGS)
print(updated_code)
I used .ss in my example (I know that’s not very relevant, since most of the time .ss are automatic, but)
Here is the code of my .ss01 :
#ifdef LATIN
featureNames {
name "Latin ss01";
};
sub A by A.ss01;
#endif
#ifdef CYRILLIC
featureNames {
name "Cyrillic ss01";
};
sub Be-cy by Be-cy.ss01;
#endif
#ifdef GREEK
featureNames {
name "Greek ss01";
};
sub Delta by Delta.ss01;
#endif
Ideally, it should now work with Instance Custom Parameters Keep OTFeature Code for tags. By doing this, to generate a script subset, we will no longer encounter errors due to OTFeature with manual code.
Sounds good. Make that script a Filter plugin and rename the custom parameter to something like “OpenType feature macros’ to make more generally useful.
There is a kind of hack to make it work with Filter plugin ?
I guess that for usual Filter plugin, when some changes are applied to layer, theses changes are only applied in exported layer, not in the source layer.
How can I change the content of Feature codes, only during the export ?
class OpenTypeFeatureMacros(FilterWithoutDialog):
@objc.python_method
def settings(self):
self.menuName = "OpenTypeFeatureMacros"
self.instances_proc = []
@objc.python_method
def filter(self, layer, inEditView, customParameters):
for instance in Glyphs.font.instances:
if instance not in self.instances_pass:
self.instances_pass.append(instance)
Glyphs.font.features["ss01"].code = "sub a by b;"
@objc.python_method
def __file__(self):
"""Please leave this method unchanged"""
return __file__