Add QuartzPen
This commit is contained in:
parent
ae07ce6e55
commit
742983f087
46
Lib/fontTools/pens/quartzPen.py
Normal file
46
Lib/fontTools/pens/quartzPen.py
Normal 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)
|
||||
|
25
README.rst
25
README.rst
@ -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
|
||||
~~~~~~~~~~
|
||||
|
59
Tests/pens/cocoaPen_test.py
Normal file
59
Tests/pens/cocoaPen_test.py
Normal 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())
|
79
Tests/pens/quartzPen_test.py
Normal file
79
Tests/pens/quartzPen_test.py
Normal 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())
|
Loading…
x
Reference in New Issue
Block a user