xref: /aosp_15_r20/external/fonttools/Lib/fontTools/pens/roundingPen.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1*e1fe3e4aSElliott Hughesfrom fontTools.misc.roundTools import noRound, otRound
2*e1fe3e4aSElliott Hughesfrom fontTools.misc.transform import Transform
3*e1fe3e4aSElliott Hughesfrom fontTools.pens.filterPen import FilterPen, FilterPointPen
4*e1fe3e4aSElliott Hughes
5*e1fe3e4aSElliott Hughes
6*e1fe3e4aSElliott Hughes__all__ = ["RoundingPen", "RoundingPointPen"]
7*e1fe3e4aSElliott Hughes
8*e1fe3e4aSElliott Hughes
9*e1fe3e4aSElliott Hughesclass RoundingPen(FilterPen):
10*e1fe3e4aSElliott Hughes    """
11*e1fe3e4aSElliott Hughes    Filter pen that rounds point coordinates and component XY offsets to integer. For
12*e1fe3e4aSElliott Hughes    rounding the component transform values, a separate round function can be passed to
13*e1fe3e4aSElliott Hughes    the pen.
14*e1fe3e4aSElliott Hughes
15*e1fe3e4aSElliott Hughes    >>> from fontTools.pens.recordingPen import RecordingPen
16*e1fe3e4aSElliott Hughes    >>> recpen = RecordingPen()
17*e1fe3e4aSElliott Hughes    >>> roundpen = RoundingPen(recpen)
18*e1fe3e4aSElliott Hughes    >>> roundpen.moveTo((0.4, 0.6))
19*e1fe3e4aSElliott Hughes    >>> roundpen.lineTo((1.6, 2.5))
20*e1fe3e4aSElliott Hughes    >>> roundpen.qCurveTo((2.4, 4.6), (3.3, 5.7), (4.9, 6.1))
21*e1fe3e4aSElliott Hughes    >>> roundpen.curveTo((6.4, 8.6), (7.3, 9.7), (8.9, 10.1))
22*e1fe3e4aSElliott Hughes    >>> roundpen.addComponent("a", (1.5, 0, 0, 1.5, 10.5, -10.5))
23*e1fe3e4aSElliott Hughes    >>> recpen.value == [
24*e1fe3e4aSElliott Hughes    ...     ('moveTo', ((0, 1),)),
25*e1fe3e4aSElliott Hughes    ...     ('lineTo', ((2, 3),)),
26*e1fe3e4aSElliott Hughes    ...     ('qCurveTo', ((2, 5), (3, 6), (5, 6))),
27*e1fe3e4aSElliott Hughes    ...     ('curveTo', ((6, 9), (7, 10), (9, 10))),
28*e1fe3e4aSElliott Hughes    ...     ('addComponent', ('a', (1.5, 0, 0, 1.5, 11, -10))),
29*e1fe3e4aSElliott Hughes    ... ]
30*e1fe3e4aSElliott Hughes    True
31*e1fe3e4aSElliott Hughes    """
32*e1fe3e4aSElliott Hughes
33*e1fe3e4aSElliott Hughes    def __init__(self, outPen, roundFunc=otRound, transformRoundFunc=noRound):
34*e1fe3e4aSElliott Hughes        super().__init__(outPen)
35*e1fe3e4aSElliott Hughes        self.roundFunc = roundFunc
36*e1fe3e4aSElliott Hughes        self.transformRoundFunc = transformRoundFunc
37*e1fe3e4aSElliott Hughes
38*e1fe3e4aSElliott Hughes    def moveTo(self, pt):
39*e1fe3e4aSElliott Hughes        self._outPen.moveTo((self.roundFunc(pt[0]), self.roundFunc(pt[1])))
40*e1fe3e4aSElliott Hughes
41*e1fe3e4aSElliott Hughes    def lineTo(self, pt):
42*e1fe3e4aSElliott Hughes        self._outPen.lineTo((self.roundFunc(pt[0]), self.roundFunc(pt[1])))
43*e1fe3e4aSElliott Hughes
44*e1fe3e4aSElliott Hughes    def curveTo(self, *points):
45*e1fe3e4aSElliott Hughes        self._outPen.curveTo(
46*e1fe3e4aSElliott Hughes            *((self.roundFunc(x), self.roundFunc(y)) for x, y in points)
47*e1fe3e4aSElliott Hughes        )
48*e1fe3e4aSElliott Hughes
49*e1fe3e4aSElliott Hughes    def qCurveTo(self, *points):
50*e1fe3e4aSElliott Hughes        self._outPen.qCurveTo(
51*e1fe3e4aSElliott Hughes            *((self.roundFunc(x), self.roundFunc(y)) for x, y in points)
52*e1fe3e4aSElliott Hughes        )
53*e1fe3e4aSElliott Hughes
54*e1fe3e4aSElliott Hughes    def addComponent(self, glyphName, transformation):
55*e1fe3e4aSElliott Hughes        xx, xy, yx, yy, dx, dy = transformation
56*e1fe3e4aSElliott Hughes        self._outPen.addComponent(
57*e1fe3e4aSElliott Hughes            glyphName,
58*e1fe3e4aSElliott Hughes            Transform(
59*e1fe3e4aSElliott Hughes                self.transformRoundFunc(xx),
60*e1fe3e4aSElliott Hughes                self.transformRoundFunc(xy),
61*e1fe3e4aSElliott Hughes                self.transformRoundFunc(yx),
62*e1fe3e4aSElliott Hughes                self.transformRoundFunc(yy),
63*e1fe3e4aSElliott Hughes                self.roundFunc(dx),
64*e1fe3e4aSElliott Hughes                self.roundFunc(dy),
65*e1fe3e4aSElliott Hughes            ),
66*e1fe3e4aSElliott Hughes        )
67*e1fe3e4aSElliott Hughes
68*e1fe3e4aSElliott Hughes
69*e1fe3e4aSElliott Hughesclass RoundingPointPen(FilterPointPen):
70*e1fe3e4aSElliott Hughes    """
71*e1fe3e4aSElliott Hughes    Filter point pen that rounds point coordinates and component XY offsets to integer.
72*e1fe3e4aSElliott Hughes    For rounding the component scale values, a separate round function can be passed to
73*e1fe3e4aSElliott Hughes    the pen.
74*e1fe3e4aSElliott Hughes
75*e1fe3e4aSElliott Hughes    >>> from fontTools.pens.recordingPen import RecordingPointPen
76*e1fe3e4aSElliott Hughes    >>> recpen = RecordingPointPen()
77*e1fe3e4aSElliott Hughes    >>> roundpen = RoundingPointPen(recpen)
78*e1fe3e4aSElliott Hughes    >>> roundpen.beginPath()
79*e1fe3e4aSElliott Hughes    >>> roundpen.addPoint((0.4, 0.6), 'line')
80*e1fe3e4aSElliott Hughes    >>> roundpen.addPoint((1.6, 2.5), 'line')
81*e1fe3e4aSElliott Hughes    >>> roundpen.addPoint((2.4, 4.6))
82*e1fe3e4aSElliott Hughes    >>> roundpen.addPoint((3.3, 5.7))
83*e1fe3e4aSElliott Hughes    >>> roundpen.addPoint((4.9, 6.1), 'qcurve')
84*e1fe3e4aSElliott Hughes    >>> roundpen.endPath()
85*e1fe3e4aSElliott Hughes    >>> roundpen.addComponent("a", (1.5, 0, 0, 1.5, 10.5, -10.5))
86*e1fe3e4aSElliott Hughes    >>> recpen.value == [
87*e1fe3e4aSElliott Hughes    ...     ('beginPath', (), {}),
88*e1fe3e4aSElliott Hughes    ...     ('addPoint', ((0, 1), 'line', False, None), {}),
89*e1fe3e4aSElliott Hughes    ...     ('addPoint', ((2, 3), 'line', False, None), {}),
90*e1fe3e4aSElliott Hughes    ...     ('addPoint', ((2, 5), None, False, None), {}),
91*e1fe3e4aSElliott Hughes    ...     ('addPoint', ((3, 6), None, False, None), {}),
92*e1fe3e4aSElliott Hughes    ...     ('addPoint', ((5, 6), 'qcurve', False, None), {}),
93*e1fe3e4aSElliott Hughes    ...     ('endPath', (), {}),
94*e1fe3e4aSElliott Hughes    ...     ('addComponent', ('a', (1.5, 0, 0, 1.5, 11, -10)), {}),
95*e1fe3e4aSElliott Hughes    ... ]
96*e1fe3e4aSElliott Hughes    True
97*e1fe3e4aSElliott Hughes    """
98*e1fe3e4aSElliott Hughes
99*e1fe3e4aSElliott Hughes    def __init__(self, outPen, roundFunc=otRound, transformRoundFunc=noRound):
100*e1fe3e4aSElliott Hughes        super().__init__(outPen)
101*e1fe3e4aSElliott Hughes        self.roundFunc = roundFunc
102*e1fe3e4aSElliott Hughes        self.transformRoundFunc = transformRoundFunc
103*e1fe3e4aSElliott Hughes
104*e1fe3e4aSElliott Hughes    def addPoint(
105*e1fe3e4aSElliott Hughes        self, pt, segmentType=None, smooth=False, name=None, identifier=None, **kwargs
106*e1fe3e4aSElliott Hughes    ):
107*e1fe3e4aSElliott Hughes        self._outPen.addPoint(
108*e1fe3e4aSElliott Hughes            (self.roundFunc(pt[0]), self.roundFunc(pt[1])),
109*e1fe3e4aSElliott Hughes            segmentType=segmentType,
110*e1fe3e4aSElliott Hughes            smooth=smooth,
111*e1fe3e4aSElliott Hughes            name=name,
112*e1fe3e4aSElliott Hughes            identifier=identifier,
113*e1fe3e4aSElliott Hughes            **kwargs,
114*e1fe3e4aSElliott Hughes        )
115*e1fe3e4aSElliott Hughes
116*e1fe3e4aSElliott Hughes    def addComponent(self, baseGlyphName, transformation, identifier=None, **kwargs):
117*e1fe3e4aSElliott Hughes        xx, xy, yx, yy, dx, dy = transformation
118*e1fe3e4aSElliott Hughes        self._outPen.addComponent(
119*e1fe3e4aSElliott Hughes            baseGlyphName=baseGlyphName,
120*e1fe3e4aSElliott Hughes            transformation=Transform(
121*e1fe3e4aSElliott Hughes                self.transformRoundFunc(xx),
122*e1fe3e4aSElliott Hughes                self.transformRoundFunc(xy),
123*e1fe3e4aSElliott Hughes                self.transformRoundFunc(yx),
124*e1fe3e4aSElliott Hughes                self.transformRoundFunc(yy),
125*e1fe3e4aSElliott Hughes                self.roundFunc(dx),
126*e1fe3e4aSElliott Hughes                self.roundFunc(dy),
127*e1fe3e4aSElliott Hughes            ),
128*e1fe3e4aSElliott Hughes            identifier=identifier,
129*e1fe3e4aSElliott Hughes            **kwargs,
130*e1fe3e4aSElliott Hughes        )
131