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``
- ``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
Python and the Objective-C runtime (macOS platform only).
@ -211,16 +211,17 @@ Acknowledgements
In alphabetical order:
Olivier Berten, Samyak Bhuta, Erik van Blokland, Petr van Blokland,
Jelle Bosma, Sascha Brawer, Tom Byrer, Frédéric Coiffier, Vincent
Connare, David Corbett, Simon Cozens, Dave Crossland, Simon Daniels,
Peter Dekkers, Behdad Esfahbod, Behnam Esfahbod, Hannes Famira, Sam
Fishman, Matt Fontaine, Yannis Haralambous, Greg Hitchcock, Jeremie
Hornus, Khaled Hosny, John Hudson, Denis Moyogo Jacquerye, Jack Jansen,
Tom Kacvinsky, Jens Kutilek, Antoine Leca, Werner Lemberg, Tal Leming,
Peter Lofting, Cosimo Lupo, Masaya Nakamura, Dave Opstad, Laurence
Penney, Roozbeh Pournader, Garret Rieger, Read Roberts, Guido van
Rossum, Just van Rossum, Andreas Seidel, Georg Seifert, Chris Simpkins,
Miguel Sousa, Adam Twardoch, Adrien Tétar, Vitaly Volkov, Paul Wise.
Jelle Bosma, Sascha Brawer, Tom Byrer, Antonio Cavedoni, Frédéric
Coiffier, Vincent Connare, David Corbett, Simon Cozens, Dave Crossland,
Simon Daniels, Peter Dekkers, Behdad Esfahbod, Behnam Esfahbod, Hannes
Famira, Sam Fishman, Matt Fontaine, Yannis Haralambous, Greg Hitchcock,
Jeremie Hornus, Khaled Hosny, John Hudson, Denis Moyogo Jacquerye, Jack
Jansen, Tom Kacvinsky, Jens Kutilek, Antoine Leca, Werner Lemberg, Tal
Leming, Peter Lofting, Cosimo Lupo, Masaya Nakamura, Dave Opstad,
Laurence Penney, Roozbeh Pournader, Garret Rieger, Read Roberts, Guido
van Rossum, Just van Rossum, Andreas Seidel, Georg Seifert, Chris
Simpkins, Miguel Sousa, Adam Twardoch, Adrien Tétar, Vitaly Volkov,
Paul Wise.
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())