Merge pull request #2164 from fonttools/pen-maintenance

Pen maintenance
This commit is contained in:
Nikolaus Waxweiler 2021-02-22 10:26:39 +00:00 committed by GitHub
commit f6a62e0db1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 145 additions and 69 deletions

View File

@ -1,3 +1 @@
"""Empty __init__.py file to signal Python this directory is a package."""
from fontTools.misc.py23 import *

View File

@ -1,6 +1,5 @@
"""Calculate the area of a glyph."""
from fontTools.misc.py23 import *
from fontTools.pens.basePen import BasePen

View File

@ -36,26 +36,52 @@ Coordinates are usually expressed as (x, y) tuples, but generally any
sequence of length 2 will do.
"""
from fontTools.misc.py23 import *
import abc
from typing import Any, Tuple
from fontTools.misc.loggingTools import LogMixin
__all__ = ["AbstractPen", "NullPen", "BasePen",
"decomposeSuperBezierSegment", "decomposeQuadraticSegment"]
class AbstractPen(object):
class AbstractPen(abc.ABC):
@classmethod
def __subclasshook__(cls, subclass: Any) -> bool:
if cls is not AbstractPen:
return NotImplemented
return (
hasattr(subclass, "moveTo")
and callable(subclass.moveTo)
and hasattr(subclass, "lineTo")
and callable(subclass.lineTo)
and hasattr(subclass, "curveTo")
and callable(subclass.curveTo)
and hasattr(subclass, "qCurveTo")
and callable(subclass.qCurveTo)
and hasattr(subclass, "closePath")
and callable(subclass.closePath)
and hasattr(subclass, "endPath")
and callable(subclass.endPath)
and hasattr(subclass, "addComponent")
and callable(subclass.addComponent)
or NotImplemented
)
def moveTo(self, pt):
@abc.abstractmethod
def moveTo(self, pt: Tuple[float, float]) -> None:
"""Begin a new sub path, set the current point to 'pt'. You must
end each sub path with a call to pen.closePath() or pen.endPath().
"""
raise NotImplementedError
def lineTo(self, pt):
@abc.abstractmethod
def lineTo(self, pt: Tuple[float, float]) -> None:
"""Draw a straight line from the current point to 'pt'."""
raise NotImplementedError
def curveTo(self, *points):
@abc.abstractmethod
def curveTo(self, *points: Tuple[float, float]) -> None:
"""Draw a cubic bezier with an arbitrary number of control points.
The last point specified is on-curve, all others are off-curve
@ -76,7 +102,8 @@ class AbstractPen(object):
"""
raise NotImplementedError
def qCurveTo(self, *points):
@abc.abstractmethod
def qCurveTo(self, *points: Tuple[float, float]) -> None:
"""Draw a whole string of quadratic curve segments.
The last point specified is on-curve, all others are off-curve
@ -93,19 +120,26 @@ class AbstractPen(object):
"""
raise NotImplementedError
def closePath(self):
@abc.abstractmethod
def closePath(self) -> None:
"""Close the current sub path. You must call either pen.closePath()
or pen.endPath() after each sub path.
"""
pass
def endPath(self):
@abc.abstractmethod
def endPath(self) -> None:
"""End the current sub path, but don't close it. You must call
either pen.closePath() or pen.endPath() after each sub path.
"""
pass
def addComponent(self, glyphName, transformation):
@abc.abstractmethod
def addComponent(
self,
glyphName: str,
transformation: Tuple[float, float, float, float, float, float]
) -> None:
"""Add a sub glyph. The 'transformation' argument must be a 6-tuple
containing an affine transformation, or a Transform object from the
fontTools.misc.transform module. More precisely: it should be a
@ -114,7 +148,7 @@ class AbstractPen(object):
raise NotImplementedError
class NullPen(object):
class NullPen(AbstractPen):
"""A pen that does nothing.
"""

View File

