Merge branch 'master' into fealib-duplicate-sub-warning

This commit is contained in:
Jens Kutilek 2019-12-18 11:24:00 +01:00
commit 720488ed4b
26 changed files with 312 additions and 35 deletions

View File

@ -4,6 +4,6 @@ from fontTools.misc.loggingTools import configLogger
log = logging.getLogger(__name__)
version = __version__ = "4.2.1.dev0"
version = __version__ = "4.2.3.dev0"
__all__ = ["version", "log", "configLogger"]

View File

@ -751,6 +751,20 @@ class FontBuilder(object):
from .feaLib.builder import addOpenTypeFeaturesFromString
addOpenTypeFeaturesFromString(self.font, features, filename=filename, tables=tables)
def addFeatureVariations(self, conditionalSubstitutions, featureTag="rvrn"):
"""Add conditional substitutions to a Variable Font.
See `fontTools.varLib.featureVars.addFeatureVariations`.
"""
from .varLib import featureVars
if "fvar" not in self.font:
raise KeyError("'fvar' table is missing; can't add FeatureVariations.")
featureVars.addFeatureVariations(
self.font, conditionalSubstitutions, featureTag=featureTag
)
def buildCmapSubTable(cmapping, format, platformID, platEncID):
subTable = cmap_classes[format](format)

View File

@ -1307,6 +1307,9 @@ def subset_features(self, feature_indices):
self.ensureDecompiled()
self.SubstitutionRecord = [r for r in self.SubstitutionRecord
if r.FeatureIndex in feature_indices]
# remap feature indices
for r in self.SubstitutionRecord:
r.FeatureIndex = feature_indices.index(r.FeatureIndex)
self.SubstitutionCount = len(self.SubstitutionRecord)
return bool(self.SubstitutionCount)

View File

@ -1179,7 +1179,7 @@ class Glyph(object):
for end in endPts:
end = end + 1
contour = coordinates[start:end]
cFlags = flags[start:end]
cFlags = [flagOnCurve & f for f in flags[start:end]]
start = end
if 1 not in cFlags:
# There is not a single on-curve point on the curve,
@ -1198,7 +1198,10 @@ class Glyph(object):
while contour:
nextOnCurve = cFlags.index(1) + 1
if nextOnCurve == 1:
pen.lineTo(contour[0])
# Skip a final lineTo(), as it is implied by
# pen.closePath()
if len(contour) > 1:
pen.lineTo(contour[0])
else:
pen.qCurveTo(*contour[:nextOnCurve])
contour = contour[nextOnCurve:]
@ -1230,7 +1233,7 @@ class Glyph(object):
# Start with the appropriate segment type based on the final segment
segmentType = "line" if cFlags[-1] == 1 else "qcurve"
for i, pt in enumerate(contour):
if cFlags[i] == 1:
if cFlags[i] & flagOnCurve == 1:
pen.addPoint(pt, segmentType=segmentType)
segmentType = "line"
else:

View File

@ -852,7 +852,11 @@ def validateLayerInfoVersion3Data(infoData):
# -----------------
def _glifTreeFromFile(aFile):
root = etree.parse(aFile).getroot()
if etree._have_lxml:
tree = etree.parse(aFile, parser=etree.XMLParser(remove_comments=True))
else:
tree = etree.parse(aFile)
root = tree.getroot()
if root.tag != "glyph":
raise GlifLibError("The GLIF is not properly formatted.")
if root.text and root.text.strip() != '':
@ -862,7 +866,10 @@ def _glifTreeFromFile(aFile):
def _glifTreeFromString(aString):
data = tobytes(aString, encoding="utf-8")
root = etree.fromstring(data)
if etree._have_lxml:
root = etree.fromstring(data, parser=etree.XMLParser(remove_comments=True))
else:
root = etree.fromstring(data)
if root.tag != "glyph":
raise GlifLibError("The GLIF is not properly formatted.")
if root.text and root.text.strip() != '':

