def test_compile_version_11_as_float(self): vhea = self.font['vhea'] vhea.tableVersion = 1.0625 with CapturingLogHandler(log, "WARNING") as captor: self.assertEqual(VHEA_DATA_VERSION_11, vhea.compile(self.font)) self.assertTrue( len([r for r in captor.records if "Table version value is a float" in r.msg]) == 1)
def test_pairPos_enumRuleOverridenBySinglePair_DEBUG(self): logger = logging.getLogger("fontTools.otlLib.builder") with CapturingLogHandler(logger, "DEBUG") as captor: self.build("feature test {" " enum pos A [V Y] -80;" " pos A V -75;" "} test;") captor.assertRegex("Already defined position for pair A V at")
def test_addMultilingualName_noTTFont(self): # If the ttFont argument is not passed, the implementation # should add whatever names it can, but it should not crash # just because it cannot build an ltag table. nameTable = newTable("name") with CapturingLogHandler(log, "WARNING") as captor: nameTable.addMultilingualName({"en": "A", "la": "ⱾƤℚⱤ"}) captor.assertRegex("cannot store language la into 'ltag' table")
def test_dangling_layer(self): self.font.glyphs[0].layers[0].layerId = "yyy" self.font.glyphs[0].layers[0].associatedMasterId = "xxx" with CapturingLogHandler(self.logger, level="WARNING") as captor: to_ufos(self.font, minimize_glyphs_diffs=True) captor.assertRegex("is dangling and will be skipped")
def test_compile_version_10_as_float(self): hhea = self.font['hhea'] hhea.tableVersion = 1.0 with CapturingLogHandler(log, "WARNING") as captor: self.assertEqual(HHEA_DATA, hhea.compile(self.font)) self.assertTrue( len([r for r in captor.records if "Table version value is a float" in r.msg]) == 1)
def test_toXML_version_as_float(self): hhea = self.font['hhea'] hhea.tableVersion = 1.0 with CapturingLogHandler(log, "WARNING") as captor: self.assertEqual(getXML(hhea.toXML), HHEA_XML) self.assertTrue( len([r for r in captor.records if "Table version value is a float" in r.msg]) == 1)
def test_toXML_version_11_as_float(self): vhea = self.font['vhea'] vhea.tableVersion = 1.0625 with CapturingLogHandler(log, "WARNING") as captor: self.assertEqual(getXML(vhea.toXML), VHEA_XML_VERSION_11) self.assertTrue( len([r for r in captor.records if "Table version value is a float" in r.msg]) == 1)
def test_read_replace_not_ascii(self): reader = OTTableReader(b"Hello \xE4 world" + 100 * b"\0") with CapturingLogHandler(otConverters.log, "WARNING") as captor: data = self.converter.read(reader, self.font, {}) self.assertEqual(data, "Hello � world") self.assertEqual(reader.pos, 64) self.assertIn('replaced non-ASCII characters in "Hello � world"', [r.msg for r in captor.records])
def test_decompile_too_much_data(self): font = self.makeFont(numGlyphs=1, numberOfMetrics=1) mtxTable = newTable(self.tag) msg = "too much '%s' table data" % self.tag with CapturingLogHandler(log, "WARNING") as captor: mtxTable.decompile(b"\0\0\0\0\0", font) self.assertTrue(len([r for r in captor.records if msg == r.msg]) == 1)
def test_normal_layer(self): with CapturingLogHandler(self.logger, level="WARNING") as captor: to_ufos(self.font) # no warnings are emitted self.assertRaises(AssertionError, captor.assertRegex, "is dangling and will be skipped") self.assertRaises(AssertionError, captor.assertRegex, "layer without a name")
def test_decompilePoints_shouldAcceptBadPointNumbers(self): decompilePoints = TupleVariation.decompilePoints_ # 2 points; first run: [3, 9]. numPointsInGlyph = 8 with CapturingLogHandler(log, "WARNING") as captor: decompilePoints(numPointsInGlyph, deHexStr("02 01 03 06"), 0, "cvar") self.assertIn("point 9 out of range in 'cvar' table", [r.msg for r in captor.records])
def test_warn_no_version(self): """Test that a warning is printed when app version is missing.""" font = generate_minimal_font() font.appVersion = "0" with CapturingLogHandler(builder.logger, "WARNING") as captor: to_ufos(font) self.assertEqual( len([r for r in captor.records if "outdated version" in r.msg]), 1)
def test_fromXML_version_as_float(self): hhea = self.font['hhea'] with CapturingLogHandler(log, "WARNING") as captor: for name, attrs, content in parseXML(HHEA_XML_VERSION_AS_FLOAT): hhea.fromXML(name, attrs, content, self.font) self.assertTrue( len([r for r in captor.records if "Table version value is a float" in r.msg]) == 1) for key in hhea.__dict__: self.assertEqual(getattr(hhea, key), HHEA_AS_DICT[key])
def test_empty_string(self): inputstr = '' with CapturingLogHandler(builder.logger, "ERROR") as captor: result = parse_glyphs_filter(inputstr) self.assertGreater(len([ r for r in captor.records if 'Failed to parse glyphs filter' in r.msg ]), 0, msg='Empty string should trigger an error message')
def test_decompile_num_metrics_greater_than_glyphs(self): font = self.makeFont(numGlyphs=1, numberOfMetrics=2) mtxTable = newTable(self.tag) msg = "The %s.%s exceeds the maxp.numGlyphs" % ( self.tableClass.headerTag, self.tableClass.numberOfMetricsName) with CapturingLogHandler(log, "WARNING") as captor: mtxTable.decompile(b"\0\0\0\0", font) self.assertTrue(len([r for r in captor.records if msg == r.msg]) == 1)
def test_multipleSubst_multipleIdenticalSubstitutionsForSameGlyph_info(self): logger = logging.getLogger("fontTools.feaLib.builder") with CapturingLogHandler(logger, "INFO") as captor: self.build( "feature test {" " sub f_f_i by f f i;" " sub c_t by c t;" " sub f_f_i by f f i;" "} test;") captor.assertRegex(r"Removing duplicate multiple substitution from glyph \"f_f_i\" to \('f', 'f', 'i'\)")
def test_singleSubst_multipleIdenticalSubstitutionsForSameGlyph_info(self): logger = logging.getLogger("fontTools.feaLib.builder") with CapturingLogHandler(logger, "INFO") as captor: self.build("feature test {" " sub A by A.sc;" " sub B by B.sc;" " sub A by A.sc;" "} test;") captor.assertRegex( 'Removing duplicate single substitution from glyph "A" to "A.sc"')
def test_no_name(self): inputstr = ";OffsetX:2" with CapturingLogHandler(builder.logger, "ERROR") as captor: parse_glyphs_filter(inputstr) self.assertGreater( len( [r for r in captor.records if "Failed to parse glyphs filter" in r.msg] ), 0, msg="Empty string with no filter name should trigger an error message", )
def test_xmlWrite_missingID(self): writer = makeXMLWriter() with CapturingLogHandler(otConverters.log, "WARNING") as captor: self.converter.xmlWrite(writer, self.makeFont(), 666, "Entity", [("attrib", "val")]) self.assertIn("name id 666 missing from name table", [r.msg for r in captor.records]) xml = writer.file.getvalue().decode("utf-8").rstrip() self.assertEqual( xml, '<Entity attrib="val"' ' value="666"/> <!-- missing from name table -->')
def test_toXML_badDeltaFormat(self): writer = XMLWriter(BytesIO()) g = TupleVariation(AXES, ["String"]) with CapturingLogHandler(log, "ERROR") as captor: g.toXML(writer, ["wdth"]) self.assertIn("bad delta format", [r.msg for r in captor.records]) self.assertEqual([ '<tuple>', '<coord axis="wdth" min="0.3" value="0.4" max="0.5"/>', '<!-- bad delta #0 -->', '</tuple>', ], TupleVariationTest.xml_lines(writer))
def test_GSUB_merge_with_different_dups_and_no_second_GSUB(self): self.table2 = NotImplemented self.merger.duplicateGlyphsPerFont = [{}, {'uni0032#0': 'uni0032#1'}] self.merger.fonts = [] self.merger.fontfiles = ['font1', 'font2'] with mockIsGlyphSame(False): with CapturingLogHandler(fontTools.merge.log, "WARNING") as captor: self.mergedTable.merge(self.merger, [self.table1, self.table2]) table = self.mergedTable.table self.assertEqual(table, self.table1.table) self.assertTrue(len([r for r in captor.records if r.msg != '']) > 0)
def test_compile_negative_advance(self): font = self.makeFont(numGlyphs=1, numberOfMetrics=1) mtxTable = font[self.tag] = newTable(self.tag) mtxTable.metrics = {'A': [-1, 0]} with CapturingLogHandler(log, "ERROR") as captor: with self.assertRaisesRegex(TTLibError, "negative advance"): mtxTable.compile(font) self.assertTrue( len([r for r in captor.records if "Glyph 'A' has negative advance" in r.msg]) == 1)
def test_unsupported_subtable_break(self): logger = logging.getLogger("fontTools.otlLib.builder") with CapturingLogHandler(logger, level='WARNING') as captor: self.build("feature test {" " pos a 10;" " subtable;" " pos b 10;" "} test;") captor.assertRegex( '<features>:1:32: unsupported "subtable" statement for lookup type' )
def test_unsupported_subtable_break_1(ttfont): location = MockBuilderLocation((0, "alpha")) logger = logging.getLogger("fontTools.otlLib.builder") with CapturingLogHandler(logger, "INFO") as captor: builder = SinglePosBuilder(ttfont, location) builder.add_subtable_break(MockBuilderLocation((5, "beta"))) builder.build() captor.assertRegex( '5:beta: unsupported "subtable" statement for lookup type')
def test_duplicate_exclude_include(self): inputstr = 'thisisaname;34;-3.4;exclude:uni1111;include:uni0022;exclude:uni2222' expected = { 'name': 'thisisaname', 'args': [34, -3.4], 'exclude': ['uni2222'], } with CapturingLogHandler(builder.logger, "ERROR") as captor: result = parse_glyphs_filter(inputstr) self.assertGreater(len([r for r in captor.records if 'can only present as the last argument' in r.msg]), 0, msg='The parse_glyphs_filter should warn user that the exclude/include should only be the last argument in the filter string.') self.assertEqual(result, expected)
def test_decompile_possibly_negative_advance(self): font = self.makeFont(numGlyphs=1, numberOfMetrics=1) # we warn if advance is > 0x7FFF as it might be interpreted as signed # by some authoring tools data = deHexStr("8000 0000") mtxTable = newTable(self.tag) with CapturingLogHandler(log, "WARNING") as captor: mtxTable.decompile(data, font) self.assertTrue( len([r for r in captor.records if "has a huge advance" in r.msg]) == 1)
def test_decompile_offset_past_end(empty_font): empty_font.glyphOrder = ['foo', 'bar'] content = 'baz' data = tobytes(content) empty_font['TSI0'].indices = [(0, len(data), 0), (1, 1, len(data) + 1)] table = table_T_S_I__1() with CapturingLogHandler(table.log, "WARNING") as captor: table.decompile(data, empty_font) # the 'bar' program is skipped because its offset > len(data) assert table.glyphPrograms == {'foo': 'baz'} assert any("textOffset > totalLength" in r.msg for r in captor.records)
def test_addMultilingualName_legacyMacEncoding(self): # Windows has no language code for Latin; MacOS has a code; # and we actually can convert the name to the legacy MacRoman # encoding. In this case, we expect that the name gets encoded # as Macintosh name (platformID 1) with the corresponding Mac # language code (133); the 'ltag' table should not be used. font = FakeFont(glyphs=[".notdef", "A"]) nameTable = font.tables['name'] = newTable("name") with CapturingLogHandler(log, "WARNING") as captor: nameTable.addMultilingualName({"la": "SPQR"}, ttFont=font) captor.assertRegex("cannot add Windows name in language la") self.assertEqual(names(nameTable), [(256, 1, 0, 131, "SPQR")]) self.assertNotIn("ltag", font.tables.keys())
def test_addMultilingualName_legacyMacEncodingButUnencodableName(self): # Windows has no language code for Latin; MacOS has a code; # but we cannot encode the name into this encoding because # it contains characters that are not representable. # In this case, we expect that the name gets encoded as # Unicode name (platformID 0) with the language tag being # added to the 'ltag' table. font = FakeFont(glyphs=[".notdef", "A"]) nameTable = font.tables['name'] = newTable("name") with CapturingLogHandler(log, "WARNING") as captor: nameTable.addMultilingualName({"la": "ⱾƤℚⱤ"}, ttFont=font) captor.assertRegex("cannot add Windows name in language la") self.assertEqual(names(nameTable), [(256, 0, 4, 0, "ⱾƤℚⱤ")]) self.assertIn("ltag", font.tables) self.assertEqual(font["ltag"].tags, ["la"])
def test_save_in_place_invalid_ufo(self): path = makeTestFontCopy() font = Font(path) layercontents = os.path.join(path, "layercontents.plist") os.remove(layercontents) self.assertFalse(os.path.exists(layercontents)) logger = logging.getLogger("defcon.objects.font") with CapturingLogHandler(logger, level="ERROR") as captor: font.save() captor.assertRegex("Invalid ufo found") self.assertTrue(os.path.exists(layercontents)) font = Font(path) _ = font.layers font.save()