[docs] fontTools.misc.* part 1 (#1956)
* Document misc.arrayTools * Document misc.bezierTools * Document cliTools * Document eexec
This commit is contained in:
parent
db26cf804e
commit
76902b7129
@ -1,8 +1,9 @@
|
||||
##########
|
||||
arrayTools
|
||||
##########
|
||||
#############################################
|
||||
arrayTools: Various array and rectangle tools
|
||||
#############################################
|
||||
|
||||
.. automodule:: fontTools.misc.arrayTools
|
||||
:member-order: bysource
|
||||
:inherited-members:
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
@ -1,6 +1,6 @@
|
||||
###########
|
||||
bezierTools
|
||||
###########
|
||||
####################################################
|
||||
bezierTools: Routines for working with Bezier curves
|
||||
####################################################
|
||||
|
||||
.. automodule:: fontTools.misc.bezierTools
|
||||
:inherited-members:
|
||||
|
@ -1,6 +1,6 @@
|
||||
########
|
||||
cliTools
|
||||
########
|
||||
###################################################################
|
||||
cliTools: Utilities for command-line interfaces and console scripts
|
||||
###################################################################
|
||||
|
||||
.. automodule:: fontTools.misc.cliTools
|
||||
:inherited-members:
|
||||
|
@ -1,6 +1,6 @@
|
||||
#####
|
||||
eexec
|
||||
#####
|
||||
###############################################################
|
||||
eexec: PostScript charstring encryption and decryption routines
|
||||
###############################################################
|
||||
|
||||
.. automodule:: fontTools.misc.eexec
|
||||
:inherited-members:
|
||||
|
@ -1,6 +1,9 @@
|
||||
####
|
||||
misc
|
||||
####
|
||||
##########################################################
|
||||
misc: Miscellaneous libraries helpful for font engineering
|
||||
##########################################################
|
||||
|
||||
This is a collection of packages, most of which are used as internal support
|
||||
utilities by fontTools, but some of which may be more generally useful.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
@ -1,8 +1,6 @@
|
||||
#
|
||||
# Various array and rectangle tools, but mostly rectangles, hence the
|
||||
# name of this module (not).
|
||||
#
|
||||
|
||||
"""Routines for calculating bounding boxes, point in rectangle calculations and
|
||||
so on.
|
||||
"""
|
||||
|
||||
from fontTools.misc.py23 import *
|
||||
from fontTools.misc.fixedTools import otRound
|
||||
@ -11,8 +9,13 @@ import math
|
||||
import operator
|
||||
|
||||
def calcBounds(array):
|
||||
"""Return the bounding rectangle of a 2D points array as a tuple:
|
||||
(xMin, yMin, xMax, yMax)
|
||||
"""Calculate the bounding rectangle of a 2D points array.
|
||||
|
||||
Args:
|
||||
array: A sequence of 2D tuples.
|
||||
|
||||
Returns:
|
||||
A four-item tuple representing the bounding rectangle ``(xMin, yMin, xMax, yMax)``.
|
||||
"""
|
||||
if len(array) == 0:
|
||||
return 0, 0, 0, 0
|
||||
@ -21,29 +24,64 @@ def calcBounds(array):
|
||||
return min(xs), min(ys), max(xs), max(ys)
|
||||
|
||||
def calcIntBounds(array, round=otRound):
|
||||
"""Return the integer bounding rectangle of a 2D points array as a
|
||||
tuple: (xMin, yMin, xMax, yMax)
|
||||
Values are rounded to closest integer towards +Infinity using otRound
|
||||
function by default, unless an optional 'round' function is passed.
|
||||
"""Calculate the integer bounding rectangle of a 2D points array.
|
||||
|
||||
Values are rounded to closest integer towards ``+Infinity`` using the
|
||||
:func:`fontTools.misc.fixedTools.otRound` function by default, unless
|
||||
an optional ``round`` function is passed.
|
||||
|
||||
Args:
|
||||
array: A sequence of 2D tuples.
|
||||
round: A rounding function of type ``f(x: float) -> int``.
|
||||
|
||||
Returns:
|
||||
A four-item tuple of integers representing the bounding rectangle:
|
||||
``(xMin, yMin, xMax, yMax)``.
|
||||
"""
|
||||
return tuple(round(v) for v in calcBounds(array))
|
||||
|
||||
|
||||
def updateBounds(bounds, p, min=min, max=max):
|
||||
"""Return the bounding recangle of rectangle bounds and point (x, y)."""
|
||||
"""Add a point to a bounding rectangle.
|
||||
|
||||
Args:
|
||||
bounds: A bounding rectangle expressed as a tuple
|
||||
``(xMin, yMin, xMax, yMax)``.
|
||||
p: A 2D tuple representing a point.
|
||||
min,max: functions to compute the minimum and maximum.
|
||||
|
||||
Returns:
|
||||
The updated bounding rectangle ``(xMin, yMin, xMax, yMax)``.
|
||||
"""
|
||||
(x, y) = p
|
||||
xMin, yMin, xMax, yMax = bounds
|
||||
return min(xMin, x), min(yMin, y), max(xMax, x), max(yMax, y)
|
||||
|
||||
def pointInRect(p, rect):
|
||||
"""Return True when point (x, y) is inside rect."""
|
||||
"""Test if a point is inside a bounding rectangle.
|
||||
|
||||
Args:
|
||||
p: A 2D tuple representing a point.
|
||||
rect: A bounding rectangle expressed as a tuple
|
||||
``(xMin, yMin, xMax, yMax)``.
|
||||
|
||||
Returns:
|
||||
``True`` if the point is inside the rectangle, ``False`` otherwise.
|
||||
"""
|
||||
(x, y) = p
|
||||
xMin, yMin, xMax, yMax = rect
|
||||
return (xMin <= x <= xMax) and (yMin <= y <= yMax)
|
||||
|
||||
def pointsInRect(array, rect):
|
||||
"""Find out which points or array are inside rect.
|
||||
Returns an array with a boolean for each point.
|
||||
"""Determine which points are inside a bounding rectangle.
|
||||
|
||||
Args:
|
||||
array: A sequence of 2D tuples.
|
||||
rect: A bounding rectangle expressed as a tuple
|
||||
``(xMin, yMin, xMax, yMax)``.
|
||||
|
||||
Returns:
|
||||
A list containing the points inside the rectangle.
|
||||
"""
|
||||
if len(array) < 1:
|
||||
return []
|
||||
@ -51,40 +89,104 @@ def pointsInRect(array, rect):
|
||||
return [(xMin <= x <= xMax) and (yMin <= y <= yMax) for x, y in array]
|
||||
|
||||
def vectorLength(vector):
|
||||
"""Return the length of the given vector."""
|
||||
"""Calculate the length of the given vector.
|
||||
|
||||
Args:
|
||||
vector: A 2D tuple.
|
||||
|
||||
Returns:
|
||||
The Euclidean length of the vector.
|
||||
"""
|
||||
x, y = vector
|
||||
return math.sqrt(x**2 + y**2)
|
||||
|
||||
def asInt16(array):
|
||||
"""Round and cast to 16 bit integer."""
|
||||
"""Round a list of floats to 16-bit signed integers.
|
||||
|
||||
Args:
|
||||
array: List of float values.
|
||||
|
||||
Returns:
|
||||
A list of rounded integers.
|
||||
"""
|
||||
return [int(math.floor(i+0.5)) for i in array]
|
||||
|
||||
|
||||
def normRect(rect):
|
||||
"""Normalize the rectangle so that the following holds:
|
||||
"""Normalize a bounding box rectangle.
|
||||
|
||||
This function "turns the rectangle the right way up", so that the following
|
||||
holds::
|
||||
|
||||
xMin <= xMax and yMin <= yMax
|
||||
|
||||
Args:
|
||||
rect: A bounding rectangle expressed as a tuple
|
||||
``(xMin, yMin, xMax, yMax)``.
|
||||
|
||||
Returns:
|
||||
A normalized bounding rectangle.
|
||||
"""
|
||||
(xMin, yMin, xMax, yMax) = rect
|
||||
return min(xMin, xMax), min(yMin, yMax), max(xMin, xMax), max(yMin, yMax)
|
||||
|
||||
def scaleRect(rect, x, y):
|
||||
"""Scale the rectangle by x, y."""
|
||||
"""Scale a bounding box rectangle.
|
||||
|
||||
Args:
|
||||
rect: A bounding rectangle expressed as a tuple
|
||||
``(xMin, yMin, xMax, yMax)``.
|
||||
x: Factor to scale the rectangle along the X axis.
|
||||
Y: Factor to scale the rectangle along the Y axis.
|
||||
|
||||
Returns:
|
||||
A scaled bounding rectangle.
|
||||
"""
|
||||
(xMin, yMin, xMax, yMax) = rect
|
||||
return xMin * x, yMin * y, xMax * x, yMax * y
|
||||
|
||||
def offsetRect(rect, dx, dy):
|
||||
"""Offset the rectangle by dx, dy."""
|
||||
"""Offset a bounding box rectangle.
|
||||
|
||||
Args:
|
||||
rect: A bounding rectangle expressed as a tuple
|
||||
``(xMin, yMin, xMax, yMax)``.
|
||||
dx: Amount to offset the rectangle along the X axis.
|
||||
dY: Amount to offset the rectangle along the Y axis.
|
||||
|
||||
Returns:
|
||||
An offset bounding rectangle.
|
||||
"""
|
||||
(xMin, yMin, xMax, yMax) = rect
|
||||
return xMin+dx, yMin+dy, xMax+dx, yMax+dy
|
||||
|
||||
def insetRect(rect, dx, dy):
|
||||
"""Inset the rectangle by dx, dy on all sides."""
|
||||
"""Inset a bounding box rectangle on all sides.
|
||||
|
||||
Args:
|
||||
rect: A bounding rectangle expressed as a tuple
|
||||
``(xMin, yMin, xMax, yMax)``.
|
||||
dx: Amount to inset the rectangle along the X axis.
|
||||
dY: Amount to inset the rectangle along the Y axis.
|
||||
|
||||
Returns:
|
||||
An inset bounding rectangle.
|
||||
"""
|
||||
(xMin, yMin, xMax, yMax) = rect
|
||||
return xMin+dx, yMin+dy, xMax-dx, yMax-dy
|
||||
|
||||
def sectRect(rect1, rect2):
|
||||
"""Return a boolean and a rectangle. If the input rectangles intersect, return
|
||||
True and the intersecting rectangle. Return False and (0, 0, 0, 0) if the input
|
||||
"""Test for rectangle-rectangle intersection.
|
||||
|
||||
Args:
|
||||
rect1: First bounding rectangle, expressed as tuples
|
||||
``(xMin, yMin, xMax, yMax)``.
|
||||
rect2: Second bounding rectangle.
|
||||
|
||||
Returns:
|
||||
A boolean and a rectangle.
|
||||
If the input rectangles intersect, returns ``True`` and the intersecting
|
||||
rectangle. Returns ``False`` and ``(0, 0, 0, 0)`` if the input
|
||||
rectangles don't intersect.
|
||||
"""
|
||||
(xMin1, yMin1, xMax1, yMax1) = rect1
|
||||
@ -96,9 +198,16 @@ def sectRect(rect1, rect2):
|
||||
return True, (xMin, yMin, xMax, yMax)
|
||||
|
||||
def unionRect(rect1, rect2):
|
||||
"""Return the smallest rectangle in which both input rectangles are fully
|
||||
enclosed. In other words, return the total bounding rectangle of both input
|
||||
rectangles.
|
||||
"""Determine union of bounding rectangles.
|
||||
|
||||
Args:
|
||||
rect1: First bounding rectangle, expressed as tuples
|
||||
``(xMin, yMin, xMax, yMax)``.
|
||||
rect2: Second bounding rectangle.
|
||||
|
||||
Returns:
|
||||
The smallest rectangle in which both input rectangles are fully
|
||||
enclosed.
|
||||
"""
|
||||
(xMin1, yMin1, xMax1, yMax1) = rect1
|
||||
(xMin2, yMin2, xMax2, yMax2) = rect2
|
||||
@ -106,16 +215,32 @@ def unionRect(rect1, rect2):
|
||||
max(xMax1, xMax2), max(yMax1, yMax2))
|
||||
return (xMin, yMin, xMax, yMax)
|
||||
|
||||
def rectCenter(rect0):
|
||||
"""Return the center of the rectangle as an (x, y) coordinate."""
|
||||
(xMin, yMin, xMax, yMax) = rect0
|
||||
def rectCenter(rect):
|
||||
"""Determine rectangle center.
|
||||
|
||||
Args:
|
||||
rect: Bounding rectangle, expressed as tuples
|
||||
``(xMin, yMin, xMax, yMax)``.
|
||||
|
||||
Returns:
|
||||
A 2D tuple representing the point at the center of the rectangle.
|
||||
"""
|
||||
(xMin, yMin, xMax, yMax) = rect
|
||||
return (xMin+xMax)/2, (yMin+yMax)/2
|
||||
|
||||
def intRect(rect1):
|
||||
"""Return the rectangle, rounded off to integer values, but guaranteeing that
|
||||
the resulting rectangle is NOT smaller than the original.
|
||||
def intRect(rect):
|
||||
"""Round a rectangle to integer values.
|
||||
|
||||
Guarantees that the resulting rectangle is NOT smaller than the original.
|
||||
|
||||
Args:
|
||||
rect: Bounding rectangle, expressed as tuples
|
||||
``(xMin, yMin, xMax, yMax)``.
|
||||
|
||||
Returns:
|
||||
A rounded bounding rectangle.
|
||||
"""
|
||||
(xMin, yMin, xMax, yMax) = rect1
|
||||
(xMin, yMin, xMax, yMax) = rect
|
||||
xMin = int(math.floor(xMin))
|
||||
yMin = int(math.floor(yMin))
|
||||
xMax = int(math.ceil(xMax))
|
||||
@ -124,9 +249,18 @@ def intRect(rect1):
|
||||
|
||||
|
||||
class Vector(object):
|
||||
"""A math-like vector."""
|
||||
"""A math-like vector.
|
||||
|
||||
Represents an n-dimensional numeric vector. ``Vector`` objects support
|
||||
vector addition and subtraction, scalar multiplication and division,
|
||||
negation, rounding, and comparison tests.
|
||||
|
||||
Attributes:
|
||||
values: Sequence of values stored in the vector.
|
||||
"""
|
||||
|
||||
def __init__(self, values, keep=False):
|
||||
"""Initialize a vector. If ``keep`` is true, values will be copied."""
|
||||
self.values = values if keep else list(values)
|
||||
|
||||
def __getitem__(self, index):
|
||||
@ -191,6 +325,7 @@ class Vector(object):
|
||||
def __round__(self):
|
||||
return Vector(self._unaryOp(round), keep=True)
|
||||
def toInt(self):
|
||||
"""Synonym for ``round``."""
|
||||
return self.__round__()
|
||||
|
||||
def __eq__(self, other):
|
||||
@ -208,6 +343,8 @@ class Vector(object):
|
||||
def __abs__(self):
|
||||
return math.sqrt(sum([x*x for x in self.values]))
|
||||
def dot(self, other):
|
||||
"""Performs vector dot product, returning sum of
|
||||
``a[0] * b[0], a[1] * b[1], ...``"""
|
||||
a = self.values
|
||||
b = other.values if type(other) == Vector else b
|
||||
assert len(a) == len(b)
|
||||
@ -215,8 +352,16 @@ class Vector(object):
|
||||
|
||||
|
||||
def pairwise(iterable, reverse=False):
|
||||
"""Iterate over current and next items in iterable, optionally in
|
||||
reverse order.
|
||||
"""Iterate over current and next items in iterable.
|
||||
|
||||
Args:
|
||||
iterable: An iterable
|
||||
reverse: If true, iterate in reverse order.
|
||||
|
||||
Returns:
|
||||
A iterable yielding two elements per iteration.
|
||||
|
||||
Example:
|
||||
|
||||
>>> tuple(pairwise([]))
|
||||
()
|
||||
|
@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""fontTools.misc.bezierTools.py -- tools for working with bezier path segments.
|
||||
"""fontTools.misc.bezierTools.py -- tools for working with Bezier path segments.
|
||||
"""
|
||||
|
||||
from fontTools.misc.arrayTools import calcBounds
|
||||
@ -29,7 +29,19 @@ __all__ = [
|
||||
|
||||
|
||||
def calcCubicArcLength(pt1, pt2, pt3, pt4, tolerance=0.005):
|
||||
"""Return the arc length for a cubic bezier segment."""
|
||||
"""Calculates the arc length for a cubic Bezier segment.
|
||||
|
||||
Whereas :func:`approximateCubicArcLength` approximates the length, this
|
||||
function calculates it by "measuring", recursively dividing the curve
|
||||
until the divided segments are shorter than ``tolerance``.
|
||||
|
||||
Args:
|
||||
pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
|
||||
tolerance: Controls the precision of the calcuation.
|
||||
|
||||
Returns:
|
||||
Arc length value.
|
||||
"""
|
||||
return calcCubicArcLengthC(complex(*pt1), complex(*pt2), complex(*pt3), complex(*pt4), tolerance)
|
||||
|
||||
|
||||
@ -49,7 +61,15 @@ def _calcCubicArcLengthCRecurse(mult, p0, p1, p2, p3):
|
||||
return _calcCubicArcLengthCRecurse(mult, *one) + _calcCubicArcLengthCRecurse(mult, *two)
|
||||
|
||||
def calcCubicArcLengthC(pt1, pt2, pt3, pt4, tolerance=0.005):
|
||||
"""Return the arc length for a cubic bezier segment using complex points."""
|
||||
"""Calculates the arc length for a cubic Bezier segment.
|
||||
|
||||
Args:
|
||||
pt1,pt2,pt3,pt4: Control points of the Bezier as complex numbers.
|
||||
tolerance: Controls the precision of the calcuation.
|
||||
|
||||
Returns:
|
||||
Arc length value.
|
||||
"""
|
||||
mult = 1. + 1.5 * tolerance # The 1.5 is a empirical hack; no math
|
||||
return _calcCubicArcLengthCRecurse(mult, pt1, pt2, pt3, pt4)
|
||||
|
||||
@ -69,8 +89,17 @@ def _intSecAtan(x):
|
||||
|
||||
|
||||
def calcQuadraticArcLength(pt1, pt2, pt3):
|
||||
"""Return the arc length for a qudratic bezier segment.
|
||||
pt1 and pt3 are the "anchor" points, pt2 is the "handle".
|
||||
"""Calculates the arc length for a quadratic Bezier segment.
|
||||
|
||||
Args:
|
||||
pt1: Start point of the Bezier as 2D tuple.
|
||||
pt2: Handle point of the Bezier as 2D tuple.
|
||||
pt3: End point of the Bezier as 2D tuple.
|
||||
|
||||
Returns:
|
||||
Arc length value.
|
||||
|
||||
Example::
|
||||
|
||||
>>> calcQuadraticArcLength((0, 0), (0, 0), (0, 0)) # empty segment
|
||||
0.0
|
||||
@ -95,9 +124,16 @@ def calcQuadraticArcLength(pt1, pt2, pt3):
|
||||
|
||||
|
||||
def calcQuadraticArcLengthC(pt1, pt2, pt3):
|
||||
"""Return the arc length for a qudratic bezier segment using complex points.
|
||||
pt1 and pt3 are the "anchor" points, pt2 is the "handle"."""
|
||||
"""Calculates the arc length for a quadratic Bezier segment.
|
||||
|
||||
Args:
|
||||
pt1: Start point of the Bezier as a complex number.
|
||||
pt2: Handle point of the Bezier as a complex number.
|
||||
pt3: End point of the Bezier as a complex number.
|
||||
|
||||
Returns:
|
||||
Arc length value.
|
||||
"""
|
||||
# Analytical solution to the length of a quadratic bezier.
|
||||
# I'll explain how I arrived at this later.
|
||||
d0 = pt2 - pt1
|
||||
@ -120,15 +156,36 @@ def calcQuadraticArcLengthC(pt1, pt2, pt3):
|
||||
|
||||
|
||||
def approximateQuadraticArcLength(pt1, pt2, pt3):
|
||||
# Approximate length of quadratic Bezier curve using Gauss-Legendre quadrature
|
||||
# with n=3 points.
|
||||
"""Calculates the arc length for a quadratic Bezier segment.
|
||||
|
||||
Uses Gauss-Legendre quadrature for a branch-free approximation.
|
||||
See :func:`calcQuadraticArcLength` for a slower but more accurate result.
|
||||
|
||||
Args:
|
||||
pt1: Start point of the Bezier as 2D tuple.
|
||||
pt2: Handle point of the Bezier as 2D tuple.
|
||||
pt3: End point of the Bezier as 2D tuple.
|
||||
|
||||
Returns:
|
||||
Approximate arc length value.
|
||||
"""
|
||||
return approximateQuadraticArcLengthC(complex(*pt1), complex(*pt2), complex(*pt3))
|
||||
|
||||
|
||||
def approximateQuadraticArcLengthC(pt1, pt2, pt3):
|
||||
# Approximate length of quadratic Bezier curve using Gauss-Legendre quadrature
|
||||
# with n=3 points for complex points.
|
||||
#
|
||||
"""Calculates the arc length for a quadratic Bezier segment.
|
||||
|
||||
Uses Gauss-Legendre quadrature for a branch-free approximation.
|
||||
See :func:`calcQuadraticArcLength` for a slower but more accurate result.
|
||||
|
||||
Args:
|
||||
pt1: Start point of the Bezier as a complex number.
|
||||
pt2: Handle point of the Bezier as a complex number.
|
||||
pt3: End point of the Bezier as a complex number.
|
||||
|
||||
Returns:
|
||||
Approximate arc length value.
|
||||
"""
|
||||
# This, essentially, approximates the length-of-derivative function
|
||||
# to be integrated with the best-matching fifth-degree polynomial
|
||||
# approximation of it.
|
||||
@ -145,8 +202,17 @@ def approximateQuadraticArcLengthC(pt1, pt2, pt3):
|
||||
|
||||
|
||||
def calcQuadraticBounds(pt1, pt2, pt3):
|
||||
"""Return the bounding rectangle for a qudratic bezier segment.
|
||||
pt1 and pt3 are the "anchor" points, pt2 is the "handle".
|
||||
"""Calculates the bounding rectangle for a quadratic Bezier segment.
|
||||
|
||||
Args:
|
||||
pt1: Start point of the Bezier as a 2D tuple.
|
||||
pt2: Handle point of the Bezier as a 2D tuple.
|
||||
pt3: End point of the Bezier as a 2D tuple.
|
||||
|
||||
Returns:
|
||||
A four-item tuple representing the bounding rectangle ``(xMin, yMin, xMax, yMax)``.
|
||||
|
||||
Example::
|
||||
|
||||
>>> calcQuadraticBounds((0, 0), (50, 100), (100, 0))
|
||||
(0, 0, 100, 50.0)
|
||||
@ -166,8 +232,18 @@ def calcQuadraticBounds(pt1, pt2, pt3):
|
||||
|
||||
|
||||
def approximateCubicArcLength(pt1, pt2, pt3, pt4):
|
||||
"""Return the approximate arc length for a cubic bezier segment.
|
||||
pt1 and pt4 are the "anchor" points, pt2 and pt3 are the "handles".
|
||||
"""Approximates the arc length for a cubic Bezier segment.
|
||||
|
||||
Uses Gauss-Lobatto quadrature with n=5 points to approximate arc length.
|
||||
See :func:`calcCubicArcLength` for a slower but more accurate result.
|
||||
|
||||
Args:
|
||||
pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
|
||||
|
||||
Returns:
|
||||
Arc length value.
|
||||
|
||||
Example::
|
||||
|
||||
>>> approximateCubicArcLength((0, 0), (25, 100), (75, 100), (100, 0))
|
||||
190.04332968932817
|
||||
@ -180,18 +256,18 @@ def approximateCubicArcLength(pt1, pt2, pt3, pt4):
|
||||
>>> approximateCubicArcLength((0, 0), (50, 0), (100, -50), (-50, 0)) # cusp
|
||||
154.80848416537057
|
||||
"""
|
||||
# Approximate length of cubic Bezier curve using Gauss-Lobatto quadrature
|
||||
# with n=5 points.
|
||||
return approximateCubicArcLengthC(complex(*pt1), complex(*pt2), complex(*pt3), complex(*pt4))
|
||||
|
||||
|
||||
def approximateCubicArcLengthC(pt1, pt2, pt3, pt4):
|
||||
"""Return the approximate arc length for a cubic bezier segment of complex points.
|
||||
pt1 and pt4 are the "anchor" points, pt2 and pt3 are the "handles"."""
|
||||
"""Approximates the arc length for a cubic Bezier segment.
|
||||
|
||||
# Approximate length of cubic Bezier curve using Gauss-Lobatto quadrature
|
||||
# with n=5 points for complex points.
|
||||
#
|
||||
Args:
|
||||
pt1,pt2,pt3,pt4: Control points of the Bezier as complex numbers.
|
||||
|
||||
Returns:
|
||||
Arc length value.
|
||||
"""
|
||||
# This, essentially, approximates the length-of-derivative function
|
||||
# to be integrated with the best-matching seventh-degree polynomial
|
||||
# approximation of it.
|
||||
@ -210,8 +286,15 @@ def approximateCubicArcLengthC(pt1, pt2, pt3, pt4):
|
||||
|
||||
|
||||
def calcCubicBounds(pt1, pt2, pt3, pt4):
|
||||
"""Return the bounding rectangle for a cubic bezier segment.
|
||||
pt1 and pt4 are the "anchor" points, pt2 and pt3 are the "handles".
|
||||
"""Calculates the bounding rectangle for a quadratic Bezier segment.
|
||||
|
||||
Args:
|
||||
pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
|
||||
|
||||
Returns:
|
||||
A four-item tuple representing the bounding rectangle ``(xMin, yMin, xMax, yMax)``.
|
||||
|
||||
Example::
|
||||
|
||||
>>> calcCubicBounds((0, 0), (25, 100), (75, 100), (100, 0))
|
||||
(0, 0, 100, 75.0)
|
||||
@ -235,12 +318,23 @@ def calcCubicBounds(pt1, pt2, pt3, pt4):
|
||||
|
||||
|
||||
def splitLine(pt1, pt2, where, isHorizontal):
|
||||
"""Split the line between pt1 and pt2 at position 'where', which
|
||||
is an x coordinate if isHorizontal is False, a y coordinate if
|
||||
isHorizontal is True. Return a list of two line segments if the
|
||||
line was successfully split, or a list containing the original
|
||||
"""Split a line at a given coordinate.
|
||||
|
||||
Args:
|
||||
pt1: Start point of line as 2D tuple.
|
||||
pt2: End point of line as 2D tuple.
|
||||
where: Position at which to split the line.
|
||||
isHorizontal: Direction of the ray splitting the line. If true,
|
||||
``where`` is interpreted as a Y coordinate; if false, then
|
||||
``where`` is interpreted as an X coordinate.
|
||||
|
||||
Returns:
|
||||
A list of two line segments (each line segment being two 2D tuples)
|
||||
if the line was successfully split, or a list containing the original
|
||||
line.
|
||||
|
||||
Example::
|
||||
|
||||
>>> printSegments(splitLine((0, 0), (100, 100), 50, True))
|
||||
((0, 0), (50, 50))
|
||||
((50, 50), (100, 100))
|
||||
@ -281,9 +375,21 @@ def splitLine(pt1, pt2, where, isHorizontal):
|
||||
|
||||
|
||||
def splitQuadratic(pt1, pt2, pt3, where, isHorizontal):
|
||||
"""Split the quadratic curve between pt1, pt2 and pt3 at position 'where',
|
||||
which is an x coordinate if isHorizontal is False, a y coordinate if
|
||||
isHorizontal is True. Return a list of curve segments.
|
||||
"""Split a quadratic Bezier curve at a given coordinate.
|
||||
|
||||
Args:
|
||||
pt1,pt2,pt3: Control points of the Bezier as 2D tuples.
|
||||
where: Position at which to split the curve.
|
||||
isHorizontal: Direction of the ray splitting the curve. If true,
|
||||
``where`` is interpreted as a Y coordinate; if false, then
|
||||
``where`` is interpreted as an X coordinate.
|
||||
|
||||
Returns:
|
||||
A list of two curve segments (each curve segment being three 2D tuples)
|
||||
if the curve was successfully split, or a list containing the original
|
||||
curve.
|
||||
|
||||
Example::
|
||||
|
||||
>>> printSegments(splitQuadratic((0, 0), (50, 100), (100, 0), 150, False))
|
||||
((0, 0), (50, 100), (100, 0))
|
||||
@ -313,9 +419,21 @@ def splitQuadratic(pt1, pt2, pt3, where, isHorizontal):
|
||||
|
||||
|
||||
def splitCubic(pt1, pt2, pt3, pt4, where, isHorizontal):
|
||||
"""Split the cubic curve between pt1, pt2, pt3 and pt4 at position 'where',
|
||||
which is an x coordinate if isHorizontal is False, a y coordinate if
|
||||
isHorizontal is True. Return a list of curve segments.
|
||||
"""Split a cubic Bezier curve at a given coordinate.
|
||||
|
||||
Args:
|
||||
pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
|
||||
where: Position at which to split the curve.
|
||||
isHorizontal: Direction of the ray splitting the curve. If true,
|
||||
``where`` is interpreted as a Y coordinate; if false, then
|
||||
``where`` is interpreted as an X coordinate.
|
||||
|
||||
Returns:
|
||||
A list of two curve segments (each curve segment being four 2D tuples)
|
||||
if the curve was successfully split, or a list containing the original
|
||||
curve.
|
||||
|
||||
Example::
|
||||
|
||||
>>> printSegments(splitCubic((0, 0), (25, 100), (75, 100), (100, 0), 150, False))
|
||||
((0, 0), (25, 100), (75, 100), (100, 0))
|
||||
@ -337,8 +455,16 @@ def splitCubic(pt1, pt2, pt3, pt4, where, isHorizontal):
|
||||
|
||||
|
||||
def splitQuadraticAtT(pt1, pt2, pt3, *ts):
|
||||
"""Split the quadratic curve between pt1, pt2 and pt3 at one or more
|
||||
values of t. Return a list of curve segments.
|
||||
"""Split a quadratic Bezier curve at one or more values of t.
|
||||
|
||||
Args:
|
||||
pt1,pt2,pt3: Control points of the Bezier as 2D tuples.
|
||||
*ts: Positions at which to split the curve.
|
||||
|
||||
Returns:
|
||||
A list of curve segments (each curve segment being three 2D tuples).
|
||||
|
||||
Examples::
|
||||
|
||||
>>> printSegments(splitQuadraticAtT((0, 0), (50, 100), (100, 0), 0.5))
|
||||
((0, 0), (25, 50), (50, 50))
|
||||
@ -353,8 +479,16 @@ def splitQuadraticAtT(pt1, pt2, pt3, *ts):
|
||||
|
||||
|
||||
def splitCubicAtT(pt1, pt2, pt3, pt4, *ts):
|
||||
"""Split the cubic curve between pt1, pt2, pt3 and pt4 at one or more
|
||||
values of t. Return a list of curve segments.
|
||||
"""Split a cubic Bezier curve at one or more values of t.
|
||||
|
||||
Args:
|
||||
pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.
|
||||
*ts: Positions at which to split the curve.
|
||||
|
||||
Returns:
|
||||
A list of curve segments (each curve segment being four 2D tuples).
|
||||
|
||||
Examples::
|
||||
|
||||
>>> printSegments(splitCubicAtT((0, 0), (25, 100), (75, 100), (100, 0), 0.5))
|
||||
((0, 0), (12.5, 50), (31.25, 75), (50, 75))
|
||||
@ -437,10 +571,18 @@ from math import sqrt, acos, cos, pi
|
||||
|
||||
def solveQuadratic(a, b, c,
|
||||
sqrt=sqrt):
|
||||
"""Solve a quadratic equation where a, b and c are real.
|
||||
a*x*x + b*x + c = 0
|
||||
This function returns a list of roots. Note that the returned list
|
||||
is neither guaranteed to be sorted nor to contain unique values!
|
||||
"""Solve a quadratic equation.
|
||||
|
||||
Solves *a*x*x + b*x + c = 0* where a, b and c are real.
|
||||
|
||||
Args:
|
||||
a: coefficient of *x²*
|
||||
b: coefficient of *x*
|
||||
c: constant term
|
||||
|
||||
Returns:
|
||||
A list of roots. Note that the returned list is neither guaranteed to
|
||||
be sorted nor to contain unique values!
|
||||
"""
|
||||
if abs(a) < epsilon:
|
||||
if abs(b) < epsilon:
|
||||
@ -462,10 +604,21 @@ def solveQuadratic(a, b, c,
|
||||
|
||||
|
||||
def solveCubic(a, b, c, d):
|
||||
"""Solve a cubic equation where a, b, c and d are real.
|
||||
a*x*x*x + b*x*x + c*x + d = 0
|
||||
This function returns a list of roots. Note that the returned list
|
||||
is neither guaranteed to be sorted nor to contain unique values!
|
||||
"""Solve a cubic equation.
|
||||
|
||||
Solves *a*x*x*x + b*x*x + c*x + d = 0* where a, b, c and d are real.
|
||||
|
||||
Args:
|
||||
a: coefficient of *x³*
|
||||
b: coefficient of *x²*
|
||||
c: coefficient of *x*
|
||||
d: constant term
|
||||
|
||||
Returns:
|
||||
A list of roots. Note that the returned list is neither guaranteed to
|
||||
be sorted nor to contain unique values!
|
||||
|
||||
Examples::
|
||||
|
||||
>>> solveCubic(1, 1, -6, 0)
|
||||
[-3.0, -0.0, 2.0]
|
||||
|
@ -8,6 +8,28 @@ numberAddedRE = re.compile(r"#\d+$")
|
||||
|
||||
|
||||
def makeOutputFileName(input, outputDir=None, extension=None, overWrite=False):
|
||||
"""Generates a suitable file name for writing output.
|
||||
|
||||
Often tools will want to take a file, do some kind of transformation to it,
|
||||
and write it out again. This function determines an appropriate name for the
|
||||
output file, through one or more of the following steps:
|
||||
|
||||
- changing the output directory
|
||||
- replacing the file extension
|
||||
- suffixing the filename with a number (``#1``, ``#2``, etc.) to avoid
|
||||
overwriting an existing file.
|
||||
|
||||
Args:
|
||||
input: Name of input file.
|
||||
outputDir: Optionally, a new directory to write the file into.
|
||||
extension: Optionally, a replacement for the current file extension.
|
||||
overWrite: Overwriting an existing file is permitted if true; if false
|
||||
and the proposed filename exists, a new name will be generated by
|
||||
adding an appropriate number suffix.
|
||||
|
||||
Returns:
|
||||
str: Suitable output filename
|
||||
"""
|
||||
dirName, fileName = os.path.split(input)
|
||||
fileName, ext = os.path.splitext(fileName)
|
||||
if outputDir:
|
||||
|
@ -1,5 +1,15 @@
|
||||
"""fontTools.misc.eexec.py -- Module implementing the eexec and
|
||||
charstring encryption algorithm as used by PostScript Type 1 fonts.
|
||||
"""
|
||||
PostScript Type 1 fonts make use of two types of encryption: charstring
|
||||
encryption and ``eexec`` encryption. Charstring encryption is used for
|
||||
the charstrings themselves, while ``eexec`` is used to encrypt larger
|
||||
sections of the font program, such as the ``Private`` and ``CharStrings``
|
||||
dictionaries. Despite the different names, the algorithm is the same,
|
||||
although ``eexec`` encryption uses a fixed initial key R=55665.
|
||||
|
||||
The algorithm uses cipher feedback, meaning that the ciphertext is used
|
||||
to modify the key. Because of this, the routines in this module return
|
||||
the new key at the end of the operation.
|
||||
|
||||
"""
|
||||
|
||||
from fontTools.misc.py23 import *
|
||||
@ -19,6 +29,18 @@ def _encryptChar(plain, R):
|
||||
|
||||
def decrypt(cipherstring, R):
|
||||
r"""
|
||||
Decrypts a string using the Type 1 encryption algorithm.
|
||||
|
||||
Args:
|
||||
cipherstring: String of ciphertext.
|
||||
R: Initial key.
|
||||
|
||||
Returns:
|
||||
decryptedStr: Plaintext string.
|
||||
R: Output key for subsequent decryptions.
|
||||
|
||||
Examples::
|
||||
|
||||
>>> testStr = b"\0\0asdadads asds\265"
|
||||
>>> decryptedStr, R = decrypt(testStr, 12321)
|
||||
>>> decryptedStr == b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1'
|
||||
@ -35,6 +57,30 @@ def decrypt(cipherstring, R):
|
||||
|
||||
def encrypt(plainstring, R):
|
||||
r"""
|
||||
Encrypts a string using the Type 1 encryption algorithm.
|
||||
|
||||
Note that the algorithm as described in the Type 1 specification requires the
|
||||
plaintext to be prefixed with a number of random bytes. (For ``eexec`` the
|
||||
number of random bytes is set to 4.) This routine does *not* add the random
|
||||
prefix to its input.
|
||||
|
||||
Args:
|
||||
plainstring: String of plaintext.
|
||||
R: Initial key.
|
||||
|
||||
Returns:
|
||||
cipherstring: Ciphertext string.
|
||||
R: Output key for subsequent encryptions.
|
||||
|
||||
Examples::
|
||||
|
||||
>>> testStr = b"\0\0asdadads asds\265"
|
||||
>>> decryptedStr, R = decrypt(testStr, 12321)
|
||||
>>> decryptedStr == b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1'
|
||||
True
|
||||
>>> R == 36142
|
||||
True
|
||||
|
||||
>>> testStr = b'0d\nh\x15\xe8\xc4\xb2\x15\x1d\x108\x1a<6\xa1'
|
||||
>>> encryptedStr, R = encrypt(testStr, 12321)
|
||||
>>> encryptedStr == b"\0\0asdadads asds\265"
|
||||
|
Loading…
x
Reference in New Issue
Block a user