View File

@ -141,8 +141,14 @@ def _add_avar(font, axes):
assert axis.maximum == max(keys)
assert axis.default in keys
# No duplicates
assert len(set(keys)) == len(keys)
assert len(set(vals)) == len(vals)
assert len(set(keys)) == len(keys), (
f"{axis.tag} axis: All axis mapping input='...' "
"values must be unique, but we found duplicates."
)
assert len(set(vals)) == len(vals), (
f"{axis.tag} axis: All axis mapping output='...' "
"values must be unique, but we found duplicates."
)
# Ascending values
assert sorted(vals) == vals

View File

@ -1,4 +1,4 @@
#! /usr/bin/env python
#! /usr/bin/env python3
import sys
import os

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
"""
Tools to parse data files from the Unicode Character Database.
"""

View File

@ -1,4 +1,4 @@
#! /usr/bin/env python
#! /usr/bin/env python3
"""usage: ttroundtrip [options] font1 ... fontN

View File

@ -1,3 +1,21 @@
4.2.2 (released 2019-12-12)
---------------------------
- [subset] Fixed issue with subsetting FeatureVariations table when the index
of features changes as features get dropped. The feature index need to be
remapped to point to index of the remaining features (#1777, #1782).
- [fontBuilder] Added `addFeatureVariations` method to `FontBuilder` class. This
is a shorthand for calling `featureVars.addFeatureVariations` on the builder's
TTFont object (#1781).
- [glyf] Fixed the flags bug in glyph.drawPoints() like we did for glyph.draw()
(#1771, #1774).
4.2.1 (released 2019-12-06)
---------------------------
- [glyf] Use the ``flagOnCurve`` bit mask in ``glyph.draw()``, so that we ignore
the ``overlap`` flag that may be set when instantiating variable fonts (#1771).
4.2.0 (released 2019-11-28)
---------------------------

View File

@ -1,4 +1,4 @@
#! /usr/bin/env python
#! /usr/bin/env python3
# Sample script to convert legacy cmap subtables to format-4
# subtables. Note that this is rarely what one needs. You

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
import argparse
import logging

View File

@ -1,4 +1,4 @@
#! /usr/bin/env python
#! /usr/bin/env python3
# Illustrates how a fonttools script can construct variable fonts.
#

View File

@ -1,4 +1,4 @@
#! /usr/bin/env python
#! /usr/bin/env python3
from fontTools.misc.py23 import *
from fontTools.ttLib import TTFont

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
import argparse
import logging

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
"""Script to add a suffix to all family names in the input font's `name` table,
and to optionally rename the output files with the given suffix.

View File

@ -1,4 +1,4 @@
#! /usr/bin/env python
#! /usr/bin/env python3
from fontTools.misc.py23 import *
from fontTools.ttLib import TTFont

View File

