The dunder method doesn't seem to be doing anything other than providing
an `in_cff2` attribute. Do that with a property instead of bending
__getattr__.
This one confused me when I was working on
https://github.com/fonttools/fonttools/pull/1488.
We need to raise AttributeError for non-existing dunder methods like
'__deepcopy__' or '__getstate__', because deepcopy() and pickle.load()
test for these on the instance using getattr() and treat the resulting
AttributeError as a signal that the object doesn't implement these custom
hooks. If we don't do that, we enter an infinite recursion as we attempt
to look up the missing dunder methods in the 'rawDict' dictionary,
because 'rawDict' is set inside __init__, but __init__ is not invoked
while unpickling (only __new__ is); thus self.rawDict is also missing
and __getattr__ is invoked with argument 'rawDict' again and again until
it crashes with RecursionError. Phew.
Fixes https://github.com/fonttools/fonttools/pull/1488
@bedhad
Address issues raised in #1403
I do think setting the dummy CFF2 PrivateDict nominalWidthX and defaultWidthX to None, which leads to the charstring.width also being None, is a good idea. I originally set them to 0, which produces a charstring width of 0, in order to avoid problems with logic that assumes that the field is good for math. However, I now think that it is better to find errors around charstring type assumptions earlier than later.
"drop_hints()" is actually not wrong - I did look at this when making the changes. For CFF2 charstrings, self.width is always equal to self.private.defaultWidthX, so the width is never inserted. This is because in psCharstrings.py::T2WidthExtractor.popallWidth(), the test "evenOdd ^ (len(args) % 2)" is alway False. Left to myself, I would not change this code. If the CFF2 charstring is correct, there is not a problem. if the CFF2 charstring is not correct, then both in drop_hints() and in T2WidthExtractor.popallWidth(), the logic will stack dump. I did add asserts, but am not totally sure it is worth the extra calls.
Removed check_program functions. Supporting these requires knowledge of CFF vs CFF2 state, whci is leads to wide-spread diffuse changes. Also, not needed - the endchar/return opcodes are removed when compiling for CFF2.
Removed CFF2Subr class. This was used for CFF2 CharStrings, and allowed avoiding referencing the width fields. I worked around this by providing dummy values for the Private.nominalWidthX and defaultWidthX.
Added a public method PrivateDict.in_cff2.
Fix syntax error reported by build system: can't mix string string types when doing literal concatenation
Fix local import reference - doesn't work in Python3.
Addressed issues raised by @msousa for PR 1345 yesterday.
Will change cff2_merge_funcs.py and cff2mergePen.py from tab to space indentations after the current comments are resolved.
Add various improvements from comments:
- do not edit the post table under varLib.build(). Setting post table format 2 or 3 is now expected to be managed by whatever calls varLib.build().
- In the t2CharStringPen module, rename closure _round() nested in makeRoundFunc to an exportable function, and use it in cff2mergePen.
- remove TypeSupply copyright from cff2mergePen.
- use modulo function to convert float to int when it is meant to be 0 in cff2mergePen.
cff2_merge_funcs.py:merge_PrivateDicts() should only be blending the hint related fields in the PrivateDict. This oversight that was surfaced by @madig reporting an error building his Cantrell font. The bug appeared when the font was subroutinized, as the pen draw method then has to interpret the Subr field in order to access T2Charstring subroutines.
Fix expected ttx output file. When I removed the logic to add glyph names to the post table, glyph names in the ttx file changed.
Miguel prefers a simple list for readability in cff2_merge_funs.py:138.
I'm also unimpressed by the copy-pasted bounds logic in hhea and vhea,
and the fact that that's coded in there instead of calling a function
on CFF / glyf tables respectively.
This also fixes fonttools/fonttools/issues/1030.
Although the roundtrip is generally possible when a VariationStore is built from source font data using the Superpolator model, it is possible to build region definitions that do not follow this model. Behdad cited the Skia "Q" example, where the tail of the Q is affected by two regions defined as:
min=0 peak=0.5 max=0.51 delta=+10
min=0.49 peak=0.5 max=0.51 delta=-10
There can only be one TopDict in an OpenType font, whether CFF or CFF2;
plus in the latter, TopDict INDEX and Names INDEX are gone, just the
one TopDict is left. Most of the time, one simple wants to get to
that single TopDict instance.
So instead of doing this:
topDict = font['CFF '].cff.values()[0]
one can alternatively do this now:
topDict = font['CFF '].cff[0]
* Removed `CFFContext`
* Added `isCFF2` argument to CFFFontSet.decompile/compile, used from
respective ttLib classes
* Index classes get a `isCFF2` argument in constructor (used for
decompiling); must be True/False if `file` argument is not None;
it is stored as self._isCFF2 to support lazy loading
* Removed `TopDictData` class; reuse same `TopDictIndexCompiler` for
both CFF and CFF2
* `CFFWriter` and all `*Compiler` classes get an `isCFF2` argument;
defaults to the parent compiler's `isCFF2` attribute
* Removed `size` argument from `produceItem` method as unused and
useless (`len(data)` is the same)
* psCharStrings: removed useless ByteCodeBase class
* A reference to the TopDict's VarStoreData is passed down to all
the FontDicts' PrivateDict, so it can be used to get the number of
regions while decompiling blend and vsindex operators
See dicussion:
https://github.com/fonttools/fonttools/pull/968#issuecomment-309920007