Open MattMatic opened 1 month ago
Built Gulzar (debug):
Released Gulzar 1.002:
Crowbar for Built (debug):
Crowbar for Released:
Note the extra GSUB rlig stage before the GPOS that involves the DotAvoidance_reverse_top stage.
@simoncozens
I'm interested in Gulzar, and I've created a ttf file by adjusting the Makefile. However, I've noticed that the font does not match between my generated ttf and the released Gulzar.ttf file.
For example, I've noticed that the HAMZA_ABOVE and the adjacent Nukta are clashing with the two letters سائنس (science) and سائنسی (scientific). I think this might be caused by the DotAvoidance.py script in karakul, just like @MattMatic mentioned.
So, it would be appreciated if you could check the following points.
Thank you.
I'm sorry about this; I should really have been more diligent about pinning Python dependency versions for the release build. In my defence I was building a lot of the tooling on the fly and wanted to make sure I always had the latest versions in the build...
I will try and take a look at this but it's going to take me a while to get around to it and then to get my head back into the Gulzar headspace - sorry...
I completely understand! Really appreciate any help at all. 😊 One nice side effect is that I'm beginning to get my head into the right space. 🤯
Am switching to build on macOS as well. It's highlighted some other issues, but when built it still produces the same issue as WSL.
FWIW, have installed libraries only under venv, and these seemed crucial:
python3 -m venv venv
pip install -r requirements.txt
while under venvAm currently working backwards from DotAvoidance to rules.csv and back to Gulzar.glyphs to understand what might have happened.
@simoncozens
Thank you so much for your quick reply! I really appreciate it. I'm looking forward to hearing from you, and I'll also keep researching in the meantime.
Thank you.
@simoncozens I'm not sure if this is relevant, but I found it interesting...
I tapped into DotAvoidance.py of karakul to build an SVG output of the collisions so I get some insight into the test above.
(The marks-only on the left that are checked, and the full set of glyphs in the test sequence shown on the right)
If I understand this correctly, the nastaliqConnections from Gulzar.glyphs is extracted into rules.csv, and then a sequence of all combinations of marks are tested for collisions - but only at the font's design positions. What's interesting is that the DotAvoidance appears to be checking for collision in the default Y position of the marks (i.e. at an early stage)/
The SVG for HAMZA_ABOVE + sda is colliding by the smallest amount - wonder if this changed somewhere down the line with the Python libraries - but the later GPOS would've made them not collide anyway.
Is the early collision check done for the benefit of GSUB/GPOS building, or could the moving of the marks be handled in GPOS as a last stage? (Even thinking about either option is frying my head... so please discard if this is nonsense!)
Update: I believe collidoscope has an error in the scale_path
method.
It looks like each path is being centered on its own bounding box, rather than on the overall glyph bounding box.
Background: (Simon - I know you know this, but the debug process might help others)
I started working through Fez + karakul and trying to understand how everything fits together.
In DotAvoidance.py there's position_glyphs
, and I see that it's very neatly pulling out the anchors from the Glyphs font, working out exit-to-entry cursive points, and matching up the "top" anchor of a base glyph to the "_top" anchor of the mark glyph. And Fez has nicely created additional anchors for "top.one", "top.two" etc. Very cool. 😎
Wrote some rough code to output the state of the paths and glyphs and positions, and try to tie them all back to what should be output.
After a few hours of puzzling, I notice the "BEmsd2" glyph shown above in red is wrong. Really wrong. The 'tail' is almost separate from the 'body'. 😟
So, I manually check the paths in Gulzar.glyph, and compare manually to B_E_msd2.glif - seem to match almost 100% I only have FontLab 8, but it pulls in the UFO format and shows pretty much the same glyph (Bezier issues notwithstanding), and can see clearly the two paths that should overlap. I dig into DotAvoidance to spit out an SVG of just "BEmsd2" - and it's wrong. 😟
Then I work through and find that collidoscope is scaling each path. To test this theory, I hack into DotAvoidance to do this:
paths = self.c.get_beziers("BEmsd2")
self.report_txt.write("Beziers:%s\n" % (paths))
self.report_txt.write("\t<svg preserveAspectRatio=\"xMidYMax\" width=150 height=100 viewBox=\"120 330 474 262\">")
for p in paths:
self.report_txt.write("<path d=\"%s\" fill=\"red\"/>\n" % (p.to_svg()))
self.report_txt.write("</svg>\n")
After a bit of manual cut-n-paste into HTML, I get the SVG output:
So, then I add the scale call:
paths = self.c.get_beziers("BEmsd2")
paths = [self.c.scale_path(p) for p in paths] # ----whoops!
self.report_txt.write("Beziers:%s\n" % (paths))
self.report_txt.write("\t<svg preserveAspectRatio=\"xMidYMax\" width=150 height=100 viewBox=\"120 330 474 262\">")
for p in paths:
self.report_txt.write("<path d=\"%s\" fill=\"red\"/>\n" % (p.to_svg()))
self.report_txt.write("</svg>\n")
And get the dud output:
This is definitely a big part of the build mismatch issue.... I suspect even though karakul DotAvoidance is placing the individual glyphs in the right space, the paths have been wrongly constructed and are creating a collision that shouldn't happen.
@simoncozens - I hope this helps short-cut some of your dev time and checks!
...and setting the scale_factor = 1.00
in the call in DotAvoidance seems like it's working (though some extra tolerance gap would probably be a good idea - like the original 1.22 value) 😎
self.c = Collidoscope(self.parser.font, { "marks": True, "bases": False, "faraway": True}, scale_factor = 1.00)
Will give some more thought to the scaling function of collidoscope, and will resume other tests later.
(Apologies if this is too much info)
By comparing the centroids of each step of the scale_factor
, it appears that combining the path transformations in one line produces the wrong result.
(scale_path
of collidoscope's __init__.py
)
transform = out * scale * in_
return transform * p
Will push the x,y origins further out, relative to the scale_factor.
Showing 2.0x, 1.5x, and 1.0x
Executing the transformations one at a time produces the expected centroid-centric result:
(scale_path
of collidoscope's __init__.py
)
p2 = out * p
p2 = scale * p2
p2 = in_ * p2
return p2
I would argue that using a centroid is not equivalent to expanding the stroke outwards - which should be the behaviour for collision detection. Other geometry libraries refer to this as a "buffer", while photo editors might use the term "expand" (as if the path has been stroked by a larger pen).
Even after making adjustments to the makefile, the font does not match released Gulzar.
As an example: سائنس (science) creates a clash in the HAMZA_ABOVE and the adjacent Nukta.
Examining the debug build with Crowbar (super helpful!!), there is an additional GSUB
rlig
stage after "connections" that invokes "DotAvoidance_reverse_top" that appears to detect "BEmsd2" and "HAMZA_ABOVE" and incorrectly change to "HAMZA_ABOVE.one" - creating the clash.This appears to originate from karakul's DotAvoidance.py - which detects "Old TE schema", and from what I understand triggers a dot_combinations rule (though I'm not 100% sure).
Is it possible I have is a Python library mistmatch since 2022-Nov-14?
What have I missed?