@ -1,8 +1,8 @@
#!/usr/bin/env python
#!/usr/bin/env python3
""" Convert SVG paths to UFO glyphs. """
__requires__ = ["FontTools", "ufoLib"]
__requires__ = ["fontTools"]
from fontTools.misc.py23 import SimpleNamespace
from fontTools.svgLib import SVGPath

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.32">
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.2">
<GlyphOrder>
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
@ -19,7 +19,7 @@
<unitsPerEm value="1024"/>
<created value="Thu Nov 1 20:29:01 2018"/>
<modified value="Thu Nov 1 20:29:01 2018"/>
<xMin value="100"/>
<xMin value="50"/>
<yMin value="0"/>
<xMax value="500"/>
<yMax value="400"/>
@ -36,7 +36,7 @@
<descent value="200"/>
<lineGap value="0"/>
<advanceWidthMax value="600"/>
<minLeftSideBearing value="100"/>
<minLeftSideBearing value="50"/>
<minRightSideBearing value="100"/>
<xMaxExtent value="500"/>
<caretSlopeRise value="1"/>
@ -126,7 +126,7 @@
<mtx name=".notdef" width="600" lsb="0"/>
<mtx name=".null" width="600" lsb="0"/>
<mtx name="A" width="600" lsb="100"/>
<mtx name="a" width="600" lsb="100"/>
<mtx name="a" width="600" lsb="50"/>
</hmtx>
<cmap>
@ -164,12 +164,12 @@
<instructions/>
</TTGlyph>
<TTGlyph name="a" xMin="100" yMin="0" xMax="500" yMax="400">
<TTGlyph name="a" xMin="50" yMin="0" xMax="250" yMax="200">
<contour>
<pt x="100" y="0" on="1"/>
<pt x="100" y="400" on="1"/>
<pt x="500" y="400" on="1"/>
<pt x="500" y="0" on="1"/>
<pt x="50" y="0" on="1"/>
<pt x="50" y="200" on="1"/>
<pt x="250" y="200" on="1"/>
<pt x="250" y="0" on="1"/>
</contour>
<instructions/>
</TTGlyph>
@ -269,6 +269,100 @@
</extraNames>
</post>
<GSUB>
<Version value="0x00010001"/>
<ScriptList>
<!-- ScriptCount=1 -->
<ScriptRecord index="0">
<ScriptTag value="DFLT"/>
<Script>
<DefaultLangSys>
<ReqFeatureIndex value="65535"/>
<!-- FeatureCount=1 -->
<FeatureIndex index="0" value="0"/>
</DefaultLangSys>
<!-- LangSysCount=0 -->
</Script>
</ScriptRecord>
</ScriptList>
<FeatureList>
<!-- FeatureCount=1 -->
<FeatureRecord index="0">
<FeatureTag value="rclt"/>
<Feature>
<!-- LookupCount=0 -->
</Feature>
</FeatureRecord>
</FeatureList>
<LookupList>
<!-- LookupCount=1 -->
<Lookup index="0">
<LookupType value="1"/>
<LookupFlag value="0"/>
<!-- SubTableCount=1 -->
<SingleSubst index="0" Format="1">
<Substitution in="A" out="a"/>
</SingleSubst>
</Lookup>
</LookupList>
<FeatureVariations>
<Version value="0x00010000"/>
<!-- FeatureVariationCount=2 -->
<FeatureVariationRecord index="0">
<ConditionSet>
<!-- ConditionCount=2 -->
<ConditionTable index="0" Format="1">
<AxisIndex value="3"/>
<FilterRangeMinValue value="0.8"/>
<FilterRangeMaxValue value="1.0"/>
</ConditionTable>
<ConditionTable index="1" Format="1">
<AxisIndex value="0"/>
<FilterRangeMinValue value="0.8"/>
<FilterRangeMaxValue value="1.0"/>
</ConditionTable>
</ConditionSet>
<FeatureTableSubstitution>
<Version value="0x00010000"/>
<!-- SubstitutionCount=1 -->
<SubstitutionRecord index="0">
<FeatureIndex value="0"/>
<Feature>
<!-- LookupCount=1 -->
<LookupListIndex index="0" value="0"/>
</Feature>
</SubstitutionRecord>
</FeatureTableSubstitution>
</FeatureVariationRecord>
<FeatureVariationRecord index="1">
<ConditionSet>
<!-- ConditionCount=2 -->
<ConditionTable index="0" Format="1">
<AxisIndex value="1"/>
<FilterRangeMinValue value="0.8"/>
<FilterRangeMaxValue value="1.0"/>
</ConditionTable>
<ConditionTable index="1" Format="1">
<AxisIndex value="2"/>
<FilterRangeMinValue value="0.8"/>
<FilterRangeMaxValue value="1.0"/>
</ConditionTable>
</ConditionSet>
<FeatureTableSubstitution>
<Version value="0x00010000"/>
<!-- SubstitutionCount=1 -->
<SubstitutionRecord index="0">
<FeatureIndex value="0"/>
<Feature>
<!-- LookupCount=1 -->
<LookupListIndex index="0" value="0"/>
</Feature>
</SubstitutionRecord>
</FeatureTableSubstitution>
</FeatureVariationRecord>
</FeatureVariations>
</GSUB>
<fvar>
<!-- Left -->

