2015-10-01 15:07:22 -07:00
|
|
|
from __future__ import print_function, division, absolute_import
|
|
|
|
from fontTools.misc.py23 import *
|
2015-11-04 17:18:03 -08:00
|
|
|
from array import array
|
2015-11-05 13:57:53 -08:00
|
|
|
from fontTools.pens.basePen import AbstractPen
|
2015-11-04 17:18:03 -08:00
|
|
|
from fontTools.pens.transformPen import TransformPen
|
2015-10-01 15:07:22 -07:00
|
|
|
from fontTools.ttLib.tables import ttProgram
|
|
|
|
from fontTools.ttLib.tables._g_l_y_f import Glyph
|
|
|
|
from fontTools.ttLib.tables._g_l_y_f import GlyphComponent
|
|
|
|
from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
|
|
|
|
|
|
|
|
|
|
|
|
__all__ = ["TTGlyphPen"]
|
|
|
|
|
|
|
|
|
2018-03-01 11:52:39 +00:00
|
|
|
ALMOST2 = 1.99993896484375 # F2Dot14 0b1.11111111111111
|
|
|
|
EPSILON= .5 / (1 << 14)
|
|
|
|
|
|
|
|
|
|
|
|
def _closeTo2(value):
|
|
|
|
return abs(value - 2) <= EPSILON
|
|
|
|
|
|
|
|
|
2015-11-05 13:57:53 -08:00
|
|
|
class TTGlyphPen(AbstractPen):
|
2015-10-01 15:07:22 -07:00
|
|
|
"""Pen used for drawing to a TrueType glyph."""
|
|
|
|
|
2015-11-04 17:18:03 -08:00
|
|
|
def __init__(self, glyphSet):
|
2015-11-05 13:57:53 -08:00
|
|
|
self.glyphSet = glyphSet
|
2015-11-04 17:19:58 -08:00
|
|
|
self.init()
|
|
|
|
|
|
|
|
def init(self):
|
2015-10-01 15:07:22 -07:00
|
|
|
self.points = []
|
|
|
|
self.endPts = []
|
|
|
|
self.types = []
|
|
|
|
self.components = []
|
|
|
|
|
|
|
|
def _addPoint(self, pt, onCurve):
|
2016-02-16 17:17:14 -08:00
|
|
|
self.points.append(pt)
|
2015-10-01 15:07:22 -07:00
|
|
|
self.types.append(onCurve)
|
|
|
|
|
2015-11-05 13:57:06 -08:00
|
|
|
def _popPoint(self):
|
|
|
|
self.points.pop()
|
|
|
|
self.types.pop()
|
|
|
|
|
2015-11-05 13:54:51 -08:00
|
|
|
def _isClosed(self):
|
|
|
|
return (
|
|
|
|
(not self.points) or
|
|
|
|
(self.endPts and self.endPts[-1] == len(self.points) - 1))
|
|
|
|
|
2015-11-05 13:57:53 -08:00
|
|
|
def lineTo(self, pt):
|
2015-10-01 15:07:22 -07:00
|
|
|
self._addPoint(pt, 1)
|
|
|
|
|
2015-11-05 13:57:53 -08:00
|
|
|
def moveTo(self, pt):
|
2015-11-05 13:54:51 -08:00
|
|
|
assert self._isClosed(), '"move"-type point must begin a new contour.'
|
2015-11-04 17:21:17 -08:00
|
|
|
self._addPoint(pt, 1)
|
2015-10-01 15:07:22 -07:00
|
|
|
|
2015-11-05 13:57:53 -08:00
|
|
|
def qCurveTo(self, *points):
|
2016-03-09 17:57:10 +00:00
|
|
|
assert len(points) >= 1
|
2015-11-05 13:57:53 -08:00
|
|
|
for pt in points[:-1]:
|
|
|
|
self._addPoint(pt, 0)
|
2015-10-01 15:07:22 -07:00
|
|
|
|
2015-11-05 13:57:53 -08:00
|
|
|
# last point is None if there are no on-curve points
|
2016-03-09 17:57:10 +00:00
|
|
|
if points[-1] is not None:
|
2015-11-05 13:57:53 -08:00
|
|
|
self._addPoint(points[-1], 1)
|
|
|
|
|
|
|
|
def closePath(self):
|
2015-10-01 15:07:22 -07:00
|
|
|
endPt = len(self.points) - 1
|
|
|
|
|
2015-11-05 15:26:24 -08:00
|
|
|
# ignore anchors (one-point paths)
|
|
|
|
if endPt == 0 or (self.endPts and endPt == self.endPts[-1] + 1):
|
|
|
|
self._popPoint()
|
|
|
|
return
|
|
|
|
|
2015-11-05 15:12:18 -08:00
|
|
|
# if first and last point on this path are the same, remove last
|
2015-11-04 17:58:27 -08:00
|
|
|
startPt = 0
|
|
|
|
if self.endPts:
|
|
|
|
startPt = self.endPts[-1] + 1
|
|
|
|
if self.points[startPt] == self.points[endPt]:
|
2015-11-05 13:57:06 -08:00
|
|
|
self._popPoint()
|
2015-11-04 17:58:27 -08:00
|
|
|
endPt -= 1
|
|
|
|
|
2015-10-01 15:07:22 -07:00
|
|
|
self.endPts.append(endPt)
|
|
|
|
|
2015-11-05 13:57:53 -08:00
|
|
|
def endPath(self):
|
2015-10-01 15:07:22 -07:00
|
|
|
# TrueType contours are always "closed"
|
|
|
|
self.closePath()
|
|
|
|
|
|
|
|
def addComponent(self, glyphName, transformation):
|
2015-11-04 17:18:03 -08:00
|
|
|
self.components.append((glyphName, transformation))
|
2015-10-01 15:07:22 -07:00
|
|
|
|
2016-03-07 14:47:02 -08:00
|
|
|
def glyph(self, componentFlags=0x4):
|
2015-11-05 13:54:51 -08:00
|
|
|
assert self._isClosed(), "Didn't close last contour."
|
2015-10-01 15:07:22 -07:00
|
|
|
|
2018-03-01 11:52:39 +00:00
|
|
|
for i, (glyphName, transformation) in enumerate(self.components):
|
|
|
|
if any(_closeTo2(s) for s in transformation[:4]):
|
|
|
|
# use closest F2Dot14 value to 2 when possible
|
|
|
|
transformation = (
|
|
|
|
ALMOST2 if _closeTo2(transformation[0]) else transformation[0],
|
|
|
|
ALMOST2 if _closeTo2(transformation[1]) else transformation[1],
|
|
|
|
ALMOST2 if _closeTo2(transformation[2]) else transformation[2],
|
|
|
|
ALMOST2 if _closeTo2(transformation[3]) else transformation[3],
|
|
|
|
transformation[4],
|
|
|
|
transformation[5]
|
|
|
|
)
|
|
|
|
self.components[i] = (glyphName, transformation)
|
|
|
|
elif any(s > 2 or s < -2 for s in transformation[:4]):
|
|
|
|
# can't have scale >= 2 or scale < -2:
|
|
|
|
tpen = TransformPen(self, transformation)
|
|
|
|
self.glyphSet[glyphName].draw(tpen)
|
|
|
|
self.components.remove((glyphName, transformation))
|
|
|
|
|
2015-11-04 17:18:03 -08:00
|
|
|
components = []
|
|
|
|
for glyphName, transformation in self.components:
|
|
|
|
if self.points:
|
|
|
|
# can't have both, so decompose the glyph
|
|
|
|
tpen = TransformPen(self, transformation)
|
|
|
|
self.glyphSet[glyphName].draw(tpen)
|
|
|
|
continue
|
|
|
|
|
|
|
|
component = GlyphComponent()
|
|
|
|
component.glyphName = glyphName
|
2015-11-04 17:59:57 -08:00
|
|
|
if transformation[:4] != (1, 0, 0, 1):
|
|
|
|
component.transform = (transformation[:2], transformation[2:4])
|
2016-02-16 17:17:14 -08:00
|
|
|
component.x, component.y = transformation[4:]
|
2016-03-07 14:47:02 -08:00
|
|
|
component.flags = componentFlags
|
2015-11-04 17:18:03 -08:00
|
|
|
components.append(component)
|
|
|
|
|
2015-11-05 15:19:53 -08:00
|
|
|
glyph = Glyph()
|
2015-10-01 15:07:22 -07:00
|
|
|
glyph.coordinates = GlyphCoordinates(self.points)
|
|
|
|
glyph.endPtsOfContours = self.endPts
|
|
|
|
glyph.flags = array("B", self.types)
|
2015-11-04 17:19:58 -08:00
|
|
|
self.init()
|
|
|
|
|
2015-11-04 17:18:03 -08:00
|
|
|
if components:
|
|
|
|
glyph.components = components
|
2015-10-01 15:07:22 -07:00
|
|
|
glyph.numberOfContours = -1
|
|
|
|
else:
|
|
|
|
glyph.numberOfContours = len(glyph.endPtsOfContours)
|
2016-03-11 10:53:41 +00:00
|
|
|
glyph.program = ttProgram.Program()
|
|
|
|
glyph.program.fromBytecode(b"")
|
2015-10-01 15:07:22 -07:00
|
|
|
|
|
|
|
return glyph
|