Fonttools: [varLib.instancer] L4) moving the default location of an axis

Created on 25 Jan 2021  Â·  5Comments  Â·  Source: fonttools/fonttools

Hey hey. I just wanted to bring the pending L4 feature to your attention.
I think the instancer is already a handy function and will be very useful in the future. But implementing "L4) Move the default position of an axis" is definitely something that would make the tool even more useable in the workflow.

Is anyone currently working on L4? And is there anything I can do to further the implementation efforts?

Most helpful comment

I've been thinking about this. And like to see it happen. Solving the general-case can be a bit hairy to think about. But I think is doable.

I looked at the instancer code. Currently, using the following function, it splits desired subset designspace into axes that got "pinned" and those that remain a range:

def splitAxisLocationAndRanges(axisLimits, rangeType=AxisRange):
    location, axisRanges = {}, {}
    for axisTag, value in axisLimits.items():
        if isinstance(value, rangeType):
            axisRanges[axisTag] = value
        elif isinstance(value, (int, float)):
            location[axisTag] = value
        elif isinstance(value, tuple):
            axisRanges[axisTag] = rangeType(*value)
        else:
            raise TypeError(
                f"Expected number or {rangeType.__name__}, "
                f"got {type(value).__name__}: {value!r}"
            )
    return location, axisRanges

to support L4, we would allow an axis to be returned in both of those sets, both as "pinned" to new default, as well as carrying a range.

Then we need to update the entire code for that change.

A utility function which I thought of, that might make the code easier to navigate and understand, is a function that then takes the output of the above function as well as a delta+support, and returns a list of delta+support, as well as the "bias" to add to the value being variated. This is similar to limitTupleVariationAxisRange, but can be written more generally and reused.

Perhaps those should be added right into varLib.models.

All 5 comments

Hi, thanks for filing this

Is anyone currently working on L4?

No, I don't think so.

implementing "L4) Move the default position of an axis" is definitely something that would make the tool even more useable

could you please elaborate on that? I'm curious to understand how you'd find this feature useful for your workflow.

Among the main goals of the instancer was to produce a partial VF that contains less variation data and is thus smaller in size. Changing the default position would not necessarily yield a result that reduces the amount/size of variation data in the VF (though in some situations it may).

Thanks for your quick reply!

could you please elaborate on that?

Sure! Let's say I want to generate a partial variable font with an axis range that does not include the default position.
Currently, I have to do a work-around, open the Glyphs file, change the variable font origin to something within the desired range, export the variable font again, and then run instancer.AxisRange with the desired values.

Moving the default position of an axis may not change the file size, but in combination with the other functions of the Instancer, it allows you to create a larger number of subsets without having to manipulate the source file (glyphs file).

Joining here again, and bringing the already mentioned stuff from this issue over here.

"… To specify, I meant, if you subset a variable font that has 2 axes originally, but the subset will only have one of the axes, what will happen with the origin location if that one originally sits on the dropped axis? …"

As far as I see you cannot slice/subset an axis range, when the target range does not include the variable font origin, right? That’s exactly the problem. So if you have an input font with

weight 0..1000 and the origin lies on 200, it’s currently not possible to get a subset to a range of e.g. 400…1000. The instancer will just spit an error (as documented).

I've been thinking about this. And like to see it happen. Solving the general-case can be a bit hairy to think about. But I think is doable.

I looked at the instancer code. Currently, using the following function, it splits desired subset designspace into axes that got "pinned" and those that remain a range:

def splitAxisLocationAndRanges(axisLimits, rangeType=AxisRange):
    location, axisRanges = {}, {}
    for axisTag, value in axisLimits.items():
        if isinstance(value, rangeType):
            axisRanges[axisTag] = value
        elif isinstance(value, (int, float)):
            location[axisTag] = value
        elif isinstance(value, tuple):
            axisRanges[axisTag] = rangeType(*value)
        else:
            raise TypeError(
                f"Expected number or {rangeType.__name__}, "
                f"got {type(value).__name__}: {value!r}"
            )
    return location, axisRanges

to support L4, we would allow an axis to be returned in both of those sets, both as "pinned" to new default, as well as carrying a range.

Then we need to update the entire code for that change.

A utility function which I thought of, that might make the code easier to navigate and understand, is a function that then takes the output of the above function as well as a delta+support, and returns a list of delta+support, as well as the "bias" to add to the value being variated. This is similar to limitTupleVariationAxisRange, but can be written more generally and reused.

Perhaps those should be added right into varLib.models.

Hey @behdad. This is good news. Thanks for doing the research and thinking about the implementation. Excited to see it happen!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

black7375 picture black7375  Â·  4Comments

typefacts picture typefacts  Â·  4Comments

cjdunn picture cjdunn  Â·  11Comments

behdad picture behdad  Â·  5Comments

Pomax picture Pomax  Â·  4Comments