Hi, I am working on exactly this at the moment, but trying to solve it with a script which generates a file with the extra master that is needed in order to provide the variable font origin.
After exporting, you can use the app Slicer to create a subset variable font: https://slice-gui.netlify.app/
Here’s my code if you want to play around with it, it is extremely WIP. It tries to take care of glyphs with alternates as well, as these are not automatically added when running Instance as Master:
# MenuTitle: Create Variable Subset File
# -*- coding: utf-8 -*-
__doc__ = """
Creates a file to export a subset variable font.
"""
import vanilla
class CreateSubset:
def __init__(self):
if Font is None:
Message("No font selected", "Select a font project!")
return
self.font = Font
self.axisRanges = {axis.name: sorted(set(master.axes[i] for master in self.font.masters))
for i, axis in enumerate(self.font.axes)}
for axis in self.axisRanges:
del self.axisRanges[axis][1:-1]
self.variable_font_origin_master = self.font.masters[0]
for master in Font.masters:
if master.id == self.font.customParameters["Variable Font Origin"]:
self.variable_font_origin_master = master
self.subsetValues = {}
self.w = vanilla.FloatingWindow((0, 0), "Create Variable Subset File")
self.ypos = 10
self.w.newNameTitle = vanilla.TextBox((10, self.ypos, -10, 14), "Family name", sizeStyle="small")
self.w.newNameEntry = vanilla.EditText((90, self.ypos, -10, 19), text=self.font.familyName,
sizeStyle="small", callback=self.check_name)
self.ypos += 30
self.w.divider = vanilla.HorizontalLine((10, self.ypos, -10, 1))
self.ypos += 10
self.w.MinTitle = vanilla.TextBox((60, self.ypos, 40, 14), "Min", alignment="center", sizeStyle="small")
self.w.toTitle = vanilla.TextBox((110, self.ypos, 40, 14), "Max", alignment="center", sizeStyle="small")
self.w.defaultTitle = vanilla.TextBox((150, self.ypos, 60, 14), "Default", alignment="center",
sizeStyle="small")
self.ypos += 20
for i, axis in enumerate(self.font.axes):
axis_title = vanilla.TextBox((10, self.ypos, -10, 17), axis.name, sizeStyle="small")
axis_min = vanilla.EditText((60, self.ypos, 40, 19), text=self.axisRanges[axis.name][0], sizeStyle="small",
callback=self.define_axis_ranges)
axis_from = vanilla.EditText((110, self.ypos, 40, 19), text=self.axisRanges[axis.name][1], sizeStyle="small",
callback=self.define_axis_ranges)
separator = vanilla.TextBox((95, self.ypos, 20, 17), ":", alignment="center")
bracketleft = vanilla.TextBox((145, self.ypos, 20, 17), "[", alignment="center")
axis_default = vanilla.EditText((160, self.ypos, 40, 19),
text=self.variable_font_origin_master.axes[i], sizeStyle="small",
callback=self.define_axis_ranges)
bracketright = vanilla.TextBox((195, self.ypos, 20, 17), "]", alignment="center")
reset_button = vanilla.SquareButton((210, self.ypos + 2, 17, 17), u"↺", sizeStyle="small",
callback=self.reset_value)
setattr(self.w, axis.axisTag, axis_title)
setattr(self.w, axis.axisTag + "Min", axis_min)
setattr(self.w, axis.axisTag + "Max", axis_from)
setattr(self.w, axis.axisTag + "Separator", separator)
setattr(self.w, axis.axisTag + "Bracketleft", bracketleft)
setattr(self.w, axis.axisTag + "Default", axis_default)
setattr(self.w, axis.axisTag + "Bracketright", bracketright)
setattr(self.w, axis.axisTag + "Button", reset_button)
self.subsetValues[axis.name] = [int(axis_min.get()),
int(axis_from.get()),
int(axis_default.get())]
self.ypos += 24
self.ypos += 10
self.w.makeButton = vanilla.Button((10, self.ypos, -10, 17), "Make file", callback=self.make_subset_file)
self.w.makeButton.enable(self.font.familyName != self.w.newNameEntry.get())
self.ypos += 27
self.w.setDefaultButton(self.w.makeButton)
self.w.resize(237, self.ypos)
self.w.open()
self.w.makeKey()
def check_name(self, sender):
self.w.makeButton.enable(self.font.familyName != self.w.newNameEntry.get())
def define_axis_ranges(self, sender):
for axis in self.font.axes:
self.subsetValues[axis.name] = [int(getattr(self.w, axis.axisTag + "Min").get() or 0),
int(getattr(self.w, axis.axisTag + "Max").get() or 0),
int(getattr(self.w, axis.axisTag + "Default").get() or 0)]
def reset_value(self, sender):
for i, axis in enumerate(self.font.axes):
if sender is getattr(self.w, axis.axisTag + "Button"):
getattr(self.w, axis.axisTag + "Min").set(self.axisRanges[axis.name][0])
getattr(self.w, axis.axisTag + "Max").set(self.axisRanges[axis.name][1])
getattr(self.w, axis.axisTag + "Default").set(self.variable_font_origin_master.axes[i])
def master_manager(self):
for master in self.font.masters:
match_count = 0
for i, axis in enumerate(self.font.axes):
if self.subsetValues[axis.name][2] == master.axes[i]:
match_count += 1
if match_count == len(self.font.axes):
found_origin_master = master
break
try:
print("origin", found_origin_master)
except:
origin_instance = GSInstance()
origin_instance.name = "Variable Font Origin"
self.font.instances.append(origin_instance)
self.font.instances[-1].axes = [self.subsetValues[axis][2] for axis in self.subsetValues]
self.font.instances[-1].addAsMaster()
self.font.masters[-1].axes = [self.subsetValues[axis][2] for axis in self.subsetValues]
# removeList = []
# try:
# for master in self.font.masters:
# for i, axisValue in enumerate(master.axes):
# if axisValue < self.subsetValues[self.font.axes[i].name][0] or axisValue > self.subsetValues[
# self.font.axes[i].name][1]:
# removeList.append(master)
# continue
# for removeMaster in removeList:
# # self.font.masters.remove(removeMaster)
# print("remove", removeMaster)
# except Exception as e:
# print(e)
for instance in self.font.instances:
if instance.type:
for fontInfo in instance.properties:
if fontInfo.key == "familyNames":
for value in fontInfo.values:
if value.languageTag == "dflt":
value.value = self.w.newNameEntry.get()
instance.customParameters["fileName"] = self.w.newNameEntry.get().replace(" ", "-")
def fix_special_layers(self):
self.font.disableUpdateInterface()
for glyph in Font.glyphs:
if glyph.mastersCompatible:
continue
print(glyph)
backup_glyph = glyph.duplicate(name=glyph.name + ".specialLayers")
for layer in backup_glyph.layers:
if layer.isSpecialLayer:
copy_layer = layer.copy()
del copy_layer.attributes["axisRules"]
backup_glyph.layers[layer.associatedMasterId] = copy_layer
for layer in backup_glyph.layers:
if layer.name == "Variable Font Origin":
layer.reinterpolate()
final_layer = layer
final_layer.attributes["axisRules"] = glyph.layers[-1].attributes["axisRules"]
glyph.layers.append(final_layer.copy())
del Font.glyphs[glyph.name + ".specialLayers"]
self.font.enableUpdateInterface()
def make_subset_file(self, sender):
self.master_manager()
self.font.save(path=self.font.filepath.replace(self.font.filepath.split("/")[-1],
self.w.newNameEntry.get() + ".glyphs"))
self.fix_special_layers()
self.font.save()
CreateSubset()