From 35f2a657ead86bd93184ff6c282a49eaa717f6ff Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 5 Dec 2019 15:17:53 +0000 Subject: [PATCH 01/19] Update ./fonttools script to use Python 3 --- fonttools | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fonttools b/fonttools index e914367aa..90bc63f99 100755 --- a/fonttools +++ b/fonttools @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys import os.path From cfc5b44c65af8480a6ecd8de61d91fea20bc3031 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Thu, 5 Dec 2019 15:28:19 +0000 Subject: [PATCH 02/19] change all scripts' shebangs to use python3 --- MetaTools/buildTableList.py | 2 +- MetaTools/buildUCD.py | 2 +- MetaTools/roundTrip.py | 2 +- Snippets/cmap-format.py | 2 +- Snippets/fix-dflt-langsys.py | 2 +- Snippets/interpolate.py | 2 +- Snippets/layout-features.py | 2 +- Snippets/otf2ttf.py | 2 +- Snippets/rename-fonts.py | 2 +- Snippets/subset-fpgm.py | 2 +- Snippets/svg2glif.py | 2 +- setup.py | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/MetaTools/buildTableList.py b/MetaTools/buildTableList.py index 9ee164e1e..825f0db1a 100755 --- a/MetaTools/buildTableList.py +++ b/MetaTools/buildTableList.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python3 import sys import os diff --git a/MetaTools/buildUCD.py b/MetaTools/buildUCD.py index 798ab44e7..16ae150a4 100755 --- a/MetaTools/buildUCD.py +++ b/MetaTools/buildUCD.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ Tools to parse data files from the Unicode Character Database. """ diff --git a/MetaTools/roundTrip.py b/MetaTools/roundTrip.py index 648bc9d90..d02ec4a5e 100755 --- a/MetaTools/roundTrip.py +++ b/MetaTools/roundTrip.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python3 """usage: ttroundtrip [options] font1 ... fontN diff --git a/Snippets/cmap-format.py b/Snippets/cmap-format.py index 16bd6a323..369df0009 100755 --- a/Snippets/cmap-format.py +++ b/Snippets/cmap-format.py @@ -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 diff --git a/Snippets/fix-dflt-langsys.py b/Snippets/fix-dflt-langsys.py index d8eccb440..c072117a7 100644 --- a/Snippets/fix-dflt-langsys.py +++ b/Snippets/fix-dflt-langsys.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import argparse import logging diff --git a/Snippets/interpolate.py b/Snippets/interpolate.py index 5802d049d..ca4498542 100755 --- a/Snippets/interpolate.py +++ b/Snippets/interpolate.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python3 # Illustrates how a fonttools script can construct variable fonts. # diff --git a/Snippets/layout-features.py b/Snippets/layout-features.py index fb90882b2..60ed20d33 100755 --- a/Snippets/layout-features.py +++ b/Snippets/layout-features.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python3 from fontTools.misc.py23 import * from fontTools.ttLib import TTFont diff --git a/Snippets/otf2ttf.py b/Snippets/otf2ttf.py index ec8dada57..a144f66e9 100755 --- a/Snippets/otf2ttf.py +++ b/Snippets/otf2ttf.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import argparse import logging diff --git a/Snippets/rename-fonts.py b/Snippets/rename-fonts.py index cf87c8d0d..0a43dc2ad 100755 --- a/Snippets/rename-fonts.py +++ b/Snippets/rename-fonts.py @@ -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. diff --git a/Snippets/subset-fpgm.py b/Snippets/subset-fpgm.py index 6ba92945b..e242a77c4 100755 --- a/Snippets/subset-fpgm.py +++ b/Snippets/subset-fpgm.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python3 from fontTools.misc.py23 import * from fontTools.ttLib import TTFont diff --git a/Snippets/svg2glif.py b/Snippets/svg2glif.py index d50149c16..ca1793650 100755 --- a/Snippets/svg2glif.py +++ b/Snippets/svg2glif.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ Convert SVG paths to UFO glyphs. """ diff --git a/setup.py b/setup.py index fb017ed06..97c465399 100755 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python3 from __future__ import print_function import io From 46a06cabf2e6e4453ad98c4065aac23f819cd93a Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Fri, 6 Dec 2019 10:27:31 +0100 Subject: [PATCH 03/19] [ttLib.glyf] Make sure to use the flagOnCurve mask in glyph.draw() (#1772) * When drawing glyf outlines to a pen, make sure to use the flagOnCurve mask, so we don't trip over the overlap flag, that is set when instantiating variable fonts to indicate that overlaps are ok. Fixes #1771. --- Lib/fontTools/ttLib/tables/_g_l_y_f.py | 2 +- Tests/ttLib/tables/_g_l_y_f_test.py | 27 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py index 050e8d5dc..0f458a14b 100644 --- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py +++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py @@ -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, diff --git a/Tests/ttLib/tables/_g_l_y_f_test.py b/Tests/ttLib/tables/_g_l_y_f_test.py index 583d7672d..e17b52a56 100644 --- a/Tests/ttLib/tables/_g_l_y_f_test.py +++ b/Tests/ttLib/tables/_g_l_y_f_test.py @@ -2,6 +2,7 @@ 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 from fontTools.ttLib import TTFont, newTable, TTLibError from fontTools.ttLib.tables._g_l_y_f import ( GlyphCoordinates, @@ -284,6 +285,32 @@ 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),)), + ('lineTo', ((501, 1430),)), + ('closePath', ()), + ('moveTo', ((249, 514),)), + ('lineTo', ((935, 514),)), + ('lineTo', ((935, 352),)), + ('lineTo', ((249, 352),)), + ('lineTo', ((249, 514),)), + ('closePath', ())] + self.assertEqual(pen.value, expected) + class GlyphComponentTest: From 15732894d87e100e9b5d693585683ac9d46347a1 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Fri, 6 Dec 2019 09:42:07 +0000 Subject: [PATCH 04/19] Update changelog --- NEWS.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index 37834877b..ac41daba5 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,3 +1,6 @@ +- [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) --------------------------- From 12a1da6de384572b6efd6136171bca23161f938d Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Fri, 6 Dec 2019 09:42:18 +0000 Subject: [PATCH 05/19] Release 4.2.1 --- Lib/fontTools/__init__.py | 2 +- NEWS.rst | 3 +++ setup.cfg | 2 +- setup.py | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Lib/fontTools/__init__.py b/Lib/fontTools/__init__.py index 79759acf1..cda122167 100644 --- a/Lib/fontTools/__init__.py +++ b/Lib/fontTools/__init__.py @@ -4,6 +4,6 @@ from fontTools.misc.loggingTools import configLogger log = logging.getLogger(__name__) -version = __version__ = "4.2.1.dev0" +version = __version__ = "4.2.1" __all__ = ["version", "log", "configLogger"] diff --git a/NEWS.rst b/NEWS.rst index ac41daba5..a78982432 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,3 +1,6 @@ +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). diff --git a/setup.cfg b/setup.cfg index da9d3f3e8..f5aa577e6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 4.2.1.dev0 +current_version = 4.2.1 commit = True tag = False tag_name = {new_version} diff --git a/setup.py b/setup.py index 97c465399..8c892e128 100755 --- a/setup.py +++ b/setup.py @@ -345,7 +345,7 @@ def find_data_files(manpath="share/man"): setup( name="fonttools", - version="4.2.1.dev0", + version="4.2.1", description="Tools to manipulate font files", author="Just van Rossum", author_email="just@letterror.com", From a4b0992472c11f6fd9342f885513afc751dbca78 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Fri, 6 Dec 2019 09:42:19 +0000 Subject: [PATCH 06/19] =?UTF-8?q?Bump=20version:=204.2.1=20=E2=86=92=204.2?= =?UTF-8?q?.2.dev0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/fontTools/__init__.py | 2 +- setup.cfg | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/fontTools/__init__.py b/Lib/fontTools/__init__.py index cda122167..a5c84deef 100644 --- a/Lib/fontTools/__init__.py +++ b/Lib/fontTools/__init__.py @@ -4,6 +4,6 @@ from fontTools.misc.loggingTools import configLogger log = logging.getLogger(__name__) -version = __version__ = "4.2.1" +version = __version__ = "4.2.2.dev0" __all__ = ["version", "log", "configLogger"] diff --git a/setup.cfg b/setup.cfg index f5aa577e6..3b835e430 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 4.2.1 +current_version = 4.2.2.dev0 commit = True tag = False tag_name = {new_version} diff --git a/setup.py b/setup.py index 8c892e128..0a28ad6df 100755 --- a/setup.py +++ b/setup.py @@ -345,7 +345,7 @@ def find_data_files(manpath="share/man"): setup( name="fonttools", - version="4.2.1", + version="4.2.2.dev0", description="Tools to manipulate font files", author="Just van Rossum", author_email="just@letterror.com", From e2c60e3dcb74279732e0e78346eff90cd8f219fd Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sun, 8 Dec 2019 21:55:30 +0100 Subject: [PATCH 07/19] [ttLib.glyf] Fix flag bug in glyph.drawPoints() (#1774) This was the same problem as glyph.draw() had, as reported in #1771. --- Lib/fontTools/ttLib/tables/_g_l_y_f.py | 2 +- Tests/ttLib/tables/_g_l_y_f_test.py | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py index 0f458a14b..59bda4c89 100644 --- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py +++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py @@ -1230,7 +1230,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: diff --git a/Tests/ttLib/tables/_g_l_y_f_test.py b/Tests/ttLib/tables/_g_l_y_f_test.py index e17b52a56..389b7f750 100644 --- a/Tests/ttLib/tables/_g_l_y_f_test.py +++ b/Tests/ttLib/tables/_g_l_y_f_test.py @@ -2,7 +2,7 @@ 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 +from fontTools.pens.recordingPen import RecordingPen, RecordingPointPen from fontTools.ttLib import TTFont, newTable, TTLibError from fontTools.ttLib.tables._g_l_y_f import ( GlyphCoordinates, @@ -311,6 +311,24 @@ class glyfTableTest(unittest.TestCase): ('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) + class GlyphComponentTest: From 9a49b01ec1d9e042801fdeac18387d68851d36d2 Mon Sep 17 00:00:00 2001 From: ln_north Date: Tue, 10 Dec 2019 09:39:53 +0900 Subject: [PATCH 08/19] fix requirements in svg2glif.py - Remove unused `ufoLib` package - FontTools => fontTools --- Snippets/svg2glif.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Snippets/svg2glif.py b/Snippets/svg2glif.py index ca1793650..22fcc7d1f 100755 --- a/Snippets/svg2glif.py +++ b/Snippets/svg2glif.py @@ -2,7 +2,7 @@ """ Convert SVG paths to UFO glyphs. """ -__requires__ = ["FontTools", "ufoLib"] +__requires__ = ["fontTools"] from fontTools.misc.py23 import SimpleNamespace from fontTools.svgLib import SVGPath From 2d33117c95d4bb3802b7bab4c436d97b01f1c00f Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler Date: Thu, 12 Dec 2019 11:32:13 +0000 Subject: [PATCH 09/19] Legible error message for axis mapping duplicate check --- Lib/fontTools/varLib/__init__.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Lib/fontTools/varLib/__init__.py b/Lib/fontTools/varLib/__init__.py index 66dfa3572..b428580d6 100644 --- a/Lib/fontTools/varLib/__init__.py +++ b/Lib/fontTools/varLib/__init__.py @@ -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 From e821f1fb9eb6025cbce8ffd2c3dcf83c8c04ced5 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Thu, 12 Dec 2019 11:54:58 +0000 Subject: [PATCH 10/19] fontBuilder: add FontBuilder.addFeatureVariations method like the existing addOpenTypeFeatures it simply forwards to featureVars.addFeatureVariations --- Lib/fontTools/fontBuilder.py | 14 +++ Tests/fontBuilder/data/test_var.ttf.ttx | 112 ++++++++++++++++++++++-- Tests/fontBuilder/fontBuilder_test.py | 24 ++++- 3 files changed, 139 insertions(+), 11 deletions(-) diff --git a/Lib/fontTools/fontBuilder.py b/Lib/fontTools/fontBuilder.py index bd6b2e50e..29d58fb6f 100644 --- a/Lib/fontTools/fontBuilder.py +++ b/Lib/fontTools/fontBuilder.py @@ -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) diff --git a/Tests/fontBuilder/data/test_var.ttf.ttx b/Tests/fontBuilder/data/test_var.ttf.ttx index 382d29e10..bc1aae250 100644 --- a/Tests/fontBuilder/data/test_var.ttf.ttx +++ b/Tests/fontBuilder/data/test_var.ttf.ttx @@ -1,5 +1,5 @@ - + @@ -19,7 +19,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -126,7 +126,7 @@ - + @@ -164,12 +164,12 @@ - + - - - - + + + + @@ -269,6 +269,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tests/fontBuilder/fontBuilder_test.py b/Tests/fontBuilder/fontBuilder_test.py index e13f11616..8c5127626 100644 --- a/Tests/fontBuilder/fontBuilder_test.py +++ b/Tests/fontBuilder/fontBuilder_test.py @@ -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() From 0d0fe2faace4e50d63037bd037a0717906c285b4 Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler Date: Thu, 12 Dec 2019 12:09:16 +0000 Subject: [PATCH 11/19] Add NEWS entry [ci skip] --- NEWS.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index a78982432..a3c1bfbf4 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,3 +1,6 @@ +- [varLib] Added legible error messages to the ``_add_avar()`` axis mapping duplicate + check. + 4.2.1 (released 2019-12-06) --------------------------- From 9c0c3a1375e9d9b6d9df4f43e942ed20672a4e3f Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Thu, 12 Dec 2019 12:29:08 +0000 Subject: [PATCH 12/19] subset_test: Add (failing) test to repro #1777 the 'rvrn' feature is being incorrectly dropped because the feature indexes change if some other feature occuring before 'rvrn' is dropped, like in this test case. --- Tests/subset/subset_test.py | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Tests/subset/subset_test.py b/Tests/subset/subset_test.py index 42d302151..3d8c27ecf 100644 --- a/Tests/subset/subset_test.py +++ b/Tests/subset/subset_test.py @@ -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()) From d68a59a2f255fbcb58c484213fb7c49ab498ffb8 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Thu, 12 Dec 2019 12:38:31 +0000 Subject: [PATCH 13/19] subset: remap FeatureVariations SubstitutionRecord.FeatureIndex Fixes #1777 --- Lib/fontTools/subset/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Lib/fontTools/subset/__init__.py b/Lib/fontTools/subset/__init__.py index 581c3948a..7205c347e 100644 --- a/Lib/fontTools/subset/__init__.py +++ b/Lib/fontTools/subset/__init__.py @@ -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) From cf0e43d6e574b12fbb942108385e6ff6eaa4cd58 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Thu, 12 Dec 2019 13:44:10 +0100 Subject: [PATCH 14/19] [ttLib.glyf] make glyph.draw() skip redundant final lineTo() (#1775) * [ttLib.glyf] make glyph.draw() skip redundant final lineTo() This ensures that g.draw(pen) and g.drawPoints(PointToSegmentPen(pen)) are now 100% equivalent. --- Lib/fontTools/ttLib/tables/_g_l_y_f.py | 5 ++++- Tests/ttLib/tables/_g_l_y_f_test.py | 13 +++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py index 59bda4c89..cc22ad0a8 100644 --- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py +++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py @@ -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:] diff --git a/Tests/ttLib/tables/_g_l_y_f_test.py b/Tests/ttLib/tables/_g_l_y_f_test.py index 389b7f750..9e2b3555e 100644 --- a/Tests/ttLib/tables/_g_l_y_f_test.py +++ b/Tests/ttLib/tables/_g_l_y_f_test.py @@ -3,6 +3,7 @@ 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, @@ -301,13 +302,11 @@ class glyfTableTest(unittest.TestCase): ('lineTo', ((591, 1193),)), ('lineTo', ((199, 0),)), ('lineTo', ((12, 0),)), - ('lineTo', ((501, 1430),)), ('closePath', ()), ('moveTo', ((249, 514),)), ('lineTo', ((935, 514),)), ('lineTo', ((935, 352),)), ('lineTo', ((249, 352),)), - ('lineTo', ((249, 514),)), ('closePath', ())] self.assertEqual(pen.value, expected) @@ -329,6 +328,16 @@ class glyfTableTest(unittest.TestCase): ] 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: From 0649663ebc45d7a1b7883d2833ce0612e87c38ef Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Thu, 12 Dec 2019 16:48:42 +0000 Subject: [PATCH 15/19] Update changelog --- NEWS.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/NEWS.rst b/NEWS.rst index a3c1bfbf4..02a6a3d21 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,5 +1,11 @@ -- [varLib] Added legible error messages to the ``_add_avar()`` axis mapping duplicate - check. +- [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) --------------------------- From 632e065bcd8990912c68c9d5da7b96b5ca860146 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Thu, 12 Dec 2019 16:48:52 +0000 Subject: [PATCH 16/19] Release 4.2.2 --- Lib/fontTools/__init__.py | 2 +- NEWS.rst | 3 +++ setup.cfg | 2 +- setup.py | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Lib/fontTools/__init__.py b/Lib/fontTools/__init__.py index a5c84deef..5f0d28327 100644 --- a/Lib/fontTools/__init__.py +++ b/Lib/fontTools/__init__.py @@ -4,6 +4,6 @@ from fontTools.misc.loggingTools import configLogger log = logging.getLogger(__name__) -version = __version__ = "4.2.2.dev0" +version = __version__ = "4.2.2" __all__ = ["version", "log", "configLogger"] diff --git a/NEWS.rst b/NEWS.rst index 02a6a3d21..1a0698a4a 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,3 +1,6 @@ +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). diff --git a/setup.cfg b/setup.cfg index 3b835e430..d3c0ef7b4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 4.2.2.dev0 +current_version = 4.2.2 commit = True tag = False tag_name = {new_version} diff --git a/setup.py b/setup.py index 0a28ad6df..06a05f153 100755 --- a/setup.py +++ b/setup.py @@ -345,7 +345,7 @@ def find_data_files(manpath="share/man"): setup( name="fonttools", - version="4.2.2.dev0", + version="4.2.2", description="Tools to manipulate font files", author="Just van Rossum", author_email="just@letterror.com", From fd606ae813f1ed3f8ea42f487968fd036eb06d8a Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Thu, 12 Dec 2019 16:48:53 +0000 Subject: [PATCH 17/19] =?UTF-8?q?Bump=20version:=204.2.2=20=E2=86=92=204.2?= =?UTF-8?q?.3.dev0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/fontTools/__init__.py | 2 +- setup.cfg | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/fontTools/__init__.py b/Lib/fontTools/__init__.py index 5f0d28327..0db40fcf7 100644 --- a/Lib/fontTools/__init__.py +++ b/Lib/fontTools/__init__.py @@ -4,6 +4,6 @@ from fontTools.misc.loggingTools import configLogger log = logging.getLogger(__name__) -version = __version__ = "4.2.2" +version = __version__ = "4.2.3.dev0" __all__ = ["version", "log", "configLogger"] diff --git a/setup.cfg b/setup.cfg index d3c0ef7b4..64283d5bf 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 4.2.2 +current_version = 4.2.3.dev0 commit = True tag = False tag_name = {new_version} diff --git a/setup.py b/setup.py index 06a05f153..a188d599a 100755 --- a/setup.py +++ b/setup.py @@ -345,7 +345,7 @@ def find_data_files(manpath="share/man"): setup( name="fonttools", - version="4.2.2", + version="4.2.3.dev0", description="Tools to manipulate font files", author="Just van Rossum", author_email="just@letterror.com", From 70c3eccb4efe500b33c8d5f41308b93cb1bd453b Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Mon, 16 Dec 2019 12:01:40 +0000 Subject: [PATCH 18/19] glifLib_test: test xml containing --- Tests/ufoLib/glifLib_test.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Tests/ufoLib/glifLib_test.py b/Tests/ufoLib/glifLib_test.py index 06aca9729..a29c76bf4 100644 --- a/Tests/ufoLib/glifLib_test.py +++ b/Tests/ufoLib/glifLib_test.py @@ -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""" + + + + + + + """ + + g = _Glyph() + readGlyphFromString(s, g) + + assert g.name == "A" + assert g.width == 1290 + assert g.unicodes == [0x0041] From 337bb662119e19026e56c2913e15c1d03e339f2d Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Mon, 16 Dec 2019 12:05:13 +0000 Subject: [PATCH 19/19] glifLib: strip comments when parsing with lxml they are already ignored when parsing via built-in ElementTree --- Lib/fontTools/ufoLib/glifLib.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Lib/fontTools/ufoLib/glifLib.py b/Lib/fontTools/ufoLib/glifLib.py index 9a83fee40..b18ac4897 100755 --- a/Lib/fontTools/ufoLib/glifLib.py +++ b/Lib/fontTools/ufoLib/glifLib.py @@ -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() != '':