afdko icon indicating copy to clipboard operation
afdko copied to clipboard

[buildmasterotfs] incompatible sources produced

Open frankrolf opened this issue 1 year ago • 5 comments

This scenario has been coming up in FDK workshops periodically. I explain that VFs by themselves do not support extrapolation, but it is possible to “fake” an extrapolating VF, by extrapolating static UFOs, and assigning those as new sources.

The problem is that sometimes those new sources are no longer compatible – in this specific scenario, an implied closepath (contour 0 point 0) is extrapolated into non-existence: implied_closepath

Since the buildmasterotfs tool is basically just running makeotf, there is no reason for the tool to insert a closepath when start- and endpoint of the contour are equal (as in this example). The result is a set of incompatible OTFs.

When the buildcff2vf step is taken, this is the output:

Reading source fonts...
Building variable OTF (CFF2) font...
The input set requires compatibilization. Please try again with the -c (--check-compat) option.

using the -c option:

Reading source fonts...
Checking outline compatibility in source fonts...
Traceback (most recent call last):
  File "/bin/buildcff2vf", line 8, in <module>
    sys.exit(main())
  File "/lib/python3.10/site-packages/afdko/buildcff2vf.py", line 564, in main
    do_compatibility(vf, font_list, ds_data.base_idx)
  File "/lib/python3.10/site-packages/afdko/buildcff2vf.py", line 332, in do_compatibility
    region_charstring.draw(compat_pen)
  File "/lib/python3.10/site-packages/fontTools/misc/psCharStrings.py", line 1148, in draw
    extractor.execute(self)
  File "/lib/python3.10/site-packages/fontTools/misc/psCharStrings.py", line 616, in execute
    super().execute(charString)
  File "/lib/python3.10/site-packages/fontTools/misc/psCharStrings.py", line 340, in execute
    rv = handler(index)
  File "/lib/python3.10/site-packages/fontTools/misc/psCharStrings.py", line 676, in op_rmoveto
    self.rMoveTo(self.popallWidth())
  File "/lib/python3.10/site-packages/fontTools/misc/psCharStrings.py", line 628, in rMoveTo
    self.pen.moveTo(self._nextPoint(point))
  File "/lib/python3.10/site-packages/fontTools/pens/basePen.py", line 324, in moveTo
    self._moveTo(pt)
  File "/lib/python3.10/site-packages/fontTools/varLib/cff.py", line 612, in _moveTo
    self.add_point("rmoveto", pt_coords)
  File "/lib/python3.10/site-packages/afdko/buildcff2vf.py", line 134, in add_point
    raise VarLibCFFPointTypeMergeError(
fontTools.varLib.errors.VarLibCFFPointTypeMergeError: Glyph 'sterling': 'rmoveto' at point index 14 in master index 1 differs from the default font point type 'rlineto'

While this particular scenario may not be all too common, the fact that buildmasterotfs is unaware of point compatibility concerns is a problem.

frankrolf avatar May 28 '24 13:05 frankrolf

To reproduce the problem, you may use the attached source files and run the following commands:

makeinstancesufo -ac -d 1_extrapolation.designspace
buildmasterotfs -d 2_Exercise2.designspace
buildcff2vf -d 2_Exercise2.designspace

1745.zip

frankrolf avatar May 28 '24 13:05 frankrolf

This happens because of generalizeCFF in buildmasterotfs. I'm not sure what the reason was for adding that, but maybe we should have a flag to skip it for situations like this. For CJK production I don't use buildmasterotfs because it's not needed at all. I just run makeotf.

punchcutter avatar Sep 10 '24 18:09 punchcutter

This should be addressed by https://github.com/adobe-type-tools/afdko/pull/1754 (I think?) @punchcutter ?

frankrolf avatar Jan 22 '25 22:01 frankrolf

I don't think so. That's only updating makeinstancesufo. The problem in this one is happening inside buildmasterotfs.

punchcutter avatar Jan 22 '25 22:01 punchcutter

🤦 My bad - should have read better, I guess

frankrolf avatar Jan 23 '25 11:01 frankrolf

Skef will look at generalizeCFF

skef avatar May 07 '25 21:05 skef

This actually happens before generalizeCFF when we produce the initial CFF using tx. I believe the problem traces to a coincidence: The instance has a zero length line at the end of a path. The ABF closepath function has code to remove path-closing lines that are too short, where a length of 0 counts as too short.

I think the solution will be to add a new flag at the same level as ABF_PATH_REMOVE_OVERLAP (the only existing flag for abfEndFont) to suppress this removal and then hook it up to some option to tx , which we can then pass from buildmasterotfs.

skef avatar May 08 '25 10:05 skef

I lost my nerve when discussing this with @punchcutter earlier today but I went back and I still think the issue does actually occur prior to generalizeCFF, even though removing the call to generalizeCFF allows the paths to interpolate.

Before the call to generlizeCFF each version of the glyph looks pretty much like this in ttx:

        <CharString name="sterling">
          581 39 hmoveto
          533 214 -220 4 hlineto
          16 26 -1 12 10 36 -15 46 34 vvcurveto
          55 25 1 22 8 13 -2 -21 19 vhcurveto
          134 135 rlineto
          68 -60 -63 24 -81 hhcurveto
          -144 -116 -92 -150 hvcurveto
          -33 36 -60 -38 vvcurveto
          -65 -62 -33 -44 -12 vhcurveto
          12 89 rmoveto
          420 150 -351 hlineto
          -69 -9 rlineto
          endchar
        </CharString>

Note that there's a vhcurveto right before the second (r)moveto, even though those paths end with a line. So what's happening here is that at this point the last line was already optimized away as unnecessary because CFF will close the path.

Then what happens is we call generalizeCFF, which among other things ensures that all paths are closed. It therefore adds the line back in in three out of four cases. In the fourth case the end of the path was already coincident with the start, so it does nothing. Now the paths don't interpolate.

Eliminating the first optimization should fix this.

skef avatar May 09 '25 07:05 skef

Ok, the problem was here. The fix is trivial -- just don't suppress the closing lineTo. The question is whether to add a flag to tx to turn this on or just do it by default.

Adding a flag isn't great because our flags are organized by output format rather than input format, so people might not notice its there. And it was a questionable thing to do in the first place.

On the other hand, changing it generally makes 75 tests "break" (not because anything is actually broken, but because the generated output doesn't match what we're checking against) ...

skef avatar May 09 '25 08:05 skef