View File

@ -164,13 +164,20 @@ def test_build_var(tmpdir):
pen.lineTo((500, 400))
pen.lineTo((500, 000))
pen.closePath()
glyph1 = pen.glyph()
glyph = pen.glyph()
pen = TTGlyphPen(None)
pen.moveTo((50, 0))
pen.lineTo((50, 200))
pen.lineTo((250, 200))
pen.lineTo((250, 0))
pen.closePath()
glyph2 = pen.glyph()
pen = TTGlyphPen(None)
emptyGlyph = pen.glyph()
glyphs = {".notdef": emptyGlyph, "A": glyph, "a": glyph, ".null": emptyGlyph}
glyphs = {".notdef": emptyGlyph, "A": glyph1, "a": glyph2, ".null": emptyGlyph}
fb.setupGlyf(glyphs)
metrics = {}
glyphTable = fb.font["glyf"]
@ -206,6 +213,19 @@ def test_build_var(tmpdir):
]
fb.setupGvar(variations)
fb.addFeatureVariations(
[
(
[
{"LEFT": (0.8, 1), "DOWN": (0.8, 1)},
{"RGHT": (0.8, 1), "UPPP": (0.8, 1)},
],
{"A": "a"}
)
],
featureTag="rclt",
)
fb.setupOS2()
fb.setupPost()
fb.setupDummyDSIG()

View File

@ -1,5 +1,7 @@
import io
from fontTools.misc.py23 import *
from fontTools import subset
from fontTools.fontBuilder import FontBuilder
from fontTools.ttLib import TTFont, newTable
from fontTools.misc.loggingTools import CapturingLogHandler
import difflib
@ -728,5 +730,43 @@ class SubsetTest(unittest.TestCase):
self.assertEqual(ttf.flavor, None)
def test_subset_feature_variations():
fb = FontBuilder(unitsPerEm=100)
fb.setupGlyphOrder([".notdef", "f", "f_f", "dollar", "dollar.rvrn"])
fb.setupCharacterMap({ord("f"): "f", ord("$"): "dollar"})
fb.setupNameTable({"familyName": "TestFeatureVars", "styleName": "Regular"})
fb.setupPost()
fb.setupFvar(axes=[("wght", 100, 400, 900, "Weight")], instances=[])
fb.addOpenTypeFeatures("""\
feature dlig {
sub f f by f_f;
} dlig;
""")
fb.addFeatureVariations(
[([{"wght": (0.20886, 1.0)}], {"dollar": "dollar.rvrn"})],
featureTag="rvrn"
)
buf = io.BytesIO()
fb.save(buf)
buf.seek(0)
font = TTFont(buf)
options = subset.Options()
subsetter = subset.Subsetter(options)
subsetter.populate(unicodes=[ord("f"), ord("$")])
subsetter.subset(font)
featureTags = {
r.FeatureTag for r in font["GSUB"].table.FeatureList.FeatureRecord
}
# 'dlig' is discretionary so it is dropped by default
assert "dlig" not in featureTags
assert "f_f" not in font.getGlyphOrder()
# 'rvrn' is required so it is kept by default
assert "rvrn" in featureTags
assert "dollar.rvrn" in font.getGlyphOrder()
if __name__ == "__main__":
sys.exit(unittest.main())

View File

