def test_sum_deltas_gvar(self):
        var1 = TupleVariation({}, [
            (-20, 0),
            (-20, 0),
            (20, 0),
            (20, 0),
            (0, 0),
            (0, 0),
            (0, 0),
            (0, 0),
        ])
        var2 = TupleVariation({}, [
            (-10, 0),
            (-10, 0),
            (10, 0),
            (10, 0),
            (0, 0),
            (20, 0),
            (0, 0),
            (0, 0),
        ])

        var1 += var2

        self.assertEqual(var1.coordinates, [
            (-30, 0),
            (-30, 0),
            (30, 0),
            (30, 0),
            (0, 0),
            (20, 0),
            (0, 0),
            (0, 0),
        ])
Example #2
0
 def test_compileTupleVariationStore_roundTrip_gvar(self):
     deltas = [(1, 1), (2, 2), (3, 3), (4, 4)]
     variations = [
         TupleVariation({
             "wght": (0.5, 1.0, 1.0),
             "wdth": (1.0, 1.0, 1.0)
         }, deltas),
         TupleVariation({
             "wght": (1.0, 1.0, 1.0),
             "wdth": (1.0, 1.0, 1.0)
         }, deltas)
     ]
     tupleVariationCount, tuples, data = compileTupleVariationStore(
         variations,
         pointCount=4,
         axisTags=["wght", "wdth"],
         sharedTupleIndices={})
     self.assertEqual(
         decompileTupleVariationStore("gvar", ["wght", "wdth"],
                                      tupleVariationCount,
                                      pointCount=4,
                                      sharedTuples={},
                                      data=(tuples + data),
                                      pos=0,
                                      dataPos=len(tuples)), variations)
    def test_sum_deltas_gvar_invalid_length(self):
        var1 = TupleVariation({}, [(1, 2)])
        var2 = TupleVariation({}, [(1, 2), (3, 4)])

        with self.assertRaisesRegex(ValueError,
                                    "deltas with different lengths"):
            var1 += var2
Example #4
0
def _add_gvar(font, model, master_ttfs, tolerance=0.5, optimize=True):

    assert tolerance >= 0

    log.info("Generating gvar")
    assert "gvar" not in font
    gvar = font["gvar"] = newTable('gvar')
    gvar.version = 1
    gvar.reserved = 0
    gvar.variations = {}

    for glyph in font.getGlyphOrder():

        allData = [_GetCoordinates(m, glyph) for m in master_ttfs]
        allCoords = [d[0] for d in allData]
        allControls = [d[1] for d in allData]
        control = allControls[0]
        if (any(c != control for c in allControls)):
            log.warning("glyph %s has incompatible masters; skipping" % glyph)
            continue
        del allControls

        # Update gvar
        gvar.variations[glyph] = []
        deltas = model.getDeltas(allCoords)
        supports = model.supports
        assert len(deltas) == len(supports)

        # Prepare for IUP optimization
        origCoords = deltas[0]
        endPts = control[1] if control[0] >= 1 else list(range(len(
            control[1])))

        for i, (delta, support) in enumerate(zip(deltas[1:], supports[1:])):
            if all(abs(v) <= tolerance for v in delta.array):
                continue
            var = TupleVariation(support, delta)
            if optimize:
                delta_opt = iup_delta_optimize(delta,
                                               origCoords,
                                               endPts,
                                               tolerance=tolerance)

                if None in delta_opt:
                    # Use "optimized" version only if smaller...
                    var_opt = TupleVariation(support, delta_opt)

                    axis_tags = sorted(support.keys(
                    ))  # Shouldn't matter that this is different from fvar...?
                    tupleData, auxData, _ = var.compile(axis_tags, [], None)
                    unoptimized_len = len(tupleData) + len(auxData)
                    tupleData, auxData, _ = var_opt.compile(
                        axis_tags, [], None)
                    optimized_len = len(tupleData) + len(auxData)

                    if optimized_len < unoptimized_len:
                        var = var_opt

            gvar.variations[glyph].append(var)
    def test_sum_deltas_cvar(self):
        axes = {"wght": (0.0, 1.0, 1.0)}
        var1 = TupleVariation(axes, [0, 1, None, None])
        var2 = TupleVariation(axes, [None, 2, None, 3])
        var3 = TupleVariation(axes, [None, None, None, 4])

        var1 += var2
        var1 += var3

        self.assertEqual(var1.coordinates, [0, 3, None, 7])
Example #6
0
	def test_compileTupleVariationStore_allVariationsRedundant(self):
		axes = {"wght": (0.3, 0.4, 0.5), "opsz": (0.7, 0.8, 0.9)}
		variations = [
			TupleVariation(axes, [None] * 4),
			TupleVariation(axes, [None] * 4),
			TupleVariation(axes, [None] * 4)
		]
		self.assertEqual(
			compileTupleVariationStore(variations, pointCount=8,
			                           axisTags=["wght", "opsz"],
			                           sharedTupleIndices={}),
            (0, b"", b""))
    def test_optimize_isComposite(self):
        # when a composite glyph's deltas are all (0, 0), we still want
        # to write out an entry in gvar, else macOS doesn't apply any
        # variations to the composite glyph (even if its individual components
        # do vary).
        # https://github.com/fonttools/fonttools/issues/1381
        var = TupleVariation({"wght": (0.0, 1.0, 1.0)}, [(0, 0)] * 5)
        var.optimize([(0, 0)] * 5, [0], isComposite=True)
        self.assertEqual(var.coordinates, [(0, 0)] * 5)

        # it takes more than 128 (0, 0) deltas before the optimized tuple with
        # (None) inferred deltas (except for the first) becomes smaller than
        # the un-optimized one that has all deltas explicitly set to (0, 0).
        var = TupleVariation({"wght": (0.0, 1.0, 1.0)}, [(0, 0)] * 129)
        var.optimize([(0, 0)] * 129, list(range(129 - 4)), isComposite=True)
        self.assertEqual(var.coordinates, [(0, 0)] + [None] * 128)
    def test_calcInferredDeltas(self):
        var = TupleVariation({}, [(0, 0), None, None, None])
        coords = [(1, 1), (1, 1), (1, 1), (1, 1)]

        var.calcInferredDeltas(coords, [])

        self.assertEqual(var.coordinates, [(0, 0), (0, 0), (0, 0), (0, 0)])
Example #9
0
 def test_compileDeltas_points(self):
     var = TupleVariation({}, [(0, 0), (1, 0), (2, 0), None, (4, 0),
                               (5, 0)])
     points = {1, 2, 3, 4}
     # deltaX for points: [1, 2, 4]; deltaY for points: [0, 0, 0]
     self.assertEqual("02 01 02 04 82",
                      hexencode(var.compileDeltas(points)))
Example #10
0
	def test_decompileCoord_roundTrip(self):
		# Make sure we are not affected by https://github.com/fonttools/fonttools/issues/286
		data = deHexStr("7F B9 80 35")
		values, _ = TupleVariation.decompileCoord_(["wght", "wdth"], data, 0)
		axisValues = {axis:(val, val, val) for axis, val in  values.items()}
		var = TupleVariation(axisValues, [None] * 4)
		self.assertEqual("7F B9 80 35", hexencode(var.compileCoord(["wght", "wdth"])))
Example #11
0
	def test_fromXML_badDeltaFormat(self):
		g = TupleVariation({}, [])
		with CapturingLogHandler(log, "WARNING") as captor:
			for name, attrs, content in parseXML('<delta a="1" b="2"/>'):
				g.fromXML(name, attrs, content)
		self.assertIn("bad delta format: a, b",
		              [r.msg for r in captor.records])
	def test_calcInferredDeltas_invalid(self):
		# cvar tuples can't have inferred deltas
		with self.assertRaises(TypeError):
			TupleVariation({}, [0]).calcInferredDeltas([], [])

		# origCoords must have same length as self.coordinates
		with self.assertRaises(ValueError):
			TupleVariation({}, [(0, 0), None]).calcInferredDeltas([], [])

		# at least 4 phantom points required
		with self.assertRaises(AssertionError):
			TupleVariation({}, [(0, 0), None]).calcInferredDeltas([(0, 0), (0, 0)], [])

		with self.assertRaises(AssertionError):
			TupleVariation({}, [(0, 0)] + [None]*5).calcInferredDeltas(
				[(0, 0)]*6,
				[1, 0]  # endPts not in increasing order
			)
Example #13
0
    def test_getCoordWidth(self):
        empty = TupleVariation({}, [])
        self.assertEqual(empty.getCoordWidth(), 0)

        empty = TupleVariation({}, [None])
        self.assertEqual(empty.getCoordWidth(), 0)

        gvarTuple = TupleVariation({}, [None, (0, 0)])
        self.assertEqual(gvarTuple.getCoordWidth(), 2)

        cvarTuple = TupleVariation({}, [None, 0])
        self.assertEqual(cvarTuple.getCoordWidth(), 1)

        cvarTuple.coordinates[1] *= 1.0
        self.assertEqual(cvarTuple.getCoordWidth(), 1)

        with self.assertRaises(TypeError):
            TupleVariation({}, [None, "a"]).getCoordWidth()
Example #14
0
 def test_toXML_allDeltasNone(self):
     writer = XMLWriter(BytesIO())
     axes = {"wght": (0.0, 1.0, 1.0)}
     g = TupleVariation(axes, [None] * 5)
     g.toXML(writer, ["wght", "wdth"])
     self.assertEqual([
         '<tuple>', '<coord axis="wght" value="1.0"/>',
         '<!-- no deltas -->', '</tuple>'
     ], TupleVariationTest.xml_lines(writer))
	def test_compile_embeddedPeak_nonIntermediate_sharedConstants(self):
		var = TupleVariation(
			{"wght": (0.0, 0.5, 0.5), "wdth": (0.0, 0.8, 0.8)},
			[3, 1, 4])
		tup, deltas = var.compile(axisTags=["wght", "wdth"], pointData=b'')
		# len(deltas)=4; flags=EMBEDDED_PEAK_TUPLE
		# embeddedPeak=[(0.5, 0.8)]; intermediateCoord=[]
		self.assertEqual("00 04 80 00 20 00 33 33", hexencode(tup))
		self.assertEqual("02 03 01 04",     # delta: [3, 1, 4]
						 hexencode(deltas))
	def test_compile_embeddedPeak_nonIntermediate_sharedPoints(self):
		var = TupleVariation(
			{"wght": (0.0, 0.5, 0.5), "wdth": (0.0, 0.8, 0.8)},
			[(7,4), (8,5), (9,6)])
		tup, deltas = var.compile(axisTags=["wght", "wdth"], pointData=b'')
		# len(deltas)=8; flags=EMBEDDED_PEAK_TUPLE
		# embeddedPeak=[(0.5, 0.8)]; intermediateCoord=[]
		self.assertEqual("00 08 80 00 20 00 33 33", hexencode(tup))
		self.assertEqual("02 07 08 09 "     # deltaX: [7, 8, 9]
						 "02 04 05 06",     # deltaY: [4, 5, 6]
						 hexencode(deltas))
Example #17
0
	def test_fromXML_points(self):
		g = TupleVariation({}, [None] * 4)
		for name, attrs, content in parseXML(
				'<coord axis="wdth" min="0.3" value="0.4" max="0.5"/>'
				'<coord axis="wght" value="1.0"/>'
				'<coord axis="opsz" value="-0.7"/>'
				'<delta pt="1" x="33" y="44"/>'
				'<delta pt="2" x="-2" y="170"/>'):
			g.fromXML(name, attrs, content)
		self.assertEqual(AXES, g.axes)
		self.assertEqual([None, (33, 44), (-2, 170), None], g.coordinates)
Example #18
0
 def test_compileCoord(self):
     var = TupleVariation(
         {
             "wght": (-1.0, -1.0, -1.0),
             "wdth": (0.4, 0.5, 0.6)
         }, [None] * 4)
     self.assertEqual("C0 00 20 00",
                      hexencode(var.compileCoord(["wght", "wdth"])))
     self.assertEqual("20 00 C0 00",
                      hexencode(var.compileCoord(["wdth", "wght"])))
     self.assertEqual("C0 00", hexencode(var.compileCoord(["wght"])))
Example #19
0
 def test_toXML_constants(self):
     writer = XMLWriter(BytesIO())
     g = TupleVariation(AXES, [42, None, 23, 0, -17, None])
     g.toXML(writer, ["wdth", "wght", "opsz"])
     self.assertEqual([
         '<tuple>', '<coord axis="wdth" max="0.5" min="0.3" value="0.4"/>',
         '<coord axis="wght" value="1.0"/>',
         '<coord axis="opsz" value="-0.7"/>', '<delta cvt="0" value="42"/>',
         '<delta cvt="2" value="23"/>', '<delta cvt="3" value="0"/>',
         '<delta cvt="4" value="-17"/>', '</tuple>'
     ], TupleVariationTest.xml_lines(writer))
	def test_compile_embeddedPeak_nonIntermediate_privateConstants(self):
		var = TupleVariation(
			{"wght": (0.0, 0.5, 0.5), "wdth": (0.0, 0.8, 0.8)},
			[7, 8, 9])
		tup, deltas = var.compile(axisTags=["wght", "wdth"])
		# len(deltas)=5; flags=PRIVATE_POINT_NUMBERS|EMBEDDED_PEAK_TUPLE
		# embeddedPeak=[(0.5, 0.8)]; intermediateCoord=[]
		self.assertEqual("00 05 A0 00 20 00 33 33", hexencode(tup))
		self.assertEqual("00 "           # all points in glyph
		                 "02 07 08 09",  # delta: [7, 8, 9]
		                 hexencode(deltas))
Example #21
0
	def test_fromXML_constants(self):
		g = TupleVariation({}, [None] * 4)
		for name, attrs, content in parseXML(
				'<coord axis="wdth" min="0.3" value="0.4" max="0.5"/>'
				'<coord axis="wght" value="1.0"/>'
				'<coord axis="opsz" value="-0.7"/>'
				'<delta cvt="1" value="42"/>'
				'<delta cvt="2" value="-23"/>'):
			g.fromXML(name, attrs, content)
		self.assertEqual(AXES, g.axes)
		self.assertEqual([None, 42, -23, None], g.coordinates)
Example #22
0
	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))
Example #23
0
 def test_toXML_points(self):
     writer = XMLWriter(BytesIO())
     g = TupleVariation(AXES, [(9, 8), None, (7, 6), (0, 0),
                               (-1, -2), None])
     g.toXML(writer, ["wdth", "wght", "opsz"])
     self.assertEqual([
         '<tuple>', '<coord axis="wdth" max="0.5" min="0.3" value="0.4"/>',
         '<coord axis="wght" value="1.0"/>',
         '<coord axis="opsz" value="-0.7"/>', '<delta pt="0" x="9" y="8"/>',
         '<delta pt="2" x="7" y="6"/>', '<delta pt="3" x="0" y="0"/>',
         '<delta pt="4" x="-1" y="-2"/>', '</tuple>'
     ], TupleVariationTest.xml_lines(writer))
Example #24
0
    def test_scaleDeltas_cvar(self):
        var = TupleVariation({}, [100, None])

        var.scaleDeltas(1.0)
        self.assertEqual(var.coordinates, [100, None])

        var.scaleDeltas(0.333)
        self.assertAlmostEqual(var.coordinates[0], 33.3)
        self.assertIsNone(var.coordinates[1])

        var.scaleDeltas(0.0)
        self.assertEqual(var.coordinates, [0, None])
Example #25
0
 def fromXML(self, name, attrs, content, ttFont):
     if name == "version":
         self.majorVersion = int(attrs.get("major", "1"))
         self.minorVersion = int(attrs.get("minor", "0"))
     elif name == "tuple":
         valueCount = len(ttFont["cvt "].values)
         var = TupleVariation({}, [None] * valueCount)
         self.variations.append(var)
         for tupleElement in content:
             if isinstance(tupleElement, tuple):
                 tupleName, tupleAttrs, tupleContent = tupleElement
                 var.fromXML(tupleName, tupleAttrs, tupleContent)
Example #26
0
	def test_compileSharedTuples(self):
		# Below, the peak coordinate {"wght": 1.0, "wdth": 0.7} appears
		# three times; {"wght": 1.0, "wdth": 0.8} appears twice.
		# Because the start and end of variation ranges is not encoded
		# into the shared pool, they should get ignored.
		deltas = [None] * 4
		variations = [
			TupleVariation({
				"wght": (1.0, 1.0, 1.0),
				"wdth": (0.5, 0.7, 1.0)
			}, deltas),
			TupleVariation({
				"wght": (1.0, 1.0, 1.0),
				"wdth": (0.2, 0.7, 1.0)
			}, deltas),
			TupleVariation({
				"wght": (1.0, 1.0, 1.0),
				"wdth": (0.2, 0.8, 1.0)
			}, deltas),
			TupleVariation({
				"wght": (1.0, 1.0, 1.0),
				"wdth": (0.3, 0.7, 1.0)
			}, deltas),
			TupleVariation({
				"wght": (1.0, 1.0, 1.0),
				"wdth": (0.3, 0.8, 1.0)
			}, deltas),
			TupleVariation({
				"wght": (1.0, 1.0, 1.0),
				"wdth": (0.3, 0.9, 1.0)
            }, deltas)
		]
		result = compileSharedTuples(["wght", "wdth"], variations)
		self.assertEqual([hexencode(c) for c in result],
		                 ["40 00 2C CD", "40 00 33 33"])
	def test_compile_sharedPeaks_intermediate_sharedPoints(self):
		var = TupleVariation(
			{"wght": (0.3, 0.5, 0.7), "wdth": (0.1, 0.8, 0.9)},
			[(7,4), (8,5), (9,6)])
		axisTags = ["wght", "wdth"]
		sharedPeakIndices = { var.compileCoord(axisTags): 0x77 }
		tup, deltas = var.compile(axisTags, sharedPeakIndices, pointData=b'')
		# len(deltas)=8; flags=INTERMEDIATE_REGION; tupleIndex=0x77
		# embeddedPeak=[]; intermediateCoord=[(0.3, 0.1), (0.7, 0.9)]
		self.assertEqual("00 08 40 77 13 33 06 66 2C CD 39 9A", hexencode(tup))
		self.assertEqual("02 07 08 09 "     # deltaX: [7, 8, 9]
						 "02 04 05 06",     # deltaY: [4, 5, 6]
						 hexencode(deltas))
Example #28
0
	def test_compile_embeddedPeak_nonIntermediate_privatePoints(self):
		var = TupleVariation(
			{"wght": (0.0, 0.5, 0.5), "wdth": (0.0, 0.8, 0.8)},
			[(7,4), (8,5), (9,6)])
		tup, deltas, _ = var.compile(
			axisTags=["wght", "wdth"], sharedCoordIndices={}, sharedPoints=None)
		# len(deltas)=9; flags=PRIVATE_POINT_NUMBERS|EMBEDDED_PEAK_TUPLE
		# embeddedPeak=[(0.5, 0.8)]; intermediateCoord=[]
		self.assertEqual("00 09 A0 00 20 00 33 33", hexencode(tup))
		self.assertEqual("00 "           # all points in glyph
		                 "02 07 08 09 "  # deltaX: [7, 8, 9]
		                 "02 04 05 06",  # deltaY: [4, 5, 6]
		                 hexencode(deltas))
	def test_compile_embeddedPeak_intermediate_privateConstants(self):
		var = TupleVariation(
			{"wght": (0.4, 0.5, 0.6), "wdth": (0.7, 0.8, 0.9)},
			[7, 8, 9])
		tup, deltas = var.compile(axisTags = ["wght", "wdth"])
		# len(deltas)=5;
		# flags=PRIVATE_POINT_NUMBERS|INTERMEDIATE_REGION|EMBEDDED_PEAK_TUPLE
		# embeddedPeak=(0.5, 0.8); intermediateCoord=[(0.4, 0.7), (0.6, 0.9)]
		self.assertEqual("00 05 E0 00 20 00 33 33 19 9A 2C CD 26 66 39 9A",
		                 hexencode(tup))
		self.assertEqual("00 "             # all points in glyph
		                 "02 07 08 09",    # delta: [7, 8, 9]
		                 hexencode(deltas))
Example #30
0
    def test_scaleDeltas_gvar(self):
        var = TupleVariation({}, [(100, 200), None])

        var.scaleDeltas(1.0)
        self.assertEqual(var.coordinates, [(100, 200), None])

        var.scaleDeltas(0.333)
        self.assertAlmostEqual(var.coordinates[0][0], 33.3)
        self.assertAlmostEqual(var.coordinates[0][1], 66.6)
        self.assertIsNone(var.coordinates[1])

        var.scaleDeltas(0.0)
        self.assertEqual(var.coordinates, [(0, 0), None])