Similar to what we do with “post” table names. I’d have said the font is
broken, but all applications I tried it with loaded it just fine, though
they differed in which glyph to pick from the duplicate ones.
Fixes https://github.com/fonttools/fonttools/issues/1602
Introduced in CFF2 table, but code does not limit it to CFF2 table. If
FDSelect format is set to 4, it will be read/written regardless of the
table version.
This required a fix to fontTools/cffLib. When reading a CFF2 variable font (VF) from XML, the VF state in FontDict and PrivateDict does not get set. I made a temporary fix by adding a loop to set PrivateDict.vstore for all the PrivateDict objects after the XML file has been read. This should not be necessary, and in the near future I will revisit both this issue, and the related use of isCFF2 when compiling/decompiling.
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.
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
SimpleCFF2DEcompiler
CFF2CharString
GlobalSubrsIndex2
SubrsIndex2
CharstringIndex2
Working towards using one set of classes for both CFF2 and CFF data.