-
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
Implements part of #754 via getter/setters for proxying the file format 3 attributes #825
Conversation
fc2b551
to
b0ef7f9
Compare
Nvm, squashed the commits into one anyway, seemed cleaner :) |
Any chance to get this reviewed? :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just left minor comments below. Ideally @simoncozens could take another look
To fix the CI erros you can try to rebase on main branch (or merge origin/main into yours) so that it pulls updated requirements
64a5f11
to
66d6149
Compare
Thanks for those suggestions @anthrotype — I revised the tests and checking some. After rebasing I get 17 test failures related to instancing, but those appear present also in |
66d6149
to
212d531
Compare
Rebased on |
the CI checks seem to be passing now, not sure what you're referring to: https://github.com/googlefonts/glyphsLib/actions/runs/3627890015 I'll take another review pass |
I'm manually running e.g. |
the regression tests failing is expected when adding new features from a PR branch, but once that's merged to main, they fix themselves (the regression test CI job compares output from PR branch with that from main branch). One needs to manually check that the changes are as expected: |
the CI also runs tox the same way as you do so that's unexpected.. let me try reproduce locally from a copy of your PR branch |
try to also pass the |
I checked out your branch locally and run |
the regression tests failures seem all benign, I can see that postscriptBlueValues are now being compiled which is what this PR is about |
Ah, perfect, thanks for the |
Re: ' |
In general I would recommend to adapt the API of Glyphs 3 and make the builder aware of it instead of fitting the data into the Glyphs 2 API |
@anthrotype yep, those type checks are better now, thanks for pointing that out. regarding @schriftgestalt 's suggestion: I agree in general, but this would probably need a more orchestrated effort overall, no? it shouldn't just prioritise v3 handling for one attribute. I can rework this for the |
does glyphs.app python API still provide getters like |
Yes, it does. But only as a compatibility measure. And the API doesn’t distinguish between version 2 and 3 files. In memory they are "upgraded" to version 3. |
7e42f63
to
aa0358e
Compare
I said the other day to Cosimo that we should just support the G3 model and have a separate bit of code to “upgrade” G2 files on load. Trying to manipulate both systems is making the code atrocious, and G3 is much cleaner. But this would be a major overhaul. |
Lib/glyphsLib/classes.py
Outdated
"cap height", | ||
"x-height", | ||
"baseline", | ||
"descender", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where's this list of validZoneMetrics taken from? it seems constant, it should probably be defined at global scope, or in the constants module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and why skip some of the font metric types as not "valid" when fetching a given master metric values? where is this logic encoded?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically I went by what's in the UI (mind you I missed bodyHeight
):
Outputting the GSMetric.type
of these objects of a file gives:
ascender
cap height
x-height
baseline
descender
italic angle
bodyHeight
The last empty line for type is what the UI has as "Custom Name" metric. This may or may not be an overshoot — presumably things with a two numeric value / position + overshoot we want to treat as an alignmentZone as well, no matter if it is not in what format 2 allowed as alignmentZones?
I think when I did a "has no overshoot" exclusion instead of explicitly checking for the type a lot of tests outright failed because the font metricValues does not match a given master's metricValues length (e.g. in all sorts of constructed test cases or programmatic access of those objects that don't adhere to the file format 3 referencing the font metricValues from the master by index). As far as I recall the "baseline": 0
default above also addressed this.
Note sure what to do here, but I'll ponder it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated in code below in forced push. Disregarding the "italic angle" type of metrics early was key to avoiding all those issues in tests that construct just masters. Gets rid of the explicit magic list and does not require the baseline
default either. Thanks for poking holes @anthrotype :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... because the font metricValues does not match a given master's metricValues length (e.g. in all sorts of constructed test cases or programmatic access of those objects that don't adhere to the file format 3 referencing the font metricValues from the master by index
if it's invalid that len(master.metricValues) != len(font.metrics) then it is those tests that would need to be fixed...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tests were correct, only when not filtering things that are now returned by a master'a metrics which are not alignmentZones in format 3 those test broke because without knowledge of the master my previous iteration of the code could not filter those properly. Different approach in the updated code, essentially discarding the italic angle
from being an alignmentZone outright, and further removing metrics which do not have an overshoot, e.g. do not form a bluezone because they would be a "line" only.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can a valid "italic angle" metric ever have overshoot != 0? I suppose not. And if not, then simply filtering out metrics with overshoot=0 (without special-casing italic angle itself) should accomplish the same thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In here
for index, fontMetric in enumerate(self.font.metrics):
# Ignore the "italic angle" "metric", it is not an alignmentZone
if fontMetric.type == "italic angle":
continue
metric = self.metrics[index]
# Ignore metric without overshoot, it is not an alignmentZone
if metric.overshoot == 0:
continue
zone = GSAlignmentZone(pos=metric.position, size=metric.overshoot)
zones.append(zone)
self.font.metrics
is a GSMetric value which has no overshoot. One has to use the index of this font metric values to get the master GSMetricValue value, and then discard things which have no overshoot. Filtering the font metric values by type italic angle
is necessary because just accessing master.metrics[]
by index will not have a value for a master metric that is an italic angle.
The format 3 handling of metric values referencing back up to the font metrics list is convoluted imo.
return zones | ||
|
||
@alignmentZones.setter | ||
def alignmentZones(self, entries): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like alignmentZones is a read-only (https://docu.glyphsapp.com/#GSFontMaster.alignmentZones) so maybe we should not add a setter.
I agree with @simoncozens when he wrote
We should store things as v3 format internally to the object but provide accessors to get at information in a v2 way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of the existing tests assign alignmentZones
like this for constructing objects via code only (as opposed to actual conversion). Happy to drop the setter and tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If one wants to set these alignment zones using Glyphs.app v3 scripting API (where alignmentZones is read-only), how would one do that exactly?
Does glyphsLib already support this? If not, what do we need to support it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Going by what I found in the code. The primary use case in the library seems to be here setting the alignmentZones when converting from UFO to glyphs.
Also tests here, here, here and here — my assumption being that a new feature or refactor should not break existing tests and thus a setter needs to be supported.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'm ok to keep a setter for alignmentZones, but I wonder maybe it should store them internally as v3 metricValues.
Say you go from UFO => glyphs and want to save as format_version=3, those zones will not be written out in the generated .glyphs file.
I think we should have a method to convert from v2 (alignmentZones and stems) to v3 metricValues and call that when saving a font and format_version=3.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but that setter only sets self._alignmentZones
, which as far as I understand is not written out to .glyphs source when format_version=3,
if writer.format_version == 2:
writer.writeObjectKeyValue(self, "alignmentZones", "if_true")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so I presume that roundtripped glyphs file of yours has format version 2.. but when Glyphs.app v3 opens it up it automatically populate the metric values.
That's what we are missing here, a way to upgrade these metrics from v2 to v3. I am not saying that we should do within this same PR, just that this is something we would like to support
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see, didn't understand that's what's happening. Yes, correct.
I'll have a look at that, either as part of this or separate. Follows the same obtuse logic of font metrics vs master metric 😄 Happy to adopt that if there is a more general v2 > v3 structure in place. That section of code you quoted, I suppose, is at the heart of what would need a more elegant and transparent file format version upgrade.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does one actually write something out in format 3? Tried to find tests that verify those other self.format == 3
written things (e.g. here onwards), but could only find this one. Am I completely off track or is there pretty little actually for outputting/writing/testing v3? Would testing the output of the writer in writer_tests.py
the right spot to implement checking for a yet-to-be-done implementation of writing those metrics back to v3 files?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but could only find this one.
This looks like the way to do this. While writing out, check the format and what data you have and act accordingly.
aa0358e
to
999d649
Compare
Rebased and refactored the |
@kontur can you fix the lint job failure? then I suppose we can merget this one, as at least allows one to psautohint a font (/cc @guidoferreyra). We can keep #754 open to address the things discussed in #825 (comment) and the general issue of "a more elegant and transparent file format version upgrade". |
…tems on GSFontMaster
999d649
to
af8d361
Compare
@anthrotype Ah sorry, must have missed the lint issues locally and didn't follow the updates of the test runner here. Fixed and rebased on |
patiently waiting |
thanks for the patience |
This implements writing
postscriptBlueValues
,postscriptOtherBlues
,postscriptStemSnapV
andpostscriptStemSnapH
for glyphs > ufo.I added ./tests/glyphs3_test.py and other tests are passing as well.
I'm not sure how elegant or clumsy it is to have those
GSFontMaster._...
underscore internals and accessing them via the getter/setter proxies, but it seemed the most practical way synthesize them from theGSFont
attributes required for the offsets.There are this, this & this rather blunt stop gaps in the getters which mainly function to return nothing for cases where the masters are constructed via code, e.g. in the tests (about 50 or so fail otherwise). In a way this could mask a real issue as well, but not sure how realistic it is to rewrite the tests, or add some kind of "defaulting" structures to keep the indexes for constructed objects in sync between GSFont and GSFontMaster.
Should I squash my commits into one? The first commit attempts were a bit hit and miss...