-
Notifications
You must be signed in to change notification settings - Fork 51
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
Support "Axis Mapping" custom parameter for GlyphsApp fonts #568
Comments
@arrowtype We have rearranged our file as you advised us to do, but it doesnt pars through fontmake. Barbara took the file and deleted everything except ABC like you did and it worked... We are now trying to find out what could be the culprit. Maybe you have an idea ? And then in the end we deleted the kerning exceptions. And the font was produced... |
Hey Stephen, yes we actually asked Georg to add that feature, as we want to use it when exporting designspace from glyphsLib. I just wasn't aware this had landed already in Glyphs.app. We definitely want to support this in the near future. |
For reference, this is the discussion where the Axis Mapping idea comes from |
@schriftgestalt how do the |
The |
is this new |
It is not documented jet. I need to polish the implementation a bit first. But the data should stay like this:
|
@schriftgestalt thanks! does |
not really. The |
but the two parameters can't contradict one another. They must match. Axis Location assigns a user-space location (i.e. in the same coordinate space as fvar) to a given master. This has the effect of implicitly mapping a master's internal design location to the given user-space location. Axis Mappings provides a mapping from user-space locations to internal design location (at least, that's the interpretation of DesignSpace.axes |
I had a look at how Axes and Axis Mappings behave in Glyphs 2.6.4. Axes supplies the list of axes which can then be selected in Axis Mappings. This would map nicely onto DS semantics. I can imagine a future where glyphsLib hard-requires an Axis parameter and only sets mappings through Axis Mappings, any Axis Location parameter anywhere would then result in an error. |
As I said, the current idea is that the Axis Mapping is only effecting the avar table and the axis location is effecting the external axis metrics in the fvar table. |
by which you mean the axes bounds (minumum, default and maximum values) in user-scale, right?
avar mappings are used to map from the default normalized coordinates (i.e. resulting from mapping fvar axis min to -1, default to 0 and max to +1, and interpolating the in-betweens) to the modified normalized coordinates (-1, 0 and +1 stay the same, in-between segments can be stretched or compresed). Your Axis Mappings font-wide custom parameter, from what I can see, maps from user-scale coordinates to internal design coordinates (just like the DesignSpace axes map elements do). The Axis Mappings param can be translated into avar mappings by simply piecewise-linearly mapping both the keys and the values to the normalized scale -1,0,+1. This also requires that there exist at least three required key-value pairs in a valid Axis Mappings param: one for min, one for default and one for max along a given axis. So far so good. But you can immediately see that Axis Location master params (defined as the user-scale coordinate of the masters) on the one hand, and the Axis Mappings keys on the other hand, must be in agreement with each other. So one can argue that Axis Mappings supersed Axis Location in that it is more general, since it allows to assign mappings not only for the masters but also any value in between them. And that storing the same values twice is redundant and may result in contradictory pieces of data. |
It is only used to define the min, default, max values. For masters that are in different position, it is ignored. The Axis Mapping is supposed to map from design coordinates to design coordinates. I really need to have a look at that. I think it should map from design to user coordinates. I’ll have a look at that as soon ASAP and will update here. |
no, please, have it map from user to design coordinates, like DesignSpace axis maps already do, as that more clearly reflects the data in avar (where input is [normalized] user coords [the 'user' is the font user, not the designer] and output is [normalized] internal coords) |
I’ll think about that. |
I hope we find a good solution to this before Glyphs 3 is released. I'd like to see axes management more front and center in the font info dialog instead of being relegated to hard to understand custom parameters. FontLab 7's axes dialog is a good starting point. |
@schriftgestalt how do you determine the min/default/max masters for an arbitrary axis? |
Min/max are the lowest/highest position of all masters. Default comes from the default master (the first or where the |
So, I'm back on trying to knock some sense into glyphsLib's axis handling. I don't know how to proceed. Data that influences the mapping:
Cosimo suggests:
In this scheme, Axis Location would be completely ignored. @schriftgestalt This is just ugh. Is there still time to find a better solution for Glyphs 3? Something like a dedicated "Axes" font info pane that centralizes axis management? Instances then default to the OS/2 weight and width values derived from their axis location with overrides available through custom parameters? |
I should strongly suggest to not look at the Weight/Width class of the instances. That is what the Axis Mapping parameter is for. You don’t seem to like the Axis Location. It is as a external design space mapping. So each master has two coordinates. One in the internal design space and one in the external (variable font) space. I’m myself working on how to bring all of that together. I’ll report when I’m through with it. |
But is the Axis Mapping parameter then used to set the OS/2 classes of static fonts? What happens if the instance values disagree?
Rather, I don't understand its purpose when one has Axis Mappings? It just seems to be a limited version of it?
But it's only honored on min/max/default masters? |
No, the static font don’t use any of this.
That is what I’m working on right now. And mapping between the two spaces is tricky. But that has to be done regardless where the mapping comes from. And relying on avar to fix things later doesn't work. And for me the AxisMapping is something extra, to fine tune or to do funny stuff. Not something I like to get my data for the fvar table. |
what me and Nikolaus are trying to say is that the OS2 weight/width class of the static fonts, on the one hand, and the fvar user-space (or external if you like) locations of the VF named instances, on the other hand, must not contradict one another. This of course is to enable interoperability between the static fonts and the VF generated from the same set of sources, so one can swap one with the other without visible changes. See "wght" registered axis spec
Or the "wdth" registered axis spec:
How do we make sure that:
|
it is not once the user has the ability to specify these explicitly, as it can be done with the .designspace axis and map elements. Let me recap how these work and how they are translated into avar mappings. There is a global list of axes. Each axis has a minimum, default and maximum values, specified in user-scale coordinates. These value go straight into fvar axes definitions. An axis can optionally contain a set of mappings from user-scale to internal design-scale coordinates (the Some (registered) axes are required by the OT spec to have a predefined scale: wght must go from 1..1000, wdth 50..[100]..150 (%) etc. Other axes may not have a required user scale, but the designer, for whatever reason, may chose to define one set of values for the UI (fvar) and another set of values for the internal interpolation. This ability to warp the design-space (compressing or enlarging specific segments along a given axis without the need to insert additional intermediate masters) may also be used as a design tool. However for some axes (like wght or wdth) these mappings are required (unless in very simple setups where a designer is happy with using the same predefined user-scale also for the internal interpolation values, in which case there is a 1:1 relationship between external and internal values and there is no need for a custom mapping). Each axis can be assigned a list of key:value pairs. The key (or input) is understood as the value that the user selects when requesting an instance (be it a named instance or a custom one). The value (or output) is what the user input key maps to internally in the variation space, and belongs to the same (arbitrary) scale defined by the designer to set up the interpolation. Not all axes need to have custom avar mappings. If external, user values and internal design values are the same for that axis, axis mappings won't actually do anything (all map to themselves), then no avar segments are needed for that axis. Only when an axis does have some interesting mappings, we do create avar segments for it. The avar uses normalized coordinates for both keys and values: i.e. all values must be in the -1..+1 range, where -1 is always defined as the axis minimum, 0 as the axis default, +1 as the axis maximum. The So, we can say by default there are at least 3 predefined axis mappings that are always present and are determined by the that axis min/default/max values, the locations of the masters and the identification of the default or base master (the Variable Font Origin). When an axis default is equal to either the min or max, we only need 2 predefined axis mappings. For example, we have a single axis with the following (min, default, max) values To convert from the (external, internal) values to the normalized ones in the range -1..0..+1, we need to apply the normalization based on the (min, default, max) triplet which is appropriate for each space, whether external or internal: if we are normalizing an external coordinate, we use as the (min, default, max) triplet the user-space coordinates of the masters at the minimum, default and maximum points along an axisl; whereas if we are normalizing an internal design coordinate, we use for triplet the (min, default, max) interpolation values of the masters (see normalizeValue function in varLib.models). # external, user-space coordinates
normalizeValue(100, (100, 400, 700)) == -1
normalizeValue(400, (100, 400, 700)) == 0
normalizeValue(700, (100, 400, 700)) == 1
# internal, design-space coordinates
normalizeValue(80, (80, 120, 180)) == -1
normalizeValue(120, (80, 120, 180)) == 0
normalizeValue(180, (80, 120, 180)) == 1 Values that are in-between these predefined mappings will be linearly interpolated between them. E.g. say our axis mappings contain an extra map at (150, 90), the resulting avar normalized key:value pair will be
So you see that if we have access to the following data structures: axes = {
"wght": (100, 400, 700), # (min, max, default)
"wdth": (100, 100, 150),
}
axis_mappings = {
"wght": [
(100, 80), # (external, internal)
(150, 90),
(400, 120),
(700, 180),
],
# wdth has no custom mappings in this example
}
# locations use internal design coordinates
masters = [
Master(location={"wght": 80, "wdth": 100}),
Master(location={"wght": 90, "wdth": 100}),
Master(location={"wght": 120, "wdth": 100}), # this one is, by definition, the default base master,
... # given the definitions of the axes defaults above
] ... we can easily build our avar (and that's exactly what varLib._add_avar method is doing -- the code is perhaps clearer than thousands words). |
@schriftgestalt any updates on this? |
As far as I'm able to understand, here, the real difficulty is how to make a Glyphs UI such that users can:
My best guess is, Georg is trying to make part 1 work for everyone, part 2 available for advanced use cases, and part 3 such that people don't put themselves into a confusing corner. I'm betting this is what he meant by saying "mapping between the two spaces is tricky"? If this is the case, I agree that it is a tricky app UX/UI challenge. My two cents is that:
Just some thoughts. I could be wrong about any of this. |
No, you make all valid points, thanks Stephen for chiming in.
you're not the first, nor last one. I got tripped a few times as well 😅 I like both your UI suggestions, especially the idea of pre-populating Axis Mappings with non-editable values from those implicit in the Masters/Instances tabs. |
I've been fighting this issue for a while so I thought I'd do some research. My research is too long to post as an issue (it also goes off topic a bit) so I'm producing a doc (wip) which should help Georg fix variable font generation in Glyphsapp and help the developers of glyphsLib better understand what this param does. I'm still thinking through how I'd like to see it implemented in glyphsLib though. If anyone would like me to conduct any further tests, I'm more than happy to do so. @schriftgestalt More than happy to elaborate on any points I've brought up. |
Thanks. I’m planing to work on this, soon. |
should be supported now after #618, thanks Marc! |
GlyphsApp has a Custom Property for "Axis Mappings" (it appears to have been added in v1261).
However, adding this has no effect on the Designspace output from glyphsLib, as in a FontMake build.
The only way to get around this as a user, as far as I know, is to have
Axis Location
custom props on Instances, but not on Masters. I get this impression from thebuilder/axes
code, and a test file only got the proper designspace mapping once I did removedaxis location
props from Masters.glyphsLib/Lib/glyphsLib/builder/axes.py
Lines 137 to 215 in 264afc2
However, it's problematic to not be able to control the axis maps at a single, top level. In the Nunito project, it means that I also had to add a random extra Instance, so that the whole Slant range would be covered by Instances, and the Slant axis would go from the min to max values present in sources (otherwise, the build fails).
Here are the files I was using:
nunito-build-test.zip
NunitoSans_Pitstop-26.glyphs
is the full GlyphsApp file, with the problem of only-partial Weight axis mapping. With this, the generated wght map is partial (resulting in instances with weight values that are a bit off, with floating-point values):NunitoSans_Pitstop-26--testfile_ABC.glyphs
is a GlyphsApp file with the problem fixed as a hack, but with the superfluous Instance to make the Slant axis work. With this, the generated map is:I'm building with FontMake:
This issue was filed on behalf of @Fonthausen, but I can try to answer questions if I left anything unclear.
The text was updated successfully, but these errors were encountered: