Merge branch 'master' into fealib-duplicate-sub-warning
This commit is contained in:
commit
bf4fedaa74
@ -4,6 +4,6 @@ from fontTools.misc.loggingTools import configLogger
|
|||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
version = __version__ = "4.1.1.dev0"
|
version = __version__ = "4.2.1.dev0"
|
||||||
|
|
||||||
__all__ = ["version", "log", "configLogger"]
|
__all__ = ["version", "log", "configLogger"]
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
from fontTools.misc.py23 import *
|
from fontTools.misc.py23 import *
|
||||||
from fontTools.pens.basePen import AbstractPen
|
from fontTools.pens.basePen import AbstractPen
|
||||||
|
from fontTools.pens.pointPen import AbstractPointPen
|
||||||
from fontTools.pens.recordingPen import RecordingPen
|
from fontTools.pens.recordingPen import RecordingPen
|
||||||
|
|
||||||
|
|
||||||
class _PassThruComponentsMixin(object):
|
class _PassThruComponentsMixin(object):
|
||||||
|
|
||||||
def addComponent(self, glyphName, transformation):
|
def addComponent(self, glyphName, transformation, **kwargs):
|
||||||
self._outPen.addComponent(glyphName, transformation)
|
self._outPen.addComponent(glyphName, transformation, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class FilterPen(_PassThruComponentsMixin, AbstractPen):
|
class FilterPen(_PassThruComponentsMixin, AbstractPen):
|
||||||
@ -118,3 +119,41 @@ class ContourFilterPen(_PassThruComponentsMixin, RecordingPen):
|
|||||||
Otherwise, the return value is drawn with the output pen.
|
Otherwise, the return value is drawn with the output pen.
|
||||||
"""
|
"""
|
||||||
return # or return contour
|
return # or return contour
|
||||||
|
|
||||||
|
|
||||||
|
class FilterPointPen(_PassThruComponentsMixin, AbstractPointPen):
|
||||||
|
""" Baseclass for point pens that apply some transformation to the
|
||||||
|
coordinates they receive and pass them to another point pen.
|
||||||
|
|
||||||
|
You can override any of its methods. The default implementation does
|
||||||
|
nothing, but passes the commands unmodified to the other pen.
|
||||||
|
|
||||||
|
>>> from fontTools.pens.recordingPen import RecordingPointPen
|
||||||
|
>>> rec = RecordingPointPen()
|
||||||
|
>>> pen = FilterPointPen(rec)
|
||||||
|
>>> v = iter(rec.value)
|
||||||
|
>>> pen.beginPath(identifier="abc")
|
||||||
|
>>> next(v)
|
||||||
|
('beginPath', (), {'identifier': 'abc'})
|
||||||
|
>>> pen.addPoint((1, 2), "line", False)
|
||||||
|
>>> next(v)
|
||||||
|
('addPoint', ((1, 2), 'line', False, None), {})
|
||||||
|
>>> pen.addComponent("a", (2, 0, 0, 2, 10, -10), identifier="0001")
|
||||||
|
>>> next(v)
|
||||||
|
('addComponent', ('a', (2, 0, 0, 2, 10, -10)), {'identifier': '0001'})
|
||||||
|
>>> pen.endPath()
|
||||||
|
>>> next(v)
|
||||||
|
('endPath', (), {})
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, outPointPen):
|
||||||
|
self._outPen = outPointPen
|
||||||
|
|
||||||
|
def beginPath(self, **kwargs):
|
||||||
|
self._outPen.beginPath(**kwargs)
|
||||||
|
|
||||||
|
def endPath(self):
|
||||||
|
self._outPen.endPath()
|
||||||
|
|
||||||
|
def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
|
||||||
|
self._outPen.addPoint(pt, segmentType, smooth, name, **kwargs)
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
"""Pen recording operations that can be accessed or replayed."""
|
"""Pen recording operations that can be accessed or replayed."""
|
||||||
from fontTools.misc.py23 import *
|
from fontTools.misc.py23 import *
|
||||||
from fontTools.pens.basePen import AbstractPen, DecomposingPen
|
from fontTools.pens.basePen import AbstractPen, DecomposingPen
|
||||||
|
from fontTools.pens.pointPen import AbstractPointPen
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["replayRecording", "RecordingPen", "DecomposingRecordingPen"]
|
__all__ = [
|
||||||
|
"replayRecording",
|
||||||
|
"RecordingPen",
|
||||||
|
"DecomposingRecordingPen",
|
||||||
|
"RecordingPointPen",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def replayRecording(recording, pen):
|
def replayRecording(recording, pen):
|
||||||
@ -89,6 +95,51 @@ class DecomposingRecordingPen(DecomposingPen, RecordingPen):
|
|||||||
skipMissingComponents = False
|
skipMissingComponents = False
|
||||||
|
|
||||||
|
|
||||||
|
class RecordingPointPen(AbstractPointPen):
|
||||||
|
"""PointPen recording operations that can be accessed or replayed.
|
||||||
|
|
||||||
|
The recording can be accessed as pen.value; or replayed using
|
||||||
|
pointPen.replay(otherPointPen).
|
||||||
|
|
||||||
|
Usage example:
|
||||||
|
==============
|
||||||
|
from defcon import Font
|
||||||
|
from fontTools.pens.recordingPen import RecordingPointPen
|
||||||
|
|
||||||
|
glyph_name = 'a'
|
||||||
|
font_path = 'MyFont.ufo'
|
||||||
|
|
||||||
|
font = Font(font_path)
|
||||||
|
glyph = font[glyph_name]
|
||||||
|
|
||||||
|
pen = RecordingPointPen()
|
||||||
|
glyph.drawPoints(pen)
|
||||||
|
print(pen.value)
|
||||||
|
|
||||||
|
new_glyph = font.newGlyph('b')
|
||||||
|
pen.replay(new_glyph.getPointPen())
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.value = []
|
||||||
|
|
||||||
|
def beginPath(self, **kwargs):
|
||||||
|
self.value.append(("beginPath", (), kwargs))
|
||||||
|
|
||||||
|
def endPath(self):
|
||||||
|
self.value.append(("endPath", (), {}))
|
||||||
|
|
||||||
|
def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
|
||||||
|
self.value.append(("addPoint", (pt, segmentType, smooth, name), kwargs))
|
||||||
|
|
||||||
|
def addComponent(self, baseGlyphName, transformation, **kwargs):
|
||||||
|
self.value.append(("addComponent", (baseGlyphName, transformation), kwargs))
|
||||||
|
|
||||||
|
def replay(self, pointPen):
|
||||||
|
for operator, args, kwargs in self.value:
|
||||||
|
getattr(pointPen, operator)(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
from fontTools.pens.basePen import _TestPen
|
from fontTools.pens.basePen import _TestPen
|
||||||
pen = RecordingPen()
|
pen = RecordingPen()
|
||||||
|
112
Lib/fontTools/pens/roundingPen.py
Normal file
112
Lib/fontTools/pens/roundingPen.py
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
from fontTools.misc.fixedTools import otRound
|
||||||
|
from fontTools.misc.transform import Transform
|
||||||
|
from fontTools.pens.filterPen import FilterPen, FilterPointPen
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["RoundingPen", "RoundingPointPen"]
|
||||||
|
|
||||||
|
|
||||||
|
class RoundingPen(FilterPen):
|
||||||
|
"""
|
||||||
|
Filter pen that rounds point coordinates and component XY offsets to integer.
|
||||||
|
|
||||||
|
>>> from fontTools.pens.recordingPen import RecordingPen
|
||||||
|
>>> recpen = RecordingPen()
|
||||||
|
>>> roundpen = RoundingPen(recpen)
|
||||||
|
>>> roundpen.moveTo((0.4, 0.6))
|
||||||
|
>>> roundpen.lineTo((1.6, 2.5))
|
||||||
|
>>> roundpen.qCurveTo((2.4, 4.6), (3.3, 5.7), (4.9, 6.1))
|
||||||
|
>>> roundpen.curveTo((6.4, 8.6), (7.3, 9.7), (8.9, 10.1))
|
||||||
|
>>> roundpen.addComponent("a", (1.5, 0, 0, 1.5, 10.5, -10.5))
|
||||||
|
>>> recpen.value == [
|
||||||
|
... ('moveTo', ((0, 1),)),
|
||||||
|
... ('lineTo', ((2, 3),)),
|
||||||
|
... ('qCurveTo', ((2, 5), (3, 6), (5, 6))),
|
||||||
|
... ('curveTo', ((6, 9), (7, 10), (9, 10))),
|
||||||
|
... ('addComponent', ('a', (1.5, 0, 0, 1.5, 11, -10))),
|
||||||
|
... ]
|
||||||
|
True
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, outPen, roundFunc=otRound):
|
||||||
|
super().__init__(outPen)
|
||||||
|
self.roundFunc = roundFunc
|
||||||
|
|
||||||
|
def moveTo(self, pt):
|
||||||
|
self._outPen.moveTo((self.roundFunc(pt[0]), self.roundFunc(pt[1])))
|
||||||
|
|
||||||
|
def lineTo(self, pt):
|
||||||
|
self._outPen.lineTo((self.roundFunc(pt[0]), self.roundFunc(pt[1])))
|
||||||
|
|
||||||
|
def curveTo(self, *points):
|
||||||
|
self._outPen.curveTo(
|
||||||
|
*((self.roundFunc(x), self.roundFunc(y)) for x, y in points)
|
||||||
|
)
|
||||||
|
|
||||||
|
def qCurveTo(self, *points):
|
||||||
|
self._outPen.qCurveTo(
|
||||||
|
*((self.roundFunc(x), self.roundFunc(y)) for x, y in points)
|
||||||
|
)
|
||||||
|
|
||||||
|
def addComponent(self, glyphName, transformation):
|
||||||
|
self._outPen.addComponent(
|
||||||
|
glyphName,
|
||||||
|
Transform(
|
||||||
|
*transformation[:4],
|
||||||
|
self.roundFunc(transformation[4]),
|
||||||
|
self.roundFunc(transformation[5]),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class RoundingPointPen(FilterPointPen):
|
||||||
|
"""
|
||||||
|
Filter point pen that rounds point coordinates and component XY offsets to integer.
|
||||||
|
|
||||||
|
>>> from fontTools.pens.recordingPen import RecordingPointPen
|
||||||
|
>>> recpen = RecordingPointPen()
|
||||||
|
>>> roundpen = RoundingPointPen(recpen)
|
||||||
|
>>> roundpen.beginPath()
|
||||||
|
>>> roundpen.addPoint((0.4, 0.6), 'line')
|
||||||
|
>>> roundpen.addPoint((1.6, 2.5), 'line')
|
||||||
|
>>> roundpen.addPoint((2.4, 4.6))
|
||||||
|
>>> roundpen.addPoint((3.3, 5.7))
|
||||||
|
>>> roundpen.addPoint((4.9, 6.1), 'qcurve')
|
||||||
|
>>> roundpen.endPath()
|
||||||
|
>>> roundpen.addComponent("a", (1.5, 0, 0, 1.5, 10.5, -10.5))
|
||||||
|
>>> recpen.value == [
|
||||||
|
... ('beginPath', (), {}),
|
||||||
|
... ('addPoint', ((0, 1), 'line', False, None), {}),
|
||||||
|
... ('addPoint', ((2, 3), 'line', False, None), {}),
|
||||||
|
... ('addPoint', ((2, 5), None, False, None), {}),
|
||||||
|
... ('addPoint', ((3, 6), None, False, None), {}),
|
||||||
|
... ('addPoint', ((5, 6), 'qcurve', False, None), {}),
|
||||||
|
... ('endPath', (), {}),
|
||||||
|
... ('addComponent', ('a', (1.5, 0, 0, 1.5, 11, -10)), {}),
|
||||||
|
... ]
|
||||||
|
True
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, outPen, roundFunc=otRound):
|
||||||
|
super().__init__(outPen)
|
||||||
|
self.roundFunc = roundFunc
|
||||||
|
|
||||||
|
def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
|
||||||
|
self._outPen.addPoint(
|
||||||
|
(self.roundFunc(pt[0]), self.roundFunc(pt[1])),
|
||||||
|
segmentType=segmentType,
|
||||||
|
smooth=smooth,
|
||||||
|
name=name,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
def addComponent(self, baseGlyphName, transformation, **kwargs):
|
||||||
|
self._outPen.addComponent(
|
||||||
|
baseGlyphName,
|
||||||
|
Transform(
|
||||||
|
*transformation[:4],
|
||||||
|
self.roundFunc(transformation[4]),
|
||||||
|
self.roundFunc(transformation[5]),
|
||||||
|
),
|
||||||
|
**kwargs,
|
||||||
|
)
|
@ -1,5 +1,5 @@
|
|||||||
from fontTools.misc.py23 import *
|
from fontTools.misc.py23 import *
|
||||||
from fontTools.pens.filterPen import FilterPen
|
from fontTools.pens.filterPen import FilterPen, FilterPointPen
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["TransformPen"]
|
__all__ = ["TransformPen"]
|
||||||
@ -55,6 +55,51 @@ class TransformPen(FilterPen):
|
|||||||
self._outPen.addComponent(glyphName, transformation)
|
self._outPen.addComponent(glyphName, transformation)
|
||||||
|
|
||||||
|
|
||||||
|
class TransformPointPen(FilterPointPen):
|
||||||
|
"""PointPen that transforms all coordinates using a Affine transformation,
|
||||||
|
and passes them to another PointPen.
|
||||||
|
|
||||||
|
>>> from fontTools.pens.recordingPen import RecordingPointPen
|
||||||
|
>>> rec = RecordingPointPen()
|
||||||
|
>>> pen = TransformPointPen(rec, (2, 0, 0, 2, -10, 5))
|
||||||
|
>>> v = iter(rec.value)
|
||||||
|
>>> pen.beginPath(identifier="contour-0")
|
||||||
|
>>> next(v)
|
||||||
|
('beginPath', (), {'identifier': 'contour-0'})
|
||||||
|
>>> pen.addPoint((100, 100), "line")
|
||||||
|
>>> next(v)
|
||||||
|
('addPoint', ((190, 205), 'line', False, None), {})
|
||||||
|
>>> pen.endPath()
|
||||||
|
>>> next(v)
|
||||||
|
('endPath', (), {})
|
||||||
|
>>> pen.addComponent("a", (1, 0, 0, 1, -10, 5), identifier="component-0")
|
||||||
|
>>> next(v)
|
||||||
|
('addComponent', ('a', <Transform [2 0 0 2 -30 15]>), {'identifier': 'component-0'})
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, outPointPen, transformation):
|
||||||
|
"""The 'outPointPen' argument is another point pen object.
|
||||||
|
It will receive the transformed coordinates.
|
||||||
|
The 'transformation' argument can either be a six-tuple, or a
|
||||||
|
fontTools.misc.transform.Transform object.
|
||||||
|
"""
|
||||||
|
super().__init__(outPointPen)
|
||||||
|
if not hasattr(transformation, "transformPoint"):
|
||||||
|
from fontTools.misc.transform import Transform
|
||||||
|
transformation = Transform(*transformation)
|
||||||
|
self._transformation = transformation
|
||||||
|
self._transformPoint = transformation.transformPoint
|
||||||
|
|
||||||
|
def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
|
||||||
|
self._outPen.addPoint(
|
||||||
|
self._transformPoint(pt), segmentType, smooth, name, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
def addComponent(self, baseGlyphName, transformation, **kwargs):
|
||||||
|
transformation = self._transformation.transform(transformation)
|
||||||
|
self._outPen.addComponent(baseGlyphName, transformation, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
from fontTools.pens.basePen import _TestPen
|
from fontTools.pens.basePen import _TestPen
|
||||||
pen = TransformPen(_TestPen(None), (2, 0, 0.5, 2, -10, 0))
|
pen = TransformPen(_TestPen(None), (2, 0, 0.5, 2, -10, 0))
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from fontTools.misc.py23 import *
|
from fontTools.misc.py23 import *
|
||||||
from array import array
|
from array import array
|
||||||
from fontTools.misc.fixedTools import MAX_F2DOT14
|
from fontTools.misc.fixedTools import MAX_F2DOT14, otRound
|
||||||
from fontTools.pens.basePen import LoggingPen
|
from fontTools.pens.basePen import LoggingPen
|
||||||
from fontTools.pens.transformPen import TransformPen
|
from fontTools.pens.transformPen import TransformPen
|
||||||
from fontTools.ttLib.tables import ttProgram
|
from fontTools.ttLib.tables import ttProgram
|
||||||
@ -118,7 +118,7 @@ class TTGlyphPen(LoggingPen):
|
|||||||
|
|
||||||
component = GlyphComponent()
|
component = GlyphComponent()
|
||||||
component.glyphName = glyphName
|
component.glyphName = glyphName
|
||||||
component.x, component.y = transformation[4:]
|
component.x, component.y = (otRound(v) for v in transformation[4:])
|
||||||
transformation = transformation[:4]
|
transformation = transformation[:4]
|
||||||
if transformation != (1, 0, 0, 1):
|
if transformation != (1, 0, 0, 1):
|
||||||
if (self.handleOverflowingTransforms and
|
if (self.handleOverflowingTransforms and
|
||||||
@ -138,6 +138,7 @@ class TTGlyphPen(LoggingPen):
|
|||||||
|
|
||||||
glyph = Glyph()
|
glyph = Glyph()
|
||||||
glyph.coordinates = GlyphCoordinates(self.points)
|
glyph.coordinates = GlyphCoordinates(self.points)
|
||||||
|
glyph.coordinates.toInt()
|
||||||
glyph.endPtsOfContours = self.endPts
|
glyph.endPtsOfContours = self.endPts
|
||||||
glyph.flags = array("B", self.types)
|
glyph.flags = array("B", self.types)
|
||||||
self.init()
|
self.init()
|
||||||
|
@ -339,7 +339,8 @@ class UFOReader(_UFOBaseIO):
|
|||||||
# convert kerning and groups
|
# convert kerning and groups
|
||||||
kerning, groups, conversionMaps = convertUFO1OrUFO2KerningToUFO3Kerning(
|
kerning, groups, conversionMaps = convertUFO1OrUFO2KerningToUFO3Kerning(
|
||||||
self._upConvertedKerningData["originalKerning"],
|
self._upConvertedKerningData["originalKerning"],
|
||||||
deepcopy(self._upConvertedKerningData["originalGroups"])
|
deepcopy(self._upConvertedKerningData["originalGroups"]),
|
||||||
|
self.getGlyphSet()
|
||||||
)
|
)
|
||||||
# store
|
# store
|
||||||
self._upConvertedKerningData["kerning"] = kerning
|
self._upConvertedKerningData["kerning"] = kerning
|
||||||
@ -637,7 +638,7 @@ class UFOReader(_UFOBaseIO):
|
|||||||
|
|
||||||
``validateRead`` will validate the read data, by default it is set to the
|
``validateRead`` will validate the read data, by default it is set to the
|
||||||
class's validate value, can be overridden.
|
class's validate value, can be overridden.
|
||||||
``validateWrte`` will validate the written data, by default it is set to the
|
``validateWrite`` will validate the written data, by default it is set to the
|
||||||
class's validate value, can be overridden.
|
class's validate value, can be overridden.
|
||||||
"""
|
"""
|
||||||
from fontTools.ufoLib.glifLib import GlyphSet
|
from fontTools.ufoLib.glifLib import GlyphSet
|
||||||
|
@ -6,16 +6,16 @@ Conversion functions.
|
|||||||
|
|
||||||
# adapted from the UFO spec
|
# adapted from the UFO spec
|
||||||
|
|
||||||
def convertUFO1OrUFO2KerningToUFO3Kerning(kerning, groups):
|
def convertUFO1OrUFO2KerningToUFO3Kerning(kerning, groups, glyphSet=()):
|
||||||
# gather known kerning groups based on the prefixes
|
# gather known kerning groups based on the prefixes
|
||||||
firstReferencedGroups, secondReferencedGroups = findKnownKerningGroups(groups)
|
firstReferencedGroups, secondReferencedGroups = findKnownKerningGroups(groups)
|
||||||
# Make lists of groups referenced in kerning pairs.
|
# Make lists of groups referenced in kerning pairs.
|
||||||
for first, seconds in list(kerning.items()):
|
for first, seconds in list(kerning.items()):
|
||||||
if first in groups:
|
if first in groups and first not in glyphSet:
|
||||||
if not first.startswith("public.kern1."):
|
if not first.startswith("public.kern1."):
|
||||||
firstReferencedGroups.add(first)
|
firstReferencedGroups.add(first)
|
||||||
for second in list(seconds.keys()):
|
for second in list(seconds.keys()):
|
||||||
if second in groups:
|
if second in groups and second not in glyphSet:
|
||||||
if not second.startswith("public.kern2."):
|
if not second.startswith("public.kern2."):
|
||||||
secondReferencedGroups.add(second)
|
secondReferencedGroups.add(second)
|
||||||
# Create new names for these groups.
|
# Create new names for these groups.
|
||||||
@ -154,7 +154,7 @@ def test():
|
|||||||
... "DGroup" : ["D"],
|
... "DGroup" : ["D"],
|
||||||
... }
|
... }
|
||||||
>>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
|
>>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
|
||||||
... testKerning, testGroups)
|
... testKerning, testGroups, [])
|
||||||
>>> expected = {
|
>>> expected = {
|
||||||
... "A" : {
|
... "A" : {
|
||||||
... "A": 1,
|
... "A": 1,
|
||||||
@ -220,7 +220,7 @@ def test():
|
|||||||
... "@MMK_R_XGroup" : ["X"],
|
... "@MMK_R_XGroup" : ["X"],
|
||||||
... }
|
... }
|
||||||
>>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
|
>>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
|
||||||
... testKerning, testGroups)
|
... testKerning, testGroups, [])
|
||||||
>>> expected = {
|
>>> expected = {
|
||||||
... "A" : {
|
... "A" : {
|
||||||
... "A": 1,
|
... "A": 1,
|
||||||
@ -293,7 +293,7 @@ def test():
|
|||||||
... "DGroup" : ["D"],
|
... "DGroup" : ["D"],
|
||||||
... }
|
... }
|
||||||
>>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
|
>>> kerning, groups, maps = convertUFO1OrUFO2KerningToUFO3Kerning(
|
||||||
... testKerning, testGroups)
|
... testKerning, testGroups, [])
|
||||||
>>> expected = {
|
>>> expected = {
|
||||||
... "A" : {
|
... "A" : {
|
||||||
... "A": 1,
|
... "A": 1,
|
||||||
|
@ -883,7 +883,7 @@ def groupsValidator(value):
|
|||||||
return False, "A group has an empty name."
|
return False, "A group has an empty name."
|
||||||
if groupName.startswith("public."):
|
if groupName.startswith("public."):
|
||||||
if not groupName.startswith("public.kern1.") and not groupName.startswith("public.kern2."):
|
if not groupName.startswith("public.kern1.") and not groupName.startswith("public.kern2."):
|
||||||
# unknown pubic.* name. silently skip.
|
# unknown public.* name. silently skip.
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
if len("public.kernN.") == len(groupName):
|
if len("public.kernN.") == len(groupName):
|
||||||
|
@ -795,7 +795,7 @@ def set_default_weight_width_slant(font, location):
|
|||||||
if "wght" in location:
|
if "wght" in location:
|
||||||
weight_class = otRound(max(1, min(location["wght"], 1000)))
|
weight_class = otRound(max(1, min(location["wght"], 1000)))
|
||||||
if font["OS/2"].usWeightClass != weight_class:
|
if font["OS/2"].usWeightClass != weight_class:
|
||||||
log.info("Setting OS/2.usWidthClass = %s", weight_class)
|
log.info("Setting OS/2.usWeightClass = %s", weight_class)
|
||||||
font["OS/2"].usWeightClass = weight_class
|
font["OS/2"].usWeightClass = weight_class
|
||||||
|
|
||||||
if "wdth" in location:
|
if "wdth" in location:
|
||||||
|
18
NEWS.rst
18
NEWS.rst
@ -1,3 +1,21 @@
|
|||||||
|
4.2.0 (released 2019-11-28)
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
- [pens] Added the following pens:
|
||||||
|
|
||||||
|
* ``roundingPen.RoundingPen``: filter pen that rounds coordinates and components'
|
||||||
|
offsets to integer;
|
||||||
|
* ``roundingPen.RoundingPointPen``: like the above, but using PointPen protocol.
|
||||||
|
* ``filterPen.FilterPointPen``: base class for filter point pens;
|
||||||
|
* ``transformPen.TransformPointPen``: filter point pen to apply affine transform;
|
||||||
|
* ``recordingPen.RecordingPointPen``: records and replays point-pen commands.
|
||||||
|
|
||||||
|
- [ttGlyphPen] Always round float coordinates and component offsets to integers
|
||||||
|
(#1763).
|
||||||
|
- [ufoLib] When converting kerning groups from UFO2 to UFO3, avoid confusing
|
||||||
|
groups with the same name as one of the glyphs (#1761, #1762,
|
||||||
|
unified-font-object/ufo-spec#98).
|
||||||
|
|
||||||
4.1.0 (released 2019-11-18)
|
4.1.0 (released 2019-11-18)
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
|
@ -1,19 +1,29 @@
|
|||||||
from fontTools.misc.py23 import *
|
from fontTools.pens.recordingPen import (
|
||||||
from fontTools.pens.recordingPen import RecordingPen, DecomposingRecordingPen
|
RecordingPen,
|
||||||
|
DecomposingRecordingPen,
|
||||||
|
RecordingPointPen,
|
||||||
|
)
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
class _TestGlyph(object):
|
class _TestGlyph(object):
|
||||||
|
|
||||||
def draw(self, pen):
|
def draw(self, pen):
|
||||||
pen.moveTo((0.0, 0.0))
|
pen.moveTo((0.0, 0.0))
|
||||||
pen.lineTo((0.0, 100.0))
|
pen.lineTo((0.0, 100.0))
|
||||||
pen.curveTo((50.0, 75.0), (60.0, 50.0), (50.0, 0.0))
|
pen.curveTo((50.0, 75.0), (60.0, 50.0), (50.0, 0.0))
|
||||||
pen.closePath()
|
pen.closePath()
|
||||||
|
|
||||||
|
def drawPoints(self, pen):
|
||||||
|
pen.beginPath(identifier="abc")
|
||||||
|
pen.addPoint((0.0, 0.0), "line", False, "start", identifier="0000")
|
||||||
|
pen.addPoint((0.0, 100.0), "line", False, None, identifier="0001")
|
||||||
|
pen.addPoint((50.0, 75.0), None, False, None, identifier="0002")
|
||||||
|
pen.addPoint((60.0, 50.0), None, False, None, identifier="0003")
|
||||||
|
pen.addPoint((50.0, 0.0), "curve", True, "last", identifier="0004")
|
||||||
|
pen.endPath()
|
||||||
|
|
||||||
|
|
||||||
class RecordingPenTest(object):
|
class RecordingPenTest(object):
|
||||||
|
|
||||||
def test_addComponent(self):
|
def test_addComponent(self):
|
||||||
pen = RecordingPen()
|
pen = RecordingPen()
|
||||||
pen.addComponent("a", (2, 0, 0, 3, -10, 5))
|
pen.addComponent("a", (2, 0, 0, 3, -10, 5))
|
||||||
@ -21,18 +31,42 @@ class RecordingPenTest(object):
|
|||||||
|
|
||||||
|
|
||||||
class DecomposingRecordingPenTest(object):
|
class DecomposingRecordingPenTest(object):
|
||||||
|
|
||||||
def test_addComponent_decomposed(self):
|
def test_addComponent_decomposed(self):
|
||||||
pen = DecomposingRecordingPen({"a": _TestGlyph()})
|
pen = DecomposingRecordingPen({"a": _TestGlyph()})
|
||||||
pen.addComponent("a", (2, 0, 0, 3, -10, 5))
|
pen.addComponent("a", (2, 0, 0, 3, -10, 5))
|
||||||
assert pen.value == [
|
assert pen.value == [
|
||||||
('moveTo', ((-10.0, 5.0),)),
|
("moveTo", ((-10.0, 5.0),)),
|
||||||
('lineTo', ((-10.0, 305.0),)),
|
("lineTo", ((-10.0, 305.0),)),
|
||||||
('curveTo', ((90.0, 230.0), (110.0, 155.0), (90.0, 5.0),)),
|
("curveTo", ((90.0, 230.0), (110.0, 155.0), (90.0, 5.0))),
|
||||||
('closePath', ())]
|
("closePath", ()),
|
||||||
|
]
|
||||||
|
|
||||||
def test_addComponent_missing_raises(self):
|
def test_addComponent_missing_raises(self):
|
||||||
pen = DecomposingRecordingPen(dict())
|
pen = DecomposingRecordingPen(dict())
|
||||||
with pytest.raises(KeyError) as excinfo:
|
with pytest.raises(KeyError) as excinfo:
|
||||||
pen.addComponent("a", (1, 0, 0, 1, 0, 0))
|
pen.addComponent("a", (1, 0, 0, 1, 0, 0))
|
||||||
assert excinfo.value.args[0] == "a"
|
assert excinfo.value.args[0] == "a"
|
||||||
|
|
||||||
|
|
||||||
|
class RecordingPointPenTest:
|
||||||
|
def test_record_and_replay(self):
|
||||||
|
pen = RecordingPointPen()
|
||||||
|
glyph = _TestGlyph()
|
||||||
|
glyph.drawPoints(pen)
|
||||||
|
pen.addComponent("a", (2, 0, 0, 2, -10, 5))
|
||||||
|
|
||||||
|
assert pen.value == [
|
||||||
|
("beginPath", (), {"identifier": "abc"}),
|
||||||
|
("addPoint", ((0.0, 0.0), "line", False, "start"), {"identifier": "0000"}),
|
||||||
|
("addPoint", ((0.0, 100.0), "line", False, None), {"identifier": "0001"}),
|
||||||
|
("addPoint", ((50.0, 75.0), None, False, None), {"identifier": "0002"}),
|
||||||
|
("addPoint", ((60.0, 50.0), None, False, None), {"identifier": "0003"}),
|
||||||
|
("addPoint", ((50.0, 0.0), "curve", True, "last"), {"identifier": "0004"}),
|
||||||
|
("endPath", (), {}),
|
||||||
|
("addComponent", ("a", (2, 0, 0, 2, -10, 5)), {}),
|
||||||
|
]
|
||||||
|
|
||||||
|
pen2 = RecordingPointPen()
|
||||||
|
pen.replay(pen2)
|
||||||
|
|
||||||
|
assert pen2.value == pen.value
|
||||||
|
@ -239,6 +239,31 @@ class TTGlyphPenTest(TestCase):
|
|||||||
with self.assertRaises(struct.error):
|
with self.assertRaises(struct.error):
|
||||||
compositeGlyph.compile({'a': baseGlyph})
|
compositeGlyph.compile({'a': baseGlyph})
|
||||||
|
|
||||||
|
def assertGlyphBoundsEqual(self, glyph, bounds):
|
||||||
|
self.assertEqual((glyph.xMin, glyph.yMin, glyph.xMax, glyph.yMax), bounds)
|
||||||
|
|
||||||
|
def test_round_float_coordinates_and_component_offsets(self):
|
||||||
|
glyphSet = {}
|
||||||
|
pen = TTGlyphPen(glyphSet)
|
||||||
|
|
||||||
|
pen.moveTo((0, 0))
|
||||||
|
pen.lineTo((0, 1))
|
||||||
|
pen.lineTo((367.6, 0))
|
||||||
|
pen.closePath()
|
||||||
|
simpleGlyph = pen.glyph()
|
||||||
|
|
||||||
|
simpleGlyph.recalcBounds(glyphSet)
|
||||||
|
self.assertGlyphBoundsEqual(simpleGlyph, (0, 0, 368, 1))
|
||||||
|
|
||||||
|
componentName = 'a'
|
||||||
|
glyphSet[componentName] = simpleGlyph
|
||||||
|
|
||||||
|
pen.addComponent(componentName, (1, 0, 0, 1, -86.4, 0))
|
||||||
|
compositeGlyph = pen.glyph()
|
||||||
|
|
||||||
|
compositeGlyph.recalcBounds(glyphSet)
|
||||||
|
self.assertGlyphBoundsEqual(compositeGlyph, (-86, 0, 282, 1))
|
||||||
|
|
||||||
|
|
||||||
class _TestGlyph(object):
|
class _TestGlyph(object):
|
||||||
def __init__(self, glyph):
|
def __init__(self, glyph):
|
||||||
|
@ -137,7 +137,9 @@ class KerningUpConversionTestCase(unittest.TestCase):
|
|||||||
("A", "public.kern2.CGroup"): 3,
|
("A", "public.kern2.CGroup"): 3,
|
||||||
("A", "public.kern2.DGroup"): 4,
|
("A", "public.kern2.DGroup"): 4,
|
||||||
("A", "A"): 1,
|
("A", "A"): 1,
|
||||||
("A", "B"): 2
|
("A", "B"): 2,
|
||||||
|
("X", "A"): 13,
|
||||||
|
("X", "public.kern2.CGroup"): 14
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedGroups = {
|
expectedGroups = {
|
||||||
@ -148,7 +150,8 @@ class KerningUpConversionTestCase(unittest.TestCase):
|
|||||||
"public.kern1.CGroup": ["C", "Ccedilla"],
|
"public.kern1.CGroup": ["C", "Ccedilla"],
|
||||||
"public.kern2.CGroup": ["C", "Ccedilla"],
|
"public.kern2.CGroup": ["C", "Ccedilla"],
|
||||||
"public.kern2.DGroup": ["D"],
|
"public.kern2.DGroup": ["D"],
|
||||||
"Not A Kerning Group" : ["A"]
|
"Not A Kerning Group" : ["A"],
|
||||||
|
"X": ["X", "X.sc"]
|
||||||
}
|
}
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -163,6 +166,20 @@ class KerningUpConversionTestCase(unittest.TestCase):
|
|||||||
self.clearUFO()
|
self.clearUFO()
|
||||||
if not os.path.exists(self.ufoPath):
|
if not os.path.exists(self.ufoPath):
|
||||||
os.mkdir(self.ufoPath)
|
os.mkdir(self.ufoPath)
|
||||||
|
|
||||||
|
# glyphs
|
||||||
|
glyphsPath = os.path.join(self.ufoPath, "glyphs")
|
||||||
|
if not os.path.exists(glyphsPath):
|
||||||
|
os.mkdir(glyphsPath)
|
||||||
|
glyphFile = "X_.glif"
|
||||||
|
glyphsContents = dict(X=glyphFile)
|
||||||
|
path = os.path.join(glyphsPath, "contents.plist")
|
||||||
|
with open(path, "wb") as f:
|
||||||
|
plistlib.dump(glyphsContents, f)
|
||||||
|
path = os.path.join(glyphsPath, glyphFile)
|
||||||
|
with open(path, "w") as f:
|
||||||
|
f.write('<?xml version="1.0" encoding="UTF-8"?>\n')
|
||||||
|
|
||||||
# metainfo.plist
|
# metainfo.plist
|
||||||
metaInfo = dict(creator="test", formatVersion=formatVersion)
|
metaInfo = dict(creator="test", formatVersion=formatVersion)
|
||||||
path = os.path.join(self.ufoPath, "metainfo.plist")
|
path = os.path.join(self.ufoPath, "metainfo.plist")
|
||||||
@ -187,6 +204,10 @@ class KerningUpConversionTestCase(unittest.TestCase):
|
|||||||
"B" : 10,
|
"B" : 10,
|
||||||
"CGroup" : 11,
|
"CGroup" : 11,
|
||||||
"DGroup" : 12
|
"DGroup" : 12
|
||||||
|
},
|
||||||
|
"X": {
|
||||||
|
"A" : 13,
|
||||||
|
"CGroup" : 14
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
path = os.path.join(self.ufoPath, "kerning.plist")
|
path = os.path.join(self.ufoPath, "kerning.plist")
|
||||||
@ -197,7 +218,8 @@ class KerningUpConversionTestCase(unittest.TestCase):
|
|||||||
"BGroup" : ["B"],
|
"BGroup" : ["B"],
|
||||||
"CGroup" : ["C", "Ccedilla"],
|
"CGroup" : ["C", "Ccedilla"],
|
||||||
"DGroup" : ["D"],
|
"DGroup" : ["D"],
|
||||||
"Not A Kerning Group" : ["A"]
|
"Not A Kerning Group" : ["A"],
|
||||||
|
"X" : ["X", "X.sc"] # a group with a name that is also a glyph name
|
||||||
}
|
}
|
||||||
path = os.path.join(self.ufoPath, "groups.plist")
|
path = os.path.join(self.ufoPath, "groups.plist")
|
||||||
with open(path, "wb") as f:
|
with open(path, "wb") as f:
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
current_version = 4.1.1.dev0
|
current_version = 4.2.1.dev0
|
||||||
commit = True
|
commit = True
|
||||||
tag = False
|
tag = False
|
||||||
tag_name = {new_version}
|
tag_name = {new_version}
|
||||||
|
2
setup.py
2
setup.py
@ -345,7 +345,7 @@ def find_data_files(manpath="share/man"):
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="fonttools",
|
name="fonttools",
|
||||||
version="4.1.1.dev0",
|
version="4.2.1.dev0",
|
||||||
description="Tools to manipulate font files",
|
description="Tools to manipulate font files",
|
||||||
author="Just van Rossum",
|
author="Just van Rossum",
|
||||||
author_email="just@letterror.com",
|
author_email="just@letterror.com",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user