Add QuartzPen

This commit is contained in:
Antonio Cavedoni 2020-11-19 11:54:56 +01:00
parent ae07ce6e55
commit 742983f087
4 changed files with 197 additions and 12 deletions

View File

@ -0,0 +1,46 @@
from fontTools.misc.py23 import *
from fontTools.pens.basePen import BasePen
from Quartz.CoreGraphics import CGPathCreateMutable, CGPathMoveToPoint
from Quartz.CoreGraphics import CGPathAddLineToPoint, CGPathAddCurveToPoint
from Quartz.CoreGraphics import CGPathAddQuadCurveToPoint, CGPathCloseSubpath
__all__ = ["QuartzPen"]
class QuartzPen(BasePen):
"""A pen that creates a CGPath
Parameters
- path: an optional CGPath to add to
- xform: an optional CGAffineTransform to apply to the path
"""
def __init__(self, glyphSet, path=None, xform=None):
BasePen.__init__(self, glyphSet)
if path is None:
path = CGPathCreateMutable()
self.path = path
self.xform = xform
def _moveTo(self, pt):
x, y = pt
CGPathMoveToPoint(self.path, self.xform, x, y)
def _lineTo(self, pt):
x, y = pt
CGPathAddLineToPoint(self.path, self.xform, x, y)
def _curveToOne(self, p1, p2, p3):
(x1, y1), (x2, y2), (x3, y3) = p1, p2, p3
CGPathAddCurveToPoint(self.path, self.xform, x1, y1, x2, y2, x3, y3)
def _qCurveToOne(self, p1, p2):
(x1, y1), (x2, y2) = p1, p2
CGPathAddQuadCurveToPoint(self.path, self.xform, x1, y1, x2, y2)
def _closePath(self):
CGPathCloseSubpath(self.path)

View File

@ -184,9 +184,9 @@ are required to unlock the extra features named "ufo", etc.
*Extra:* ``pathops`` *Extra:* ``pathops``
- ``Lib/fontTools/pens/cocoaPen.py`` - ``Lib/fontTools/pens/cocoaPen.py`` and ``Lib/fontTools/pens/quartzPen.py``
Pen for drawing glyphs with Cocoa ``NSBezierPath``, requires: Pens for drawing glyphs with Cocoa ``NSBezierPath`` or ``CGPath`` require:
* `PyObjC <https://pypi.python.org/pypi/pyobjc>`__: the bridge between * `PyObjC <https://pypi.python.org/pypi/pyobjc>`__: the bridge between
Python and the Objective-C runtime (macOS platform only). Python and the Objective-C runtime (macOS platform only).
@ -211,16 +211,17 @@ Acknowledgements
In alphabetical order: In alphabetical order:
Olivier Berten, Samyak Bhuta, Erik van Blokland, Petr van Blokland, Olivier Berten, Samyak Bhuta, Erik van Blokland, Petr van Blokland,
Jelle Bosma, Sascha Brawer, Tom Byrer, Frédéric Coiffier, Vincent Jelle Bosma, Sascha Brawer, Tom Byrer, Antonio Cavedoni, Frédéric
Connare, David Corbett, Simon Cozens, Dave Crossland, Simon Daniels, Coiffier, Vincent Connare, David Corbett, Simon Cozens, Dave Crossland,
Peter Dekkers, Behdad Esfahbod, Behnam Esfahbod, Hannes Famira, Sam Simon Daniels, Peter Dekkers, Behdad Esfahbod, Behnam Esfahbod, Hannes
Fishman, Matt Fontaine, Yannis Haralambous, Greg Hitchcock, Jeremie Famira, Sam Fishman, Matt Fontaine, Yannis Haralambous, Greg Hitchcock,
Hornus, Khaled Hosny, John Hudson, Denis Moyogo Jacquerye, Jack Jansen, Jeremie Hornus, Khaled Hosny, John Hudson, Denis Moyogo Jacquerye, Jack
Tom Kacvinsky, Jens Kutilek, Antoine Leca, Werner Lemberg, Tal Leming, Jansen, Tom Kacvinsky, Jens Kutilek, Antoine Leca, Werner Lemberg, Tal
Peter Lofting, Cosimo Lupo, Masaya Nakamura, Dave Opstad, Laurence Leming, Peter Lofting, Cosimo Lupo, Masaya Nakamura, Dave Opstad,
Penney, Roozbeh Pournader, Garret Rieger, Read Roberts, Guido van Laurence Penney, Roozbeh Pournader, Garret Rieger, Read Roberts, Guido
Rossum, Just van Rossum, Andreas Seidel, Georg Seifert, Chris Simpkins, van Rossum, Just van Rossum, Andreas Seidel, Georg Seifert, Chris
Miguel Sousa, Adam Twardoch, Adrien Tétar, Vitaly Volkov, Paul Wise. Simpkins, Miguel Sousa, Adam Twardoch, Adrien Tétar, Vitaly Volkov,
Paul Wise.
Copyrights Copyrights
~~~~~~~~~~ ~~~~~~~~~~

