Merge branch 'fonttools:main' into ttf2otf
This commit is contained in:
commit
09df5fcc02
@ -46,7 +46,7 @@ class FreeTypePen(BasePen):
|
||||
glyphSet: a dictionary of drawable glyph objects keyed by name
|
||||
used to resolve component references in composite glyphs.
|
||||
|
||||
:Examples:
|
||||
Examples:
|
||||
If `numpy` and `matplotlib` is available, the following code will
|
||||
show the glyph image of `fi` in a new window::
|
||||
|
||||
@ -178,7 +178,7 @@ class FreeTypePen(BasePen):
|
||||
object of the resulted bitmap and ``size`` is a 2-tuple of its
|
||||
dimension.
|
||||
|
||||
:Notes:
|
||||
Notes:
|
||||
The image size should always be given explicitly if you need to get
|
||||
a proper glyph image. When ``width`` and ``height`` are omitted, it
|
||||
forcifully fits to the bounding box and the side bearings get
|
||||
@ -188,15 +188,15 @@ class FreeTypePen(BasePen):
|
||||
maintained but RSB won’t. The difference between the two becomes
|
||||
more obvious when rotate or skew transformation is applied.
|
||||
|
||||
:Example:
|
||||
.. code-block::
|
||||
Example:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>>
|
||||
>> pen = FreeTypePen(None)
|
||||
>> glyph.draw(pen)
|
||||
>> buf, size = pen.buffer(width=500, height=1000)
|
||||
>> type(buf), len(buf), size
|
||||
(<class 'bytes'>, 500000, (500, 1000))
|
||||
|
||||
"""
|
||||
transform = transform or Transform()
|
||||
if not hasattr(transform, "transformPoint"):
|
||||
@ -269,7 +269,7 @@ class FreeTypePen(BasePen):
|
||||
A ``numpy.ndarray`` object with a shape of ``(height, width)``.
|
||||
Each element takes a value in the range of ``[0.0, 1.0]``.
|
||||
|
||||
:Notes:
|
||||
Notes:
|
||||
The image size should always be given explicitly if you need to get
|
||||
a proper glyph image. When ``width`` and ``height`` are omitted, it
|
||||
forcifully fits to the bounding box and the side bearings get
|
||||
@ -279,15 +279,17 @@ class FreeTypePen(BasePen):
|
||||
maintained but RSB won’t. The difference between the two becomes
|
||||
more obvious when rotate or skew transformation is applied.
|
||||
|
||||
:Example:
|
||||
.. code-block::
|
||||
Example:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>>
|
||||
>> pen = FreeTypePen(None)
|
||||
>> glyph.draw(pen)
|
||||
>> arr = pen.array(width=500, height=1000)
|
||||
>> type(a), a.shape
|
||||
(<class 'numpy.ndarray'>, (1000, 500))
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
|
||||
buf, size = self.buffer(
|
||||
@ -318,7 +320,7 @@ class FreeTypePen(BasePen):
|
||||
rendering glyphs with negative sidebearings without clipping.
|
||||
evenOdd: Pass ``True`` for even-odd fill instead of non-zero.
|
||||
|
||||
:Notes:
|
||||
Notes:
|
||||
The image size should always be given explicitly if you need to get
|
||||
a proper glyph image. When ``width`` and ``height`` are omitted, it
|
||||
forcifully fits to the bounding box and the side bearings get
|
||||
@ -328,9 +330,10 @@ class FreeTypePen(BasePen):
|
||||
maintained but RSB won’t. The difference between the two becomes
|
||||
more obvious when rotate or skew transformation is applied.
|
||||
|
||||
:Example:
|
||||
.. code-block::
|
||||
Example:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>>
|
||||
>> pen = FreeTypePen(None)
|
||||
>> glyph.draw(pen)
|
||||
>> pen.show(width=500, height=1000)
|
||||
@ -370,7 +373,7 @@ class FreeTypePen(BasePen):
|
||||
A ``PIL.image`` object. The image is filled in black with alpha
|
||||
channel obtained from the rendered bitmap.
|
||||
|
||||
:Notes:
|
||||
Notes:
|
||||
The image size should always be given explicitly if you need to get
|
||||
a proper glyph image. When ``width`` and ``height`` are omitted, it
|
||||
forcifully fits to the bounding box and the side bearings get
|
||||
@ -380,9 +383,10 @@ class FreeTypePen(BasePen):
|
||||
maintained but RSB won’t. The difference between the two becomes
|
||||
more obvious when rotate or skew transformation is applied.
|
||||
|
||||
:Example:
|
||||
.. code-block::
|
||||
Example:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>>
|
||||
>> pen = FreeTypePen(None)
|
||||
>> glyph.draw(pen)
|
||||
>> img = pen.image(width=500, height=1000)
|
||||
|
@ -15,7 +15,8 @@ class PointInsidePen(BasePen):
|
||||
Instances of this class can be recycled, as long as the
|
||||
setTestPoint() method is used to set the new point to test.
|
||||
|
||||
Typical usage:
|
||||
:Example:
|
||||
.. code-block::
|
||||
|
||||
pen = PointInsidePen(glyphSet, (100, 200))
|
||||
outline.draw(pen)
|
||||
|
@ -33,6 +33,7 @@ class RecordingPen(AbstractPen):
|
||||
pen.replay(otherPen).
|
||||
|
||||
:Example:
|
||||
.. code-block::
|
||||
|
||||
from fontTools.ttLib import TTFont
|
||||
from fontTools.pens.recordingPen import RecordingPen
|
||||
@ -91,47 +92,48 @@ class DecomposingRecordingPen(DecomposingPen, RecordingPen):
|
||||
by thir name; other arguments are forwarded to the DecomposingPen's
|
||||
constructor::
|
||||
|
||||
>>> class SimpleGlyph(object):
|
||||
... def draw(self, pen):
|
||||
... pen.moveTo((0, 0))
|
||||
... pen.curveTo((1, 1), (2, 2), (3, 3))
|
||||
... pen.closePath()
|
||||
>>> class CompositeGlyph(object):
|
||||
... def draw(self, pen):
|
||||
... pen.addComponent('a', (1, 0, 0, 1, -1, 1))
|
||||
>>> class MissingComponent(object):
|
||||
... def draw(self, pen):
|
||||
... pen.addComponent('foobar', (1, 0, 0, 1, 0, 0))
|
||||
>>> class FlippedComponent(object):
|
||||
... def draw(self, pen):
|
||||
... pen.addComponent('a', (-1, 0, 0, 1, 0, 0))
|
||||
>>> glyphSet = {
|
||||
... 'a': SimpleGlyph(),
|
||||
... 'b': CompositeGlyph(),
|
||||
... 'c': MissingComponent(),
|
||||
... 'd': FlippedComponent(),
|
||||
... }
|
||||
>>> for name, glyph in sorted(glyphSet.items()):
|
||||
... pen = DecomposingRecordingPen(glyphSet)
|
||||
... try:
|
||||
... glyph.draw(pen)
|
||||
... except pen.MissingComponentError:
|
||||
... pass
|
||||
... print("{}: {}".format(name, pen.value))
|
||||
a: [('moveTo', ((0, 0),)), ('curveTo', ((1, 1), (2, 2), (3, 3))), ('closePath', ())]
|
||||
b: [('moveTo', ((-1, 1),)), ('curveTo', ((0, 2), (1, 3), (2, 4))), ('closePath', ())]
|
||||
c: []
|
||||
d: [('moveTo', ((0, 0),)), ('curveTo', ((-1, 1), (-2, 2), (-3, 3))), ('closePath', ())]
|
||||
>>> for name, glyph in sorted(glyphSet.items()):
|
||||
... pen = DecomposingRecordingPen(
|
||||
... glyphSet, skipMissingComponents=True, reverseFlipped=True,
|
||||
... )
|
||||
... glyph.draw(pen)
|
||||
... print("{}: {}".format(name, pen.value))
|
||||
a: [('moveTo', ((0, 0),)), ('curveTo', ((1, 1), (2, 2), (3, 3))), ('closePath', ())]
|
||||
b: [('moveTo', ((-1, 1),)), ('curveTo', ((0, 2), (1, 3), (2, 4))), ('closePath', ())]
|
||||
c: []
|
||||
d: [('moveTo', ((0, 0),)), ('lineTo', ((-3, 3),)), ('curveTo', ((-2, 2), (-1, 1), (0, 0))), ('closePath', ())]
|
||||
>>> class SimpleGlyph(object):
|
||||
... def draw(self, pen):
|
||||
... pen.moveTo((0, 0))
|
||||
... pen.curveTo((1, 1), (2, 2), (3, 3))
|
||||
... pen.closePath()
|
||||
>>> class CompositeGlyph(object):
|
||||
... def draw(self, pen):
|
||||
... pen.addComponent('a', (1, 0, 0, 1, -1, 1))
|
||||
>>> class MissingComponent(object):
|
||||
... def draw(self, pen):
|
||||
... pen.addComponent('foobar', (1, 0, 0, 1, 0, 0))
|
||||
>>> class FlippedComponent(object):
|
||||
... def draw(self, pen):
|
||||
... pen.addComponent('a', (-1, 0, 0, 1, 0, 0))
|
||||
>>> glyphSet = {
|
||||
... 'a': SimpleGlyph(),
|
||||
... 'b': CompositeGlyph(),
|
||||
... 'c': MissingComponent(),
|
||||
... 'd': FlippedComponent(),
|
||||
... }
|
||||
>>> for name, glyph in sorted(glyphSet.items()):
|
||||
... pen = DecomposingRecordingPen(glyphSet)
|
||||
... try:
|
||||
... glyph.draw(pen)
|
||||
... except pen.MissingComponentError:
|
||||
... pass
|
||||
... print("{}: {}".format(name, pen.value))
|
||||
a: [('moveTo', ((0, 0),)), ('curveTo', ((1, 1), (2, 2), (3, 3))), ('closePath', ())]
|
||||
b: [('moveTo', ((-1, 1),)), ('curveTo', ((0, 2), (1, 3), (2, 4))), ('closePath', ())]
|
||||
c: []
|
||||
d: [('moveTo', ((0, 0),)), ('curveTo', ((-1, 1), (-2, 2), (-3, 3))), ('closePath', ())]
|
||||
|
||||
>>> for name, glyph in sorted(glyphSet.items()):
|
||||
... pen = DecomposingRecordingPen(
|
||||
... glyphSet, skipMissingComponents=True, reverseFlipped=True,
|
||||
... )
|
||||
... glyph.draw(pen)
|
||||
... print("{}: {}".format(name, pen.value))
|
||||
a: [('moveTo', ((0, 0),)), ('curveTo', ((1, 1), (2, 2), (3, 3))), ('closePath', ())]
|
||||
b: [('moveTo', ((-1, 1),)), ('curveTo', ((0, 2), (1, 3), (2, 4))), ('closePath', ())]
|
||||
c: []
|
||||
d: [('moveTo', ((0, 0),)), ('lineTo', ((-3, 3),)), ('curveTo', ((-2, 2), (-1, 1), (0, 0))), ('closePath', ())]
|
||||
"""
|
||||
|
||||
# raises MissingComponentError(KeyError) if base glyph is not found in glyphSet
|
||||
@ -145,6 +147,7 @@ class RecordingPointPen(AbstractPointPen):
|
||||
pointPen.replay(otherPointPen).
|
||||
|
||||
:Example:
|
||||
.. code-block::
|
||||
|
||||
from defcon import Font
|
||||
from fontTools.pens.recordingPen import RecordingPointPen
|
||||
@ -211,81 +214,82 @@ class DecomposingRecordingPointPen(DecomposingPointPen, RecordingPointPen):
|
||||
keyed by thir name; other arguments are forwarded to the DecomposingPointPen's
|
||||
constructor::
|
||||
|
||||
>>> from pprint import pprint
|
||||
>>> class SimpleGlyph(object):
|
||||
... def drawPoints(self, pen):
|
||||
... pen.beginPath()
|
||||
... pen.addPoint((0, 0), "line")
|
||||
... pen.addPoint((1, 1))
|
||||
... pen.addPoint((2, 2))
|
||||
... pen.addPoint((3, 3), "curve")
|
||||
... pen.endPath()
|
||||
>>> class CompositeGlyph(object):
|
||||
... def drawPoints(self, pen):
|
||||
... pen.addComponent('a', (1, 0, 0, 1, -1, 1))
|
||||
>>> class MissingComponent(object):
|
||||
... def drawPoints(self, pen):
|
||||
... pen.addComponent('foobar', (1, 0, 0, 1, 0, 0))
|
||||
>>> class FlippedComponent(object):
|
||||
... def drawPoints(self, pen):
|
||||
... pen.addComponent('a', (-1, 0, 0, 1, 0, 0))
|
||||
>>> glyphSet = {
|
||||
... 'a': SimpleGlyph(),
|
||||
... 'b': CompositeGlyph(),
|
||||
... 'c': MissingComponent(),
|
||||
... 'd': FlippedComponent(),
|
||||
... }
|
||||
>>> for name, glyph in sorted(glyphSet.items()):
|
||||
... pen = DecomposingRecordingPointPen(glyphSet)
|
||||
... try:
|
||||
... glyph.drawPoints(pen)
|
||||
... except pen.MissingComponentError:
|
||||
... pass
|
||||
... pprint({name: pen.value})
|
||||
{'a': [('beginPath', (), {}),
|
||||
('addPoint', ((0, 0), 'line', False, None), {}),
|
||||
('addPoint', ((1, 1), None, False, None), {}),
|
||||
('addPoint', ((2, 2), None, False, None), {}),
|
||||
('addPoint', ((3, 3), 'curve', False, None), {}),
|
||||
('endPath', (), {})]}
|
||||
{'b': [('beginPath', (), {}),
|
||||
('addPoint', ((-1, 1), 'line', False, None), {}),
|
||||
('addPoint', ((0, 2), None, False, None), {}),
|
||||
('addPoint', ((1, 3), None, False, None), {}),
|
||||
('addPoint', ((2, 4), 'curve', False, None), {}),
|
||||
('endPath', (), {})]}
|
||||
{'c': []}
|
||||
{'d': [('beginPath', (), {}),
|
||||
('addPoint', ((0, 0), 'line', False, None), {}),
|
||||
('addPoint', ((-1, 1), None, False, None), {}),
|
||||
('addPoint', ((-2, 2), None, False, None), {}),
|
||||
('addPoint', ((-3, 3), 'curve', False, None), {}),
|
||||
('endPath', (), {})]}
|
||||
>>> for name, glyph in sorted(glyphSet.items()):
|
||||
... pen = DecomposingRecordingPointPen(
|
||||
... glyphSet, skipMissingComponents=True, reverseFlipped=True,
|
||||
... )
|
||||
... glyph.drawPoints(pen)
|
||||
... pprint({name: pen.value})
|
||||
{'a': [('beginPath', (), {}),
|
||||
('addPoint', ((0, 0), 'line', False, None), {}),
|
||||
('addPoint', ((1, 1), None, False, None), {}),
|
||||
('addPoint', ((2, 2), None, False, None), {}),
|
||||
('addPoint', ((3, 3), 'curve', False, None), {}),
|
||||
('endPath', (), {})]}
|
||||
{'b': [('beginPath', (), {}),
|
||||
('addPoint', ((-1, 1), 'line', False, None), {}),
|
||||
('addPoint', ((0, 2), None, False, None), {}),
|
||||
('addPoint', ((1, 3), None, False, None), {}),
|
||||
('addPoint', ((2, 4), 'curve', False, None), {}),
|
||||
('endPath', (), {})]}
|
||||
{'c': []}
|
||||
{'d': [('beginPath', (), {}),
|
||||
('addPoint', ((0, 0), 'curve', False, None), {}),
|
||||
('addPoint', ((-3, 3), 'line', False, None), {}),
|
||||
('addPoint', ((-2, 2), None, False, None), {}),
|
||||
('addPoint', ((-1, 1), None, False, None), {}),
|
||||
('endPath', (), {})]}
|
||||
>>> from pprint import pprint
|
||||
>>> class SimpleGlyph(object):
|
||||
... def drawPoints(self, pen):
|
||||
... pen.beginPath()
|
||||
... pen.addPoint((0, 0), "line")
|
||||
... pen.addPoint((1, 1))
|
||||
... pen.addPoint((2, 2))
|
||||
... pen.addPoint((3, 3), "curve")
|
||||
... pen.endPath()
|
||||
>>> class CompositeGlyph(object):
|
||||
... def drawPoints(self, pen):
|
||||
... pen.addComponent('a', (1, 0, 0, 1, -1, 1))
|
||||
>>> class MissingComponent(object):
|
||||
... def drawPoints(self, pen):
|
||||
... pen.addComponent('foobar', (1, 0, 0, 1, 0, 0))
|
||||
>>> class FlippedComponent(object):
|
||||
... def drawPoints(self, pen):
|
||||
... pen.addComponent('a', (-1, 0, 0, 1, 0, 0))
|
||||
>>> glyphSet = {
|
||||
... 'a': SimpleGlyph(),
|
||||
... 'b': CompositeGlyph(),
|
||||
... 'c': MissingComponent(),
|
||||
... 'd': FlippedComponent(),
|
||||
... }
|
||||
>>> for name, glyph in sorted(glyphSet.items()):
|
||||
... pen = DecomposingRecordingPointPen(glyphSet)
|
||||
... try:
|
||||
... glyph.drawPoints(pen)
|
||||
... except pen.MissingComponentError:
|
||||
... pass
|
||||
... pprint({name: pen.value})
|
||||
{'a': [('beginPath', (), {}),
|
||||
('addPoint', ((0, 0), 'line', False, None), {}),
|
||||
('addPoint', ((1, 1), None, False, None), {}),
|
||||
('addPoint', ((2, 2), None, False, None), {}),
|
||||
('addPoint', ((3, 3), 'curve', False, None), {}),
|
||||
('endPath', (), {})]}
|
||||
{'b': [('beginPath', (), {}),
|
||||
('addPoint', ((-1, 1), 'line', False, None), {}),
|
||||
('addPoint', ((0, 2), None, False, None), {}),
|
||||
('addPoint', ((1, 3), None, False, None), {}),
|
||||
('addPoint', ((2, 4), 'curve', False, None), {}),
|
||||
('endPath', (), {})]}
|
||||
{'c': []}
|
||||
{'d': [('beginPath', (), {}),
|
||||
('addPoint', ((0, 0), 'line', False, None), {}),
|
||||
('addPoint', ((-1, 1), None, False, None), {}),
|
||||
('addPoint', ((-2, 2), None, False, None), {}),
|
||||
('addPoint', ((-3, 3), 'curve', False, None), {}),
|
||||
('endPath', (), {})]}
|
||||
|
||||
>>> for name, glyph in sorted(glyphSet.items()):
|
||||
... pen = DecomposingRecordingPointPen(
|
||||
... glyphSet, skipMissingComponents=True, reverseFlipped=True,
|
||||
... )
|
||||
... glyph.drawPoints(pen)
|
||||
... pprint({name: pen.value})
|
||||
{'a': [('beginPath', (), {}),
|
||||
('addPoint', ((0, 0), 'line', False, None), {}),
|
||||
('addPoint', ((1, 1), None, False, None), {}),
|
||||
('addPoint', ((2, 2), None, False, None), {}),
|
||||
('addPoint', ((3, 3), 'curve', False, None), {}),
|
||||
('endPath', (), {})]}
|
||||
{'b': [('beginPath', (), {}),
|
||||
('addPoint', ((-1, 1), 'line', False, None), {}),
|
||||
('addPoint', ((0, 2), None, False, None), {}),
|
||||
('addPoint', ((1, 3), None, False, None), {}),
|
||||
('addPoint', ((2, 4), 'curve', False, None), {}),
|
||||
('endPath', (), {})]}
|
||||
{'c': []}
|
||||
{'d': [('beginPath', (), {}),
|
||||
('addPoint', ((0, 0), 'curve', False, None), {}),
|
||||
('addPoint', ((-3, 3), 'line', False, None), {}),
|
||||
('addPoint', ((-2, 2), None, False, None), {}),
|
||||
('addPoint', ((-1, 1), None, False, None), {}),
|
||||
('endPath', (), {})]}
|
||||
"""
|
||||
|
||||
# raises MissingComponentError(KeyError) if base glyph is not found in glyphSet
|
||||
|
@ -9,27 +9,30 @@ def pointToString(pt, ntos=str):
|
||||
class SVGPathPen(BasePen):
|
||||
"""Pen to draw SVG path d commands.
|
||||
|
||||
Example::
|
||||
>>> pen = SVGPathPen(None)
|
||||
>>> pen.moveTo((0, 0))
|
||||
>>> pen.lineTo((1, 1))
|
||||
>>> pen.curveTo((2, 2), (3, 3), (4, 4))
|
||||
>>> pen.closePath()
|
||||
>>> pen.getCommands()
|
||||
'M0 0 1 1C2 2 3 3 4 4Z'
|
||||
|
||||
Args:
|
||||
glyphSet: a dictionary of drawable glyph objects keyed by name
|
||||
used to resolve component references in composite glyphs.
|
||||
ntos: a callable that takes a number and returns a string, to
|
||||
customize how numbers are formatted (default: str).
|
||||
|
||||
:Example:
|
||||
.. code-block::
|
||||
|
||||
>>> pen = SVGPathPen(None)
|
||||
>>> pen.moveTo((0, 0))
|
||||
>>> pen.lineTo((1, 1))
|
||||
>>> pen.curveTo((2, 2), (3, 3), (4, 4))
|
||||
>>> pen.closePath()
|
||||
>>> pen.getCommands()
|
||||
'M0 0 1 1C2 2 3 3 4 4Z'
|
||||
|
||||
Note:
|
||||
Fonts have a coordinate system where Y grows up, whereas in SVG,
|
||||
Y grows down. As such, rendering path data from this pen in
|
||||
SVG typically results in upside-down glyphs. You can fix this
|
||||
by wrapping the data from this pen in an SVG group element with
|
||||
transform, or wrap this pen in a transform pen. For example:
|
||||
.. code-block:: python
|
||||
|
||||
spen = svgPathPen.SVGPathPen(glyphset)
|
||||
pen= TransformPen(spen , (1, 0, 0, -1, 0, 0))
|
||||
|
@ -58,22 +58,27 @@ 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'})
|
||||
For example::
|
||||
|
||||
>>> 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):
|
||||
|
@ -14,6 +14,8 @@ class SVGPath(object):
|
||||
|
||||
For example, reading from an SVG file and drawing to a Defcon Glyph:
|
||||
|
||||
.. code-block::
|
||||
|
||||
import defcon
|
||||
glyph = defcon.Glyph()
|
||||
pen = glyph.getPen()
|
||||
@ -23,6 +25,8 @@ class SVGPath(object):
|
||||
Or reading from a string containing SVG data, using the alternative
|
||||
'fromstring' (a class method):
|
||||
|
||||
.. code-block::
|
||||
|
||||
data = '<?xml version="1.0" ...'
|
||||
svg = SVGPath.fromstring(data)
|
||||
svg.draw(pen)
|
||||
|
@ -103,6 +103,8 @@ def parse_path(pathdef, pen, current_pos=(0, 0), arc_class=EllipticalArc):
|
||||
If the pen has an "arcTo" method, it is called with the original values
|
||||
of the elliptical arc curve commands:
|
||||
|
||||
.. code-block::
|
||||
|
||||
pen.arcTo(rx, ry, rotation, arc_large, arc_sweep, (x, y))
|
||||
|
||||
Otherwise, the arcs are approximated by series of cubic Bezier segments
|
||||
|
@ -1,8 +1,9 @@
|
||||
"""ttLib/sfnt.py -- low-level module to deal with the sfnt file format.
|
||||
|
||||
Defines two public classes:
|
||||
SFNTReader
|
||||
SFNTWriter
|
||||
|
||||
- SFNTReader
|
||||
- SFNTWriter
|
||||
|
||||
(Normally you don't have to use these classes explicitly; they are
|
||||
used automatically by ttLib.TTFont.)
|
||||
|
@ -26,37 +26,43 @@ class TTFont(object):
|
||||
accessing tables. Tables will be only decompiled when necessary, ie. when
|
||||
they're actually accessed. This means that simple operations can be extremely fast.
|
||||
|
||||
Example usage::
|
||||
Example usage:
|
||||
.. code-block:: pycon
|
||||
|
||||
>> from fontTools import ttLib
|
||||
>> tt = ttLib.TTFont("afont.ttf") # Load an existing font file
|
||||
>> tt['maxp'].numGlyphs
|
||||
242
|
||||
>> tt['OS/2'].achVendID
|
||||
'B&H\000'
|
||||
>> tt['head'].unitsPerEm
|
||||
2048
|
||||
>>>
|
||||
>> from fontTools import ttLib
|
||||
>> tt = ttLib.TTFont("afont.ttf") # Load an existing font file
|
||||
>> tt['maxp'].numGlyphs
|
||||
242
|
||||
>> tt['OS/2'].achVendID
|
||||
'B&H\000'
|
||||
>> tt['head'].unitsPerEm
|
||||
2048
|
||||
|
||||
For details of the objects returned when accessing each table, see :ref:`tables`.
|
||||
To add a table to the font, use the :py:func:`newTable` function::
|
||||
To add a table to the font, use the :py:func:`newTable` function:
|
||||
.. code-block:: pycon
|
||||
|
||||
>> os2 = newTable("OS/2")
|
||||
>> os2.version = 4
|
||||
>> # set other attributes
|
||||
>> font["OS/2"] = os2
|
||||
>>>
|
||||
>> os2 = newTable("OS/2")
|
||||
>> os2.version = 4
|
||||
>> # set other attributes
|
||||
>> font["OS/2"] = os2
|
||||
|
||||
TrueType fonts can also be serialized to and from XML format (see also the
|
||||
:ref:`ttx` binary)::
|
||||
:ref:`ttx` binary):
|
||||
.. code-block:: pycon
|
||||
|
||||
>> tt.saveXML("afont.ttx")
|
||||
Dumping 'LTSH' table...
|
||||
Dumping 'OS/2' table...
|
||||
[...]
|
||||
>>
|
||||
>> tt.saveXML("afont.ttx")
|
||||
Dumping 'LTSH' table...
|
||||
Dumping 'OS/2' table...
|
||||
[...]
|
||||
|
||||
>> tt2 = ttLib.TTFont() # Create a new font object
|
||||
>> tt2.importXML("afont.ttx")
|
||||
>> tt2['maxp'].numGlyphs
|
||||
242
|
||||
>> tt2 = ttLib.TTFont() # Create a new font object
|
||||
>> tt2.importXML("afont.ttx")
|
||||
>> tt2['maxp'].numGlyphs
|
||||
242
|
||||
|
||||
The TTFont object may be used as a context manager; this will cause the file
|
||||
reader to be closed after the context ``with`` block is exited::
|
||||
@ -981,14 +987,16 @@ def tagToIdentifier(tag):
|
||||
letters get an underscore after the letter. Trailing spaces are
|
||||
trimmed. Illegal characters are escaped as two hex bytes. If the
|
||||
result starts with a number (as the result of a hex escape), an
|
||||
extra underscore is prepended. Examples::
|
||||
extra underscore is prepended. Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> tagToIdentifier('glyf')
|
||||
'_g_l_y_f'
|
||||
>>> tagToIdentifier('cvt ')
|
||||
'_c_v_t'
|
||||
>>> tagToIdentifier('OS/2')
|
||||
'O_S_2f_2'
|
||||
>>>
|
||||
>> tagToIdentifier('glyf')
|
||||
'_g_l_y_f'
|
||||
>> tagToIdentifier('cvt ')
|
||||
'_c_v_t'
|
||||
>> tagToIdentifier('OS/2')
|
||||
'O_S_2f_2'
|
||||
"""
|
||||
import re
|
||||
|
||||
|
@ -7,25 +7,29 @@ of the specification.
|
||||
|
||||
Sets that list the font info attribute names for the fontinfo.plist
|
||||
formats are available for external use. These are:
|
||||
fontInfoAttributesVersion1
|
||||
fontInfoAttributesVersion2
|
||||
fontInfoAttributesVersion3
|
||||
|
||||
- fontInfoAttributesVersion1
|
||||
- fontInfoAttributesVersion2
|
||||
- fontInfoAttributesVersion3
|
||||
|
||||
A set listing the fontinfo.plist attributes that were deprecated
|
||||
in version 2 is available for external use:
|
||||
deprecatedFontInfoAttributesVersion2
|
||||
|
||||
- deprecatedFontInfoAttributesVersion2
|
||||
|
||||
Functions that do basic validation on values for fontinfo.plist
|
||||
are available for external use. These are
|
||||
validateFontInfoVersion2ValueForAttribute
|
||||
validateFontInfoVersion3ValueForAttribute
|
||||
|
||||
- validateFontInfoVersion2ValueForAttribute
|
||||
- validateFontInfoVersion3ValueForAttribute
|
||||
|
||||
Value conversion functions are available for converting
|
||||
fontinfo.plist values between the possible format versions.
|
||||
convertFontInfoValueForAttributeFromVersion1ToVersion2
|
||||
convertFontInfoValueForAttributeFromVersion2ToVersion1
|
||||
convertFontInfoValueForAttributeFromVersion2ToVersion3
|
||||
convertFontInfoValueForAttributeFromVersion3ToVersion2
|
||||
|
||||
- convertFontInfoValueForAttributeFromVersion1ToVersion2
|
||||
- convertFontInfoValueForAttributeFromVersion2ToVersion1
|
||||
- convertFontInfoValueForAttributeFromVersion2ToVersion3
|
||||
- convertFontInfoValueForAttributeFromVersion3ToVersion2
|
||||
"""
|
||||
|
||||
import os
|
||||
|
@ -10,10 +10,14 @@ ttf-interpolatable files for the masters and build a variable-font from
|
||||
them. Such ttf-interpolatable and designspace files can be generated from
|
||||
a Glyphs source, eg., using noto-source as an example:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ fontmake -o ttf-interpolatable -g NotoSansArabic-MM.glyphs
|
||||
|
||||
Then you can make a variable-font this way:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ fonttools varLib master_ufo/NotoSansArabic.designspace
|
||||
|
||||
API *will* change in near future.
|
||||
|
@ -96,7 +96,7 @@ def merge_PrivateDicts(top_dicts, vsindex_dict, var_model, fd_map):
|
||||
|
||||
* step through each key in FontDict.Private.
|
||||
* For each key, step through each relevant source font Private dict, and
|
||||
build a list of values to blend.
|
||||
build a list of values to blend.
|
||||
|
||||
The 'relevant' source fonts are selected by first getting the right
|
||||
submodel using ``vsindex_dict[vsindex]``. The indices of the
|
||||
|
@ -5,7 +5,9 @@ create full instances (i.e. static fonts) from variable fonts, as well as "parti
|
||||
variable fonts that only contain a subset of the original variation space.
|
||||
|
||||
For example, if you wish to pin the width axis to a given location while also
|
||||
restricting the weight axis to 400..700 range, you can do::
|
||||
restricting the weight axis to 400..700 range, you can do:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ fonttools varLib.instancer ./NotoSans-VF.ttf wdth=85 wght=400:700
|
||||
|
||||
@ -17,32 +19,38 @@ and returns a new TTFont representing either a partial VF, or full instance if a
|
||||
the VF axes were given an explicit coordinate.
|
||||
|
||||
E.g. here's how to pin the wght axis at a given location in a wght+wdth variable
|
||||
font, keeping only the deltas associated with the wdth axis::
|
||||
font, keeping only the deltas associated with the wdth axis:
|
||||
.. code-block:: pycon
|
||||
|
||||
| >>> from fontTools import ttLib
|
||||
| >>> from fontTools.varLib import instancer
|
||||
| >>> varfont = ttLib.TTFont("path/to/MyVariableFont.ttf")
|
||||
| >>> [a.axisTag for a in varfont["fvar"].axes] # the varfont's current axes
|
||||
| ['wght', 'wdth']
|
||||
| >>> partial = instancer.instantiateVariableFont(varfont, {"wght": 300})
|
||||
| >>> [a.axisTag for a in partial["fvar"].axes] # axes left after pinning 'wght'
|
||||
| ['wdth']
|
||||
>>>
|
||||
>> from fontTools import ttLib
|
||||
>> from fontTools.varLib import instancer
|
||||
>> varfont = ttLib.TTFont("path/to/MyVariableFont.ttf")
|
||||
>> [a.axisTag for a in varfont["fvar"].axes] # the varfont's current axes
|
||||
['wght', 'wdth']
|
||||
>> partial = instancer.instantiateVariableFont(varfont, {"wght": 300})
|
||||
>> [a.axisTag for a in partial["fvar"].axes] # axes left after pinning 'wght'
|
||||
['wdth']
|
||||
|
||||
If the input location specifies all the axes, the resulting instance is no longer
|
||||
'variable' (same as using fontools varLib.mutator):
|
||||
.. code-block:: pycon
|
||||
|
||||
| >>> instance = instancer.instantiateVariableFont(
|
||||
| ... varfont, {"wght": 700, "wdth": 67.5}
|
||||
| ... )
|
||||
| >>> "fvar" not in instance
|
||||
| True
|
||||
>>>
|
||||
>> instance = instancer.instantiateVariableFont(
|
||||
... varfont, {"wght": 700, "wdth": 67.5}
|
||||
... )
|
||||
>> "fvar" not in instance
|
||||
True
|
||||
|
||||
If one just want to drop an axis at the default location, without knowing in
|
||||
advance what the default value for that axis is, one can pass a `None` value:
|
||||
.. code-block:: pycon
|
||||
|
||||
| >>> instance = instancer.instantiateVariableFont(varfont, {"wght": None})
|
||||
| >>> len(varfont["fvar"].axes)
|
||||
| 1
|
||||
>>>
|
||||
>> instance = instancer.instantiateVariableFont(varfont, {"wght": None})
|
||||
>> len(varfont["fvar"].axes)
|
||||
1
|
||||
|
||||
From the console script, this is equivalent to passing `wght=drop` as input.
|
||||
|
||||
@ -56,25 +64,33 @@ course be combined:
|
||||
|
||||
L1
|
||||
dropping one or more axes while leaving the default tables unmodified;
|
||||
.. code-block:: pycon
|
||||
|
||||
| >>> font = instancer.instantiateVariableFont(varfont, {"wght": None})
|
||||
>>>
|
||||
>> font = instancer.instantiateVariableFont(varfont, {"wght": None})
|
||||
|
||||
L2
|
||||
dropping one or more axes while pinning them at non-default locations;
|
||||
|
||||
| >>> font = instancer.instantiateVariableFont(varfont, {"wght": 700})
|
||||
.. code-block:: pycon
|
||||
|
||||
>>>
|
||||
>> font = instancer.instantiateVariableFont(varfont, {"wght": 700})
|
||||
|
||||
L3
|
||||
restricting the range of variation of one or more axes, by setting either
|
||||
a new minimum or maximum, potentially -- though not necessarily -- dropping
|
||||
entire regions of variations that fall completely outside this new range.
|
||||
|
||||
| >>> font = instancer.instantiateVariableFont(varfont, {"wght": (100, 300)})
|
||||
.. code-block:: pycon
|
||||
|
||||
>>>
|
||||
>> font = instancer.instantiateVariableFont(varfont, {"wght": (100, 300)})
|
||||
|
||||
L4
|
||||
moving the default location of an axis, by specifying (min,defalt,max) values:
|
||||
|
||||
| >>> font = instancer.instantiateVariableFont(varfont, {"wght": (100, 300, 700)})
|
||||
.. code-block:: pycon
|
||||
|
||||
>>>
|
||||
>> font = instancer.instantiateVariableFont(varfont, {"wght": (100, 300, 700)})
|
||||
|
||||
Currently only TrueType-flavored variable fonts (i.e. containing 'glyf' table)
|
||||
are supported, but support for CFF2 variable fonts will be added soon.
|
||||
@ -897,7 +913,18 @@ def _instantiateGvarGlyph(
|
||||
return
|
||||
|
||||
if optimize:
|
||||
# IUP semantics depend on point equality, and so round prior to
|
||||
# optimization to ensure that comparisons that happen now will be the
|
||||
# same as those that happen at render time. This is especially needed
|
||||
# when floating point deltas have been applied to the default position.
|
||||
# See https://github.com/fonttools/fonttools/issues/3634
|
||||
# Rounding must happen only after calculating glyf metrics above, to
|
||||
# preserve backwards compatibility.
|
||||
# See 0010a3cd9aa25f84a3a6250dafb119743d32aa40
|
||||
coordinates.toInt()
|
||||
|
||||
isComposite = glyf[glyphname].isComposite()
|
||||
|
||||
for var in tupleVarStore:
|
||||
var.optimize(coordinates, endPts, isComposite=isComposite)
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
"""
|
||||
Instantiate a variation font. Run, eg:
|
||||
|
||||
$ fonttools varLib.mutator ./NotoSansArabic-VF.ttf wght=140 wdth=85
|
||||
.. code-block:: sh
|
||||
|
||||
$ fonttools varLib.mutator ./NotoSansArabic-VF.ttf wght=140 wdth=85
|
||||
"""
|
||||
|
||||
from fontTools.misc.fixedTools import floatToFixedToFloat, floatToFixed
|
||||
@ -162,7 +164,9 @@ def instantiateVariableFont(varfont, location, inplace=False, overlap=True):
|
||||
defining the desired location along the variable font's axes.
|
||||
The location values must be specified as user-space coordinates, e.g.:
|
||||
|
||||
{'wght': 400, 'wdth': 100}
|
||||
.. code-block::
|
||||
|
||||
{'wght': 400, 'wdth': 100}
|
||||
|
||||
By default, a new TTFont object is returned. If ``inplace`` is True, the
|
||||
input varfont is modified and reduced to a static font.
|
||||
|
@ -7,12 +7,16 @@ Usage
|
||||
To convert a VTP project file:
|
||||
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ fonttools voltLib.voltToFea input.vtp output.fea
|
||||
|
||||
It is also possible convert font files with `TSIV` table (as saved from Volt),
|
||||
in this case the glyph names used in the Volt project will be mapped to the
|
||||
actual glyph names in the font files when written to the feature file:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ fonttools voltLib.voltToFea input.ttf output.fea
|
||||
|
||||
The ``--quiet`` option can be used to suppress warnings.
|
||||
|
294
Tests/varLib/instancer/data/3634-VF.ttx
Normal file
294
Tests/varLib/instancer/data/3634-VF.ttx
Normal file
@ -0,0 +1,294 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.53">
|
||||
|
||||
<GlyphOrder>
|
||||
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
|
||||
<GlyphID id="0" name=".notdef"/>
|
||||
<GlyphID id="1" name="A"/>
|
||||
</GlyphOrder>
|
||||
|
||||
<head>
|
||||
<!-- Most of this table will be recalculated by the compiler -->
|
||||
<tableVersion value="1.0"/>
|
||||
<fontRevision value="0.0"/>
|
||||
<checkSumAdjustment value="0x139cb6b8"/>
|
||||
<magicNumber value="0x5f0f3cf5"/>
|
||||
<flags value="00000000 00000011"/>
|
||||
<unitsPerEm value="1000"/>
|
||||
<created value="Wed Sep 11 11:44:03 2024"/>
|
||||
<modified value="Wed Sep 11 11:44:03 2024"/>
|
||||
<xMin value="0"/>
|
||||
<yMin value="-200"/>
|
||||
<xMax value="1000"/>
|
||||
<yMax value="1000"/>
|
||||
<macStyle value="00000000 00000000"/>
|
||||
<lowestRecPPEM value="6"/>
|
||||
<fontDirectionHint value="2"/>
|
||||
<indexToLocFormat value="0"/>
|
||||
<glyphDataFormat value="0"/>
|
||||
</head>
|
||||
|
||||
<hhea>
|
||||
<tableVersion value="0x00010000"/>
|
||||
<ascent value="1000"/>
|
||||
<descent value="-200"/>
|
||||
<lineGap value="0"/>
|
||||
<advanceWidthMax value="1000"/>
|
||||
<minLeftSideBearing value="0"/>
|
||||
<minRightSideBearing value="0"/>
|
||||
<xMaxExtent value="1000"/>
|
||||
<caretSlopeRise value="1"/>
|
||||
<caretSlopeRun value="0"/>
|
||||
<caretOffset value="0"/>
|
||||
<reserved0 value="0"/>
|
||||
<reserved1 value="0"/>
|
||||
<reserved2 value="0"/>
|
||||
<reserved3 value="0"/>
|
||||
<metricDataFormat value="0"/>
|
||||
<numberOfHMetrics value="2"/>
|
||||
</hhea>
|
||||
|
||||
<maxp>
|
||||
<!-- Most of this table will be recalculated by the compiler -->
|
||||
<tableVersion value="0x10000"/>
|
||||
<numGlyphs value="2"/>
|
||||
<maxPoints value="8"/>
|
||||
<maxContours value="2"/>
|
||||
<maxCompositePoints value="0"/>
|
||||
<maxCompositeContours value="0"/>
|
||||
<maxZones value="1"/>
|
||||
<maxTwilightPoints value="0"/>
|
||||
<maxStorage value="0"/>
|
||||
<maxFunctionDefs value="0"/>
|
||||
<maxInstructionDefs value="0"/>
|
||||
<maxStackElements value="0"/>
|
||||
<maxSizeOfInstructions value="0"/>
|
||||
<maxComponentElements value="0"/>
|
||||
<maxComponentDepth value="0"/>
|
||||
</maxp>
|
||||
|
||||
<OS_2>
|
||||
<!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
|
||||
will be recalculated by the compiler -->
|
||||
<version value="4"/>
|
||||
<xAvgCharWidth value="750"/>
|
||||
<usWeightClass value="400"/>
|
||||
<usWidthClass value="5"/>
|
||||
<fsType value="00000000 00000100"/>
|
||||
<ySubscriptXSize value="650"/>
|
||||
<ySubscriptYSize value="600"/>
|
||||
<ySubscriptXOffset value="0"/>
|
||||
<ySubscriptYOffset value="75"/>
|
||||
<ySuperscriptXSize value="650"/>
|
||||
<ySuperscriptYSize value="600"/>
|
||||
<ySuperscriptXOffset value="0"/>
|
||||
<ySuperscriptYOffset value="350"/>
|
||||
<yStrikeoutSize value="50"/>
|
||||
<yStrikeoutPosition value="300"/>
|
||||
<sFamilyClass value="0"/>
|
||||
<panose>
|
||||
<bFamilyType value="0"/>
|
||||
<bSerifStyle value="0"/>
|
||||
<bWeight value="0"/>
|
||||
<bProportion value="0"/>
|
||||
<bContrast value="0"/>
|
||||
<bStrokeVariation value="0"/>
|
||||
<bArmStyle value="0"/>
|
||||
<bLetterForm value="0"/>
|
||||
<bMidline value="0"/>
|
||||
<bXHeight value="0"/>
|
||||
</panose>
|
||||
<ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
|
||||
<ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
|
||||
<ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
|
||||
<ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
|
||||
<achVendID value="NONE"/>
|
||||
<fsSelection value="00000000 01000000"/>
|
||||
<usFirstCharIndex value="65"/>
|
||||
<usLastCharIndex value="65"/>
|
||||
<sTypoAscender value="800"/>
|
||||
<sTypoDescender value="-200"/>
|
||||
<sTypoLineGap value="200"/>
|
||||
<usWinAscent value="1000"/>
|
||||
<usWinDescent value="200"/>
|
||||
<ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
|
||||
<ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
|
||||
<sxHeight value="500"/>
|
||||
<sCapHeight value="700"/>
|
||||
<usDefaultChar value="0"/>
|
||||
<usBreakChar value="32"/>
|
||||
<usMaxContext value="0"/>
|
||||
</OS_2>
|
||||
|
||||
<hmtx>
|
||||
<mtx name=".notdef" width="500" lsb="50"/>
|
||||
<mtx name="A" width="1000" lsb="0"/>
|
||||
</hmtx>
|
||||
|
||||
<cmap>
|
||||
<tableVersion version="0"/>
|
||||
<cmap_format_4 platformID="0" platEncID="3" language="0">
|
||||
<map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
|
||||
</cmap_format_4>
|
||||
<cmap_format_4 platformID="3" platEncID="1" language="0">
|
||||
<map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
|
||||
</cmap_format_4>
|
||||
</cmap>
|
||||
|
||||
<loca>
|
||||
<!-- The 'loca' table will be calculated by the compiler -->
|
||||
</loca>
|
||||
|
||||
<glyf>
|
||||
|
||||
<!-- The xMin, yMin, xMax and yMax values
|
||||
will be recalculated by the compiler. -->
|
||||
|
||||
<TTGlyph name=".notdef" xMin="50" yMin="-200" xMax="450" yMax="800">
|
||||
<contour>
|
||||
<pt x="50" y="-200" on="1"/>
|
||||
<pt x="50" y="800" on="1"/>
|
||||
<pt x="450" y="800" on="1"/>
|
||||
<pt x="450" y="-200" on="1"/>
|
||||
</contour>
|
||||
<contour>
|
||||
<pt x="100" y="-150" on="1"/>
|
||||
<pt x="400" y="-150" on="1"/>
|
||||
<pt x="400" y="750" on="1"/>
|
||||
<pt x="100" y="750" on="1"/>
|
||||
</contour>
|
||||
<instructions/>
|
||||
</TTGlyph>
|
||||
|
||||
<TTGlyph name="A" xMin="0" yMin="0" xMax="1000" yMax="1000">
|
||||
<contour>
|
||||
<pt x="0" y="1000" on="1"/>
|
||||
<pt x="1000" y="1000" on="1"/>
|
||||
<pt x="1000" y="0" on="1"/>
|
||||
<pt x="0" y="0" on="1"/>
|
||||
</contour>
|
||||
<instructions/>
|
||||
</TTGlyph>
|
||||
|
||||
</glyf>
|
||||
|
||||
<name>
|
||||
<namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
|
||||
Weight
|
||||
</namerecord>
|
||||
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
|
||||
New Font
|
||||
</namerecord>
|
||||
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
|
||||
Regular
|
||||
</namerecord>
|
||||
<namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
|
||||
0.000;NONE;NewFont-Regular
|
||||
</namerecord>
|
||||
<namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
|
||||
New Font Regular
|
||||
</namerecord>
|
||||
<namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
|
||||
Version 0.000
|
||||
</namerecord>
|
||||
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
|
||||
NewFont-Regular
|
||||
</namerecord>
|
||||
<namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
|
||||
Weight
|
||||
</namerecord>
|
||||
</name>
|
||||
|
||||
<post>
|
||||
<formatType value="2.0"/>
|
||||
<italicAngle value="0.0"/>
|
||||
<underlinePosition value="-75"/>
|
||||
<underlineThickness value="50"/>
|
||||
<isFixedPitch value="0"/>
|
||||
<minMemType42 value="0"/>
|
||||
<maxMemType42 value="0"/>
|
||||
<minMemType1 value="0"/>
|
||||
<maxMemType1 value="0"/>
|
||||
<psNames>
|
||||
<!-- This file uses unique glyph names based on the information
|
||||
found in the 'post' table. Since these names might not be unique,
|
||||
we have to invent artificial names in case of clashes. In order to
|
||||
be able to retain the original information, we need a name to
|
||||
ps name mapping for those cases where they differ. That's what
|
||||
you see below.
|
||||
-->
|
||||
</psNames>
|
||||
<extraNames>
|
||||
<!-- following are the name that are not taken from the standard Mac glyph order -->
|
||||
</extraNames>
|
||||
</post>
|
||||
|
||||
<HVAR>
|
||||
<Version value="0x00010000"/>
|
||||
<VarStore Format="1">
|
||||
<Format value="1"/>
|
||||
<VarRegionList>
|
||||
<!-- RegionAxisCount=1 -->
|
||||
<!-- RegionCount=0 -->
|
||||
</VarRegionList>
|
||||
<!-- VarDataCount=1 -->
|
||||
<VarData index="0">
|
||||
<!-- ItemCount=1 -->
|
||||
<NumShorts value="0"/>
|
||||
<!-- VarRegionCount=0 -->
|
||||
<Item index="0" value="[]"/>
|
||||
</VarData>
|
||||
</VarStore>
|
||||
<AdvWidthMap>
|
||||
<Map glyph=".notdef" outer="0" inner="0"/>
|
||||
<Map glyph="A" outer="0" inner="0"/>
|
||||
</AdvWidthMap>
|
||||
</HVAR>
|
||||
|
||||
<STAT>
|
||||
<Version value="0x00010001"/>
|
||||
<DesignAxisRecordSize value="8"/>
|
||||
<!-- DesignAxisCount=1 -->
|
||||
<DesignAxisRecord>
|
||||
<Axis index="0">
|
||||
<AxisTag value="wght"/>
|
||||
<AxisNameID value="256"/> <!-- Weight -->
|
||||
<AxisOrdering value="0"/>
|
||||
</Axis>
|
||||
</DesignAxisRecord>
|
||||
<!-- AxisValueCount=0 -->
|
||||
<ElidedFallbackNameID value="2"/> <!-- Regular -->
|
||||
</STAT>
|
||||
|
||||
<fvar>
|
||||
|
||||
<!-- Weight -->
|
||||
<Axis>
|
||||
<AxisTag>wght</AxisTag>
|
||||
<Flags>0x0</Flags>
|
||||
<MinValue>400.0</MinValue>
|
||||
<DefaultValue>400.0</DefaultValue>
|
||||
<MaxValue>900.0</MaxValue>
|
||||
<AxisNameID>256</AxisNameID>
|
||||
</Axis>
|
||||
</fvar>
|
||||
|
||||
<gvar>
|
||||
<version value="1"/>
|
||||
<reserved value="0"/>
|
||||
<glyphVariations glyph="A">
|
||||
<tuple>
|
||||
<coord axis="wght" value="1.0"/>
|
||||
<delta pt="0" x="499" y="0"/>
|
||||
<delta pt="1" x="500" y="0"/>
|
||||
<delta pt="2" x="500" y="0"/>
|
||||
<delta pt="3" x="500" y="0"/>
|
||||
<delta pt="4" x="0" y="0"/>
|
||||
<delta pt="5" x="0" y="0"/>
|
||||
<delta pt="6" x="0" y="0"/>
|
||||
<delta pt="7" x="0" y="0"/>
|
||||
</tuple>
|
||||
</glyphVariations>
|
||||
</gvar>
|
||||
|
||||
</ttFont>
|
294
Tests/varLib/instancer/data/test_results/3634-VF-partial.ttx
Normal file
294
Tests/varLib/instancer/data/test_results/3634-VF-partial.ttx
Normal file
@ -0,0 +1,294 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.53">
|
||||
|
||||
<GlyphOrder>
|
||||
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
|
||||
<GlyphID id="0" name=".notdef"/>
|
||||
<GlyphID id="1" name="A"/>
|
||||
</GlyphOrder>
|
||||
|
||||
<head>
|
||||
<!-- Most of this table will be recalculated by the compiler -->
|
||||
<tableVersion value="1.0"/>
|
||||
<fontRevision value="0.0"/>
|
||||
<checkSumAdjustment value="0xbd1ef780"/>
|
||||
<magicNumber value="0x5f0f3cf5"/>
|
||||
<flags value="00000000 00000011"/>
|
||||
<unitsPerEm value="1000"/>
|
||||
<created value="Wed Sep 11 11:44:03 2024"/>
|
||||
<modified value="Wed Sep 11 12:08:44 2024"/>
|
||||
<xMin value="1"/>
|
||||
<yMin value="-200"/>
|
||||
<xMax value="1001"/>
|
||||
<yMax value="1000"/>
|
||||
<macStyle value="00000000 00000000"/>
|
||||
<lowestRecPPEM value="6"/>
|
||||
<fontDirectionHint value="2"/>
|
||||
<indexToLocFormat value="0"/>
|
||||
<glyphDataFormat value="0"/>
|
||||
</head>
|
||||
|
||||
<hhea>
|
||||
<tableVersion value="0x00010000"/>
|
||||
<ascent value="1000"/>
|
||||
<descent value="-200"/>
|
||||
<lineGap value="0"/>
|
||||
<advanceWidthMax value="1000"/>
|
||||
<minLeftSideBearing value="1"/>
|
||||
<minRightSideBearing value="-1"/>
|
||||
<xMaxExtent value="1001"/>
|
||||
<caretSlopeRise value="1"/>
|
||||
<caretSlopeRun value="0"/>
|
||||
<caretOffset value="0"/>
|
||||
<reserved0 value="0"/>
|
||||
<reserved1 value="0"/>
|
||||
<reserved2 value="0"/>
|
||||
<reserved3 value="0"/>
|
||||
<metricDataFormat value="0"/>
|
||||
<numberOfHMetrics value="2"/>
|
||||
</hhea>
|
||||
|
||||
<maxp>
|
||||
<!-- Most of this table will be recalculated by the compiler -->
|
||||
<tableVersion value="0x10000"/>
|
||||
<numGlyphs value="2"/>
|
||||
<maxPoints value="8"/>
|
||||
<maxContours value="2"/>
|
||||
<maxCompositePoints value="0"/>
|
||||
<maxCompositeContours value="0"/>
|
||||
<maxZones value="1"/>
|
||||
<maxTwilightPoints value="0"/>
|
||||
<maxStorage value="0"/>
|
||||
<maxFunctionDefs value="0"/>
|
||||
<maxInstructionDefs value="0"/>
|
||||
<maxStackElements value="0"/>
|
||||
<maxSizeOfInstructions value="0"/>
|
||||
<maxComponentElements value="0"/>
|
||||
<maxComponentDepth value="0"/>
|
||||
</maxp>
|
||||
|
||||
<OS_2>
|
||||
<!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
|
||||
will be recalculated by the compiler -->
|
||||
<version value="4"/>
|
||||
<xAvgCharWidth value="750"/>
|
||||
<usWeightClass value="401"/>
|
||||
<usWidthClass value="5"/>
|
||||
<fsType value="00000000 00000100"/>
|
||||
<ySubscriptXSize value="650"/>
|
||||
<ySubscriptYSize value="600"/>
|
||||
<ySubscriptXOffset value="0"/>
|
||||
<ySubscriptYOffset value="75"/>
|
||||
<ySuperscriptXSize value="650"/>
|
||||
<ySuperscriptYSize value="600"/>
|
||||
<ySuperscriptXOffset value="0"/>
|
||||
<ySuperscriptYOffset value="350"/>
|
||||
<yStrikeoutSize value="50"/>
|
||||
<yStrikeoutPosition value="300"/>
|
||||
<sFamilyClass value="0"/>
|
||||
<panose>
|
||||
<bFamilyType value="0"/>
|
||||
<bSerifStyle value="0"/>
|
||||
<bWeight value="0"/>
|
||||
<bProportion value="0"/>
|
||||
<bContrast value="0"/>
|
||||
<bStrokeVariation value="0"/>
|
||||
<bArmStyle value="0"/>
|
||||
<bLetterForm value="0"/>
|
||||
<bMidline value="0"/>
|
||||
<bXHeight value="0"/>
|
||||
</panose>
|
||||
<ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
|
||||
<ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
|
||||
<ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
|
||||
<ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
|
||||
<achVendID value="NONE"/>
|
||||
<fsSelection value="00000000 01000000"/>
|
||||
<usFirstCharIndex value="65"/>
|
||||
<usLastCharIndex value="65"/>
|
||||
<sTypoAscender value="800"/>
|
||||
<sTypoDescender value="-200"/>
|
||||
<sTypoLineGap value="200"/>
|
||||
<usWinAscent value="1000"/>
|
||||
<usWinDescent value="200"/>
|
||||
<ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
|
||||
<ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
|
||||
<sxHeight value="500"/>
|
||||
<sCapHeight value="700"/>
|
||||
<usDefaultChar value="0"/>
|
||||
<usBreakChar value="32"/>
|
||||
<usMaxContext value="0"/>
|
||||
</OS_2>
|
||||
|
||||
<hmtx>
|
||||
<mtx name=".notdef" width="500" lsb="50"/>
|
||||
<mtx name="A" width="1000" lsb="1"/>
|
||||
</hmtx>
|
||||
|
||||
<cmap>
|
||||
<tableVersion version="0"/>
|
||||
<cmap_format_4 platformID="0" platEncID="3" language="0">
|
||||
<map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
|
||||
</cmap_format_4>
|
||||
<cmap_format_4 platformID="3" platEncID="1" language="0">
|
||||
<map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
|
||||
</cmap_format_4>
|
||||
</cmap>
|
||||
|
||||
<loca>
|
||||
<!-- The 'loca' table will be calculated by the compiler -->
|
||||
</loca>
|
||||
|
||||
<glyf>
|
||||
|
||||
<!-- The xMin, yMin, xMax and yMax values
|
||||
will be recalculated by the compiler. -->
|
||||
|
||||
<TTGlyph name=".notdef" xMin="50" yMin="-200" xMax="450" yMax="800">
|
||||
<contour>
|
||||
<pt x="50" y="-200" on="1"/>
|
||||
<pt x="50" y="800" on="1"/>
|
||||
<pt x="450" y="800" on="1"/>
|
||||
<pt x="450" y="-200" on="1"/>
|
||||
</contour>
|
||||
<contour>
|
||||
<pt x="100" y="-150" on="1"/>
|
||||
<pt x="400" y="-150" on="1"/>
|
||||
<pt x="400" y="750" on="1"/>
|
||||
<pt x="100" y="750" on="1"/>
|
||||
</contour>
|
||||
<instructions/>
|
||||
</TTGlyph>
|
||||
|
||||
<TTGlyph name="A" xMin="1" yMin="0" xMax="1001" yMax="1000">
|
||||
<contour>
|
||||
<pt x="1" y="1000" on="1"/>
|
||||
<pt x="1001" y="1000" on="1"/>
|
||||
<pt x="1001" y="0" on="1"/>
|
||||
<pt x="1" y="0" on="1"/>
|
||||
</contour>
|
||||
<instructions/>
|
||||
</TTGlyph>
|
||||
|
||||
</glyf>
|
||||
|
||||
<name>
|
||||
<namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
|
||||
Weight
|
||||
</namerecord>
|
||||
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
|
||||
New Font
|
||||
</namerecord>
|
||||
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
|
||||
Regular
|
||||
</namerecord>
|
||||
<namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
|
||||
0.000;NONE;NewFont-Regular
|
||||
</namerecord>
|
||||
<namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
|
||||
New Font Regular
|
||||
</namerecord>
|
||||
<namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
|
||||
Version 0.000
|
||||
</namerecord>
|
||||
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
|
||||
NewFont-Regular
|
||||
</namerecord>
|
||||
<namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
|
||||
Weight
|
||||
</namerecord>
|
||||
</name>
|
||||
|
||||
<post>
|
||||
<formatType value="2.0"/>
|
||||
<italicAngle value="0.0"/>
|
||||
<underlinePosition value="-75"/>
|
||||
<underlineThickness value="50"/>
|
||||
<isFixedPitch value="0"/>
|
||||
<minMemType42 value="0"/>
|
||||
<maxMemType42 value="0"/>
|
||||
<minMemType1 value="0"/>
|
||||
<maxMemType1 value="0"/>
|
||||
<psNames>
|
||||
<!-- This file uses unique glyph names based on the information
|
||||
found in the 'post' table. Since these names might not be unique,
|
||||
we have to invent artificial names in case of clashes. In order to
|
||||
be able to retain the original information, we need a name to
|
||||
ps name mapping for those cases where they differ. That's what
|
||||
you see below.
|
||||
-->
|
||||
</psNames>
|
||||
<extraNames>
|
||||
<!-- following are the name that are not taken from the standard Mac glyph order -->
|
||||
</extraNames>
|
||||
</post>
|
||||
|
||||
<HVAR>
|
||||
<Version value="0x00010000"/>
|
||||
<VarStore Format="1">
|
||||
<Format value="1"/>
|
||||
<VarRegionList>
|
||||
<!-- RegionAxisCount=1 -->
|
||||
<!-- RegionCount=0 -->
|
||||
</VarRegionList>
|
||||
<!-- VarDataCount=1 -->
|
||||
<VarData index="0">
|
||||
<!-- ItemCount=1 -->
|
||||
<NumShorts value="0"/>
|
||||
<!-- VarRegionCount=0 -->
|
||||
<Item index="0" value="[]"/>
|
||||
</VarData>
|
||||
</VarStore>
|
||||
<AdvWidthMap>
|
||||
<Map glyph=".notdef" outer="0" inner="0"/>
|
||||
<Map glyph="A" outer="0" inner="0"/>
|
||||
</AdvWidthMap>
|
||||
</HVAR>
|
||||
|
||||
<STAT>
|
||||
<Version value="0x00010001"/>
|
||||
<DesignAxisRecordSize value="8"/>
|
||||
<!-- DesignAxisCount=1 -->
|
||||
<DesignAxisRecord>
|
||||
<Axis index="0">
|
||||
<AxisTag value="wght"/>
|
||||
<AxisNameID value="256"/> <!-- Weight -->
|
||||
<AxisOrdering value="0"/>
|
||||
</Axis>
|
||||
</DesignAxisRecord>
|
||||
<!-- AxisValueCount=0 -->
|
||||
<ElidedFallbackNameID value="2"/> <!-- Regular -->
|
||||
</STAT>
|
||||
|
||||
<fvar>
|
||||
|
||||
<!-- Weight -->
|
||||
<Axis>
|
||||
<AxisTag>wght</AxisTag>
|
||||
<Flags>0x0</Flags>
|
||||
<MinValue>401.0</MinValue>
|
||||
<DefaultValue>401.0</DefaultValue>
|
||||
<MaxValue>900.0</MaxValue>
|
||||
<AxisNameID>256</AxisNameID>
|
||||
</Axis>
|
||||
</fvar>
|
||||
|
||||
<gvar>
|
||||
<version value="1"/>
|
||||
<reserved value="0"/>
|
||||
<glyphVariations glyph="A">
|
||||
<tuple>
|
||||
<coord axis="wght" value="1.0"/>
|
||||
<delta pt="0" x="498" y="0"/>
|
||||
<delta pt="1" x="499" y="0"/>
|
||||
<delta pt="2" x="499" y="0"/>
|
||||
<delta pt="3" x="499" y="0"/>
|
||||
<delta pt="4" x="0" y="0"/>
|
||||
<delta pt="5" x="0" y="0"/>
|
||||
<delta pt="6" x="0" y="0"/>
|
||||
<delta pt="7" x="0" y="0"/>
|
||||
</tuple>
|
||||
</glyphVariations>
|
||||
</gvar>
|
||||
|
||||
</ttFont>
|
@ -2390,3 +2390,41 @@ def test_set_ribbi_bits():
|
||||
assert name_id_2 == "Italic", location
|
||||
assert mac_style == 0b10, location
|
||||
assert fs_selection == 0b0000001, location
|
||||
|
||||
|
||||
def test_rounds_before_iup():
|
||||
"""Regression test for fonttools/fonttools#3634, with TTX based on
|
||||
reproduction process there."""
|
||||
|
||||
varfont = ttLib.TTFont()
|
||||
varfont.importXML(os.path.join(TESTDATA, "3634-VF.ttx"))
|
||||
|
||||
# Instantiate at a new default position, sufficient to cause differences
|
||||
# when unrounded but not when rounded.
|
||||
partial = instancer.instantiateVariableFont(varfont, {"wght": (401, 401, 900)})
|
||||
|
||||
# Save and reload actual result to recalculate bounding box values, etc.
|
||||
bytes_out = BytesIO()
|
||||
partial.save(bytes_out)
|
||||
bytes_out.seek(0)
|
||||
partial = ttLib.TTFont(bytes_out)
|
||||
|
||||
# Load expected result, then save and reload to normalise TTX output.
|
||||
expected = ttLib.TTFont()
|
||||
expected.importXML(os.path.join(TESTDATA, "test_results", "3634-VF-partial.ttx"))
|
||||
|
||||
bytes_out = BytesIO()
|
||||
expected.save(bytes_out)
|
||||
bytes_out.seek(0)
|
||||
expected = ttLib.TTFont(bytes_out)
|
||||
|
||||
# Serialise actual and expected to TTX strings, and compare.
|
||||
string_out = StringIO()
|
||||
partial.saveXML(string_out)
|
||||
partial_ttx = stripVariableItemsFromTTX(string_out.getvalue())
|
||||
|
||||
string_out = StringIO()
|
||||
expected.saveXML(string_out)
|
||||
expected_ttx = stripVariableItemsFromTTX(string_out.getvalue())
|
||||
|
||||
assert partial_ttx == expected_ttx
|
||||
|
Loading…
x
Reference in New Issue
Block a user