@ -2,6 +2,8 @@ from fontTools.misc.py23 import *
from fontTools.misc.fixedTools import otRound
from fontTools.misc.testTools import getXML, parseXML
from fontTools.pens.ttGlyphPen import TTGlyphPen
from fontTools.pens.recordingPen import RecordingPen, RecordingPointPen
from fontTools.pens.pointPen import PointToSegmentPen
from fontTools.ttLib import TTFont, newTable, TTLibError
from fontTools.ttLib.tables._g_l_y_f import (
GlyphCoordinates,
@ -284,6 +286,58 @@ class glyfTableTest(unittest.TestCase):
composite.compact(glyfTable)
def test_bit6_draw_to_pen_issue1771(self):
# https://github.com/fonttools/fonttools/issues/1771
font = TTFont(sfntVersion="\x00\x01\x00\x00")
# glyph00003 contains a bit 6 flag on the first point,
# which triggered the issue
font.importXML(GLYF_TTX)
glyfTable = font['glyf']
pen = RecordingPen()
glyfTable["glyph00003"].draw(pen, glyfTable=glyfTable)
expected = [('moveTo', ((501, 1430),)),
('lineTo', ((683, 1430),)),
('lineTo', ((1172, 0),)),
('lineTo', ((983, 0),)),
('lineTo', ((591, 1193),)),
('lineTo', ((199, 0),)),
('lineTo', ((12, 0),)),
('closePath', ()),
('moveTo', ((249, 514),)),
('lineTo', ((935, 514),)),
('lineTo', ((935, 352),)),
('lineTo', ((249, 352),)),
('closePath', ())]
self.assertEqual(pen.value, expected)
def test_bit6_draw_to_pointpen(self):
# https://github.com/fonttools/fonttools/issues/1771
font = TTFont(sfntVersion="\x00\x01\x00\x00")
# glyph00003 contains a bit 6 flag on the first point
# which triggered the issue
font.importXML(GLYF_TTX)
glyfTable = font['glyf']
pen = RecordingPointPen()
glyfTable["glyph00003"].drawPoints(pen, glyfTable=glyfTable)
expected = [
('beginPath', (), {}),
('addPoint', ((501, 1430), 'line', False, None), {}),
('addPoint', ((683, 1430), 'line', False, None), {}),
('addPoint', ((1172, 0), 'line', False, None), {}),
('addPoint', ((983, 0), 'line', False, None), {}),
]
self.assertEqual(pen.value[:len(expected)], expected)
def test_draw_vs_drawpoints(self):
font = TTFont(sfntVersion="\x00\x01\x00\x00")
font.importXML(GLYF_TTX)
glyfTable = font['glyf']
pen1 = RecordingPen()
pen2 = RecordingPen()
glyfTable["glyph00003"].draw(pen1, glyfTable)
glyfTable["glyph00003"].drawPoints(PointToSegmentPen(pen2), glyfTable)
self.assertEqual(pen1.value, pen2.value)
class GlyphComponentTest:

View File

@ -161,3 +161,21 @@ class ReadWriteFuncTest(unittest.TestCase):
def testXmlDeclaration(self):
s = writeGlyphToString("a", _Glyph())
self.assertTrue(s.startswith(XML_DECLARATION % "UTF-8"))
def test_parse_xml_remove_comments():
s = b"""<?xml version='1.0' encoding='UTF-8'?>
<!-- a comment -->
<glyph name="A" format="2">
<advance width="1290"/>
<unicode hex="0041"/>
<!-- another comment -->
</glyph>
"""
g = _Glyph()
readGlyphFromString(s, g)
assert g.name == "A"
assert g.width == 1290
assert g.unicodes == [0x0041]

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
import sys
import os.path

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 4.2.1.dev0
current_version = 4.2.3.dev0
commit = True
tag = False
tag_name = {new_version}

View File

@ -1,4 +1,4 @@
#! /usr/bin/env python
#! /usr/bin/env python3
from __future__ import print_function
import io
@ -345,7 +345,7 @@ def find_data_files(manpath="share/man"):
setup(
name="fonttools",
version="4.2.1.dev0",
version="4.2.3.dev0",
description="Tools to manipulate font files",
author="Just van Rossum",
author_email="just@letterror.com",