def test_glyph_decomposes(self): componentName = "a" glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() glyphSet[componentName] = _TestGlyph(pen.glyph()) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() pen.addComponent(componentName, (1, 0, 0, 1, 2, 0)) pen.addComponent("missing", (1, 0, 0, 1, 0, 0)) # skipped compositeGlyph = pen.glyph() pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() pen.moveTo((2, 0)) pen.lineTo((2, 1)) pen.lineTo((3, 0)) pen.closePath() plainGlyph = pen.glyph() assert plainGlyph == compositeGlyph
def test_out_of_range_transform_decomposed(self): componentName = "a" glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() glyphSet[componentName] = _TestGlyph(pen.glyph()) pen.addComponent(componentName, (3, 0, 0, 2, 0, 0)) pen.addComponent(componentName, (1, 0, 0, 1, -1, 2)) pen.addComponent(componentName, (2, 0, 0, -3, 0, 0)) compositeGlyph = pen.glyph() pen.moveTo((0, 0)) pen.lineTo((0, 2)) pen.lineTo((3, 0)) pen.closePath() pen.moveTo((-1, 2)) pen.lineTo((-1, 3)) pen.lineTo((0, 2)) pen.closePath() pen.moveTo((0, 0)) pen.lineTo((0, -3)) pen.lineTo((2, 0)) pen.closePath() expectedGlyph = pen.glyph() assert expectedGlyph == compositeGlyph
def test_scaled_component_bounds(self): glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((-231, 939)) pen.lineTo((-55, 939)) pen.lineTo((-55, 745)) pen.lineTo((-231, 745)) pen.closePath() glyphSet["gravecomb"] = pen.glyph() pen = TTGlyphPen(glyphSet) pen.moveTo((-278, 939)) pen.lineTo((8, 939)) pen.lineTo((8, 745)) pen.lineTo((-278, 745)) pen.closePath() glyphSet["circumflexcomb"] = pen.glyph() pen = TTGlyphPen(glyphSet) pen.addComponent("circumflexcomb", (1, 0, 0, 1, 0, 0)) pen.addComponent("gravecomb", (0.9, 0, 0, 0.9, 198, 180)) glyphSet["uni0302_uni0300"] = uni0302_uni0300 = pen.glyph() uni0302_uni0300.recalcBounds(glyphSet) self.assertGlyphBoundsEqual(uni0302_uni0300, (-278, 745, 148, 1025))
def test_out_of_range_transform_decomposed(self): componentName = 'a' glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() glyphSet[componentName] = _TestGlyph(pen.glyph()) pen.addComponent(componentName, (3, 0, 0, 2, 0, 0)) pen.addComponent(componentName, (1, 0, 0, 1, -1, 2)) pen.addComponent(componentName, (2, 0, 0, -3, 0, 0)) compositeGlyph = pen.glyph() pen.moveTo((0, 0)) pen.lineTo((0, 2)) pen.lineTo((3, 0)) pen.closePath() pen.moveTo((-1, 2)) pen.lineTo((-1, 3)) pen.lineTo((0, 2)) pen.closePath() pen.moveTo((0, 0)) pen.lineTo((0, -3)) pen.lineTo((2, 0)) pen.closePath() expectedGlyph = pen.glyph() self.assertEqual(expectedGlyph, compositeGlyph)
def test_glyph_decomposes(self): componentName = 'a' glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() glyphSet[componentName] = _TestGlyph(pen.glyph()) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() pen.addComponent(componentName, (1, 0, 0, 1, 2, 0)) compositeGlyph = pen.glyph() pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() pen.moveTo((2, 0)) pen.lineTo((2, 1)) pen.lineTo((3, 0)) pen.closePath() plainGlyph = pen.glyph() plainGlyph.program = compositeGlyph.program self.assertEqual(plainGlyph, compositeGlyph)
def test_glyph_decomposes(self): componentName = 'a' glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() glyphSet[componentName] = _TestGlyph(pen.glyph()) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() pen.addComponent(componentName, (1, 0, 0, 1, 2, 0)) compositeGlyph = pen.glyph() pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() pen.moveTo((2, 0)) pen.lineTo((2, 1)) pen.lineTo((3, 0)) pen.closePath() plainGlyph = pen.glyph() self.assertEqual(plainGlyph, compositeGlyph)
def test_trim_remove_hinting_composite_glyph(self): glyphSet = {"dummy": TTGlyphPen(None).glyph()} pen = TTGlyphPen(glyphSet) pen.addComponent("dummy", (1, 0, 0, 1, 0, 0)) composite = pen.glyph() p = ttProgram.Program() p.fromAssembly(['SVTCA[0]']) composite.program = p glyphSet["composite"] = composite glyfTable = newTable("glyf") glyfTable.glyphs = glyphSet glyfTable.glyphOrder = sorted(glyphSet) composite.compact(glyfTable) self.assertTrue(hasattr(composite, "data")) # remove hinting from the compacted composite glyph, without expanding it composite.trim(remove_hinting=True) # check that, after expanding the glyph, we have no instructions composite.expand(glyfTable) self.assertFalse(hasattr(composite, "program")) # now remove hinting from expanded composite glyph composite.program = p composite.trim(remove_hinting=True) # check we have no instructions self.assertFalse(hasattr(composite, "program")) composite.compact(glyfTable)
def process_glyf(self) -> None: """ Processes glyf table """ if self.ttf_components: glyf = self.font["glyf"] gs = self.font.getGlyphSet() for glyph_name in self.font.glyphOrder: if glyph_name in self.keep_g_names: continue pen = TTGlyphPen(gs) pen.addComponent(self.replacer, (1, 0, 0, 1, 0, 0)) glyf[glyph_name] = pen.glyph() else: self._process_base(self.font["glyf"]) return None
def test_recursiveComponent(self): glyphSet = {} pen_dummy = TTGlyphPen(glyphSet) glyph_dummy = pen_dummy.glyph() glyphSet["A"] = glyph_dummy glyphSet["B"] = glyph_dummy pen_A = TTGlyphPen(glyphSet) pen_A.addComponent("B", (1, 0, 0, 1, 0, 0)) pen_B = TTGlyphPen(glyphSet) pen_B.addComponent("A", (1, 0, 0, 1, 0, 0)) glyph_A = pen_A.glyph() glyph_B = pen_B.glyph() glyphSet["A"] = glyph_A glyphSet["B"] = glyph_B with self.assertRaisesRegex(TTLibError, "glyph '.' contains a recursive component reference"): glyph_A.getCoordinates(glyphSet)
def test_no_handle_overflowing_transform(self): componentName = "a" glyphSet = {} pen = TTGlyphPen(glyphSet, handleOverflowingTransforms=False) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() baseGlyph = pen.glyph() glyphSet[componentName] = _TestGlyph(baseGlyph) pen.addComponent(componentName, (3, 0, 0, 1, 0, 0)) compositeGlyph = pen.glyph() assert compositeGlyph.components[0].transform == ((3, 0), (0, 1)) with pytest.raises(struct.error): compositeGlyph.compile({"a": baseGlyph})
def test_no_handle_overflowing_transform(self): componentName = 'a' glyphSet = {} pen = TTGlyphPen(glyphSet, handleOverflowingTransforms=False) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() baseGlyph = pen.glyph() glyphSet[componentName] = _TestGlyph(baseGlyph) pen.addComponent(componentName, (3, 0, 0, 1, 0, 0)) compositeGlyph = pen.glyph() self.assertEqual(compositeGlyph.components[0].transform, ((3, 0), (0, 1))) with self.assertRaises(struct.error): compositeGlyph.compile({'a': baseGlyph})
def makeGlyfBBox1(calcBBoxes=True, composite=False): font = getTTFont(sfntTTFSourcePath, recalcBBoxes=calcBBoxes) glyf = font["glyf"] hmtx = font["hmtx"] for name in ("bbox1", "bbox2"): pen = TTGlyphPen(None) if name == "bbox1": pen.moveTo((0, 0)) pen.lineTo((0, 1000)) pen.lineTo((1000, 1000)) pen.lineTo((1000, 0)) pen.closePath() else: pen.moveTo((0, 0)) pen.qCurveTo((500, 750), (600, 500), (500, 250), (0, 0)) pen.closePath() glyph = pen.glyph() if not calcBBoxes: glyph.recalcBounds(glyf) glyph.xMax -= 100 glyf.glyphs[name] = glyph hmtx.metrics[name] = (0, 0) glyf.glyphOrder.append(name) if composite: name = "bbox3" pen = TTGlyphPen(glyf.glyphOrder) pen.addComponent("bbox1", [1, 0, 0, 1, 0, 0]) pen.addComponent("bbox2", [1, 0, 0, 1, 1000, 0]) glyph = pen.glyph() glyph.recalcBounds(glyf) glyf.glyphs[name] = glyph hmtx.metrics[name] = (0, 0) glyf.glyphOrder.append(name) tableData = getSFNTData(font)[0] font.close() del font header, directory, tableData = defaultSFNTTestData(tableData=tableData, flavor="TTF") data = packSFNT(header, directory, tableData, flavor="TTF") return data
def test_round_float_coordinates_and_component_offsets(self): glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((367.6, 0)) pen.closePath() simpleGlyph = pen.glyph() simpleGlyph.recalcBounds(glyphSet) self.assertGlyphBoundsEqual(simpleGlyph, (0, 0, 368, 1)) componentName = "a" glyphSet[componentName] = simpleGlyph pen.addComponent(componentName, (1, 0, 0, 1, -86.4, 0)) compositeGlyph = pen.glyph() compositeGlyph.recalcBounds(glyphSet) self.assertGlyphBoundsEqual(compositeGlyph, (-86, 0, 282, 1))
def test_within_range_component_transform(self): componentName = "a" glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() glyphSet[componentName] = _TestGlyph(pen.glyph()) pen.addComponent(componentName, (1.5, 0, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 0, -1.5, 0, 0)) compositeGlyph = pen.glyph() pen.addComponent(componentName, (1.5, 0, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 0, -1.5, 0, 0)) expectedGlyph = pen.glyph() assert expectedGlyph == compositeGlyph
def test_within_range_component_transform(self): componentName = 'a' glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() glyphSet[componentName] = _TestGlyph(pen.glyph()) pen.addComponent(componentName, (1.5, 0, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 0, -1.5, 0, 0)) compositeGlyph = pen.glyph() pen.addComponent(componentName, (1.5, 0, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 0, -1.5, 0, 0)) expectedGlyph = pen.glyph() self.assertEqual(expectedGlyph, compositeGlyph)
def test_clamp_to_almost_2_component_transform(self): componentName = "a" glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() glyphSet[componentName] = _TestGlyph(pen.glyph()) pen.addComponent(componentName, (1.99999, 0, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 2, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 2, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 0, 2, 0, 0)) pen.addComponent(componentName, (-2, 0, 0, -2, 0, 0)) compositeGlyph = pen.glyph() almost2 = MAX_F2DOT14 # 0b1.11111111111111 pen.addComponent(componentName, (almost2, 0, 0, 1, 0, 0)) pen.addComponent(componentName, (1, almost2, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 0, almost2, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 0, almost2, 0, 0)) pen.addComponent(componentName, (-2, 0, 0, -2, 0, 0)) expectedGlyph = pen.glyph() assert expectedGlyph == compositeGlyph
def test_getCompositeMaxpValues(self): # https://github.com/fonttools/fonttools/issues/2044 glyphSet = {} pen = TTGlyphPen(glyphSet) # empty non-composite glyph glyphSet["fraction"] = pen.glyph() glyphSet["zero.numr"] = pen.glyph() pen = TTGlyphPen(glyphSet) pen.addComponent("zero.numr", (1, 0, 0, 1, 0, 0)) glyphSet["zero.dnom"] = pen.glyph() pen = TTGlyphPen(glyphSet) pen.addComponent("zero.numr", (1, 0, 0, 1, 0, 0)) pen.addComponent("fraction", (1, 0, 0, 1, 0, 0)) pen.addComponent("zero.dnom", (1, 0, 0, 1, 0, 0)) glyphSet["percent"] = pen.glyph() pen = TTGlyphPen(glyphSet) pen.addComponent("zero.numr", (1, 0, 0, 1, 0, 0)) pen.addComponent("fraction", (1, 0, 0, 1, 0, 0)) pen.addComponent("zero.dnom", (1, 0, 0, 1, 0, 0)) pen.addComponent("zero.dnom", (1, 0, 0, 1, 0, 0)) glyphSet["perthousand"] = pen.glyph() assert glyphSet["zero.dnom"].getCompositeMaxpValues(glyphSet)[2] == 1 assert glyphSet["percent"].getCompositeMaxpValues(glyphSet)[2] == 2 assert glyphSet["perthousand"].getCompositeMaxpValues(glyphSet)[2] == 2
def test_clamp_to_almost_2_component_transform(self): componentName = 'a' glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() glyphSet[componentName] = _TestGlyph(pen.glyph()) pen.addComponent(componentName, (1.99999, 0, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 2, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 2, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 0, 2, 0, 0)) pen.addComponent(componentName, (-2, 0, 0, -2, 0, 0)) compositeGlyph = pen.glyph() almost2 = MAX_F2DOT14 # 0b1.11111111111111 pen.addComponent(componentName, (almost2, 0, 0, 1, 0, 0)) pen.addComponent(componentName, (1, almost2, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 0, almost2, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 0, almost2, 0, 0)) pen.addComponent(componentName, (-2, 0, 0, -2, 0, 0)) expectedGlyph = pen.glyph() self.assertEqual(expectedGlyph, compositeGlyph)
def test_getCoordinates(self): glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((100, 0)) pen.lineTo((100, 100)) pen.lineTo((0, 100)) pen.closePath() # simple contour glyph glyphSet["a"] = a = pen.glyph() assert a.getCoordinates(glyphSet) == ( GlyphCoordinates([(0, 0), (100, 0), (100, 100), (0, 100)]), [3], array.array("B", [1, 1, 1, 1]), ) # composite glyph with only XY offset pen = TTGlyphPen(glyphSet) pen.addComponent("a", (1, 0, 0, 1, 10, 20)) glyphSet["b"] = b = pen.glyph() assert b.getCoordinates(glyphSet) == ( GlyphCoordinates([(10, 20), (110, 20), (110, 120), (10, 120)]), [3], array.array("B", [1, 1, 1, 1]), ) # composite glyph with a scale (and referencing another composite glyph) pen = TTGlyphPen(glyphSet) pen.addComponent("b", (0.5, 0, 0, 0.5, 0, 0)) glyphSet["c"] = c = pen.glyph() assert c.getCoordinates(glyphSet) == ( GlyphCoordinates([(5, 10), (55, 10), (55, 60), (5, 60)]), [3], array.array("B", [1, 1, 1, 1]), ) # composite glyph with unscaled offset (MS-style) pen = TTGlyphPen(glyphSet) pen.addComponent("a", (0.5, 0, 0, 0.5, 10, 20)) glyphSet["d"] = d = pen.glyph() d.components[0].flags |= UNSCALED_COMPONENT_OFFSET assert d.getCoordinates(glyphSet) == ( GlyphCoordinates([(10, 20), (60, 20), (60, 70), (10, 70)]), [3], array.array("B", [1, 1, 1, 1]), ) # composite glyph with a scaled offset (Apple-style) pen = TTGlyphPen(glyphSet) pen.addComponent("a", (0.5, 0, 0, 0.5, 10, 20)) glyphSet["e"] = e = pen.glyph() e.components[0].flags |= SCALED_COMPONENT_OFFSET assert e.getCoordinates(glyphSet) == ( GlyphCoordinates([(5, 10), (55, 10), (55, 60), (5, 60)]), [3], array.array("B", [1, 1, 1, 1]), ) # composite glyph where the 2nd and 3rd components use anchor points pen = TTGlyphPen(glyphSet) pen.addComponent("a", (1, 0, 0, 1, 0, 0)) glyphSet["f"] = f = pen.glyph() comp1 = GlyphComponent() comp1.glyphName = "a" # aling the new component's pt 0 to pt 2 of contour points added so far comp1.firstPt = 2 comp1.secondPt = 0 comp1.flags = 0 f.components.append(comp1) comp2 = GlyphComponent() comp2.glyphName = "a" # aling the new component's pt 0 to pt 6 of contour points added so far comp2.firstPt = 6 comp2.secondPt = 0 comp2.transform = [[0.707107, 0.707107], [-0.707107, 0.707107]] # rotate 45 deg comp2.flags = WE_HAVE_A_TWO_BY_TWO f.components.append(comp2) coords, end_pts, flags = f.getCoordinates(glyphSet) assert end_pts == [3, 7, 11] assert flags == array.array("B", [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) assert list(sum(coords, ())) == pytest.approx([ 0, 0, 100, 0, 100, 100, 0, 100, 100, 100, 200, 100, 200, 200, 100, 200, 200, 200, 270.7107, 270.7107, 200.0, 341.4214, 129.2893, 270.7107, ])