Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[designspace] Can't generate variable font fixed axis subset #920

Open
benkiel opened this issue Jul 28, 2022 · 7 comments
Open

[designspace] Can't generate variable font fixed axis subset #920

benkiel opened this issue Jul 28, 2022 · 7 comments

Comments

@benkiel
Copy link

benkiel commented Jul 28, 2022

I have a large family with a weight and optical size axis. Currently trying to use designspace 5 to split the family into multiple variable fonts: specific values for weight with the whole optical size axis.

    <variable-font name="Elevator_Hairline" filename="Elevator_Hairline_Variable.ttf">
      <axis-subsets>
        <axis-subset name="Optical Size"/>
        <axis-subset name="Weight" uservalue="100"/>
      </axis-subsets>
    </variable-font>
    <variable-font name="Elevator_Thin" filename="Elevator_Thin_Variable.ttf">
      <axis-subsets>
        <axis-subset name="Optical Size"/>
        <axis-subset name="Weight" uservalue="200"/>
      </axis-subsets>
    </variable-font>

...

I get a InvalidDesignSpaceData("No default source.") error if I include a stop on the weight axis that doesn't have a corresponding source: i.e.uservalue="100" works, as it has a corresponding source, but uservalue="200" doesn't — it doesn't have a corresponding source in the sources for the weight axis.

This is unexpected—I was under the impressing that you could slice to a specific value anywhere on an axis, which would be so very nice. This behavior isn't documented anywhere as far as I can find; is it the correct behavior?

@benkiel benkiel changed the title [designspace] Can't generate variable font with axis subset that doesn't have a master [designspace] Can't generate variable font fixed axis subset Jul 28, 2022
@belluzj
Copy link
Collaborator

belluzj commented Jul 29, 2022

It's a current limitation of the implementation of designspace 5 in fontmake.

The format allows you to specify that, and from an implementation point of view it would make sense and there's something we could do to produce what you specified, however it's more complicated than the case where there are sources at the locations where you want to slice and so it's not implemented at the moment.

A related feature is the code in fontTools.instancer that implements slicing VFs in various ways. What you're asking for here sounds like "L4" as per the levels defined in the docs: https://fonttools.readthedocs.io/en/latest/varLib/instancer.html

But I think the latest news are that L4 is not implemented either in fontTools.instancer: fonttools/fonttools#2157

@belluzj
Copy link
Collaborator

belluzj commented Jul 29, 2022

Implementation idea in pseudo-code (not tested I mean) in case someone wants to take this:

for _location, subSpace in ds.splitInterpolable():
    # We split the big DS5 with potentially discrete axes into smaller spaces that are interpolable (no discrete axes)
    instantiator = fontmake.instantiator.from_designspace(subSpace)
    for vfName, vfSpace in subSpace.splitVariableFonts():
        # vfSpace is a designspace for each <variable-font> element
        axisExtremes = []
        for axis in vfSpace.axes:
            axisExtremes.append([
                (axis.name, axis.minimum), (axis.name, axis.default), (axis.name, axis.maximum)
            ])
        # Make the user locations of all "corners" of the designspace
        cornerUserLocations = [dict(combo) for combo in itertools.product(*axisExtremes)]
        for cornerUserLocation in cornerUserLocations:
            # Make sure there's a source in that corner, if not create it with the instantiator and insert it
            for source in vfSpace.sources:
                if vfSpace.map_backward(source.getFullDesignLocation(vfSpace)) == cornerUserLocation:
                    break;
            else:
                # No source found
                # Find the VF descriptor in subSpace so we can extract from it the axis-subsets with just 1 value
                # (= coordinates of new default location)
                for vf in subSpace.getVariableFonts():
                    if vf.name == name:
                        break;
                vfRegion = fontTools.designspaceLib.types.getVFUserRegion(subSpace, vf)
                newSourceUserLocation = {
                    **cornerUserLocation,
                    **{ axisName: value for axisName, value in vfRegion if isintance(value, numbers.Number) }
                }
                vfSpace.addSourceDescriptor(
                    location=vfSpace.map_forward(newSourceUserLocation),
                    font=instantiator.generate_instance(InstanceDescriptor(userLocation=cornerUserLocation))
                )
        # Then build the vfSpace designspace, it should work now because all the corners have been instantiated  

(edit: forgot to mix in the the axis subsets with just one value)

@benkiel
Copy link
Author

benkiel commented May 25, 2023

Pinging on this

@arrowtype
Copy link

@benkiel This is probably already obvious to you, but I think the FontTools Instancer would be able to handle this pretty well, in a post-build step. But, I agree that it would be great if it could be specified in and directly output from the designspace.

@benkiel
Copy link
Author

benkiel commented Jul 5, 2023

Doing it with FontTools Instancer is a workaround: DS5 specified this as a huge convenience win, we have what we need in code now (at first it wasn't there), so this should be supported.

@arrowtype
Copy link

Totally agreed! I found this because I am having a related issue (#1011), where an italic variable font fails even though it does have a source to support its default location. It just doesn’t have a location that matches the default of the entire designspace.

@benkiel
Copy link
Author

benkiel commented Mar 14, 2024

Pinging on this; again this is a pretty large pain point and blocks some of the really nice benefits of DS5 (yes, back to that project and tested with the latest FontMake just now, same issue — really hoped that I wouldn't need to do a complex thing for information that is best encoded in the DS5 file. Post processing is hell)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants