roundingPen.py 4.5 KB

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