-
Notifications
You must be signed in to change notification settings - Fork 43
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
Produces invalid CFF OTF files under Python 3 (repro available) #306
Comments
thanks for filing this issue and providing an exact reproducer! Comparing the ttx dumps of the two fonts generated respectively from python2 and python3 I only get this difference in the FontMatrix operator of the CFF table: $ diff -Naur output{2,3}.ttx
--- output2.ttx 2019-01-03 21:40:37.000000000 +0000
+++ output3.ttx 2019-01-03 21:40:37.000000000 +0000
@@ -14,7 +14,7 @@
<!-- Most of this table will be recalculated by the compiler -->
<tableVersion value="1.0"/>
<fontRevision value="1.0"/>
- <checkSumAdjustment value="0x7def7972"/>
+ <checkSumAdjustment value="0x90ebc0da"/>
<magicNumber value="0x5f0f3cf5"/>
<flags value="00000000 00000011"/>
<unitsPerEm value="2816"/>
@@ -197,7 +197,7 @@
<UnderlineThickness value="220"/>
<PaintType value="0"/>
<CharstringType value="2"/>
- <FontMatrix value="0.000355113636364 0 0 0.000355113636364 0 0"/>
+ <FontMatrix value="0.0003551136363636364 0 0 0.0003551136363636364 0 0"/>
<FontBBox value="72 -660 1896 2708"/>
<StrokeWidth value="0"/>
<!-- charset is dumped separately as the 'GlyphOrder' element --> the extra precision of the python3 float division seems to trigger this invalid font file somehow. I'll investigate more tomorrow, but we probably need to make sure we round these floats to some reasonable number of digits. |
the culprit seems to be in the it's calling Your font UPEM is 2816 (which is not as common value as say 1000 or 2048, which perhaps explain why we haven't caught this issue before). The FontMatrix value If I apply this patch to round the FontMatrix values to 15 decimal digits, the output from python2 and python3 is identical, and both your test fonts pass validation: diff --git a/Lib/ufo2ft/outlineCompiler.py b/Lib/ufo2ft/outlineCompiler.py
index 60a4a97..965b575 100644
--- a/Lib/ufo2ft/outlineCompiler.py
+++ b/Lib/ufo2ft/outlineCompiler.py
@@ -983,7 +983,14 @@ class OutlineOTFCompiler(BaseOutlineCompiler):
topDict.UnderlineThickness = otRound(underlineThickness)
# populate font matrix
unitsPerEm = otRound(getAttrWithFallback(info, "unitsPerEm"))
- topDict.FontMatrix = [1.0 / unitsPerEm, 0, 0, 1.0 / unitsPerEm, 0, 0]
+ topDict.FontMatrix = [
+ round(1.0 / unitsPerEm, 15),
+ 0,
+ 0,
+ round(1.0 / unitsPerEm, 15),
+ 0,
+ 0,
+ ]
# populate the width values
if not any(hasattr(info, attr) and getattr(info, attr) is not None
for attr in ("postscriptDefaultWidthX", I'm thinking of rather changing the encodeFloat function in upstream fonttools, to prevent this and similar issues with real numbers in CFF dicts. In makeotf, they seem to round to 8 decimal digits I'll sleep over it, and push a fix for it tomorrow. |
Thank you! |
Patching I've updated the repro https://github.com/rsms/ufo2ft-py3-bug with:
|
the reason the other font also fails to render is related. If you compare the ttx dumps of --- /Users/clupo/Github/ufo2ft-py3-bug/build/output2-B.ttx 2019-01-04 11:27:18.000000000 +0000
+++ /Users/clupo/Github/ufo2ft-py3-bug/build/output3-B.ttx 2019-01-04 11:27:18.000000000 +0000
@@ -14,7 +14,7 @@
<!-- Most of this table will be recalculated by the compiler -->
<tableVersion value="1.0"/>
<fontRevision value="1.0"/>
- <checkSumAdjustment value="0x67a819be"/>
+ <checkSumAdjustment value="0x15b40627"/>
<magicNumber value="0x5f0f3cf5"/>
<flags value="00000000 00011011"/>
<unitsPerEm value="2816"/>
@@ -199,12 +199,12 @@
<FamilyName value="Inter UI"/>
<Weight value="Semi-bold"/>
<isFixedPitch value="0"/>
- <ItalicAngle value="-9.4"/>
+ <ItalicAngle value="-9.399999999999999"/>
<UnderlinePosition value="-422"/>
<UnderlineThickness value="286"/>
<PaintType value="0"/>
<CharstringType value="2"/>
- <FontMatrix value="0.000355113636364 0 0 0.000355113636364 0 0"/>
+ <FontMatrix value="0.0003551136363636364 0 0 0.0003551136363636364 0 0"/>
<FontBBox value="-59 -660 2051 2708"/>
<StrokeWidth value="0"/>
<!-- charset is dumped separately as the 'GlyphOrder' element --> i'll push a fix soon |
@rsms although, the latter issue with italicAngle you should be able to fix it yourself, but modifying that value in the fontinfo.plist. Currently it is set to |
What's causing the font to be declared invalid though? |
the real number as encoded in the CFF table by this is the output from python2.7 >>> str(1.0/2816)
'0.000355113636364'
>>> from fontTools.misc.psCharStrings import *
>>> encodeFloat(1.0/2816)
'\x1e\xa0\x005Q\x13ccd\xff'
>>> read_realNumber(None, None, b'\x1e\xa0\x005Q\x13ccd\xff', 1)
(0.000355113636364, 10) and this is the output from python3.7 >>> str(1.0/2816)
'0.0003551136363636364'
>>> from fontTools.misc.psCharStrings import *
>>> encodeFloat(1.0/2816)
b'\x1e\xa0\x005Q\x13ccccd\xff'
>>> read_realNumber(None, None, b'\x1e\xa0\x005Q\x13ccccd\xff', 1)
(0.0003551136363636364, 12) |
Interestingly, if I run the |
these are the two test fonts, one made with python2.7, the other with python3.7: If I run them through macOS Font Book.app's "Validate File...", the one made with python2.7 ("output2-B.otf") passes with all green, the other one "output3-B.otf" get the only difference between the two is the one I pasted in my previous comment: #306 (comment) |
the If we round to 15 decimal digits (which is plenty) in encodeFloat function in psCharStrings module, before converting the float to @behdad Shall I just do that? |
actually, I need to set it to maximum 14 decimal digits of precision. |
something like this patch works: diff --git a/Lib/fontTools/misc/psCharStrings.py b/Lib/fontTools/misc/psCharStrings.py
index 555d9d50..5e854549 100644
--- a/Lib/fontTools/misc/psCharStrings.py
+++ b/Lib/fontTools/misc/psCharStrings.py
@@ -225,7 +225,7 @@ def encodeFixed(f, pack=struct.pack):
def encodeFloat(f):
# For CFF only, used in cffLib
- s = str(f).upper()
+ s = str(round(f, 14)).upper()
if s[:2] == "0.":
s = s[1:]
elif s[:3] == "-0.": |
by trial and error, it appears that what triggers this issue is the length (in bytes) of the encoded real number. When it is up to 10 bytes, it is accepted; if it is > 10, it is rejected as invalid. maybe the encodeFloat should be capped to max 10 bytes? /cc @readroberts |
I've put together a better version of
https://gist.github.com/rsms/efd5663749a9b66bc53be8f85becc9d2#file-encode_float-py-L78 Comes with tests and benchmarks, using the number decoder from OTFCC for test reference. Benchmark results from a run on my machine:
(I do realize this is in the |
Patch submitted to the fonttools project: fonttools/fonttools#1430 |
patch was merged upstream, a new fonttools release should be out later today, after which we shall bump the requirement in ufo2ft and release the latter as well. |
released ufo2ft 2.6.0 |
…drop our temporary fonttools patch. Related to fonttools/fonttools#1430 and googlefonts/ufo2ft#306
What I did: Compiled a simple UFO to OTF (CFF) using ufo2ft 2.5.0 (
compileOTF(ufo, optimizeCFF=CFFOptimization.NONE)
)What I expected: Resulting OTF file to be valid as per OTS, FontVal and macOS Font Book.
What actually happened: OTF file is unreadable by macOS Font Book and fails validation by OTS.
If I compile the same source with the same settings using the same version of ufo2ft using Python 2.7 instead of Python 3.7 the resulting OTF file is valid — this seems to indicate a bug related to Python 3.
Full repro available here: https://github.com/rsms/ufo2ft-py3-bug
The text was updated successfully, but these errors were encountered: