[cvar] Support XML input/output for TupleVariations on constant values
OpenType TupleVariations can be used in two places: * In the `gvar` table, they modify glyph contour points by shifting them towards a point in 2D space. * In the `cvar` table, they modify constant values. Before this change, we only had code to handle the `gvar` version which shifts points around by delta values. After this change, the XML parsing and generation routines of TupleVariations can handle deltas that modify constant values in the CVT table, as used in `cvar`. An upcoming change will add support for the binary encoding of TupleVariations as needed for `cvar`.
This commit is contained in:
parent
01f95fc190
commit
6275668840
@ -69,13 +69,22 @@ class TupleVariation(object):
|
||||
else:
|
||||
writer.simpletag("coord", axis=axis, value=value, min=minValue, max=maxValue)
|
||||
writer.newline()
|
||||
wrote_any_points = False
|
||||
for i, point in enumerate(self.coordinates):
|
||||
if point is not None:
|
||||
writer.simpletag("delta", pt=i, x=point[0], y=point[1])
|
||||
wrote_any_deltas = False
|
||||
for i, delta in enumerate(self.coordinates):
|
||||
if type(delta) == tuple and len(delta) == 2:
|
||||
writer.simpletag("delta", pt=i, x=delta[0], y=delta[1])
|
||||
writer.newline()
|
||||
wrote_any_points = True
|
||||
if not wrote_any_points:
|
||||
wrote_any_deltas = True
|
||||
elif type(delta) == int:
|
||||
writer.simpletag("delta", cvt=i, value=delta)
|
||||
writer.newline()
|
||||
wrote_any_deltas = True
|
||||
elif delta is not None:
|
||||
log.error("bad delta format")
|
||||
writer.comment("bad delta #%d" % i)
|
||||
writer.newline()
|
||||
wrote_any_deltas = True
|
||||
if not wrote_any_deltas:
|
||||
writer.comment("no deltas")
|
||||
writer.newline()
|
||||
writer.endtag("tuple")
|
||||
@ -91,10 +100,18 @@ class TupleVariation(object):
|
||||
maxValue = float(attrs.get("max", defaultMaxValue))
|
||||
self.axes[axis] = (minValue, value, maxValue)
|
||||
elif name == "delta":
|
||||
point = safeEval(attrs["pt"])
|
||||
x = safeEval(attrs["x"])
|
||||
y = safeEval(attrs["y"])
|
||||
self.coordinates[point] = (x, y)
|
||||
if "pt" in attrs:
|
||||
point = safeEval(attrs["pt"])
|
||||
x = safeEval(attrs["x"])
|
||||
y = safeEval(attrs["y"])
|
||||
self.coordinates[point] = (x, y)
|
||||
elif "cvt" in attrs:
|
||||
cvt = safeEval(attrs["cvt"])
|
||||
value = safeEval(attrs["value"])
|
||||
self.coordinates[cvt] = value
|
||||
else:
|
||||
log.warning("bad delta format: %s" %
|
||||
", ".join(sorted(attrs.keys())))
|
||||
|
||||
def compile(self, axisTags, sharedCoordIndices, sharedPoints):
|
||||
tupleData = []
|
||||
|
@ -1,9 +1,10 @@
|
||||
from __future__ import print_function, division, absolute_import, unicode_literals
|
||||
from fontTools.misc.py23 import *
|
||||
from fontTools.misc.loggingTools import CapturingLogHandler
|
||||
from fontTools.misc.testTools import parseXML
|
||||
from fontTools.misc.textTools import deHexStr, hexStr
|
||||
from fontTools.misc.xmlWriter import XMLWriter
|
||||
from fontTools.ttLib.tables.TupleVariation import TupleVariation
|
||||
from fontTools.ttLib.tables.TupleVariation import log, TupleVariation
|
||||
import random
|
||||
import unittest
|
||||
|
||||
@ -13,6 +14,13 @@ def hexencode(s):
|
||||
return ' '.join([h[i:i+2] for i in range(0, len(h), 2)])
|
||||
|
||||
|
||||
AXES = {
|
||||
"wdth":(0.3, 0.4, 0.5),
|
||||
"wght":(0.0, 1.0, 1.0),
|
||||
"opsz":(-0.7, -0.7, 0.0)
|
||||
}
|
||||
|
||||
|
||||
class TupleVariationTest(unittest.TestCase):
|
||||
def test_equal(self):
|
||||
gvar1 = TupleVariation({"wght":(0.0, 1.0, 1.0)}, [(0,0), (9,8), (7,6)])
|
||||
@ -44,10 +52,38 @@ class TupleVariationTest(unittest.TestCase):
|
||||
gvar = TupleVariation(axes, [None, None, None])
|
||||
self.assertFalse(gvar.hasImpact())
|
||||
|
||||
def test_toXML(self):
|
||||
def test_toXML_badDeltaFormat(self):
|
||||
writer = XMLWriter(BytesIO())
|
||||
axes = {"wdth":(0.3, 0.4, 0.5), "wght":(0.0, 1.0, 1.0), "opsz":(-0.7, -0.7, 0.0)}
|
||||
g = TupleVariation(axes, [(9,8), None, (7,6), (0,0), (-1,-2), None])
|
||||
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" max="0.5" min="0.3" value="0.4"/>',
|
||||
'<!-- bad delta #0 -->',
|
||||
'</tuple>',
|
||||
], TupleVariationTest.xml_lines(writer))
|
||||
|
||||
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_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>',
|
||||
@ -73,20 +109,36 @@ class TupleVariationTest(unittest.TestCase):
|
||||
'</tuple>'
|
||||
], TupleVariationTest.xml_lines(writer))
|
||||
|
||||
def test_fromXML(self):
|
||||
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_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.5"/>'
|
||||
'<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)
|
||||
|
||||
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({
|
||||
"wdth":( 0.3, 0.4, 0.5),
|
||||
"wght":( 0.0, 1.0, 1.0),
|
||||
"opsz":(-0.5, -0.5, 0.0)
|
||||
}, g.axes)
|
||||
self.assertEqual(AXES, g.axes)
|
||||
self.assertEqual([None, (33, 44), (-2, 170), None], g.coordinates)
|
||||
|
||||
def test_compile_sharedCoords_nonIntermediate_sharedPoints(self):
|
||||
|
Loading…
x
Reference in New Issue
Block a user