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

Traverse when there is no layer list #2441

Merged
merged 1 commit into from Nov 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions Lib/fontTools/misc/testTools.py
Expand Up @@ -38,6 +38,14 @@ def parseXML(xmlSnippet):
return reader.root[2]


def parseXmlInto(font, parseInto, xmlSnippet):
parsed_xml = [e for e in parseXML(xmlSnippet.strip()) if not isinstance(e, str)]
for name, attrs, content in parsed_xml:
parseInto.fromXML(name, attrs, content, font)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use four space indentation

parseInto.populateDefaults()
return parseInto


class FakeFont:
def __init__(self, glyphs):
self.glyphOrder_ = glyphs
Expand Down
6 changes: 5 additions & 1 deletion Lib/fontTools/ttLib/tables/otTables.py
Expand Up @@ -1516,7 +1516,11 @@ def toXML(self, xmlWriter, font, attrs=None, name=None):

def getChildren(self, colr):
if self.Format == PaintFormat.PaintColrLayers:
return colr.LayerList.Paint[
# https://github.com/fonttools/fonttools/issues/2438: don't die when no LayerList exists
layers = []
if colr.LayerList is not None:
layers = colr.LayerList.Paint
return layers[
self.FirstLayerIndex : self.FirstLayerIndex + self.NumLayers
]

Expand Down
29 changes: 28 additions & 1 deletion Tests/ttLib/tables/otTables_test.py
@@ -1,4 +1,4 @@
from fontTools.misc.testTools import getXML, parseXML, FakeFont
from fontTools.misc.testTools import getXML, parseXML, parseXmlInto, FakeFont
from fontTools.misc.textTools import deHexStr, hexStr
from fontTools.misc.xmlWriter import XMLWriter
from fontTools.ttLib.tables.otBase import OTTableReader, OTTableWriter
Expand Down Expand Up @@ -687,6 +687,33 @@ def test_splitMarkBasePos():
]


class ColrV1Test(unittest.TestCase):
def setUp(self):
self.font = FakeFont(['.notdef', 'meh'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Four space indentation please


def test_traverseEmptyPaintColrLayersNeedsNoLayerList(self):
colr = parseXmlInto(self.font, otTables.COLR(),
'''
<Version value="1"/>
<BaseGlyphList>
<BaseGlyphPaintRecord index="0">
<BaseGlyph value="meh"/>
<Paint Format="1"><!-- PaintColrLayers -->
<NumLayers value="0"/>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@khaledhosny @rsheeter hm, a NumLayers=0 is very odd, as the whole PaintColrLayers is no-op basically. Did nanoemoji produce that? Maybe the input SVG was completely empty (e.g. a whitespace glyph). I think in this case, we don't want to emit anything, i.e. the BaseGlyphPaintRecord.Paint should be None, not a no-op PaintColrLayers with NumLayers=0.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that indeed the case. The glyph in question is space glyph and the SVG produced by picosvg is:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 130.0 1500.0" width="130.0" height="1500.0">
  <defs/>
</svg>

<FirstLayerIndex value="42"/>
</Paint>
</BaseGlyphPaintRecord>
</BaseGlyphList>
''')
paint = colr.BaseGlyphList.BaseGlyphPaintRecord[0].Paint

# Just want to confirm we don't crash
visited = []
paint.traverse(colr, lambda p: visited.append(p))
assert len(visited) == 1



if __name__ == "__main__":
import sys
sys.exit(unittest.main())