Add transformRoundFunc parameter to RoundingPens (#3426)
* Add optional transformRoundFunc to RoundingPen and RoundingPointPen * Add tests * Add doc about comparing UFO to TTF glyphs * Use floatToFixedToFloat for example with rounding
This commit is contained in:
parent
306f40a2ae
commit
7cdac78423
@ -31,6 +31,20 @@ class HashPointPen(AbstractPointPen):
|
||||
> # The hash values are identical, the outline has not changed.
|
||||
> # Compile the hinting code ...
|
||||
> pass
|
||||
|
||||
If you want to compare a glyph from a source format which supports floating point
|
||||
coordinates and transformations against a glyph from a format which has restrictions
|
||||
on the precision of floats, e.g. UFO vs. TTF, you must use an appropriate rounding
|
||||
function to make the values comparable. For TTF fonts with composites, this
|
||||
construct can be used to make the transform values conform to F2Dot14:
|
||||
|
||||
> ttf_hash_pen = HashPointPen(ttf_glyph_width, ttFont.getGlyphSet())
|
||||
> ttf_round_pen = RoundingPointPen(ttf_hash_pen, transformRoundFunc=partial(floatToFixedToFloat, precisionBits=14))
|
||||
> ufo_hash_pen = HashPointPen(ufo_glyph.width, ufo)
|
||||
> ttf_glyph.drawPoints(ttf_round_pen, ttFont["glyf"])
|
||||
> ufo_round_pen = RoundingPointPen(ufo_hash_pen, transformRoundFunc=partial(floatToFixedToFloat, precisionBits=14))
|
||||
> ufo_glyph.drawPoints(ufo_round_pen)
|
||||
> assert ttf_hash_pen.hash == ufo_hash_pen.hash
|
||||
"""
|
||||
|
||||
def __init__(self, glyphWidth=0, glyphSet=None):
|
||||
|
@ -1,4 +1,4 @@
|
||||
from fontTools.misc.roundTools import otRound
|
||||
from fontTools.misc.roundTools import noRound, otRound
|
||||
from fontTools.misc.transform import Transform
|
||||
from fontTools.pens.filterPen import FilterPen, FilterPointPen
|
||||
|
||||
@ -8,7 +8,9 @@ __all__ = ["RoundingPen", "RoundingPointPen"]
|
||||
|
||||
class RoundingPen(FilterPen):
|
||||
"""
|
||||
Filter pen that rounds point coordinates and component XY offsets to integer.
|
||||
Filter pen that rounds point coordinates and component XY offsets to integer. For
|
||||
rounding the component transform values, a separate round function can be passed to
|
||||
the pen.
|
||||
|
||||
>>> from fontTools.pens.recordingPen import RecordingPen
|
||||
>>> recpen = RecordingPen()
|
||||
@ -28,9 +30,10 @@ class RoundingPen(FilterPen):
|
||||
True
|
||||
"""
|
||||
|
||||
def __init__(self, outPen, roundFunc=otRound):
|
||||
def __init__(self, outPen, roundFunc=otRound, transformRoundFunc=noRound):
|
||||
super().__init__(outPen)
|
||||
self.roundFunc = roundFunc
|
||||
self.transformRoundFunc = transformRoundFunc
|
||||
|
||||
def moveTo(self, pt):
|
||||
self._outPen.moveTo((self.roundFunc(pt[0]), self.roundFunc(pt[1])))
|
||||
@ -49,12 +52,16 @@ class RoundingPen(FilterPen):
|
||||
)
|
||||
|
||||
def addComponent(self, glyphName, transformation):
|
||||
xx, xy, yx, yy, dx, dy = transformation
|
||||
self._outPen.addComponent(
|
||||
glyphName,
|
||||
Transform(
|
||||
*transformation[:4],
|
||||
self.roundFunc(transformation[4]),
|
||||
self.roundFunc(transformation[5]),
|
||||
self.transformRoundFunc(xx),
|
||||
self.transformRoundFunc(xy),
|
||||
self.transformRoundFunc(yx),
|
||||
self.transformRoundFunc(yy),
|
||||
self.roundFunc(dx),
|
||||
self.roundFunc(dy),
|
||||
),
|
||||
)
|
||||
|
||||
@ -62,6 +69,8 @@ class RoundingPen(FilterPen):
|
||||
class RoundingPointPen(FilterPointPen):
|
||||
"""
|
||||
Filter point pen that rounds point coordinates and component XY offsets to integer.
|
||||
For rounding the component scale values, a separate round function can be passed to
|
||||
the pen.
|
||||
|
||||
>>> from fontTools.pens.recordingPen import RecordingPointPen
|
||||
>>> recpen = RecordingPointPen()
|
||||
@ -87,26 +96,35 @@ class RoundingPointPen(FilterPointPen):
|
||||
True
|
||||
"""
|
||||
|
||||
def __init__(self, outPen, roundFunc=otRound):
|
||||
def __init__(self, outPen, roundFunc=otRound, transformRoundFunc=noRound):
|
||||
super().__init__(outPen)
|
||||
self.roundFunc = roundFunc
|
||||
self.transformRoundFunc = transformRoundFunc
|
||||
|
||||
def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
|
||||
def addPoint(
|
||||
self, pt, segmentType=None, smooth=False, name=None, identifier=None, **kwargs
|
||||
):
|
||||
self._outPen.addPoint(
|
||||
(self.roundFunc(pt[0]), self.roundFunc(pt[1])),
|
||||
segmentType=segmentType,
|
||||
smooth=smooth,
|
||||
name=name,
|
||||
identifier=identifier,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def addComponent(self, baseGlyphName, transformation, **kwargs):
|
||||
def addComponent(self, baseGlyphName, transformation, identifier=None, **kwargs):
|
||||
xx, xy, yx, yy, dx, dy = transformation
|
||||
self._outPen.addComponent(
|
||||
baseGlyphName,
|
||||
Transform(
|
||||
*transformation[:4],
|
||||
self.roundFunc(transformation[4]),
|
||||
self.roundFunc(transformation[5]),
|
||||
baseGlyphName=baseGlyphName,
|
||||
transformation=Transform(
|
||||
self.transformRoundFunc(xx),
|
||||
self.transformRoundFunc(xy),
|
||||
self.transformRoundFunc(yx),
|
||||
self.transformRoundFunc(yy),
|
||||
self.roundFunc(dx),
|
||||
self.roundFunc(dy),
|
||||
),
|
||||
identifier=identifier,
|
||||
**kwargs,
|
||||
)
|
||||
|
69
Tests/pens/roundingPen_test.py
Normal file
69
Tests/pens/roundingPen_test.py
Normal file
@ -0,0 +1,69 @@
|
||||
from fontTools.misc.fixedTools import floatToFixedToFloat
|
||||
from fontTools.pens.recordingPen import RecordingPen, RecordingPointPen
|
||||
from fontTools.pens.roundingPen import RoundingPen, RoundingPointPen
|
||||
from functools import partial
|
||||
|
||||
|
||||
tt_scale_round = partial(floatToFixedToFloat, precisionBits=14)
|
||||
|
||||
|
||||
class RoundingPenTest(object):
|
||||
def test_general(self):
|
||||
recpen = RecordingPen()
|
||||
roundpen = RoundingPen(recpen)
|
||||
roundpen.moveTo((0.4, 0.6))
|
||||
roundpen.lineTo((1.6, 2.5))
|
||||
roundpen.qCurveTo((2.4, 4.6), (3.3, 5.7), (4.9, 6.1))
|
||||
roundpen.curveTo((6.4, 8.6), (7.3, 9.7), (8.9, 10.1))
|
||||
roundpen.addComponent("a", (1.5, 0, 0, 1.5, 10.5, -10.5))
|
||||
assert recpen.value == [
|
||||
("moveTo", ((0, 1),)),
|
||||
("lineTo", ((2, 3),)),
|
||||
("qCurveTo", ((2, 5), (3, 6), (5, 6))),
|
||||
("curveTo", ((6, 9), (7, 10), (9, 10))),
|
||||
("addComponent", ("a", (1.5, 0, 0, 1.5, 11, -10))),
|
||||
]
|
||||
|
||||
def test_transform_round(self):
|
||||
recpen = RecordingPen()
|
||||
roundpen = RoundingPen(recpen, transformRoundFunc=tt_scale_round)
|
||||
# The 0.913 is equal to 91.3% scale in a source editor
|
||||
roundpen.addComponent("a", (0.9130000305, 0, 0, -1, 10.5, -10.5))
|
||||
# The value should compare equal to its F2Dot14 representation
|
||||
assert recpen.value == [
|
||||
("addComponent", ("a", (0.91302490234375, 0, 0, -1, 11, -10))),
|
||||
]
|
||||
|
||||
|
||||
class RoundingPointPenTest(object):
|
||||
def test_general(self):
|
||||
recpen = RecordingPointPen()
|
||||
roundpen = RoundingPointPen(recpen)
|
||||
roundpen.beginPath()
|
||||
roundpen.addPoint((0.4, 0.6), "line")
|
||||
roundpen.addPoint((1.6, 2.5), "line")
|
||||
roundpen.addPoint((2.4, 4.6))
|
||||
roundpen.addPoint((3.3, 5.7))
|
||||
roundpen.addPoint((4.9, 6.1), "qcurve")
|
||||
roundpen.endPath()
|
||||
roundpen.addComponent("a", (1.5, 0, 0, 1.5, 10.5, -10.5))
|
||||
assert recpen.value == [
|
||||
("beginPath", (), {}),
|
||||
("addPoint", ((0, 1), "line", False, None), {}),
|
||||
("addPoint", ((2, 3), "line", False, None), {}),
|
||||
("addPoint", ((2, 5), None, False, None), {}),
|
||||
("addPoint", ((3, 6), None, False, None), {}),
|
||||
("addPoint", ((5, 6), "qcurve", False, None), {}),
|
||||
("endPath", (), {}),
|
||||
("addComponent", ("a", (1.5, 0, 0, 1.5, 11, -10)), {}),
|
||||
]
|
||||
|
||||
def test_transform_round(self):
|
||||
recpen = RecordingPointPen()
|
||||
roundpen = RoundingPointPen(recpen, transformRoundFunc=tt_scale_round)
|
||||
# The 0.913 is equal to 91.3% scale in a source editor
|
||||
roundpen.addComponent("a", (0.913, 0, 0, -1, 10.5, -10.5))
|
||||
# The value should compare equal to its F2Dot14 representation
|
||||
assert recpen.value == [
|
||||
("addComponent", ("a", (0.91302490234375, 0, 0, -1, 11, -10)), {}),
|
||||
]
|
Loading…
x
Reference in New Issue
Block a user