@ -1,4 +1,3 @@
from fontTools.misc.py23 import *
from fontTools.misc.arrayTools import updateBounds, pointInRect, unionRect
from fontTools.misc.bezierTools import calcCubicBounds, calcQuadraticBounds
from fontTools.pens.basePen import BasePen

View File

@ -1,4 +1,3 @@
from fontTools.misc.py23 import *
from fontTools.pens.basePen import BasePen

View File

@ -1,4 +1,3 @@
from fontTools.misc.py23 import *
from fontTools.pens.basePen import AbstractPen
from fontTools.pens.pointPen import AbstractPointPen
from fontTools.pens.recordingPen import RecordingPen

View File

@ -1,6 +1,5 @@
"""Pen calculating 0th, 1st, and 2nd moments of area of glyph shapes.
This is low-level, autogenerated pen. Use statisticsPen instead."""
from fontTools.misc.py23 import *
from fontTools.pens.basePen import BasePen

View File

@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
"""Calculate the perimeter of a glyph."""
from fontTools.misc.py23 import *
from fontTools.pens.basePen import BasePen
from fontTools.misc.bezierTools import approximateQuadraticArcLengthC, calcQuadraticArcLengthC, approximateCubicArcLengthC, calcCubicArcLengthC
import math

View File

@ -2,7 +2,6 @@
for shapes.
"""
from fontTools.misc.py23 import *
from fontTools.pens.basePen import BasePen
from fontTools.misc.bezierTools import solveQuadratic, solveCubic

View File

@ -11,8 +11,12 @@ steps through all the points in a call from glyph.drawPoints().
This allows the caller to provide more data for each point.
For instance, whether or not a point is smooth, and its name.
"""
from fontTools.pens.basePen import AbstractPen
import abc
import math
from typing import Any, List, Optional, Tuple
from fontTools.pens.basePen import AbstractPen
__all__ = [
"AbstractPointPen",
@ -24,26 +28,56 @@ __all__ = [
]
class AbstractPointPen(object):
"""
Baseclass for all PointPens.
"""
class AbstractPointPen(abc.ABC):
"""Baseclass for all PointPens."""
def beginPath(self, identifier=None, **kwargs):
@classmethod
def __subclasshook__(cls, subclass: Any) -> bool:
if cls is not AbstractPointPen:
return NotImplemented
return (
hasattr(subclass, "beginPath")
and callable(subclass.beginPath)
and hasattr(subclass, "endPath")
and callable(subclass.endPath)
and hasattr(subclass, "addPoint")
and callable(subclass.addPoint)
and hasattr(subclass, "addComponent")
and callable(subclass.addComponent)
or NotImplemented
)
@abc.abstractmethod
def beginPath(self, identifier: Optional[str] = None, **kwargs: Any) -> None:
"""Start a new sub path."""
raise NotImplementedError
def endPath(self):
@abc.abstractmethod
def endPath(self) -> None:
"""End the current sub path."""
raise NotImplementedError
def addPoint(self, pt, segmentType=None, smooth=False, name=None,
identifier=None, **kwargs):
@abc.abstractmethod
def addPoint(
self,
pt: Tuple[float, float],
segmentType: Optional[str] = None,
smooth: bool = False,
name: Optional[str] = None,
identifier: Optional[str] = None,
**kwargs: Any
) -> None:
"""Add a point to the current sub path."""
raise NotImplementedError
def addComponent(self, baseGlyphName, transformation, identifier=None,
**kwargs):
@abc.abstractmethod
def addComponent(
self,
baseGlyphName: str,
transformation: Tuple[float, float, float, float, float, float],
identifier: Optional[str] = None,
**kwargs: Any
) -> None:
"""Add a sub glyph."""
raise NotImplementedError

View File

@ -1,4 +1,3 @@
from fontTools.misc.py23 import *
from fontTools.pens.basePen import BasePen

View File

@ -1,4 +1,3 @@
from fontTools.misc.py23 import *
from fontTools.pens.basePen import BasePen
from Quartz.CoreGraphics import CGPathCreateMutable, CGPathMoveToPoint

View File

@ -1,5 +1,4 @@
"""Pen recording operations that can be accessed or replayed."""
from fontTools.misc.py23 import *
from fontTools.pens.basePen import AbstractPen, DecomposingPen
from fontTools.pens.pointPen import AbstractPointPen

View File

@ -1,4 +1,3 @@
from fontTools.misc.py23 import *
from fontTools.pens.basePen import BasePen
from reportlab.graphics.shapes import Path

View File

@ -1,4 +1,3 @@
from fontTools.misc.py23 import *
from fontTools.misc.arrayTools import pairwise
from fontTools.pens.filterPen import ContourFilterPen

View File

@ -1,6 +1,5 @@
"""Pen calculating area, center of mass, variance and standard-deviation,
covariance and correlation, and slant, of glyph shapes."""
from fontTools.misc.py23 import *
import math
from fontTools.pens.momentsPen import MomentsPen

View File

@ -1,4 +1,3 @@
from fontTools.misc.py23 import *
from fontTools.pens.basePen import BasePen

View File

@ -1,7 +1,6 @@
# Copyright (c) 2009 Type Supply LLC
# Author: Tal Leming
from fontTools.misc.py23 import *
from fontTools.misc.fixedTools import otRound
from fontTools.misc.psCharStrings import T2CharString
from fontTools.pens.basePen import BasePen

View File

@ -1,5 +1,4 @@
"""Pen multiplexing drawing to one or more pens."""
from fontTools.misc.py23 import *
from fontTools.pens.basePen import AbstractPen

View File

@ -1,4 +1,3 @@
from fontTools.misc.py23 import *
from fontTools.pens.filterPen import FilterPen, FilterPointPen

View File

@ -1,4 +1,3 @@
from fontTools.misc.py23 import *
from array import array
from fontTools.misc.fixedTools import MAX_F2DOT14, otRound, floatToFixedToFloat
from fontTools.pens.basePen import LoggingPen
@ -73,6 +72,9 @@ class TTGlyphPen(LoggingPen):
assert self._isClosed(), '"move"-type point must begin a new contour.'
self._addPoint(pt, 1)
def curveTo(self, *points):
raise NotImplementedError
def qCurveTo(self, *points):
assert len(points) >= 1
for pt in points[:-1]:

View File

@ -1,4 +1,3 @@
from fontTools.misc.py23 import *
from fontTools.pens.basePen import BasePen

View File

@ -1,4 +1,3 @@
from fontTools.misc.py23 import *
from fontTools.pens.areaPen import AreaPen
import unittest

View File

@ -1,10 +1,33 @@
from fontTools.misc.py23 import *
from fontTools.pens.basePen import \
BasePen, decomposeSuperBezierSegment, decomposeQuadraticSegment
AbstractPen, BasePen, decomposeSuperBezierSegment, decomposeQuadraticSegment
from fontTools.pens.pointPen import AbstractPointPen
from fontTools.misc.loggingTools import CapturingLogHandler
import unittest
def test_subclasshook():
class NullPen:
def moveTo(self, pt):
pass
def lineTo(self, pt):
pass
def curveTo(self, *points):
pass
def qCurveTo(self, *points):
pass
def closePath(self):
pass
def endPath(self):
pass
def addComponent(self, glyphName, transformation):
pass
assert issubclass(NullPen, AbstractPen)
assert isinstance(NullPen(), AbstractPen)
assert not issubclass(NullPen, AbstractPointPen)
assert not isinstance(NullPen(), AbstractPointPen)
class _TestPen(BasePen):
def __init__(self):
BasePen.__init__(self, glyphSet={})

View File

@ -1,4 +1,3 @@
from fontTools.misc.py23 import *
from fontTools.pens.boundsPen import BoundsPen, ControlBoundsPen
import unittest

View File

@ -1,4 +1,3 @@
from fontTools.misc.py23 import *
import unittest
try:
@ -48,7 +47,7 @@ class CocoaPenTest(unittest.TestCase):
"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))

View File

@ -1,4 +1,3 @@
from fontTools.misc.py23 import *
from fontTools.pens.perimeterPen import PerimeterPen
import unittest

View File

@ -1,4 +1,4 @@
from fontTools.misc.py23 import *
from io import StringIO
from fontTools.pens.pointInsidePen import PointInsidePen
import unittest
@ -72,16 +72,16 @@ class PointInsidePenTest(unittest.TestCase):
@staticmethod
def render(draw_function, even_odd):
result = BytesIO()
result = StringIO()
for y in range(5):
for x in range(10):
pen = PointInsidePen(None, (x + 0.5, y + 0.5), even_odd)
draw_function(pen)
if pen.getResult():
result.write(b"*")
result.write("*")
else:
result.write(b" ")
return tounicode(result.getvalue())
result.write(" ")
return result.getvalue()
def test_contour_no_solutions(self):

View File

@ -1,5 +1,3 @@
from fontTools.misc.py23 import *
from fontTools.misc.loggingTools import CapturingLogHandler
import unittest
from fontTools.pens.basePen import AbstractPen
@ -7,6 +5,23 @@ from fontTools.pens.pointPen import AbstractPointPen, PointToSegmentPen, \
SegmentToPointPen, GuessSmoothPointPen, ReverseContourPointPen
def test_subclasshook():
class NullPen:
def beginPath(self, identifier, **kwargs) -> None:
pass
def endPath(self) -> None:
pass
def addPoint(self, pt, segmentType, smooth, name, identifier, **kwargs) -> None:
pass
def addComponent(self, baseGlyphName, transformation, identifier, **kwargs) -> None:
pass
assert issubclass(NullPen, AbstractPointPen)
assert isinstance(NullPen(), AbstractPointPen)
assert not issubclass(NullPen, AbstractPen)
assert not isinstance(NullPen(), AbstractPen)
class _TestSegmentPen(AbstractPen):
def __init__(self):
@ -43,7 +58,7 @@ def _reprKwargs(kwargs):
items = []
for key in sorted(kwargs):
value = kwargs[key]
if isinstance(value, basestring):
if isinstance(value, str):
items.append("%s='%s'" % (key, value))
else:
items.append("%s=%s" % (key, value))

View File

@ -1,4 +1,3 @@
from fontTools.misc.py23 import *
import unittest
try:
@ -68,7 +67,7 @@ class QuartzPenTest(unittest.TestCase):
"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))

View File

@ -1,4 +1,3 @@
from fontTools.misc.py23 import *
from fontTools.pens.t2CharStringPen import T2CharStringPen
import unittest
@ -7,16 +6,12 @@ class T2CharStringPenTest(unittest.TestCase):
def __init__(self, methodName):
unittest.TestCase.__init__(self, methodName)
# Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
# and fires deprecation warnings if a program uses the old name.
if not hasattr(self, "assertRaisesRegex"):
self.assertRaisesRegex = self.assertRaisesRegexp
def assertAlmostEqualProgram(self, expected, actual):
self.assertEqual(len(expected), len(actual))
for i1, i2 in zip(expected, actual):
if isinstance(i1, basestring):
self.assertIsInstance(i2, basestring)
if isinstance(i1, str):
self.assertIsInstance(i2, str)
self.assertEqual(i1, i2)
else:
self.assertAlmostEqual(i1, i2)

View File

@ -1,5 +1,3 @@
from fontTools.misc.py23 import *
import os
import unittest
import struct

View File

@ -14,7 +14,7 @@
from . import CUBIC_GLYPHS
from fontTools.pens.pointPen import PointToSegmentPen, SegmentToPointPen
from fontTools.misc.py23 import isclose
from math import isclose
import unittest