View File

@ -0,0 +1,59 @@
from fontTools.misc.py23 import *
import unittest
try:
from fontTools.pens.cocoaPen import CocoaPen
from AppKit import NSBezierPathElementMoveTo, NSBezierPathElementLineTo
from AppKit import NSBezierPathElementCurveTo, NSBezierPathElementClosePath
PATH_ELEMENTS = {
# NSBezierPathElement key desc
NSBezierPathElementMoveTo: 'moveto',
NSBezierPathElementLineTo: 'lineto',
NSBezierPathElementCurveTo: 'curveto',
NSBezierPathElementClosePath: 'close',
}
PYOBJC_AVAILABLE = True
except ImportError:
PYOBJC_AVAILABLE = False
def draw(pen):
pen.moveTo((50, 0))
pen.lineTo((50, 500))
pen.lineTo((200, 500))
pen.curveTo((350, 500), (450, 400), (450, 250))
pen.curveTo((450, 100), (350, 0), (200, 0))
pen.closePath()
def cocoaPathToString(path):
num_elements = path.elementCount()
output = []
for i in range(num_elements - 1):
elem_type, elem_points = path.elementAtIndex_associatedPoints_(i)
elem_type = PATH_ELEMENTS[elem_type]
path_points = " ".join([f"{p.x} {p.y}" for p in elem_points])
output.append(f"{elem_type} {path_points}")
return " ".join(output)
@unittest.skipUnless(PYOBJC_AVAILABLE, "pyobjc not installed")
class CocoaPenTest(unittest.TestCase):
def test_draw(self):
pen = CocoaPen(None)
draw(pen)
self.assertEqual(
"moveto 50.0 0.0 lineto 50.0 500.0 lineto 200.0 500.0 curveto 350.0 500.0 450.0 400.0 450.0 250.0 curveto 450.0 100.0 350.0 0.0 200.0 0.0 close ",
cocoaPathToString(pen.path)
)
def test_empty(self):
pen = CocoaPen(None)
self.assertEqual("", cocoaPathToString(pen.path))
if __name__ == '__main__':
import sys
sys.exit(unittest.main())

View File

@ -0,0 +1,79 @@
from fontTools.misc.py23 import *
import unittest
try:
from fontTools.pens.quartzPen import QuartzPen
from Quartz.CoreGraphics import CGPathApply
from Quartz.CoreGraphics import kCGPathElementMoveToPoint
from Quartz.CoreGraphics import kCGPathElementAddLineToPoint
from Quartz.CoreGraphics import kCGPathElementAddQuadCurveToPoint
from Quartz.CoreGraphics import kCGPathElementAddCurveToPoint
from Quartz.CoreGraphics import kCGPathElementCloseSubpath
PATH_ELEMENTS = {
# CG constant key desc num_points
kCGPathElementMoveToPoint: ('moveto', 1),
kCGPathElementAddLineToPoint: ('lineto', 1),
kCGPathElementAddCurveToPoint: ('curveto', 3),
kCGPathElementAddQuadCurveToPoint: ('qcurveto', 2),
kCGPathElementCloseSubpath: ('close', 0),
}
PYOBJC_AVAILABLE = True
except ImportError:
PYOBJC_AVAILABLE = False
def draw(pen):
pen.moveTo((50, 0))
pen.lineTo((50, 500))
pen.lineTo((200, 500))
pen.curveTo((350, 500), (450, 400), (450, 250))
pen.curveTo((450, 100), (350, 0), (200, 0))
pen.closePath()
def quartzPathApplier(elements, element):
num_points = 0
elem_type = None
if element.type in PATH_ELEMENTS:
num_points = PATH_ELEMENTS[element.type][1]
elem_type = PATH_ELEMENTS[element.type][0]
elements.append((elem_type, element.points.as_tuple(num_points)))
def quartzPathElements(path):
elements = []
CGPathApply(path, elements, quartzPathApplier)
return elements
def quartzPathToString(path):
elements = quartzPathElements(path)
output = []
for element in elements:
elem_type, elem_points = element
path_points = " ".join([f"{p.x} {p.y}" for p in elem_points])
output.append(f"{elem_type} {path_points}")
return " ".join(output)
@unittest.skipUnless(PYOBJC_AVAILABLE, "pyobjc not installed")
class QuartzPenTest(unittest.TestCase):
def test_draw(self):
pen = QuartzPen(None)
draw(pen)
self.assertEqual(
"moveto 50.0 0.0 lineto 50.0 500.0 lineto 200.0 500.0 curveto 350.0 500.0 450.0 400.0 450.0 250.0 curveto 450.0 100.0 350.0 0.0 200.0 0.0 close ",
quartzPathToString(pen.path)
)
def test_empty(self):
pen = QuartzPen(None)
self.assertEqual("", quartzPathToString(pen.path))
if __name__ == '__main__':
import sys
sys.exit(unittest.main())