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``
|
*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
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
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