diff options
author | Haibo Huang <hhb@google.com> | 2019-07-11 12:34:30 -0700 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2019-07-11 12:34:30 -0700 |
commit | a43c21305607a1870f05f962a2e5db5d99dace7e (patch) | |
tree | 9004cd003599c03e7bf821adff6983de7ad156a4 | |
parent | d24a6dde80fac8ec51afcc4f7ae86d83b717b39e (diff) | |
parent | 70a8d12c5e7a898721d38e40fa5e6030cf11490a (diff) | |
download | fonttools-a43c21305607a1870f05f962a2e5db5d99dace7e.tar.gz |
Upgrade fonttools to 3.43.2 am: 3422e6b762 am: b58205c46d
am: 70a8d12c5e
Change-Id: I13e043ece60dfbdb96c1b4b93ddf3b5c1fe53b78
-rw-r--r-- | Lib/fontTools/__init__.py | 2 | ||||
-rw-r--r-- | Lib/fontTools/varLib/__init__.py | 56 | ||||
-rw-r--r--[-rwxr-xr-x] | Lib/fontTools/varLib/cff.py | 31 | ||||
-rw-r--r-- | Lib/fontTools/varLib/featureVars.py | 6 | ||||
-rw-r--r-- | Lib/fonttools.egg-info/PKG-INFO | 18 | ||||
-rw-r--r-- | METADATA | 8 | ||||
-rw-r--r-- | NEWS.rst | 6 | ||||
-rw-r--r-- | PKG-INFO | 18 | ||||
-rw-r--r-- | Tests/otlLib/builder_test.py | 1711 | ||||
-rw-r--r-- | Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w0.00.ttx | 1 | ||||
-rw-r--r-- | Tests/varLib/data/test_results/BuildMain.ttx | 2 | ||||
-rw-r--r-- | Tests/varLib/data/test_results/BuildTestCFF2.ttx | 4 | ||||
-rw-r--r-- | Tests/varLib/data/test_results/SparseMasters.ttx | 2 | ||||
-rw-r--r-- | Tests/varLib/data/test_results/TestNonMarkingCFF2.ttx | 4 | ||||
-rw-r--r-- | Tests/varLib/data/test_results/TestSparseCFF2VF.ttx | 1 | ||||
-rw-r--r-- | Tests/varLib/varLib_test.py | 77 | ||||
-rw-r--r-- | setup.cfg | 2 | ||||
-rwxr-xr-x | setup.py | 2 |
18 files changed, 1079 insertions, 872 deletions
diff --git a/Lib/fontTools/__init__.py b/Lib/fontTools/__init__.py index d6919b28..1a49c104 100644 --- a/Lib/fontTools/__init__.py +++ b/Lib/fontTools/__init__.py @@ -5,6 +5,6 @@ from fontTools.misc.loggingTools import configLogger log = logging.getLogger(__name__) -version = __version__ = "3.43.1" +version = __version__ = "3.43.2" __all__ = ["version", "log", "configLogger"] diff --git a/Lib/fontTools/varLib/__init__.py b/Lib/fontTools/varLib/__init__.py index 891e92c5..b428aeb1 100644 --- a/Lib/fontTools/varLib/__init__.py +++ b/Lib/fontTools/varLib/__init__.py @@ -23,8 +23,7 @@ from __future__ import unicode_literals from fontTools.misc.py23 import * from fontTools.misc.fixedTools import otRound from fontTools.misc.arrayTools import Vector -from fontTools.ttLib import TTFont, newTable, TTLibError -from fontTools.ttLib.tables._n_a_m_e import NameRecord +from fontTools.ttLib import TTFont, newTable from fontTools.ttLib.tables._f_v_a_r import Axis, NamedInstance from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates from fontTools.ttLib.tables.ttProgram import Program @@ -36,7 +35,7 @@ from fontTools.varLib.merger import VariationMerger from fontTools.varLib.mvar import MVAR_ENTRIES from fontTools.varLib.iup import iup_delta_optimize from fontTools.varLib.featureVars import addFeatureVariations -from fontTools.designspaceLib import DesignSpaceDocument, AxisDescriptor +from fontTools.designspaceLib import DesignSpaceDocument from collections import OrderedDict, namedtuple import os.path import logging @@ -857,6 +856,45 @@ def load_designspace(designspace): ) +# https://docs.microsoft.com/en-us/typography/opentype/spec/os2#uswidthclass +WDTH_VALUE_TO_OS2_WIDTH_CLASS = { + 50: 1, + 62.5: 2, + 75: 3, + 87.5: 4, + 100: 5, + 112.5: 6, + 125: 7, + 150: 8, + 200: 9, +} + + +def set_default_weight_width_slant(font, location): + if "OS/2" in font: + if "wght" in location: + weight_class = otRound(max(1, min(location["wght"], 1000))) + if font["OS/2"].usWeightClass != weight_class: + log.info("Setting OS/2.usWidthClass = %s", weight_class) + font["OS/2"].usWeightClass = weight_class + + if "wdth" in location: + # map 'wdth' axis (50..200) to OS/2.usWidthClass (1..9), rounding to closest + widthValue = min(max(location["wdth"], 50), 200) + widthClass = otRound( + models.piecewiseLinearMap(widthValue, WDTH_VALUE_TO_OS2_WIDTH_CLASS) + ) + if font["OS/2"].usWidthClass != widthClass: + log.info("Setting OS/2.usWidthClass = %s", widthClass) + font["OS/2"].usWidthClass = widthClass + + if "slnt" in location and "post" in font: + italicAngle = max(-90, min(location["slnt"], 90)) + if font["post"].italicAngle != italicAngle: + log.info("Setting post.italicAngle = %s", italicAngle) + font["post"].italicAngle = italicAngle + + def build(designspace, master_finder=lambda s:s, exclude=[], optimize=True): """ Build variation font from a designspace file. @@ -930,6 +968,10 @@ def build(designspace, master_finder=lambda s:s, exclude=[], optimize=True): post.extraNames = [] post.mapping = {} + set_default_weight_width_slant( + vf, location={axis.axisTag: axis.defaultValue for axis in vf["fvar"].axes} + ) + for tag in exclude: if tag in vf: del vf[tag] @@ -1064,9 +1106,6 @@ def main(args=None): designspace_filename = options.designspace finder = MasterFinder(options.master_finder) - outfile = options.outfile - if outfile is None: - outfile = os.path.splitext(designspace_filename)[0] + '-VF.ttf' vf, _, _ = build( designspace_filename, @@ -1075,6 +1114,11 @@ def main(args=None): optimize=options.optimize ) + outfile = options.outfile + if outfile is None: + ext = "otf" if vf.sfntVersion == "OTTO" else "ttf" + outfile = os.path.splitext(designspace_filename)[0] + '-VF.' + ext + log.info("Saving variation font %s", outfile) vf.save(outfile) diff --git a/Lib/fontTools/varLib/cff.py b/Lib/fontTools/varLib/cff.py index bcafdeee..aaabf857 100755..100644 --- a/Lib/fontTools/varLib/cff.py +++ b/Lib/fontTools/varLib/cff.py @@ -131,7 +131,7 @@ class MergeDictError(TypeError): def conv_to_int(num): - if num % 1 == 0: + if isinstance(num, float) and num.is_integer(): return int(num) return num @@ -198,17 +198,17 @@ def merge_PrivateDicts(top_dicts, vsindex_dict, var_model, fd_map): pds.append(pd) num_masters = len(pds) for key, value in private_dict.rawDict.items(): + dataList = [] if key not in pd_blend_fields: continue if isinstance(value, list): try: values = [pd.rawDict[key] for pd in pds] except KeyError: - del private_dict.rawDict[key] print( - b"Warning: {key} in default font Private dict is " - b"missing from another font, and was " - b"discarded.".format(key=key)) + "Warning: {key} in default font Private dict is " + "missing from another font, and was " + "discarded.".format(key=key)) continue try: values = zip(*values) @@ -227,7 +227,6 @@ def merge_PrivateDicts(top_dicts, vsindex_dict, var_model, fd_map): and is converted finally to: OtherBlues = [[-217, 17.0, 46.0], [-205, 0.0, 0.0]] """ - dataList = [] prev_val_list = [0] * num_masters any_points_differ = False for val_list in values: @@ -237,8 +236,6 @@ def merge_PrivateDicts(top_dicts, vsindex_dict, var_model, fd_map): any_points_differ = True prev_val_list = val_list deltas = sub_model.getDeltas(rel_list) - # Convert numbers with no decimal part to an int. - deltas = [conv_to_int(delta) for delta in deltas] # For PrivateDict BlueValues, the default font # values are absolute, not relative to the prior value. deltas[0] = val_list[0] @@ -253,6 +250,18 @@ def merge_PrivateDicts(top_dicts, vsindex_dict, var_model, fd_map): dataList = sub_model.getDeltas(values) else: dataList = values[0] + + # Convert numbers with no decimal part to an int + if isinstance(dataList, list): + for i, item in enumerate(dataList): + if isinstance(item, list): + for j, jtem in enumerate(item): + dataList[i][j] = conv_to_int(jtem) + else: + dataList[i] = conv_to_int(item) + else: + dataList = conv_to_int(dataList) + private_dict.rawDict[key] = dataList @@ -270,7 +279,7 @@ def getfd_map(varFont, fonts_list): num_regions = len(region_fonts) topDict = default_font['CFF '].cff.topDictIndex[0] if not hasattr(topDict, 'FDSelect'): - # All glyphs reference only one FontDict. + # All glyphs reference only one FontDict. # Map the FD index for regions to index 0. fd_map[0] = {ri:0 for ri in range(num_regions)} return fd_map @@ -390,9 +399,9 @@ def merge_charstrings(glyphOrder, num_masters, top_dicts, masterModel): # as we know it doesn't exist yet. if vsindex != 0: new_cs.program[:0] = [vsindex, 'vsindex'] - + # If there is no variation in any of the charstrings, then vsindex_dict - # never gets built. This could still be needed if there is variation + # never gets built. This could still be needed if there is variation # in the PrivatDict, so we will build the default data for vsindex = 0. if not vsindex_dict: key = (True,) * num_masters diff --git a/Lib/fontTools/varLib/featureVars.py b/Lib/fontTools/varLib/featureVars.py index 4caf30a2..e3aa6b6e 100644 --- a/Lib/fontTools/varLib/featureVars.py +++ b/Lib/fontTools/varLib/featureVars.py @@ -71,7 +71,7 @@ def overlayFeatureVariations(conditionalSubstitutions): and rules with the same Box merged. The more specific rules appear earlier in the resulting list. Moreover, instead of just a dictionary of substitutions, a list of dictionaries is returned for substitutions corresponding to each - uniq space, with each dictionary being identical to one of the input + unique space, with each dictionary being identical to one of the input substitution dictionaries. These dictionaries are not merged to allow data sharing when they are converted into font tables. @@ -79,6 +79,7 @@ def overlayFeatureVariations(conditionalSubstitutions): >>> condSubst = [ ... # A list of (Region, Substitution) tuples. ... ([{"wght": (0.5, 1.0)}], {"dollar": "dollar.rvrn"}), + ... ([{"wght": (0.5, 1.0)}], {"dollar": "dollar.rvrn"}), ... ([{"wdth": (0.5, 1.0)}], {"cent": "cent.rvrn"}), ... ] >>> from pprint import pprint @@ -107,7 +108,8 @@ def overlayFeatureVariations(conditionalSubstitutions): # rules for the same region. merged = OrderedDict() for key,value in reversed(conditionalSubstitutions): - key = tuple(sorted(hashdict(cleanupBox(k)) for k in key)) + key = tuple(sorted((hashdict(cleanupBox(k)) for k in key), + key=lambda d: tuple(sorted(d.items())))) if key in merged: merged[key].update(value) else: diff --git a/Lib/fonttools.egg-info/PKG-INFO b/Lib/fonttools.egg-info/PKG-INFO index 74bb40d0..2dc1e573 100644 --- a/Lib/fonttools.egg-info/PKG-INFO +++ b/Lib/fonttools.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: fonttools -Version: 3.43.1 +Version: 3.43.2 Summary: Tools to manipulate font files Home-page: http://github.com/fonttools/fonttools Author: Just van Rossum @@ -415,6 +415,12 @@ Description: |Travis Build Status| |Appveyor Build status| |Coverage Status| |Py Changelog ~~~~~~~~~ + 3.43.2 (released 2019-07-10) + ---------------------------- + + - [featureVars] Fixed region-merging code on python3 (#1659). + - [varLib.cff] Fixed merging of sparse PrivateDict items (#1653). + 3.43.1 (released 2019-06-19) ---------------------------- @@ -1787,13 +1793,13 @@ Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Text Processing :: Fonts Classifier: Topic :: Multimedia :: Graphics Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion +Provides-Extra: plot +Provides-Extra: unicode +Provides-Extra: all Provides-Extra: lxml Provides-Extra: interpolatable -Provides-Extra: unicode +Provides-Extra: woff +Provides-Extra: symfont Provides-Extra: type1 -Provides-Extra: all Provides-Extra: ufo Provides-Extra: graphite -Provides-Extra: woff -Provides-Extra: plot -Provides-Extra: symfont @@ -7,12 +7,12 @@ third_party { } url { type: ARCHIVE - value: "https://github.com/fonttools/fonttools/releases/download/3.43.1/fonttools-3.43.1.zip" + value: "https://github.com/fonttools/fonttools/releases/download/3.43.2/fonttools-3.43.2.zip" } - version: "3.43.1" + version: "3.43.2" last_upgrade_date { year: 2019 - month: 6 - day: 19 + month: 7 + day: 10 } } @@ -1,3 +1,9 @@ +3.43.2 (released 2019-07-10) +---------------------------- + +- [featureVars] Fixed region-merging code on python3 (#1659). +- [varLib.cff] Fixed merging of sparse PrivateDict items (#1653). + 3.43.1 (released 2019-06-19) ---------------------------- @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: fonttools -Version: 3.43.1 +Version: 3.43.2 Summary: Tools to manipulate font files Home-page: http://github.com/fonttools/fonttools Author: Just van Rossum @@ -415,6 +415,12 @@ Description: |Travis Build Status| |Appveyor Build status| |Coverage Status| |Py Changelog ~~~~~~~~~ + 3.43.2 (released 2019-07-10) + ---------------------------- + + - [featureVars] Fixed region-merging code on python3 (#1659). + - [varLib.cff] Fixed merging of sparse PrivateDict items (#1653). + 3.43.1 (released 2019-06-19) ---------------------------- @@ -1787,13 +1793,13 @@ Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Text Processing :: Fonts Classifier: Topic :: Multimedia :: Graphics Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion +Provides-Extra: plot +Provides-Extra: unicode +Provides-Extra: all Provides-Extra: lxml Provides-Extra: interpolatable -Provides-Extra: unicode +Provides-Extra: woff +Provides-Extra: symfont Provides-Extra: type1 -Provides-Extra: all Provides-Extra: ufo Provides-Extra: graphite -Provides-Extra: woff -Provides-Extra: plot -Provides-Extra: symfont diff --git a/Tests/otlLib/builder_test.py b/Tests/otlLib/builder_test.py index 0829176c..83f7c828 100644 --- a/Tests/otlLib/builder_test.py +++ b/Tests/otlLib/builder_test.py @@ -3,1011 +3,1071 @@ from __future__ import unicode_literals from fontTools.misc.testTools import getXML from fontTools.otlLib import builder from fontTools.ttLib.tables import otTables -from itertools import chain -import unittest +import pytest -class BuilderTest(unittest.TestCase): - GLYPHS = (".notdef space zero one two three four five six " - "A B C a b c grave acute cedilla f_f_i f_i c_t").split() +class BuilderTest(object): + GLYPHS = ( + ".notdef space zero one two three four five six " + "A B C a b c grave acute cedilla f_f_i f_i c_t" + ).split() GLYPHMAP = {name: num for num, name in enumerate(GLYPHS)} ANCHOR1 = builder.buildAnchor(11, -11) ANCHOR2 = builder.buildAnchor(22, -22) ANCHOR3 = builder.buildAnchor(33, -33) - def __init__(self, methodName): - unittest.TestCase.__init__(self, methodName) - # Python 3 renamed assertRaisesRegexp to assertRaisesRegex, - # and fires deprecation warnings if a program uses the old name. - if not hasattr(self, "assertRaisesRegex"): - self.assertRaisesRegex = self.assertRaisesRegexp - - @classmethod - def setUpClass(cls): - cls.maxDiff = None - def test_buildAnchor_format1(self): anchor = builder.buildAnchor(23, 42) - self.assertEqual(getXML(anchor.toXML), - ['<Anchor Format="1">', - ' <XCoordinate value="23"/>', - ' <YCoordinate value="42"/>', - '</Anchor>']) + assert getXML(anchor.toXML) == [ + '<Anchor Format="1">', + ' <XCoordinate value="23"/>', + ' <YCoordinate value="42"/>', + "</Anchor>", + ] def test_buildAnchor_format2(self): anchor = builder.buildAnchor(23, 42, point=17) - self.assertEqual(getXML(anchor.toXML), - ['<Anchor Format="2">', - ' <XCoordinate value="23"/>', - ' <YCoordinate value="42"/>', - ' <AnchorPoint value="17"/>', - '</Anchor>']) + assert getXML(anchor.toXML) == [ + '<Anchor Format="2">', + ' <XCoordinate value="23"/>', + ' <YCoordinate value="42"/>', + ' <AnchorPoint value="17"/>', + "</Anchor>", + ] def test_buildAnchor_format3(self): anchor = builder.buildAnchor( - 23, 42, + 23, + 42, deviceX=builder.buildDevice({1: 1, 0: 0}), - deviceY=builder.buildDevice({7: 7})) - self.assertEqual(getXML(anchor.toXML), - ['<Anchor Format="3">', - ' <XCoordinate value="23"/>', - ' <YCoordinate value="42"/>', - ' <XDeviceTable>', - ' <StartSize value="0"/>', - ' <EndSize value="1"/>', - ' <DeltaFormat value="1"/>', - ' <DeltaValue value="[0, 1]"/>', - ' </XDeviceTable>', - ' <YDeviceTable>', - ' <StartSize value="7"/>', - ' <EndSize value="7"/>', - ' <DeltaFormat value="2"/>', - ' <DeltaValue value="[7]"/>', - ' </YDeviceTable>', - '</Anchor>']) + deviceY=builder.buildDevice({7: 7}), + ) + assert getXML(anchor.toXML) == [ + '<Anchor Format="3">', + ' <XCoordinate value="23"/>', + ' <YCoordinate value="42"/>', + " <XDeviceTable>", + ' <StartSize value="0"/>', + ' <EndSize value="1"/>', + ' <DeltaFormat value="1"/>', + ' <DeltaValue value="[0, 1]"/>', + " </XDeviceTable>", + " <YDeviceTable>", + ' <StartSize value="7"/>', + ' <EndSize value="7"/>', + ' <DeltaFormat value="2"/>', + ' <DeltaValue value="[7]"/>', + " </YDeviceTable>", + "</Anchor>", + ] def test_buildAttachList(self): - attachList = builder.buildAttachList({ - "zero": [23, 7], "one": [1], - }, self.GLYPHMAP) - self.assertEqual(getXML(attachList.toXML), - ['<AttachList>', - ' <Coverage>', - ' <Glyph value="zero"/>', - ' <Glyph value="one"/>', - ' </Coverage>', - ' <!-- GlyphCount=2 -->', - ' <AttachPoint index="0">', - ' <!-- PointCount=2 -->', - ' <PointIndex index="0" value="7"/>', - ' <PointIndex index="1" value="23"/>', - ' </AttachPoint>', - ' <AttachPoint index="1">', - ' <!-- PointCount=1 -->', - ' <PointIndex index="0" value="1"/>', - ' </AttachPoint>', - '</AttachList>']) + attachList = builder.buildAttachList( + {"zero": [23, 7], "one": [1]}, self.GLYPHMAP + ) + assert getXML(attachList.toXML) == [ + "<AttachList>", + " <Coverage>", + ' <Glyph value="zero"/>', + ' <Glyph value="one"/>', + " </Coverage>", + " <!-- GlyphCount=2 -->", + ' <AttachPoint index="0">', + " <!-- PointCount=2 -->", + ' <PointIndex index="0" value="7"/>', + ' <PointIndex index="1" value="23"/>', + " </AttachPoint>", + ' <AttachPoint index="1">', + " <!-- PointCount=1 -->", + ' <PointIndex index="0" value="1"/>', + " </AttachPoint>", + "</AttachList>", + ] def test_buildAttachList_empty(self): - self.assertIsNone(builder.buildAttachList({}, self.GLYPHMAP)) + assert builder.buildAttachList({}, self.GLYPHMAP) is None def test_buildAttachPoint(self): attachPoint = builder.buildAttachPoint([7, 3]) - self.assertEqual(getXML(attachPoint.toXML), - ['<AttachPoint>', - ' <!-- PointCount=2 -->', - ' <PointIndex index="0" value="3"/>', - ' <PointIndex index="1" value="7"/>', - '</AttachPoint>']) + assert getXML(attachPoint.toXML) == [ + "<AttachPoint>", + " <!-- PointCount=2 -->", + ' <PointIndex index="0" value="3"/>', + ' <PointIndex index="1" value="7"/>', + "</AttachPoint>", + ] def test_buildAttachPoint_empty(self): - self.assertIsNone(builder.buildAttachPoint([])) + assert builder.buildAttachPoint([]) is None def test_buildAttachPoint_duplicate(self): attachPoint = builder.buildAttachPoint([7, 3, 7]) - self.assertEqual(getXML(attachPoint.toXML), - ['<AttachPoint>', - ' <!-- PointCount=2 -->', - ' <PointIndex index="0" value="3"/>', - ' <PointIndex index="1" value="7"/>', - '</AttachPoint>']) - + assert getXML(attachPoint.toXML) == [ + "<AttachPoint>", + " <!-- PointCount=2 -->", + ' <PointIndex index="0" value="3"/>', + ' <PointIndex index="1" value="7"/>', + "</AttachPoint>", + ] def test_buildBaseArray(self): anchor = builder.buildAnchor - baseArray = builder.buildBaseArray({ - "a": {2: anchor(300, 80)}, - "c": {1: anchor(300, 80), 2: anchor(300, -20)} - }, numMarkClasses=4, glyphMap=self.GLYPHMAP) - self.assertEqual(getXML(baseArray.toXML), - ['<BaseArray>', - ' <!-- BaseCount=2 -->', - ' <BaseRecord index="0">', - ' <BaseAnchor index="0" empty="1"/>', - ' <BaseAnchor index="1" empty="1"/>', - ' <BaseAnchor index="2" Format="1">', - ' <XCoordinate value="300"/>', - ' <YCoordinate value="80"/>', - ' </BaseAnchor>', - ' <BaseAnchor index="3" empty="1"/>', - ' </BaseRecord>', - ' <BaseRecord index="1">', - ' <BaseAnchor index="0" empty="1"/>', - ' <BaseAnchor index="1" Format="1">', - ' <XCoordinate value="300"/>', - ' <YCoordinate value="80"/>', - ' </BaseAnchor>', - ' <BaseAnchor index="2" Format="1">', - ' <XCoordinate value="300"/>', - ' <YCoordinate value="-20"/>', - ' </BaseAnchor>', - ' <BaseAnchor index="3" empty="1"/>', - ' </BaseRecord>', - '</BaseArray>']) + baseArray = builder.buildBaseArray( + {"a": {2: anchor(300, 80)}, "c": {1: anchor(300, 80), 2: anchor(300, -20)}}, + numMarkClasses=4, + glyphMap=self.GLYPHMAP, + ) + assert getXML(baseArray.toXML) == [ + "<BaseArray>", + " <!-- BaseCount=2 -->", + ' <BaseRecord index="0">', + ' <BaseAnchor index="0" empty="1"/>', + ' <BaseAnchor index="1" empty="1"/>', + ' <BaseAnchor index="2" Format="1">', + ' <XCoordinate value="300"/>', + ' <YCoordinate value="80"/>', + " </BaseAnchor>", + ' <BaseAnchor index="3" empty="1"/>', + " </BaseRecord>", + ' <BaseRecord index="1">', + ' <BaseAnchor index="0" empty="1"/>', + ' <BaseAnchor index="1" Format="1">', + ' <XCoordinate value="300"/>', + ' <YCoordinate value="80"/>', + " </BaseAnchor>", + ' <BaseAnchor index="2" Format="1">', + ' <XCoordinate value="300"/>', + ' <YCoordinate value="-20"/>', + " </BaseAnchor>", + ' <BaseAnchor index="3" empty="1"/>', + " </BaseRecord>", + "</BaseArray>", + ] def test_buildBaseRecord(self): a = builder.buildAnchor rec = builder.buildBaseRecord([a(500, -20), None, a(300, -15)]) - self.assertEqual(getXML(rec.toXML), - ['<BaseRecord>', - ' <BaseAnchor index="0" Format="1">', - ' <XCoordinate value="500"/>', - ' <YCoordinate value="-20"/>', - ' </BaseAnchor>', - ' <BaseAnchor index="1" empty="1"/>', - ' <BaseAnchor index="2" Format="1">', - ' <XCoordinate value="300"/>', - ' <YCoordinate value="-15"/>', - ' </BaseAnchor>', - '</BaseRecord>']) + assert getXML(rec.toXML) == [ + "<BaseRecord>", + ' <BaseAnchor index="0" Format="1">', + ' <XCoordinate value="500"/>', + ' <YCoordinate value="-20"/>', + " </BaseAnchor>", + ' <BaseAnchor index="1" empty="1"/>', + ' <BaseAnchor index="2" Format="1">', + ' <XCoordinate value="300"/>', + ' <YCoordinate value="-15"/>', + " </BaseAnchor>", + "</BaseRecord>", + ] def test_buildCaretValueForCoord(self): caret = builder.buildCaretValueForCoord(500) - self.assertEqual(getXML(caret.toXML), - ['<CaretValue Format="1">', - ' <Coordinate value="500"/>', - '</CaretValue>']) + assert getXML(caret.toXML) == [ + '<CaretValue Format="1">', + ' <Coordinate value="500"/>', + "</CaretValue>", + ] def test_buildCaretValueForPoint(self): caret = builder.buildCaretValueForPoint(23) - self.assertEqual(getXML(caret.toXML), - ['<CaretValue Format="2">', - ' <CaretValuePoint value="23"/>', - '</CaretValue>']) + assert getXML(caret.toXML) == [ + '<CaretValue Format="2">', + ' <CaretValuePoint value="23"/>', + "</CaretValue>", + ] def test_buildComponentRecord(self): a = builder.buildAnchor rec = builder.buildComponentRecord([a(500, -20), None, a(300, -15)]) - self.assertEqual(getXML(rec.toXML), - ['<ComponentRecord>', - ' <LigatureAnchor index="0" Format="1">', - ' <XCoordinate value="500"/>', - ' <YCoordinate value="-20"/>', - ' </LigatureAnchor>', - ' <LigatureAnchor index="1" empty="1"/>', - ' <LigatureAnchor index="2" Format="1">', - ' <XCoordinate value="300"/>', - ' <YCoordinate value="-15"/>', - ' </LigatureAnchor>', - '</ComponentRecord>']) + assert getXML(rec.toXML) == [ + "<ComponentRecord>", + ' <LigatureAnchor index="0" Format="1">', + ' <XCoordinate value="500"/>', + ' <YCoordinate value="-20"/>', + " </LigatureAnchor>", + ' <LigatureAnchor index="1" empty="1"/>', + ' <LigatureAnchor index="2" Format="1">', + ' <XCoordinate value="300"/>', + ' <YCoordinate value="-15"/>', + " </LigatureAnchor>", + "</ComponentRecord>", + ] def test_buildComponentRecord_empty(self): - self.assertIsNone(builder.buildComponentRecord([])) + assert builder.buildComponentRecord([]) is None def test_buildComponentRecord_None(self): - self.assertIsNone(builder.buildComponentRecord(None)) + assert builder.buildComponentRecord(None) is None def test_buildCoverage(self): cov = builder.buildCoverage({"two", "four"}, {"two": 2, "four": 4}) - self.assertEqual(getXML(cov.toXML), - ['<Coverage>', - ' <Glyph value="two"/>', - ' <Glyph value="four"/>', - '</Coverage>']) + assert getXML(cov.toXML) == [ + "<Coverage>", + ' <Glyph value="two"/>', + ' <Glyph value="four"/>', + "</Coverage>", + ] def test_buildCursivePos(self): - pos = builder.buildCursivePosSubtable({ - "two": (self.ANCHOR1, self.ANCHOR2), - "four": (self.ANCHOR3, self.ANCHOR1) - }, self.GLYPHMAP) - self.assertEqual(getXML(pos.toXML), - ['<CursivePos Format="1">', - ' <Coverage>', - ' <Glyph value="two"/>', - ' <Glyph value="four"/>', - ' </Coverage>', - ' <!-- EntryExitCount=2 -->', - ' <EntryExitRecord index="0">', - ' <EntryAnchor Format="1">', - ' <XCoordinate value="11"/>', - ' <YCoordinate value="-11"/>', - ' </EntryAnchor>', - ' <ExitAnchor Format="1">', - ' <XCoordinate value="22"/>', - ' <YCoordinate value="-22"/>', - ' </ExitAnchor>', - ' </EntryExitRecord>', - ' <EntryExitRecord index="1">', - ' <EntryAnchor Format="1">', - ' <XCoordinate value="33"/>', - ' <YCoordinate value="-33"/>', - ' </EntryAnchor>', - ' <ExitAnchor Format="1">', - ' <XCoordinate value="11"/>', - ' <YCoordinate value="-11"/>', - ' </ExitAnchor>', - ' </EntryExitRecord>', - '</CursivePos>']) + pos = builder.buildCursivePosSubtable( + {"two": (self.ANCHOR1, self.ANCHOR2), "four": (self.ANCHOR3, self.ANCHOR1)}, + self.GLYPHMAP, + ) + assert getXML(pos.toXML) == [ + '<CursivePos Format="1">', + " <Coverage>", + ' <Glyph value="two"/>', + ' <Glyph value="four"/>', + " </Coverage>", + " <!-- EntryExitCount=2 -->", + ' <EntryExitRecord index="0">', + ' <EntryAnchor Format="1">', + ' <XCoordinate value="11"/>', + ' <YCoordinate value="-11"/>', + " </EntryAnchor>", + ' <ExitAnchor Format="1">', + ' <XCoordinate value="22"/>', + ' <YCoordinate value="-22"/>', + " </ExitAnchor>", + " </EntryExitRecord>", + ' <EntryExitRecord index="1">', + ' <EntryAnchor Format="1">', + ' <XCoordinate value="33"/>', + ' <YCoordinate value="-33"/>', + " </EntryAnchor>", + ' <ExitAnchor Format="1">', + ' <XCoordinate value="11"/>', + ' <YCoordinate value="-11"/>', + " </ExitAnchor>", + " </EntryExitRecord>", + "</CursivePos>", + ] def test_buildDevice_format1(self): - device = builder.buildDevice({1:1, 0:0}) - self.assertEqual(getXML(device.toXML), - ['<Device>', - ' <StartSize value="0"/>', - ' <EndSize value="1"/>', - ' <DeltaFormat value="1"/>', - ' <DeltaValue value="[0, 1]"/>', - '</Device>']) + device = builder.buildDevice({1: 1, 0: 0}) + assert getXML(device.toXML) == [ + "<Device>", + ' <StartSize value="0"/>', + ' <EndSize value="1"/>', + ' <DeltaFormat value="1"/>', + ' <DeltaValue value="[0, 1]"/>', + "</Device>", + ] def test_buildDevice_format2(self): - device = builder.buildDevice({2:2, 0:1, 1:0}) - self.assertEqual(getXML(device.toXML), - ['<Device>', - ' <StartSize value="0"/>', - ' <EndSize value="2"/>', - ' <DeltaFormat value="2"/>', - ' <DeltaValue value="[1, 0, 2]"/>', - '</Device>']) + device = builder.buildDevice({2: 2, 0: 1, 1: 0}) + assert getXML(device.toXML) == [ + "<Device>", + ' <StartSize value="0"/>', + ' <EndSize value="2"/>', + ' <DeltaFormat value="2"/>', + ' <DeltaValue value="[1, 0, 2]"/>', + "</Device>", + ] def test_buildDevice_format3(self): - device = builder.buildDevice({5:3, 1:77}) - self.assertEqual(getXML(device.toXML), - ['<Device>', - ' <StartSize value="1"/>', - ' <EndSize value="5"/>', - ' <DeltaFormat value="3"/>', - ' <DeltaValue value="[77, 0, 0, 0, 3]"/>', - '</Device>']) + device = builder.buildDevice({5: 3, 1: 77}) + assert getXML(device.toXML) == [ + "<Device>", + ' <StartSize value="1"/>', + ' <EndSize value="5"/>', + ' <DeltaFormat value="3"/>', + ' <DeltaValue value="[77, 0, 0, 0, 3]"/>', + "</Device>", + ] def test_buildLigatureArray(self): anchor = builder.buildAnchor - ligatureArray = builder.buildLigatureArray({ - "f_i": [{2: anchor(300, -20)}, {}], - "c_t": [{}, {1: anchor(500, 350), 2: anchor(1300, -20)}] - }, numMarkClasses=4, glyphMap=self.GLYPHMAP) - self.assertEqual(getXML(ligatureArray.toXML), - ['<LigatureArray>', - ' <!-- LigatureCount=2 -->', - ' <LigatureAttach index="0">', # f_i - ' <!-- ComponentCount=2 -->', - ' <ComponentRecord index="0">', - ' <LigatureAnchor index="0" empty="1"/>', - ' <LigatureAnchor index="1" empty="1"/>', - ' <LigatureAnchor index="2" Format="1">', - ' <XCoordinate value="300"/>', - ' <YCoordinate value="-20"/>', - ' </LigatureAnchor>', - ' <LigatureAnchor index="3" empty="1"/>', - ' </ComponentRecord>', - ' <ComponentRecord index="1">', - ' <LigatureAnchor index="0" empty="1"/>', - ' <LigatureAnchor index="1" empty="1"/>', - ' <LigatureAnchor index="2" empty="1"/>', - ' <LigatureAnchor index="3" empty="1"/>', - ' </ComponentRecord>', - ' </LigatureAttach>', - ' <LigatureAttach index="1">', - ' <!-- ComponentCount=2 -->', - ' <ComponentRecord index="0">', - ' <LigatureAnchor index="0" empty="1"/>', - ' <LigatureAnchor index="1" empty="1"/>', - ' <LigatureAnchor index="2" empty="1"/>', - ' <LigatureAnchor index="3" empty="1"/>', - ' </ComponentRecord>', - ' <ComponentRecord index="1">', - ' <LigatureAnchor index="0" empty="1"/>', - ' <LigatureAnchor index="1" Format="1">', - ' <XCoordinate value="500"/>', - ' <YCoordinate value="350"/>', - ' </LigatureAnchor>', - ' <LigatureAnchor index="2" Format="1">', - ' <XCoordinate value="1300"/>', - ' <YCoordinate value="-20"/>', - ' </LigatureAnchor>', - ' <LigatureAnchor index="3" empty="1"/>', - ' </ComponentRecord>', - ' </LigatureAttach>', - '</LigatureArray>']) + ligatureArray = builder.buildLigatureArray( + { + "f_i": [{2: anchor(300, -20)}, {}], + "c_t": [{}, {1: anchor(500, 350), 2: anchor(1300, -20)}], + }, + numMarkClasses=4, + glyphMap=self.GLYPHMAP, + ) + assert getXML(ligatureArray.toXML) == [ + "<LigatureArray>", + " <!-- LigatureCount=2 -->", + ' <LigatureAttach index="0">', # f_i + " <!-- ComponentCount=2 -->", + ' <ComponentRecord index="0">', + ' <LigatureAnchor index="0" empty="1"/>', + ' <LigatureAnchor index="1" empty="1"/>', + ' <LigatureAnchor index="2" Format="1">', + ' <XCoordinate value="300"/>', + ' <YCoordinate value="-20"/>', + " </LigatureAnchor>", + ' <LigatureAnchor index="3" empty="1"/>', + " </ComponentRecord>", + ' <ComponentRecord index="1">', + ' <LigatureAnchor index="0" empty="1"/>', + ' <LigatureAnchor index="1" empty="1"/>', + ' <LigatureAnchor index="2" empty="1"/>', + ' <LigatureAnchor index="3" empty="1"/>', + " </ComponentRecord>", + " </LigatureAttach>", + ' <LigatureAttach index="1">', + " <!-- ComponentCount=2 -->", + ' <ComponentRecord index="0">', + ' <LigatureAnchor index="0" empty="1"/>', + ' <LigatureAnchor index="1" empty="1"/>', + ' <LigatureAnchor index="2" empty="1"/>', + ' <LigatureAnchor index="3" empty="1"/>', + " </ComponentRecord>", + ' <ComponentRecord index="1">', + ' <LigatureAnchor index="0" empty="1"/>', + ' <LigatureAnchor index="1" Format="1">', + ' <XCoordinate value="500"/>', + ' <YCoordinate value="350"/>', + " </LigatureAnchor>", + ' <LigatureAnchor index="2" Format="1">', + ' <XCoordinate value="1300"/>', + ' <YCoordinate value="-20"/>', + " </LigatureAnchor>", + ' <LigatureAnchor index="3" empty="1"/>', + " </ComponentRecord>", + " </LigatureAttach>", + "</LigatureArray>", + ] def test_buildLigatureAttach(self): anchor = builder.buildAnchor - attach = builder.buildLigatureAttach([ - [anchor(500, -10), None], - [None, anchor(300, -20), None]]) - self.assertEqual(getXML(attach.toXML), - ['<LigatureAttach>', - ' <!-- ComponentCount=2 -->', - ' <ComponentRecord index="0">', - ' <LigatureAnchor index="0" Format="1">', - ' <XCoordinate value="500"/>', - ' <YCoordinate value="-10"/>', - ' </LigatureAnchor>', - ' <LigatureAnchor index="1" empty="1"/>', - ' </ComponentRecord>', - ' <ComponentRecord index="1">', - ' <LigatureAnchor index="0" empty="1"/>', - ' <LigatureAnchor index="1" Format="1">', - ' <XCoordinate value="300"/>', - ' <YCoordinate value="-20"/>', - ' </LigatureAnchor>', - ' <LigatureAnchor index="2" empty="1"/>', - ' </ComponentRecord>', - '</LigatureAttach>']) + attach = builder.buildLigatureAttach( + [[anchor(500, -10), None], [None, anchor(300, -20), None]] + ) + assert getXML(attach.toXML) == [ + "<LigatureAttach>", + " <!-- ComponentCount=2 -->", + ' <ComponentRecord index="0">', + ' <LigatureAnchor index="0" Format="1">', + ' <XCoordinate value="500"/>', + ' <YCoordinate value="-10"/>', + " </LigatureAnchor>", + ' <LigatureAnchor index="1" empty="1"/>', + " </ComponentRecord>", + ' <ComponentRecord index="1">', + ' <LigatureAnchor index="0" empty="1"/>', + ' <LigatureAnchor index="1" Format="1">', + ' <XCoordinate value="300"/>', + ' <YCoordinate value="-20"/>', + " </LigatureAnchor>", + ' <LigatureAnchor index="2" empty="1"/>', + " </ComponentRecord>", + "</LigatureAttach>", + ] def test_buildLigatureAttach_emptyComponents(self): attach = builder.buildLigatureAttach([[], None]) - self.assertEqual(getXML(attach.toXML), - ['<LigatureAttach>', - ' <!-- ComponentCount=2 -->', - ' <ComponentRecord index="0" empty="1"/>', - ' <ComponentRecord index="1" empty="1"/>', - '</LigatureAttach>']) + assert getXML(attach.toXML) == [ + "<LigatureAttach>", + " <!-- ComponentCount=2 -->", + ' <ComponentRecord index="0" empty="1"/>', + ' <ComponentRecord index="1" empty="1"/>', + "</LigatureAttach>", + ] def test_buildLigatureAttach_noComponents(self): attach = builder.buildLigatureAttach([]) - self.assertEqual(getXML(attach.toXML), - ['<LigatureAttach>', - ' <!-- ComponentCount=0 -->', - '</LigatureAttach>']) + assert getXML(attach.toXML) == [ + "<LigatureAttach>", + " <!-- ComponentCount=0 -->", + "</LigatureAttach>", + ] def test_buildLigCaretList(self): carets = builder.buildLigCaretList( - {"f_f_i": [300, 600]}, {"c_t": [42]}, self.GLYPHMAP) - self.assertEqual(getXML(carets.toXML), - ['<LigCaretList>', - ' <Coverage>', - ' <Glyph value="f_f_i"/>', - ' <Glyph value="c_t"/>', - ' </Coverage>', - ' <!-- LigGlyphCount=2 -->', - ' <LigGlyph index="0">', - ' <!-- CaretCount=2 -->', - ' <CaretValue index="0" Format="1">', - ' <Coordinate value="300"/>', - ' </CaretValue>', - ' <CaretValue index="1" Format="1">', - ' <Coordinate value="600"/>', - ' </CaretValue>', - ' </LigGlyph>', - ' <LigGlyph index="1">', - ' <!-- CaretCount=1 -->', - ' <CaretValue index="0" Format="2">', - ' <CaretValuePoint value="42"/>', - ' </CaretValue>', - ' </LigGlyph>', - '</LigCaretList>']) + {"f_f_i": [300, 600]}, {"c_t": [42]}, self.GLYPHMAP + ) + assert getXML(carets.toXML) == [ + "<LigCaretList>", + " <Coverage>", + ' <Glyph value="f_f_i"/>', + ' <Glyph value="c_t"/>', + " </Coverage>", + " <!-- LigGlyphCount=2 -->", + ' <LigGlyph index="0">', + " <!-- CaretCount=2 -->", + ' <CaretValue index="0" Format="1">', + ' <Coordinate value="300"/>', + " </CaretValue>", + ' <CaretValue index="1" Format="1">', + ' <Coordinate value="600"/>', + " </CaretValue>", + " </LigGlyph>", + ' <LigGlyph index="1">', + " <!-- CaretCount=1 -->", + ' <CaretValue index="0" Format="2">', + ' <CaretValuePoint value="42"/>', + " </CaretValue>", + " </LigGlyph>", + "</LigCaretList>", + ] def test_buildLigCaretList_bothCoordsAndPointsForSameGlyph(self): carets = builder.buildLigCaretList( - {"f_f_i": [300]}, {"f_f_i": [7]}, self.GLYPHMAP) - self.assertEqual(getXML(carets.toXML), - ['<LigCaretList>', - ' <Coverage>', - ' <Glyph value="f_f_i"/>', - ' </Coverage>', - ' <!-- LigGlyphCount=1 -->', - ' <LigGlyph index="0">', - ' <!-- CaretCount=2 -->', - ' <CaretValue index="0" Format="1">', - ' <Coordinate value="300"/>', - ' </CaretValue>', - ' <CaretValue index="1" Format="2">', - ' <CaretValuePoint value="7"/>', - ' </CaretValue>', - ' </LigGlyph>', - '</LigCaretList>']) + {"f_f_i": [300]}, {"f_f_i": [7]}, self.GLYPHMAP + ) + assert getXML(carets.toXML) == [ + "<LigCaretList>", + " <Coverage>", + ' <Glyph value="f_f_i"/>', + " </Coverage>", + " <!-- LigGlyphCount=1 -->", + ' <LigGlyph index="0">', + " <!-- CaretCount=2 -->", + ' <CaretValue index="0" Format="1">', + ' <Coordinate value="300"/>', + " </CaretValue>", + ' <CaretValue index="1" Format="2">', + ' <CaretValuePoint value="7"/>', + " </CaretValue>", + " </LigGlyph>", + "</LigCaretList>", + ] def test_buildLigCaretList_empty(self): - self.assertIsNone(builder.buildLigCaretList({}, {}, self.GLYPHMAP)) + assert builder.buildLigCaretList({}, {}, self.GLYPHMAP) is None def test_buildLigCaretList_None(self): - self.assertIsNone(builder.buildLigCaretList(None, None, self.GLYPHMAP)) + assert builder.buildLigCaretList(None, None, self.GLYPHMAP) is None def test_buildLigGlyph_coords(self): lig = builder.buildLigGlyph([500, 800], None) - self.assertEqual(getXML(lig.toXML), - ['<LigGlyph>', - ' <!-- CaretCount=2 -->', - ' <CaretValue index="0" Format="1">', - ' <Coordinate value="500"/>', - ' </CaretValue>', - ' <CaretValue index="1" Format="1">', - ' <Coordinate value="800"/>', - ' </CaretValue>', - '</LigGlyph>']) + assert getXML(lig.toXML) == [ + "<LigGlyph>", + " <!-- CaretCount=2 -->", + ' <CaretValue index="0" Format="1">', + ' <Coordinate value="500"/>', + " </CaretValue>", + ' <CaretValue index="1" Format="1">', + ' <Coordinate value="800"/>', + " </CaretValue>", + "</LigGlyph>", + ] def test_buildLigGlyph_empty(self): - self.assertIsNone(builder.buildLigGlyph([], [])) + assert builder.buildLigGlyph([], []) is None def test_buildLigGlyph_None(self): - self.assertIsNone(builder.buildLigGlyph(None, None)) + assert builder.buildLigGlyph(None, None) is None def test_buildLigGlyph_points(self): lig = builder.buildLigGlyph(None, [2]) - self.assertEqual(getXML(lig.toXML), - ['<LigGlyph>', - ' <!-- CaretCount=1 -->', - ' <CaretValue index="0" Format="2">', - ' <CaretValuePoint value="2"/>', - ' </CaretValue>', - '</LigGlyph>']) + assert getXML(lig.toXML) == [ + "<LigGlyph>", + " <!-- CaretCount=1 -->", + ' <CaretValue index="0" Format="2">', + ' <CaretValuePoint value="2"/>', + " </CaretValue>", + "</LigGlyph>", + ] def test_buildLookup(self): s1 = builder.buildSingleSubstSubtable({"one": "two"}) s2 = builder.buildSingleSubstSubtable({"three": "four"}) lookup = builder.buildLookup([s1, s2], flags=7) - self.assertEqual(getXML(lookup.toXML), - ['<Lookup>', - ' <LookupType value="1"/>', - ' <LookupFlag value="7"/>', - ' <!-- SubTableCount=2 -->', - ' <SingleSubst index="0">', - ' <Substitution in="one" out="two"/>', - ' </SingleSubst>', - ' <SingleSubst index="1">', - ' <Substitution in="three" out="four"/>', - ' </SingleSubst>', - '</Lookup>']) + assert getXML(lookup.toXML) == [ + "<Lookup>", + ' <LookupType value="1"/>', + ' <LookupFlag value="7"/>', + " <!-- SubTableCount=2 -->", + ' <SingleSubst index="0">', + ' <Substitution in="one" out="two"/>', + " </SingleSubst>", + ' <SingleSubst index="1">', + ' <Substitution in="three" out="four"/>', + " </SingleSubst>", + "</Lookup>", + ] def test_buildLookup_badFlags(self): s = builder.buildSingleSubstSubtable({"one": "two"}) - self.assertRaisesRegex( - AssertionError, "if markFilterSet is None, " - "flags must not set LOOKUP_FLAG_USE_MARK_FILTERING_SET; " - "flags=0x0010", - builder.buildLookup, [s], - builder.LOOKUP_FLAG_USE_MARK_FILTERING_SET, None) - self.assertRaisesRegex( - AssertionError, "if markFilterSet is not None, " - "flags must set LOOKUP_FLAG_USE_MARK_FILTERING_SET; " - "flags=0x0004", - builder.buildLookup, [s], - builder.LOOKUP_FLAG_IGNORE_LIGATURES, 777) + with pytest.raises( + AssertionError, + match=( + "if markFilterSet is None, flags must not set " + "LOOKUP_FLAG_USE_MARK_FILTERING_SET; flags=0x0010" + ), + ) as excinfo: + builder.buildLookup([s], builder.LOOKUP_FLAG_USE_MARK_FILTERING_SET, None) + with pytest.raises( + AssertionError, + match=( + "if markFilterSet is not None, flags must set " + "LOOKUP_FLAG_USE_MARK_FILTERING_SET; flags=0x0004" + ), + ) as excinfo: + builder.buildLookup([s], builder.LOOKUP_FLAG_IGNORE_LIGATURES, 777) def test_buildLookup_conflictingSubtableTypes(self): s1 = builder.buildSingleSubstSubtable({"one": "two"}) s2 = builder.buildAlternateSubstSubtable({"one": ["two", "three"]}) - self.assertRaisesRegex( - AssertionError, "all subtables must have the same LookupType", - builder.buildLookup, [s1, s2]) + with pytest.raises( + AssertionError, match="all subtables must have the same LookupType" + ) as excinfo: + builder.buildLookup([s1, s2]) def test_buildLookup_noSubtables(self): - self.assertIsNone(builder.buildLookup([])) - self.assertIsNone(builder.buildLookup(None)) - self.assertIsNone(builder.buildLookup([None])) - self.assertIsNone(builder.buildLookup([None, None])) + assert builder.buildLookup([]) is None + assert builder.buildLookup(None) is None + assert builder.buildLookup([None]) is None + assert builder.buildLookup([None, None]) is None def test_buildLookup_markFilterSet(self): s = builder.buildSingleSubstSubtable({"one": "two"}) - flags = (builder.LOOKUP_FLAG_RIGHT_TO_LEFT | - builder.LOOKUP_FLAG_USE_MARK_FILTERING_SET) + flags = ( + builder.LOOKUP_FLAG_RIGHT_TO_LEFT + | builder.LOOKUP_FLAG_USE_MARK_FILTERING_SET + ) lookup = builder.buildLookup([s], flags, markFilterSet=999) - self.assertEqual(getXML(lookup.toXML), - ['<Lookup>', - ' <LookupType value="1"/>', - ' <LookupFlag value="17"/>', - ' <!-- SubTableCount=1 -->', - ' <SingleSubst index="0">', - ' <Substitution in="one" out="two"/>', - ' </SingleSubst>', - ' <MarkFilteringSet value="999"/>', - '</Lookup>']) + assert getXML(lookup.toXML) == [ + "<Lookup>", + ' <LookupType value="1"/>', + ' <LookupFlag value="17"/>', + " <!-- SubTableCount=1 -->", + ' <SingleSubst index="0">', + ' <Substitution in="one" out="two"/>', + " </SingleSubst>", + ' <MarkFilteringSet value="999"/>', + "</Lookup>", + ] def test_buildMarkArray(self): - markArray = builder.buildMarkArray({ - "acute": (7, builder.buildAnchor(300, 800)), - "grave": (2, builder.buildAnchor(10, 80)) - }, self.GLYPHMAP) - self.assertLess(self.GLYPHMAP["grave"], self.GLYPHMAP["acute"]) - self.assertEqual(getXML(markArray.toXML), - ['<MarkArray>', - ' <!-- MarkCount=2 -->', - ' <MarkRecord index="0">', - ' <Class value="2"/>', - ' <MarkAnchor Format="1">', - ' <XCoordinate value="10"/>', - ' <YCoordinate value="80"/>', - ' </MarkAnchor>', - ' </MarkRecord>', - ' <MarkRecord index="1">', - ' <Class value="7"/>', - ' <MarkAnchor Format="1">', - ' <XCoordinate value="300"/>', - ' <YCoordinate value="800"/>', - ' </MarkAnchor>', - ' </MarkRecord>', - '</MarkArray>']) + markArray = builder.buildMarkArray( + { + "acute": (7, builder.buildAnchor(300, 800)), + "grave": (2, builder.buildAnchor(10, 80)), + }, + self.GLYPHMAP, + ) + assert self.GLYPHMAP["grave"] < self.GLYPHMAP["acute"] + assert getXML(markArray.toXML) == [ + "<MarkArray>", + " <!-- MarkCount=2 -->", + ' <MarkRecord index="0">', + ' <Class value="2"/>', + ' <MarkAnchor Format="1">', + ' <XCoordinate value="10"/>', + ' <YCoordinate value="80"/>', + " </MarkAnchor>", + " </MarkRecord>", + ' <MarkRecord index="1">', + ' <Class value="7"/>', + ' <MarkAnchor Format="1">', + ' <XCoordinate value="300"/>', + ' <YCoordinate value="800"/>', + " </MarkAnchor>", + " </MarkRecord>", + "</MarkArray>", + ] def test_buildMarkBasePosSubtable(self): anchor = builder.buildAnchor marks = { "acute": (0, anchor(300, 700)), "cedilla": (1, anchor(300, -100)), - "grave": (0, anchor(300, 700)) + "grave": (0, anchor(300, 700)), } bases = { # Make sure we can handle missing entries. "A": {}, # no entry for any markClass "B": {0: anchor(500, 900)}, # only markClass 0 specified "C": {1: anchor(500, -10)}, # only markClass 1 specified - "a": {0: anchor(500, 400), 1: anchor(500, -20)}, - "b": {0: anchor(500, 800), 1: anchor(500, -20)} + "b": {0: anchor(500, 800), 1: anchor(500, -20)}, } table = builder.buildMarkBasePosSubtable(marks, bases, self.GLYPHMAP) - self.assertEqual(getXML(table.toXML), - ['<MarkBasePos Format="1">', - ' <MarkCoverage>', - ' <Glyph value="grave"/>', - ' <Glyph value="acute"/>', - ' <Glyph value="cedilla"/>', - ' </MarkCoverage>', - ' <BaseCoverage>', - ' <Glyph value="A"/>', - ' <Glyph value="B"/>', - ' <Glyph value="C"/>', - ' <Glyph value="a"/>', - ' <Glyph value="b"/>', - ' </BaseCoverage>', - ' <!-- ClassCount=2 -->', - ' <MarkArray>', - ' <!-- MarkCount=3 -->', - ' <MarkRecord index="0">', # grave - ' <Class value="0"/>', - ' <MarkAnchor Format="1">', - ' <XCoordinate value="300"/>', - ' <YCoordinate value="700"/>', - ' </MarkAnchor>', - ' </MarkRecord>', - ' <MarkRecord index="1">', # acute - ' <Class value="0"/>', - ' <MarkAnchor Format="1">', - ' <XCoordinate value="300"/>', - ' <YCoordinate value="700"/>', - ' </MarkAnchor>', - ' </MarkRecord>', - ' <MarkRecord index="2">', # cedilla - ' <Class value="1"/>', - ' <MarkAnchor Format="1">', - ' <XCoordinate value="300"/>', - ' <YCoordinate value="-100"/>', - ' </MarkAnchor>', - ' </MarkRecord>', - ' </MarkArray>', - ' <BaseArray>', - ' <!-- BaseCount=5 -->', - ' <BaseRecord index="0">', # A - ' <BaseAnchor index="0" empty="1"/>', - ' <BaseAnchor index="1" empty="1"/>', - ' </BaseRecord>', - ' <BaseRecord index="1">', # B - ' <BaseAnchor index="0" Format="1">', - ' <XCoordinate value="500"/>', - ' <YCoordinate value="900"/>', - ' </BaseAnchor>', - ' <BaseAnchor index="1" empty="1"/>', - ' </BaseRecord>', - ' <BaseRecord index="2">', # C - ' <BaseAnchor index="0" empty="1"/>', - ' <BaseAnchor index="1" Format="1">', - ' <XCoordinate value="500"/>', - ' <YCoordinate value="-10"/>', - ' </BaseAnchor>', - ' </BaseRecord>', - ' <BaseRecord index="3">', # a - ' <BaseAnchor index="0" Format="1">', - ' <XCoordinate value="500"/>', - ' <YCoordinate value="400"/>', - ' </BaseAnchor>', - ' <BaseAnchor index="1" Format="1">', - ' <XCoordinate value="500"/>', - ' <YCoordinate value="-20"/>', - ' </BaseAnchor>', - ' </BaseRecord>', - ' <BaseRecord index="4">', # b - ' <BaseAnchor index="0" Format="1">', - ' <XCoordinate value="500"/>', - ' <YCoordinate value="800"/>', - ' </BaseAnchor>', - ' <BaseAnchor index="1" Format="1">', - ' <XCoordinate value="500"/>', - ' <YCoordinate value="-20"/>', - ' </BaseAnchor>', - ' </BaseRecord>', - ' </BaseArray>', - '</MarkBasePos>']) + assert getXML(table.toXML) == [ + '<MarkBasePos Format="1">', + " <MarkCoverage>", + ' <Glyph value="grave"/>', + ' <Glyph value="acute"/>', + ' <Glyph value="cedilla"/>', + " </MarkCoverage>", + " <BaseCoverage>", + ' <Glyph value="A"/>', + ' <Glyph value="B"/>', + ' <Glyph value="C"/>', + ' <Glyph value="a"/>', + ' <Glyph value="b"/>', + " </BaseCoverage>", + " <!-- ClassCount=2 -->", + " <MarkArray>", + " <!-- MarkCount=3 -->", + ' <MarkRecord index="0">', # grave + ' <Class value="0"/>', + ' <MarkAnchor Format="1">', + ' <XCoordinate value="300"/>', + ' <YCoordinate value="700"/>', + " </MarkAnchor>", + " </MarkRecord>", + ' <MarkRecord index="1">', # acute + ' <Class value="0"/>', + ' <MarkAnchor Format="1">', + ' <XCoordinate value="300"/>', + ' <YCoordinate value="700"/>', + " </MarkAnchor>", + " </MarkRecord>", + ' <MarkRecord index="2">', # cedilla + ' <Class value="1"/>', + ' <MarkAnchor Format="1">', + ' <XCoordinate value="300"/>', + ' <YCoordinate value="-100"/>', + " </MarkAnchor>", + " </MarkRecord>", + " </MarkArray>", + " <BaseArray>", + " <!-- BaseCount=5 -->", + ' <BaseRecord index="0">', # A + ' <BaseAnchor index="0" empty="1"/>', + ' <BaseAnchor index="1" empty="1"/>', + " </BaseRecord>", + ' <BaseRecord index="1">', # B + ' <BaseAnchor index="0" Format="1">', + ' <XCoordinate value="500"/>', + ' <YCoordinate value="900"/>', + " </BaseAnchor>", + ' <BaseAnchor index="1" empty="1"/>', + " </BaseRecord>", + ' <BaseRecord index="2">', # C + ' <BaseAnchor index="0" empty="1"/>', + ' <BaseAnchor index="1" Format="1">', + ' <XCoordinate value="500"/>', + ' <YCoordinate value="-10"/>', + " </BaseAnchor>", + " </BaseRecord>", + ' <BaseRecord index="3">', # a + ' <BaseAnchor index="0" Format="1">', + ' <XCoordinate value="500"/>', + ' <YCoordinate value="400"/>', + " </BaseAnchor>", + ' <BaseAnchor index="1" Format="1">', + ' <XCoordinate value="500"/>', + ' <YCoordinate value="-20"/>', + " </BaseAnchor>", + " </BaseRecord>", + ' <BaseRecord index="4">', # b + ' <BaseAnchor index="0" Format="1">', + ' <XCoordinate value="500"/>', + ' <YCoordinate value="800"/>', + " </BaseAnchor>", + ' <BaseAnchor index="1" Format="1">', + ' <XCoordinate value="500"/>', + ' <YCoordinate value="-20"/>', + " </BaseAnchor>", + " </BaseRecord>", + " </BaseArray>", + "</MarkBasePos>", + ] def test_buildMarkGlyphSetsDef(self): marksets = builder.buildMarkGlyphSetsDef( - [{"acute", "grave"}, {"cedilla", "grave"}], self.GLYPHMAP) - self.assertEqual(getXML(marksets.toXML), - ['<MarkGlyphSetsDef>', - ' <MarkSetTableFormat value="1"/>', - ' <!-- MarkSetCount=2 -->', - ' <Coverage index="0">', - ' <Glyph value="grave"/>', - ' <Glyph value="acute"/>', - ' </Coverage>', - ' <Coverage index="1">', - ' <Glyph value="grave"/>', - ' <Glyph value="cedilla"/>', - ' </Coverage>', - '</MarkGlyphSetsDef>']) + [{"acute", "grave"}, {"cedilla", "grave"}], self.GLYPHMAP + ) + assert getXML(marksets.toXML) == [ + "<MarkGlyphSetsDef>", + ' <MarkSetTableFormat value="1"/>', + " <!-- MarkSetCount=2 -->", + ' <Coverage index="0">', + ' <Glyph value="grave"/>', + ' <Glyph value="acute"/>', + " </Coverage>", + ' <Coverage index="1">', + ' <Glyph value="grave"/>', + ' <Glyph value="cedilla"/>', + " </Coverage>", + "</MarkGlyphSetsDef>", + ] def test_buildMarkGlyphSetsDef_empty(self): - self.assertIsNone(builder.buildMarkGlyphSetsDef([], self.GLYPHMAP)) + assert builder.buildMarkGlyphSetsDef([], self.GLYPHMAP) is None def test_buildMarkGlyphSetsDef_None(self): - self.assertIsNone(builder.buildMarkGlyphSetsDef(None, self.GLYPHMAP)) + assert builder.buildMarkGlyphSetsDef(None, self.GLYPHMAP) is None def test_buildMarkLigPosSubtable(self): anchor = builder.buildAnchor marks = { "acute": (0, anchor(300, 700)), "cedilla": (1, anchor(300, -100)), - "grave": (0, anchor(300, 700)) + "grave": (0, anchor(300, 700)), } bases = { "f_i": [{}, {0: anchor(200, 400)}], # nothing on f; only 1 on i "c_t": [ - {0: anchor(500, 600), 1: anchor(500, -20)}, # c - {0: anchor(1300, 800), 1: anchor(1300, -20)} # t - ] + {0: anchor(500, 600), 1: anchor(500, -20)}, # c + {0: anchor(1300, 800), 1: anchor(1300, -20)}, # t + ], } table = builder.buildMarkLigPosSubtable(marks, bases, self.GLYPHMAP) - self.assertEqual(getXML(table.toXML), - ['<MarkLigPos Format="1">', - ' <MarkCoverage>', - ' <Glyph value="grave"/>', - ' <Glyph value="acute"/>', - ' <Glyph value="cedilla"/>', - ' </MarkCoverage>', - ' <LigatureCoverage>', - ' <Glyph value="f_i"/>', - ' <Glyph value="c_t"/>', - ' </LigatureCoverage>', - ' <!-- ClassCount=2 -->', - ' <MarkArray>', - ' <!-- MarkCount=3 -->', - ' <MarkRecord index="0">', - ' <Class value="0"/>', - ' <MarkAnchor Format="1">', - ' <XCoordinate value="300"/>', - ' <YCoordinate value="700"/>', - ' </MarkAnchor>', - ' </MarkRecord>', - ' <MarkRecord index="1">', - ' <Class value="0"/>', - ' <MarkAnchor Format="1">', - ' <XCoordinate value="300"/>', - ' <YCoordinate value="700"/>', - ' </MarkAnchor>', - ' </MarkRecord>', - ' <MarkRecord index="2">', - ' <Class value="1"/>', - ' <MarkAnchor Format="1">', - ' <XCoordinate value="300"/>', - ' <YCoordinate value="-100"/>', - ' </MarkAnchor>', - ' </MarkRecord>', - ' </MarkArray>', - ' <LigatureArray>', - ' <!-- LigatureCount=2 -->', - ' <LigatureAttach index="0">', - ' <!-- ComponentCount=2 -->', - ' <ComponentRecord index="0">', - ' <LigatureAnchor index="0" empty="1"/>', - ' <LigatureAnchor index="1" empty="1"/>', - ' </ComponentRecord>', - ' <ComponentRecord index="1">', - ' <LigatureAnchor index="0" Format="1">', - ' <XCoordinate value="200"/>', - ' <YCoordinate value="400"/>', - ' </LigatureAnchor>', - ' <LigatureAnchor index="1" empty="1"/>', - ' </ComponentRecord>', - ' </LigatureAttach>', - ' <LigatureAttach index="1">', - ' <!-- ComponentCount=2 -->', - ' <ComponentRecord index="0">', - ' <LigatureAnchor index="0" Format="1">', - ' <XCoordinate value="500"/>', - ' <YCoordinate value="600"/>', - ' </LigatureAnchor>', - ' <LigatureAnchor index="1" Format="1">', - ' <XCoordinate value="500"/>', - ' <YCoordinate value="-20"/>', - ' </LigatureAnchor>', - ' </ComponentRecord>', - ' <ComponentRecord index="1">', - ' <LigatureAnchor index="0" Format="1">', - ' <XCoordinate value="1300"/>', - ' <YCoordinate value="800"/>', - ' </LigatureAnchor>', - ' <LigatureAnchor index="1" Format="1">', - ' <XCoordinate value="1300"/>', - ' <YCoordinate value="-20"/>', - ' </LigatureAnchor>', - ' </ComponentRecord>', - ' </LigatureAttach>', - ' </LigatureArray>', - '</MarkLigPos>']) + assert getXML(table.toXML) == [ + '<MarkLigPos Format="1">', + " <MarkCoverage>", + ' <Glyph value="grave"/>', + ' <Glyph value="acute"/>', + ' <Glyph value="cedilla"/>', + " </MarkCoverage>", + " <LigatureCoverage>", + ' <Glyph value="f_i"/>', + ' <Glyph value="c_t"/>', + " </LigatureCoverage>", + " <!-- ClassCount=2 -->", + " <MarkArray>", + " <!-- MarkCount=3 -->", + ' <MarkRecord index="0">', + ' <Class value="0"/>', + ' <MarkAnchor Format="1">', + ' <XCoordinate value="300"/>', + ' <YCoordinate value="700"/>', + " </MarkAnchor>", + " </MarkRecord>", + ' <MarkRecord index="1">', + ' <Class value="0"/>', + ' <MarkAnchor Format="1">', + ' <XCoordinate value="300"/>', + ' <YCoordinate value="700"/>', + " </MarkAnchor>", + " </MarkRecord>", + ' <MarkRecord index="2">', + ' <Class value="1"/>', + ' <MarkAnchor Format="1">', + ' <XCoordinate value="300"/>', + ' <YCoordinate value="-100"/>', + " </MarkAnchor>", + " </MarkRecord>", + " </MarkArray>", + " <LigatureArray>", + " <!-- LigatureCount=2 -->", + ' <LigatureAttach index="0">', + " <!-- ComponentCount=2 -->", + ' <ComponentRecord index="0">', + ' <LigatureAnchor index="0" empty="1"/>', + ' <LigatureAnchor index="1" empty="1"/>', + " </ComponentRecord>", + ' <ComponentRecord index="1">', + ' <LigatureAnchor index="0" Format="1">', + ' <XCoordinate value="200"/>', + ' <YCoordinate value="400"/>', + " </LigatureAnchor>", + ' <LigatureAnchor index="1" empty="1"/>', + " </ComponentRecord>", + " </LigatureAttach>", + ' <LigatureAttach index="1">', + " <!-- ComponentCount=2 -->", + ' <ComponentRecord index="0">', + ' <LigatureAnchor index="0" Format="1">', + ' <XCoordinate value="500"/>', + ' <YCoordinate value="600"/>', + " </LigatureAnchor>", + ' <LigatureAnchor index="1" Format="1">', + ' <XCoordinate value="500"/>', + ' <YCoordinate value="-20"/>', + " </LigatureAnchor>", + " </ComponentRecord>", + ' <ComponentRecord index="1">', + ' <LigatureAnchor index="0" Format="1">', + ' <XCoordinate value="1300"/>', + ' <YCoordinate value="800"/>', + " </LigatureAnchor>", + ' <LigatureAnchor index="1" Format="1">', + ' <XCoordinate value="1300"/>', + ' <YCoordinate value="-20"/>', + " </LigatureAnchor>", + " </ComponentRecord>", + " </LigatureAttach>", + " </LigatureArray>", + "</MarkLigPos>", + ] def test_buildMarkRecord(self): rec = builder.buildMarkRecord(17, builder.buildAnchor(500, -20)) - self.assertEqual(getXML(rec.toXML), - ['<MarkRecord>', - ' <Class value="17"/>', - ' <MarkAnchor Format="1">', - ' <XCoordinate value="500"/>', - ' <YCoordinate value="-20"/>', - ' </MarkAnchor>', - '</MarkRecord>']) + assert getXML(rec.toXML) == [ + "<MarkRecord>", + ' <Class value="17"/>', + ' <MarkAnchor Format="1">', + ' <XCoordinate value="500"/>', + ' <YCoordinate value="-20"/>', + " </MarkAnchor>", + "</MarkRecord>", + ] def test_buildMark2Record(self): a = builder.buildAnchor rec = builder.buildMark2Record([a(500, -20), None, a(300, -15)]) - self.assertEqual(getXML(rec.toXML), - ['<Mark2Record>', - ' <Mark2Anchor index="0" Format="1">', - ' <XCoordinate value="500"/>', - ' <YCoordinate value="-20"/>', - ' </Mark2Anchor>', - ' <Mark2Anchor index="1" empty="1"/>', - ' <Mark2Anchor index="2" Format="1">', - ' <XCoordinate value="300"/>', - ' <YCoordinate value="-15"/>', - ' </Mark2Anchor>', - '</Mark2Record>']) + assert getXML(rec.toXML) == [ + "<Mark2Record>", + ' <Mark2Anchor index="0" Format="1">', + ' <XCoordinate value="500"/>', + ' <YCoordinate value="-20"/>', + " </Mark2Anchor>", + ' <Mark2Anchor index="1" empty="1"/>', + ' <Mark2Anchor index="2" Format="1">', + ' <XCoordinate value="300"/>', + ' <YCoordinate value="-15"/>', + " </Mark2Anchor>", + "</Mark2Record>", + ] def test_buildPairPosClassesSubtable(self): d20 = builder.buildValue({"XPlacement": -20}) d50 = builder.buildValue({"XPlacement": -50}) d0 = builder.buildValue({}) d8020 = builder.buildValue({"XPlacement": -80, "YPlacement": -20}) - subtable = builder.buildPairPosClassesSubtable({ - (tuple("A",), tuple(["zero"])): (d0, d50), - (tuple("A",), tuple(["one", "two"])): (None, d20), - (tuple(["B", "C"]), tuple(["zero"])): (d8020, d50), - }, self.GLYPHMAP) - self.assertEqual(getXML(subtable.toXML), - ['<PairPos Format="2">', - ' <Coverage>', - ' <Glyph value="A"/>', - ' <Glyph value="B"/>', - ' <Glyph value="C"/>', - ' </Coverage>', - ' <ValueFormat1 value="3"/>', - ' <ValueFormat2 value="1"/>', - ' <ClassDef1>', - ' <ClassDef glyph="A" class="1"/>', - ' </ClassDef1>', - ' <ClassDef2>', - ' <ClassDef glyph="one" class="1"/>', - ' <ClassDef glyph="two" class="1"/>', - ' <ClassDef glyph="zero" class="2"/>', - ' </ClassDef2>', - ' <!-- Class1Count=2 -->', - ' <!-- Class2Count=3 -->', - ' <Class1Record index="0">', - ' <Class2Record index="0">', - ' </Class2Record>', - ' <Class2Record index="1">', - ' </Class2Record>', - ' <Class2Record index="2">', - ' <Value1 XPlacement="-80" YPlacement="-20"/>', - ' <Value2 XPlacement="-50"/>', - ' </Class2Record>', - ' </Class1Record>', - ' <Class1Record index="1">', - ' <Class2Record index="0">', - ' </Class2Record>', - ' <Class2Record index="1">', - ' <Value2 XPlacement="-20"/>', - ' </Class2Record>', - ' <Class2Record index="2">', - ' <Value1/>', - ' <Value2 XPlacement="-50"/>', - ' </Class2Record>', - ' </Class1Record>', - '</PairPos>']) + subtable = builder.buildPairPosClassesSubtable( + { + (tuple("A"), tuple(["zero"])): (d0, d50), + (tuple("A"), tuple(["one", "two"])): (None, d20), + (tuple(["B", "C"]), tuple(["zero"])): (d8020, d50), + }, + self.GLYPHMAP, + ) + assert getXML(subtable.toXML) == [ + '<PairPos Format="2">', + " <Coverage>", + ' <Glyph value="A"/>', + ' <Glyph value="B"/>', + ' <Glyph value="C"/>', + " </Coverage>", + ' <ValueFormat1 value="3"/>', + ' <ValueFormat2 value="1"/>', + " <ClassDef1>", + ' <ClassDef glyph="A" class="1"/>', + " </ClassDef1>", + " <ClassDef2>", + ' <ClassDef glyph="one" class="1"/>', + ' <ClassDef glyph="two" class="1"/>', + ' <ClassDef glyph="zero" class="2"/>', + " </ClassDef2>", + " <!-- Class1Count=2 -->", + " <!-- Class2Count=3 -->", + ' <Class1Record index="0">', + ' <Class2Record index="0">', + " </Class2Record>", + ' <Class2Record index="1">', + " </Class2Record>", + ' <Class2Record index="2">', + ' <Value1 XPlacement="-80" YPlacement="-20"/>', + ' <Value2 XPlacement="-50"/>', + " </Class2Record>", + " </Class1Record>", + ' <Class1Record index="1">', + ' <Class2Record index="0">', + " </Class2Record>", + ' <Class2Record index="1">', + ' <Value2 XPlacement="-20"/>', + " </Class2Record>", + ' <Class2Record index="2">', + " <Value1/>", + ' <Value2 XPlacement="-50"/>', + " </Class2Record>", + " </Class1Record>", + "</PairPos>", + ] def test_buildPairPosGlyphs(self): d50 = builder.buildValue({"XPlacement": -50}) d8020 = builder.buildValue({"XPlacement": -80, "YPlacement": -20}) - subtables = builder.buildPairPosGlyphs({ - ("A", "zero"): (None, d50), - ("A", "one"): (d8020, d50), - }, self.GLYPHMAP) - self.assertEqual(sum([getXML(t.toXML) for t in subtables], []), - ['<PairPos Format="1">', - ' <Coverage>', - ' <Glyph value="A"/>', - ' </Coverage>', - ' <ValueFormat1 value="0"/>', - ' <ValueFormat2 value="1"/>', - ' <!-- PairSetCount=1 -->', - ' <PairSet index="0">', - ' <!-- PairValueCount=1 -->', - ' <PairValueRecord index="0">', - ' <SecondGlyph value="zero"/>', - ' <Value2 XPlacement="-50"/>', - ' </PairValueRecord>', - ' </PairSet>', - '</PairPos>', - '<PairPos Format="1">', - ' <Coverage>', - ' <Glyph value="A"/>', - ' </Coverage>', - ' <ValueFormat1 value="3"/>', - ' <ValueFormat2 value="1"/>', - ' <!-- PairSetCount=1 -->', - ' <PairSet index="0">', - ' <!-- PairValueCount=1 -->', - ' <PairValueRecord index="0">', - ' <SecondGlyph value="one"/>', - ' <Value1 XPlacement="-80" YPlacement="-20"/>', - ' <Value2 XPlacement="-50"/>', - ' </PairValueRecord>', - ' </PairSet>', - '</PairPos>']) + subtables = builder.buildPairPosGlyphs( + {("A", "zero"): (None, d50), ("A", "one"): (d8020, d50)}, self.GLYPHMAP + ) + assert sum([getXML(t.toXML) for t in subtables], []) == [ + '<PairPos Format="1">', + " <Coverage>", + ' <Glyph value="A"/>', + " </Coverage>", + ' <ValueFormat1 value="0"/>', + ' <ValueFormat2 value="1"/>', + " <!-- PairSetCount=1 -->", + ' <PairSet index="0">', + " <!-- PairValueCount=1 -->", + ' <PairValueRecord index="0">', + ' <SecondGlyph value="zero"/>', + ' <Value2 XPlacement="-50"/>', + " </PairValueRecord>", + " </PairSet>", + "</PairPos>", + '<PairPos Format="1">', + " <Coverage>", + ' <Glyph value="A"/>', + " </Coverage>", + ' <ValueFormat1 value="3"/>', + ' <ValueFormat2 value="1"/>', + " <!-- PairSetCount=1 -->", + ' <PairSet index="0">', + " <!-- PairValueCount=1 -->", + ' <PairValueRecord index="0">', + ' <SecondGlyph value="one"/>', + ' <Value1 XPlacement="-80" YPlacement="-20"/>', + ' <Value2 XPlacement="-50"/>', + " </PairValueRecord>", + " </PairSet>", + "</PairPos>", + ] def test_buildPairPosGlyphsSubtable(self): d20 = builder.buildValue({"XPlacement": -20}) d50 = builder.buildValue({"XPlacement": -50}) d0 = builder.buildValue({}) d8020 = builder.buildValue({"XPlacement": -80, "YPlacement": -20}) - subtable = builder.buildPairPosGlyphsSubtable({ - ("A", "zero"): (d0, d50), - ("A", "one"): (None, d20), - ("B", "five"): (d8020, d50), - }, self.GLYPHMAP) - self.assertEqual(getXML(subtable.toXML), - ['<PairPos Format="1">', - ' <Coverage>', - ' <Glyph value="A"/>', - ' <Glyph value="B"/>', - ' </Coverage>', - ' <ValueFormat1 value="3"/>', - ' <ValueFormat2 value="1"/>', - ' <!-- PairSetCount=2 -->', - ' <PairSet index="0">', - ' <!-- PairValueCount=2 -->', - ' <PairValueRecord index="0">', - ' <SecondGlyph value="zero"/>', - ' <Value2 XPlacement="-50"/>', - ' </PairValueRecord>', - ' <PairValueRecord index="1">', - ' <SecondGlyph value="one"/>', - ' <Value2 XPlacement="-20"/>', - ' </PairValueRecord>', - ' </PairSet>', - ' <PairSet index="1">', - ' <!-- PairValueCount=1 -->', - ' <PairValueRecord index="0">', - ' <SecondGlyph value="five"/>', - ' <Value1 XPlacement="-80" YPlacement="-20"/>', - ' <Value2 XPlacement="-50"/>', - ' </PairValueRecord>', - ' </PairSet>', - '</PairPos>']) + subtable = builder.buildPairPosGlyphsSubtable( + { + ("A", "zero"): (d0, d50), + ("A", "one"): (None, d20), + ("B", "five"): (d8020, d50), + }, + self.GLYPHMAP, + ) + assert getXML(subtable.toXML) == [ + '<PairPos Format="1">', + " <Coverage>", + ' <Glyph value="A"/>', + ' <Glyph value="B"/>', + " </Coverage>", + ' <ValueFormat1 value="3"/>', + ' <ValueFormat2 value="1"/>', + " <!-- PairSetCount=2 -->", + ' <PairSet index="0">', + " <!-- PairValueCount=2 -->", + ' <PairValueRecord index="0">', + ' <SecondGlyph value="zero"/>', + ' <Value2 XPlacement="-50"/>', + " </PairValueRecord>", + ' <PairValueRecord index="1">', + ' <SecondGlyph value="one"/>', + ' <Value2 XPlacement="-20"/>', + " </PairValueRecord>", + " </PairSet>", + ' <PairSet index="1">', + " <!-- PairValueCount=1 -->", + ' <PairValueRecord index="0">', + ' <SecondGlyph value="five"/>', + ' <Value1 XPlacement="-80" YPlacement="-20"/>', + ' <Value2 XPlacement="-50"/>', + " </PairValueRecord>", + " </PairSet>", + "</PairPos>", + ] def test_buildSinglePos(self): - subtables = builder.buildSinglePos({ - "one": builder.buildValue({"XPlacement": 500}), - "two": builder.buildValue({"XPlacement": 500}), - "three": builder.buildValue({"XPlacement": 200}), - "four": builder.buildValue({"XPlacement": 400}), - "five": builder.buildValue({"XPlacement": 500}), - "six": builder.buildValue({"YPlacement": -6}), - }, self.GLYPHMAP) - self.assertEqual(sum([getXML(t.toXML) for t in subtables], []), - ['<SinglePos Format="2">', - ' <Coverage>', - ' <Glyph value="one"/>', - ' <Glyph value="two"/>', - ' <Glyph value="three"/>', - ' <Glyph value="four"/>', - ' <Glyph value="five"/>', - ' </Coverage>', - ' <ValueFormat value="1"/>', - ' <!-- ValueCount=5 -->', - ' <Value index="0" XPlacement="500"/>', - ' <Value index="1" XPlacement="500"/>', - ' <Value index="2" XPlacement="200"/>', - ' <Value index="3" XPlacement="400"/>', - ' <Value index="4" XPlacement="500"/>', - '</SinglePos>', - '<SinglePos Format="1">', - ' <Coverage>', - ' <Glyph value="six"/>', - ' </Coverage>', - ' <ValueFormat value="2"/>', - ' <Value YPlacement="-6"/>', - '</SinglePos>']) + subtables = builder.buildSinglePos( + { + "one": builder.buildValue({"XPlacement": 500}), + "two": builder.buildValue({"XPlacement": 500}), + "three": builder.buildValue({"XPlacement": 200}), + "four": builder.buildValue({"XPlacement": 400}), + "five": builder.buildValue({"XPlacement": 500}), + "six": builder.buildValue({"YPlacement": -6}), + }, + self.GLYPHMAP, + ) + assert sum([getXML(t.toXML) for t in subtables], []) == [ + '<SinglePos Format="2">', + " <Coverage>", + ' <Glyph value="one"/>', + ' <Glyph value="two"/>', + ' <Glyph value="three"/>', + ' <Glyph value="four"/>', + ' <Glyph value="five"/>', + " </Coverage>", + ' <ValueFormat value="1"/>', + " <!-- ValueCount=5 -->", + ' <Value index="0" XPlacement="500"/>', + ' <Value index="1" XPlacement="500"/>', + ' <Value index="2" XPlacement="200"/>', + ' <Value index="3" XPlacement="400"/>', + ' <Value index="4" XPlacement="500"/>', + "</SinglePos>", + '<SinglePos Format="1">', + " <Coverage>", + ' <Glyph value="six"/>', + " </Coverage>", + ' <ValueFormat value="2"/>', + ' <Value YPlacement="-6"/>', + "</SinglePos>", + ] def test_buildSinglePos_ValueFormat0(self): - subtables = builder.buildSinglePos({ - "zero": builder.buildValue({}) - }, self.GLYPHMAP) - self.assertEqual(sum([getXML(t.toXML) for t in subtables], []), - ['<SinglePos Format="1">', - ' <Coverage>', - ' <Glyph value="zero"/>', - ' </Coverage>', - ' <ValueFormat value="0"/>', - '</SinglePos>']) + subtables = builder.buildSinglePos( + {"zero": builder.buildValue({})}, self.GLYPHMAP + ) + assert sum([getXML(t.toXML) for t in subtables], []) == [ + '<SinglePos Format="1">', + " <Coverage>", + ' <Glyph value="zero"/>', + " </Coverage>", + ' <ValueFormat value="0"/>', + "</SinglePos>", + ] def test_buildSinglePosSubtable_format1(self): - subtable = builder.buildSinglePosSubtable({ - "one": builder.buildValue({"XPlacement": 777}), - "two": builder.buildValue({"XPlacement": 777}), - }, self.GLYPHMAP) - self.assertEqual(getXML(subtable.toXML), - ['<SinglePos Format="1">', - ' <Coverage>', - ' <Glyph value="one"/>', - ' <Glyph value="two"/>', - ' </Coverage>', - ' <ValueFormat value="1"/>', - ' <Value XPlacement="777"/>', - '</SinglePos>']) + subtable = builder.buildSinglePosSubtable( + { + "one": builder.buildValue({"XPlacement": 777}), + "two": builder.buildValue({"XPlacement": 777}), + }, + self.GLYPHMAP, + ) + assert getXML(subtable.toXML) == [ + '<SinglePos Format="1">', + " <Coverage>", + ' <Glyph value="one"/>', + ' <Glyph value="two"/>', + " </Coverage>", + ' <ValueFormat value="1"/>', + ' <Value XPlacement="777"/>', + "</SinglePos>", + ] def test_buildSinglePosSubtable_format2(self): - subtable = builder.buildSinglePosSubtable({ - "one": builder.buildValue({"XPlacement": 777}), - "two": builder.buildValue({"YPlacement": -888}), - }, self.GLYPHMAP) - self.assertEqual(getXML(subtable.toXML), - ['<SinglePos Format="2">', - ' <Coverage>', - ' <Glyph value="one"/>', - ' <Glyph value="two"/>', - ' </Coverage>', - ' <ValueFormat value="3"/>', - ' <!-- ValueCount=2 -->', - ' <Value index="0" XPlacement="777"/>', - ' <Value index="1" YPlacement="-888"/>', - '</SinglePos>']) + subtable = builder.buildSinglePosSubtable( + { + "one": builder.buildValue({"XPlacement": 777}), + "two": builder.buildValue({"YPlacement": -888}), + }, + self.GLYPHMAP, + ) + assert getXML(subtable.toXML) == [ + '<SinglePos Format="2">', + " <Coverage>", + ' <Glyph value="one"/>', + ' <Glyph value="two"/>', + " </Coverage>", + ' <ValueFormat value="3"/>', + " <!-- ValueCount=2 -->", + ' <Value index="0" XPlacement="777"/>', + ' <Value index="1" YPlacement="-888"/>', + "</SinglePos>", + ] def test_buildValue(self): value = builder.buildValue({"XPlacement": 7, "YPlacement": 23}) func = lambda writer, font: value.toXML(writer, font, valueName="Val") - self.assertEqual(getXML(func), - ['<Val XPlacement="7" YPlacement="23"/>']) + assert getXML(func) == ['<Val XPlacement="7" YPlacement="23"/>'] def test_getLigatureKey(self): components = lambda s: [tuple(word) for word in s.split()] c = components("fi fl ff ffi fff") c.sort(key=builder._getLigatureKey) - self.assertEqual(c, components("fff ffi ff fi fl")) + assert c == components("fff ffi ff fi fl") def test_getSinglePosValueKey(self): - device = builder.buildDevice({10:1, 11:3}) + device = builder.buildDevice({10: 1, 11: 3}) a1 = builder.buildValue({"XPlacement": 500, "XPlaDevice": device}) a2 = builder.buildValue({"XPlacement": 500, "XPlaDevice": device}) b = builder.buildValue({"XPlacement": 500}) keyA1 = builder._getSinglePosValueKey(a1) keyA2 = builder._getSinglePosValueKey(a1) keyB = builder._getSinglePosValueKey(b) - self.assertEqual(keyA1, keyA2) - self.assertEqual(hash(keyA1), hash(keyA2)) - self.assertNotEqual(keyA1, keyB) - self.assertNotEqual(hash(keyA1), hash(keyB)) + assert keyA1 == keyA2 + assert hash(keyA1) == hash(keyA2) + assert keyA1 != keyB + assert hash(keyA1) != hash(keyB) -class ClassDefBuilderTest(unittest.TestCase): +class ClassDefBuilderTest(object): def test_build_usingClass0(self): b = builder.ClassDefBuilder(useClass0=True) b.add({"aa", "bb"}) @@ -1015,14 +1075,8 @@ class ClassDefBuilderTest(unittest.TestCase): b.add({"c"}) b.add({"e", "f", "g", "h"}) cdef = b.build() - self.assertIsInstance(cdef, otTables.ClassDef) - self.assertEqual(cdef.classDefs, { - "a": 2, - "b": 2, - "c": 3, - "aa": 1, - "bb": 1 - }) + assert isinstance(cdef, otTables.ClassDef) + assert cdef.classDefs == {"a": 2, "b": 2, "c": 3, "aa": 1, "bb": 1} def test_build_notUsingClass0(self): b = builder.ClassDefBuilder(useClass0=False) @@ -1030,30 +1084,31 @@ class ClassDefBuilderTest(unittest.TestCase): b.add({"c"}) b.add({"e", "f", "g", "h"}) cdef = b.build() - self.assertIsInstance(cdef, otTables.ClassDef) - self.assertEqual(cdef.classDefs, { + assert isinstance(cdef, otTables.ClassDef) + assert cdef.classDefs == { "a": 2, "b": 2, "c": 3, "e": 1, "f": 1, "g": 1, - "h": 1 - }) + "h": 1, + } def test_canAdd(self): b = builder.ClassDefBuilder(useClass0=True) b.add({"a", "b", "c", "d"}) b.add({"e", "f"}) - self.assertTrue(b.canAdd({"a", "b", "c", "d"})) - self.assertTrue(b.canAdd({"e", "f"})) - self.assertTrue(b.canAdd({"g", "h", "i"})) - self.assertFalse(b.canAdd({"b", "c", "d"})) - self.assertFalse(b.canAdd({"a", "b", "c", "d", "e", "f"})) - self.assertFalse(b.canAdd({"d", "e", "f"})) - self.assertFalse(b.canAdd({"f"})) + assert b.canAdd({"a", "b", "c", "d"}) + assert b.canAdd({"e", "f"}) + assert b.canAdd({"g", "h", "i"}) + assert not b.canAdd({"b", "c", "d"}) + assert not b.canAdd({"a", "b", "c", "d", "e", "f"}) + assert not b.canAdd({"d", "e", "f"}) + assert not b.canAdd({"f"}) if __name__ == "__main__": import sys - sys.exit(unittest.main()) + + sys.exit(pytest.main(sys.argv)) diff --git a/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w0.00.ttx b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w0.00.ttx index 57731584..f6568fd8 100644 --- a/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w0.00.ttx +++ b/Tests/varLib/data/master_sparse_cff2/MasterSet_Kanji-w0.00.ttx @@ -374,6 +374,7 @@ <FontName value="MasterSet_Kanji-w0.00-Generic"/> <Private> <BlueValues value="-250 -250 1100 1100"/> + <FamilyBlues value="-250 -240 1100 1110"/> <BlueScale value="0.039625"/> <BlueShift value="7"/> <BlueFuzz value="0"/> diff --git a/Tests/varLib/data/test_results/BuildMain.ttx b/Tests/varLib/data/test_results/BuildMain.ttx index 7150a579..7e5d9561 100644 --- a/Tests/varLib/data/test_results/BuildMain.ttx +++ b/Tests/varLib/data/test_results/BuildMain.ttx @@ -55,7 +55,7 @@ will be recalculated by the compiler --> <version value="4"/> <xAvgCharWidth value="506"/> - <usWeightClass value="400"/> + <usWeightClass value="368"/> <usWidthClass value="5"/> <fsType value="00000000 00000100"/> <ySubscriptXSize value="650"/> diff --git a/Tests/varLib/data/test_results/BuildTestCFF2.ttx b/Tests/varLib/data/test_results/BuildTestCFF2.ttx index 91b83fc0..29044525 100644 --- a/Tests/varLib/data/test_results/BuildTestCFF2.ttx +++ b/Tests/varLib/data/test_results/BuildTestCFF2.ttx @@ -86,10 +86,10 @@ <BlueShift value="7"/> <BlueFuzz value="0"/> <StdHW> - <blend value="67 -39.0 67.0"/> + <blend value="67 -39 67"/> </StdHW> <StdVW> - <blend value="85 -51.0 87.0"/> + <blend value="85 -51 87"/> </StdVW> </Private> </FontDict> diff --git a/Tests/varLib/data/test_results/SparseMasters.ttx b/Tests/varLib/data/test_results/SparseMasters.ttx index 06e58c92..c2aa335c 100644 --- a/Tests/varLib/data/test_results/SparseMasters.ttx +++ b/Tests/varLib/data/test_results/SparseMasters.ttx @@ -55,7 +55,7 @@ will be recalculated by the compiler --> <version value="4"/> <xAvgCharWidth value="580"/> - <usWeightClass value="400"/> + <usWeightClass value="350"/> <usWidthClass value="5"/> <fsType value="00000000 00000100"/> <ySubscriptXSize value="650"/> diff --git a/Tests/varLib/data/test_results/TestNonMarkingCFF2.ttx b/Tests/varLib/data/test_results/TestNonMarkingCFF2.ttx index 5ded5b91..26bd7ba7 100644 --- a/Tests/varLib/data/test_results/TestNonMarkingCFF2.ttx +++ b/Tests/varLib/data/test_results/TestNonMarkingCFF2.ttx @@ -31,10 +31,10 @@ <BlueShift value="7"/> <BlueFuzz value="0"/> <StdHW> - <blend value="28 39.0"/> + <blend value="28 39"/> </StdHW> <StdVW> - <blend value="34 51.0"/> + <blend value="34 51"/> </StdVW> </Private> </FontDict> diff --git a/Tests/varLib/data/test_results/TestSparseCFF2VF.ttx b/Tests/varLib/data/test_results/TestSparseCFF2VF.ttx index 442ae91e..f05f62f7 100644 --- a/Tests/varLib/data/test_results/TestSparseCFF2VF.ttx +++ b/Tests/varLib/data/test_results/TestSparseCFF2VF.ttx @@ -132,6 +132,7 @@ <FontDict index="0"> <Private> <BlueValues value="-250 -250 1100 1100"/> + <FamilyBlues value="-250 -240 1100 1110"/> <BlueScale value="0.039625"/> <BlueShift value="7"/> <BlueFuzz value="0"/> diff --git a/Tests/varLib/varLib_test.py b/Tests/varLib/varLib_test.py index e29befbf..ec05d565 100644 --- a/Tests/varLib/varLib_test.py +++ b/Tests/varLib/varLib_test.py @@ -4,6 +4,7 @@ from fontTools.ttLib import TTFont, newTable from fontTools.varLib import build from fontTools.varLib.mutator import instantiateVariableFont from fontTools.varLib import main as varLib_main, load_masters +from fontTools.varLib import set_default_weight_width_slant from fontTools.designspaceLib import ( DesignSpaceDocumentError, DesignSpaceDocument, SourceDescriptor, ) @@ -661,5 +662,81 @@ def _extract_flat_kerning(font, pairpos_table): return extracted_kerning +@pytest.fixture +def ttFont(): + f = TTFont() + f["OS/2"] = newTable("OS/2") + f["OS/2"].usWeightClass = 400 + f["OS/2"].usWidthClass = 100 + f["post"] = newTable("post") + f["post"].italicAngle = 0 + return f + + +class SetDefaultWeightWidthSlantTest(object): + @pytest.mark.parametrize( + "location, expected", + [ + ({"wght": 0}, 1), + ({"wght": 1}, 1), + ({"wght": 100}, 100), + ({"wght": 1000}, 1000), + ({"wght": 1001}, 1000), + ], + ) + def test_wght(self, ttFont, location, expected): + set_default_weight_width_slant(ttFont, location) + + assert ttFont["OS/2"].usWeightClass == expected + + @pytest.mark.parametrize( + "location, expected", + [ + ({"wdth": 0}, 1), + ({"wdth": 56}, 1), + ({"wdth": 57}, 2), + ({"wdth": 62.5}, 2), + ({"wdth": 75}, 3), + ({"wdth": 87.5}, 4), + ({"wdth": 100}, 5), + ({"wdth": 112.5}, 6), + ({"wdth": 125}, 7), + ({"wdth": 150}, 8), + ({"wdth": 200}, 9), + ({"wdth": 201}, 9), + ({"wdth": 1000}, 9), + ], + ) + def test_wdth(self, ttFont, location, expected): + set_default_weight_width_slant(ttFont, location) + + assert ttFont["OS/2"].usWidthClass == expected + + @pytest.mark.parametrize( + "location, expected", + [ + ({"slnt": -91}, -90), + ({"slnt": -90}, -90), + ({"slnt": 0}, 0), + ({"slnt": 11.5}, 11.5), + ({"slnt": 90}, 90), + ({"slnt": 91}, 90), + ], + ) + def test_slnt(self, ttFont, location, expected): + set_default_weight_width_slant(ttFont, location) + + assert ttFont["post"].italicAngle == expected + + def test_all(self, ttFont): + set_default_weight_width_slant( + ttFont, {"wght": 500, "wdth": 150, "slnt": -12.0} + ) + + assert ttFont["OS/2"].usWeightClass == 500 + assert ttFont["OS/2"].usWidthClass == 8 + assert ttFont["post"].italicAngle == -12.0 + + if __name__ == "__main__": sys.exit(unittest.main()) @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.43.1 +current_version = 3.43.2 commit = True tag = False tag_name = {new_version} @@ -352,7 +352,7 @@ def find_data_files(manpath="share/man"): setup( name="fonttools", - version="3.43.1", + version="3.43.2", description="Tools to manipulate font files", author="Just van Rossum", author_email="just@letterror.com", |