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
Set noprevcp and nonextcp only in SplineRefigure* and SplinePointCreate #4685
Conversation
Good news re: #4327. That was an important bug to fix. I can remove hacks from FRBAmericanCursive now~ |
There are legitimate test failures in this by the way https://github.com/fontforge/fontforge/pull/4685/checks?check_run_id=2212087326 |
OK, I'll take a look |
This was one of those "how was the test passing in the debug build?" type things. Just an allocation order problem. |
Is this meant to capture all references for where |
My thought at the time was basically: this is already a huge update so don't clean up every single line. Because these values are now conventionally set by SplineMake any instance where they're set and followed by a SplineMake call are harmless (as long as it's nonextcp of the first point and noprevcp of the second point). If it's preferable to go through and eliminate all of them that's fine too. |
Great, well at least that aligns with my reading of what SplineMake/Refigure were doing. I think it would be nice to eventually clean it up, as that makes it clearer that it's only canonically set in a few places, but I'm happy for it to either come in a later change or be bundled as part of this (at least given the size of this pr, it makes no difference to me whether or not it's added). |
I think for this we can put it off. There's so much potential cleanup in the source base and this is the kind of thing one can poke at when changing a file. |
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.
For most of these, it's pretty clear to see that SplineMake/Refigure is called after, but for a few of these, I found it hard to prove that they were called (and more importantly called before noprevcp
/nonextcp
were used) - I've commented where I found this to be the case.
For at least a couple of these, I'm fairly sure that there are missing calls, in which case I've left further details.
@@ -1310,14 +1310,11 @@ return( head ); | |||
} | |||
|
|||
static void InterpPoint(SplineSet *cur, SplinePoint *base, SplinePoint *other, real amount ) { |
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.
flag
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.
A general comment on these: If you allocate a new point and don't call SplineMake, or change an existing point and don't call SplineRefigure, and then put the point "back" into normal use, those are bugs. In the first case the points won't really be part of a common contour because its SplineMake that links them up. In the latter case you'll just get bad values because almost all calculations use the expanded coefficients.
For this one I don't see an issue. The case where SplineMake isn't called is when adding the first point to the SplineSet. Sure, the values are adjusted while nonextcp and noprevcp will retain the default false
values from SplinePointCreate()
, but the metaphysics of a dangling control point are unclear anyway. The worst that could happen is loosing the dangling coordinates on a round-trip, but I don't believe that's a problem with SFDs. (When round-tripping through external tools the result will be highly variable, again because of the unclear metaphysics.
fontforge/splineoverlap.c
Outdated
@@ -3825,18 +3816,21 @@ return; | |||
isp = SplineBisect(sp->prev,t); | |||
nsp->prevcp.x = nsp->me.x + (isp->prevcp.x-isp->me.x); | |||
nsp->prevcp.y = nsp->me.y + (isp->prevcp.y-isp->me.y); | |||
nsp->noprevcp = isp->noprevcp; | |||
psp->noprevcp = isp->noprevcp; |
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.
fixing a bug?
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.
Hmm. Doesn't seem like this should be right, though. I'll think about 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.
Either this is wrong or the untouched logic in the preceding psp logic is wrong.
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 think I just fat-fingered this. I don't remember making the change and analytically I don't see any reason for the change.
My guess is that at some point in editing I removed these lines (this and the one below) and then thought better of it, and when I put them back I got this one wrong.
Anyway, I put it back how it was.
Changes look fine, just pending on that comment in splineoverlap.c |
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.
lgtm but this needs a rebase
rebased |
Set noprevcp and nonextcp only in SplineRefigure* and SplinePointCreate
This PR breaks @skef already when opening the font file https://github.com/ryanoasis/nerd-fonts/blob/v2.2.2/src/unpatched-fonts/3270/Medium/3270Medium.otf some outlines come out badly destroyed. Bisected to commit 0f8fc11 (which is the first real change). The left side shows the same font as the right side, but the left side is after 0f8fc11, the right side before... The commit is so distributed/unfocused touches many files, hard to see what goes wrong (for me). Note: The current Edit: Correct font link, add bottom note |
Problem confirmed in current master. I'll try to take a look at this soon. |
The problem, at least, is showing up quite specifically: Each of the three affected glyphs has bad co-located control points on either side of the first point in a contour. Both sides should be lines (although in this font that doesn't prove much) and therefore both control points should (in FontForge) be co-located with the first point. |
Thanks again for looking into it. Maybe related, maybe not: If you generate an Iirc I get this error also in other instances, where I would not expect one, like 'removing a lot of glyphs from a font, but not changing anything else' or something. Nothing I can really pinpoint. |
It's not uncommon to run into fonts that have features or geometries that aren't technically wrong but that are "asking for it" from font tools or libraries. An angle that's off 90 degrees by a tiny amount can have unexpected results due to floating point calculation strangeness. It could be that the authors ran into some of these problems and made changes without anything being really "broken". It's still good to fix the problems thrown up by these cases unless it's just too difficult. |
Although it has a .ttf extension the linked 3270Medium.ttf file contains a CFF table rather than a glyf table. However, Neither the .ttf nor the .otf current versions of the font (with glyf and CFF tables respectively) show the problem in FontForge. The problem, when factoring out the subroutinization, reduces to this:
So, basically, there are two rmovetos in a row at the start of the path. A lone moveto in a CFF CharString can have various roles during development, depending on the tool you're using, but they're not supposed to wind up in final output. So this is a bit more towards straightforward "bad output" than "asking for it". What FontForge should do opening this file is an interesting question. There are basically two options: leave a lone "dot" at the first location or just pretend the first moveto isn't there (while still honoring the math of the "r" prefixes). It looks like the intention of the code is the latter, given that there's no dot with pre-20220308 releases. I suspect what's happening here is that the code that deals with double-movetos is moving the location of the point but not updating the initial control point, because it didn't used to matter. I'll try to dig that up. |
Thank you, great digging that out 👍 |
The reasoning is in #4125.
nonextcp
andnoprevcp
are handy in a number of circumstances, mostly for special-casing lines, but it's bad to make them authoritative for cubic splines because you can really only have a full cublic spline or a line: There's no such thing as a cubic spline with one control point.The solution is to set these flags based on the spline data in (both flavors of)
SplineRefigure()
. The remaining cases are isolated spline points and the ends of open contours, which are never passed toSplineRefigure()
. It's OK for a control point to "dangle" in some cases (like when a following point is removed) but normally one should initialize...cp
location tome
and set theno...cp
flag to true. This is best achieved by allocating all spline points withSplinePointCreate()
to set everything up well in the first place.There's code in
contrib/fonttools/acorn2sfd.c
that doesn't allocateSpline
orSplinePoint
structs withchunkalloc()
, so I just left that as-is. It may or may not cause problems.The existing tests were helpful in turning up some problems, particularly with isolated TrueType points and point numbering. The fact that point numbers are used "substantially" strongly suggests that the TrueType paths come through this pretty well. The OpenType path issues are simpler but perhaps slightly less well tested, at least by those tests. I did some testing with SVGs and Glifs and such and that's all working now.
Closes #4125
Closes #2601
Closes #4327