Skip to content
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

'NoneType' object has no attribute 'nominalWidthX' for empty glyph in FontBuilder #3245

Open
NightFurySL2001 opened this issue Aug 6, 2023 Discussed in #3231 · 3 comments

Comments

@NightFurySL2001
Copy link
Contributor

NightFurySL2001 commented Aug 6, 2023

Discussed in #3231

Originally posted by NightFurySL2001 July 30, 2023
Is it normal for FontBuilder to throw a 'NoneType' object has no attribute 'nominalWidthX' error when calculating bounds of an empty glyph?
(E.g. passing an empty SVG to SVGPen and then use the charstring generated from it.)

The code in relation is this which is given in the FontBuilder example:

# calculat lsb
lsb = {gn: cs.calcBounds(None)[0] for gn, cs in glyphs.items()}

This raises TypeError: 'NoneType' object is not subscriptable, so when I try to isolate the issue it comes down to cs.calcBounds(None) gives 'NoneType' object has no attribute 'nominalWidthX' for cs of empty glyph.

I'm building with OpenType/CFF. Slightly related to #1558 .


It seems like when building a CFF table in FontBuilder, doing charString.calcBounds(None) on an empty glyph raise an error of 'NoneType' object has no attribute 'nominalWidthX'. As pointed out by @TakWolf the empty glyph is equivalent to the following command:

pen = T2CharStringPen(600, None)
pen.closePath()

or

<CharString name="uni0020">
  endchar
</CharString>

which as stated in the CFF Type 2 Charstring Format spec is a valid operation, where nominalWidthX might not be present if the glyph should use the value of defaultWidthX in CFF data.

@TakWolf
Copy link

TakWolf commented Aug 6, 2023

TTF:

pen = TTGlyphPen()
pen.closePath()

This will cause:

  File "/Users/takwolf/Develop/Python/pixel-font-builder/venv/lib/python3.11/site-packages/fontTools/pens/ttGlyphPen.py", line 244, in closePath
    if self.points[startPt] == self.points[endPt]:
       ~~~~~~~~~~~^^^^^^^^^
IndexError: list index out of range

OTF:

pen = T2CharStringPen(width, None)
pen.closePath()

this will cause error on get lsb:

lsb = glyphs[glyph_name].calcBounds(None)[0]
TypeError: 'NoneType' object is not subscriptable

https://learn.microsoft.com/en-us/typography/opentype/spec/cff2charstr#42-finishing-a-charstring-outline-definition

The smallest valid CharString is simply an empty byte string.

@behdad
Copy link
Member

behdad commented Aug 6, 2023

The bounds are None for empty glyph indeed.

Try:

diff --git a/Lib/fontTools/fontBuilder.py b/Lib/fontTools/fontBuilder.py
index dd57a0507..9d06680af 100644
--- a/Lib/fontTools/fontBuilder.py
+++ b/Lib/fontTools/fontBuilder.py
@@ -117,6 +117,7 @@ charStrings = {
 }
 fb.setupCFF(nameStrings["psName"], {"FullName": nameStrings["psName"]}, charStrings, {})
 lsb = {gn: cs.calcBounds(None)[0] for gn, cs in charStrings.items()}
+lsb = {gn: l if l is not None else 0 for gn, l in lsb.items()}
 metrics = {}
 for gn, advanceWidth in advanceWidths.items():
     metrics[gn] = (advanceWidth, lsb[gn])

@NightFurySL2001
Copy link
Contributor Author

I might suggest to fix it as such:

fb.setupCFF(nameStrings["psName"], {"FullName": nameStrings["psName"]}, charStrings, {})
lsb = {gn: cs.calcBounds(None)[0] if cs.calcBounds(None) is not None else 0 for gn, cs in charStrings.items()}
metrics = {}
for gn, advanceWidth in advanceWidths.items():
    metrics[gn] = (advanceWidth, lsb[gn])

or

fb.setupCFF(nameStrings["psName"], {"FullName": nameStrings["psName"]}, charStrings, {})
lsb = {}
for gn, cs in charStrings.items():
    lsb[gn] = cs.calcBounds(None)[0] if cs.calcBounds(None) is not None else 0
metrics = {}
for gn, advanceWidth in advanceWidths.items():
    metrics[gn] = (advanceWidth, lsb[gn])

since subscripting None in cs.calcBounds(None)[0] caused the error mentioned above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants