We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[varLib.mutator] Does not mutate weight/width/italicAngle
Because those are not in MVAR, but in standard axes. Quoting MVAR spec:

> Note: The OS/2.usWeightClass, OS/2.usWidthClass and post.italicAngle values are not supported by variation data in the 'MVAR' table. This is because values for these three fields correspond directly to input axis values for the 'wght', 'wdth' and 'slnt' variation axes. See the discussion of these axes in the OpenType Design-Variation Axis Tag Registry for details on the relationship between these fields and the corresponding design axes.

There's probably some binary flags that we can also tweak, re boldness, italicness, etc of the font.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `Lib/fontTools/varLib/mutator.py`
Content:
```
1 """
2 Instantiate a variation font.  Run, eg:
3 
4 $ fonttools varLib.mutator ./NotoSansArabic-VF.ttf wght=140 wdth=85
5 """
6 from __future__ import print_function, division, absolute_import
7 from fontTools.misc.py23 import *
8 from fontTools.misc.fixedTools import floatToFixedToFloat, otRound
9 from fontTools.ttLib import TTFont
10 from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
11 from fontTools.varLib import _GetCoordinates, _SetCoordinates, _DesignspaceAxis
12 from fontTools.varLib.models import supportScalar, normalizeLocation
13 from fontTools.varLib.merger import MutatorMerger
14 from fontTools.varLib.varStore import VarStoreInstancer
15 from fontTools.varLib.mvar import MVAR_ENTRIES
16 from fontTools.varLib.iup import iup_delta
17 import os.path
18 import logging
19 
20 
21 log = logging.getLogger("fontTools.varlib.mutator")
22 
23 
24 def instantiateVariableFont(varfont, location, inplace=False):
25 	""" Generate a static instance from a variable TTFont and a dictionary
26 	defining the desired location along the variable font's axes.
27 	The location values must be specified as user-space coordinates, e.g.:
28 
29 		{'wght': 400, 'wdth': 100}
30 
31 	By default, a new TTFont object is returned. If ``inplace`` is True, the
32 	input varfont is modified and reduced to a static font.
33 	"""
34 	if not inplace:
35 		# make a copy to leave input varfont unmodified
36 		stream = BytesIO()
37 		varfont.save(stream)
38 		stream.seek(0)
39 		varfont = TTFont(stream)
40 
41 	fvar = varfont['fvar']
42 	axes = {a.axisTag:(a.minValue,a.defaultValue,a.maxValue) for a in fvar.axes}
43 	loc = normalizeLocation(location, axes)
44 	if 'avar' in varfont:
45 		maps = varfont['avar'].segments
46 		loc = {k:_DesignspaceAxis._map(v, maps[k]) for k,v in loc.items()}
47 	# Quantize to F2Dot14, to avoid surprise interpolations.
48 	loc = {k:floatToFixedToFloat(v, 14) for k,v in loc.items()}
49 	# Location is normalized now
50 	log.info("Normalized location: %s", loc)
51 
52 	log.info("Mutating glyf/gvar tables")
53 	gvar = varfont['gvar']
54 	glyf = varfont['glyf']
55 	# get list of glyph names in gvar sorted by component depth
56 	glyphnames = sorted(
57 		gvar.variations.keys(),
58 		key=lambda name: (
59 			glyf[name].getCompositeMaxpValues(glyf).maxComponentDepth
60 			if glyf[name].isComposite() else 0,
61 			name))
62 	for glyphname in glyphnames:
63 		variations = gvar.variations[glyphname]
64 		coordinates,_ = _GetCoordinates(varfont, glyphname)
65 		origCoords, endPts = None, None
66 		for var in variations:
67 			scalar = supportScalar(loc, var.axes)
68 			if not scalar: continue
69 			delta = var.coordinates
70 			if None in delta:
71 				if origCoords is None:
72 					origCoords,control = _GetCoordinates(varfont, glyphname)
73 					endPts = control[1] if control[0] >= 1 else list(range(len(control[1])))
74 				delta = iup_delta(delta, origCoords, endPts)
75 			coordinates += GlyphCoordinates(delta) * scalar
76 		_SetCoordinates(varfont, glyphname, coordinates)
77 
78 	if 'cvar' in varfont:
79 		log.info("Mutating cvt/cvar tables")
80 		cvar = varfont['cvar']
81 		cvt = varfont['cvt ']
82 		deltas = {}
83 		for var in cvar.variations:
84 			scalar = supportScalar(loc, var.axes)
85 			if not scalar: continue
86 			for i, c in enumerate(var.coordinates):
87 				if c is not None:
88 					deltas[i] = deltas.get(i, 0) + scalar * c
89 		for i, delta in deltas.items():
90 			cvt[i] += otRound(delta)
91 
92 	if 'MVAR' in varfont:
93 		log.info("Mutating MVAR table")
94 		mvar = varfont['MVAR'].table
95 		varStoreInstancer = VarStoreInstancer(mvar.VarStore, fvar.axes, loc)
96 		records = mvar.ValueRecord
97 		for rec in records:
98 			mvarTag = rec.ValueTag
99 			if mvarTag not in MVAR_ENTRIES:
100 				continue
101 			tableTag, itemName = MVAR_ENTRIES[mvarTag]
102 			delta = otRound(varStoreInstancer[rec.VarIdx])
103 			if not delta:
104 				continue
105 			setattr(varfont[tableTag], itemName,
106 				getattr(varfont[tableTag], itemName) + delta)
107 
108 	if 'GDEF' in varfont:
109 		log.info("Mutating GDEF/GPOS/GSUB tables")
110 		merger = MutatorMerger(varfont, loc)
111 
112 		log.info("Building interpolated tables")
113 		merger.instantiate()
114 
115 	if 'name' in varfont:
116 		log.info("Pruning name table")
117 		exclude = {a.axisNameID for a in fvar.axes}
118 		for i in fvar.instances:
119 			exclude.add(i.subfamilyNameID)
120 			exclude.add(i.postscriptNameID)
121 		varfont['name'].names[:] = [
122 			n for n in varfont['name'].names
123 			if n.nameID not in exclude
124 		]
125 
126 	log.info("Removing variable tables")
127 	for tag in ('avar','cvar','fvar','gvar','HVAR','MVAR','VVAR','STAT'):
128 		if tag in varfont:
129 			del varfont[tag]
130 
131 	return varfont
132 
133 
134 def main(args=None):
135 	from fontTools import configLogger
136 	import argparse
137 
138 	parser = argparse.ArgumentParser(
139 		"fonttools varLib.mutator", description="Instantiate a variable font")
140 	parser.add_argument(
141 		"input", metavar="INPUT.ttf", help="Input variable TTF file.")
142 	parser.add_argument(
143 		"locargs", metavar="AXIS=LOC", nargs="*",
144 		help="List of space separated locations. A location consist in "
145 		"the name of a variation axis, followed by '=' and a number. E.g.: "
146 		" wght=700 wdth=80. The default is the location of the base master.")
147 	parser.add_argument(
148 		"-o", "--output", metavar="OUTPUT.ttf", default=None,
149 		help="Output instance TTF file (default: INPUT-instance.ttf).")
150 	logging_group = parser.add_mutually_exclusive_group(required=False)
151 	logging_group.add_argument(
152 		"-v", "--verbose", action="store_true", help="Run more verbosely.")
153 	logging_group.add_argument(
154 		"-q", "--quiet", action="store_true", help="Turn verbosity off.")
155 	options = parser.parse_args(args)
156 
157 	varfilename = options.input
158 	outfile = (
159 		os.path.splitext(varfilename)[0] + '-instance.ttf'
160 		if not options.output else options.output)
161 	configLogger(level=(
162 		"DEBUG" if options.verbose else
163 		"ERROR" if options.quiet else
164 		"INFO"))
165 
166 	loc = {}
167 	for arg in options.locargs:
168 		try:
169 			tag, val = arg.split('=')
170 			assert len(tag) <= 4
171 			loc[tag.ljust(4)] = float(val)
172 		except (ValueError, AssertionError):
173 			parser.error("invalid location argument format: %r" % arg)
174 	log.info("Location: %s", loc)
175 
176 	log.info("Loading variable font")
177 	varfont = TTFont(varfilename)
178 
179 	instantiateVariableFont(varfont, loc, inplace=True)
180 
181 	log.info("Saving instance font %s", outfile)
182 	varfont.save(outfile)
183 
184 
185 if __name__ == "__main__":
186 	import sys
187 	if len(sys.argv) > 1:
188 		sys.exit(main())
189 	import doctest
190 	sys.exit(doctest.testmod().failed)
191
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/Lib/fontTools/varLib/mutator.py b/Lib/fontTools/varLib/mutator.py
--- a/Lib/fontTools/varLib/mutator.py
+++ b/Lib/fontTools/varLib/mutator.py
@@ -20,6 +20,13 @@
 
 log = logging.getLogger("fontTools.varlib.mutator")
 
+# map 'wdth' axis (1..200) to OS/2.usWidthClass (1..9), rounding to closest
+OS2_WIDTH_CLASS_VALUES = {}
+percents = [50.0, 62.5, 75.0, 87.5, 100.0, 112.5, 125.0, 150.0, 200.0]
+for i, (prev, curr) in enumerate(zip(percents[:-1], percents[1:]), start=1):
+	half = (prev + curr) / 2
+	OS2_WIDTH_CLASS_VALUES[half] = i
+
 
 def instantiateVariableFont(varfont, location, inplace=False):
 	""" Generate a static instance from a variable TTFont and a dictionary
@@ -123,6 +130,21 @@
 			if n.nameID not in exclude
 		]
 
+	if "wght" in location and "OS/2" in varfont:
+		varfont["OS/2"].usWeightClass = otRound(
+			max(1, min(location["wght"], 1000))
+		)
+	if "wdth" in location:
+		wdth = location["wdth"]
+		for percent, widthClass in sorted(OS2_WIDTH_CLASS_VALUES.items()):
+			if wdth < percent:
+				varfont["OS/2"].usWidthClass = widthClass
+				break
+		else:
+			varfont["OS/2"].usWidthClass = 9
+	if "slnt" in location and "post" in varfont:
+		varfont["post"].italicAngle = max(-90, min(location["slnt"], 90))
+
 	log.info("Removing variable tables")
 	for tag in ('avar','cvar','fvar','gvar','HVAR','MVAR','VVAR','STAT'):
 		if tag in varfont:

{"golden_diff": "diff --git a/Lib/fontTools/varLib/mutator.py b/Lib/fontTools/varLib/mutator.py\n--- a/Lib/fontTools/varLib/mutator.py\n+++ b/Lib/fontTools/varLib/mutator.py\n@@ -20,6 +20,13 @@\n \n log = logging.getLogger(\"fontTools.varlib.mutator\")\n \n+# map 'wdth' axis (1..200) to OS/2.usWidthClass (1..9), rounding to closest\n+OS2_WIDTH_CLASS_VALUES = {}\n+percents = [50.0, 62.5, 75.0, 87.5, 100.0, 112.5, 125.0, 150.0, 200.0]\n+for i, (prev, curr) in enumerate(zip(percents[:-1], percents[1:]), start=1):\n+\thalf = (prev + curr) / 2\n+\tOS2_WIDTH_CLASS_VALUES[half] = i\n+\n \n def instantiateVariableFont(varfont, location, inplace=False):\n \t\"\"\" Generate a static instance from a variable TTFont and a dictionary\n@@ -123,6 +130,21 @@\n \t\t\tif n.nameID not in exclude\n \t\t]\n \n+\tif \"wght\" in location and \"OS/2\" in varfont:\n+\t\tvarfont[\"OS/2\"].usWeightClass = otRound(\n+\t\t\tmax(1, min(location[\"wght\"], 1000))\n+\t\t)\n+\tif \"wdth\" in location:\n+\t\twdth = location[\"wdth\"]\n+\t\tfor percent, widthClass in sorted(OS2_WIDTH_CLASS_VALUES.items()):\n+\t\t\tif wdth < percent:\n+\t\t\t\tvarfont[\"OS/2\"].usWidthClass = widthClass\n+\t\t\t\tbreak\n+\t\telse:\n+\t\t\tvarfont[\"OS/2\"].usWidthClass = 9\n+\tif \"slnt\" in location and \"post\" in varfont:\n+\t\tvarfont[\"post\"].italicAngle = max(-90, min(location[\"slnt\"], 90))\n+\n \tlog.info(\"Removing variable tables\")\n \tfor tag in ('avar','cvar','fvar','gvar','HVAR','MVAR','VVAR','STAT'):\n \t\tif tag in varfont:\n", "issue": "[varLib.mutator] Does not mutate weight/width/italicAngle\nBecause those are not in MVAR, but in standard axes. Quoting MVAR spec:\r\n\r\n> Note: The OS/2.usWeightClass, OS/2.usWidthClass and post.italicAngle values are not supported by variation data in the 'MVAR' table. This is because values for these three fields correspond directly to input axis values for the 'wght', 'wdth' and 'slnt' variation axes. See the discussion of these axes in the OpenType Design-Variation Axis Tag Registry for details on the relationship between these fields and the corresponding design axes.\r\n\r\nThere's probably some binary flags that we can also tweak, re boldness, italicness, etc of the font.\n", "before_files": [{"content": "\"\"\"\nInstantiate a variation font.  Run, eg:\n\n$ fonttools varLib.mutator ./NotoSansArabic-VF.ttf wght=140 wdth=85\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\nfrom fontTools.misc.py23 import *\nfrom fontTools.misc.fixedTools import floatToFixedToFloat, otRound\nfrom fontTools.ttLib import TTFont\nfrom fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates\nfrom fontTools.varLib import _GetCoordinates, _SetCoordinates, _DesignspaceAxis\nfrom fontTools.varLib.models import supportScalar, normalizeLocation\nfrom fontTools.varLib.merger import MutatorMerger\nfrom fontTools.varLib.varStore import VarStoreInstancer\nfrom fontTools.varLib.mvar import MVAR_ENTRIES\nfrom fontTools.varLib.iup import iup_delta\nimport os.path\nimport logging\n\n\nlog = logging.getLogger(\"fontTools.varlib.mutator\")\n\n\ndef instantiateVariableFont(varfont, location, inplace=False):\n\t\"\"\" Generate a static instance from a variable TTFont and a dictionary\n\tdefining the desired location along the variable font's axes.\n\tThe location values must be specified as user-space coordinates, e.g.:\n\n\t\t{'wght': 400, 'wdth': 100}\n\n\tBy default, a new TTFont object is returned. If ``inplace`` is True, the\n\tinput varfont is modified and reduced to a static font.\n\t\"\"\"\n\tif not inplace:\n\t\t# make a copy to leave input varfont unmodified\n\t\tstream = BytesIO()\n\t\tvarfont.save(stream)\n\t\tstream.seek(0)\n\t\tvarfont = TTFont(stream)\n\n\tfvar = varfont['fvar']\n\taxes = {a.axisTag:(a.minValue,a.defaultValue,a.maxValue) for a in fvar.axes}\n\tloc = normalizeLocation(location, axes)\n\tif 'avar' in varfont:\n\t\tmaps = varfont['avar'].segments\n\t\tloc = {k:_DesignspaceAxis._map(v, maps[k]) for k,v in loc.items()}\n\t# Quantize to F2Dot14, to avoid surprise interpolations.\n\tloc = {k:floatToFixedToFloat(v, 14) for k,v in loc.items()}\n\t# Location is normalized now\n\tlog.info(\"Normalized location: %s\", loc)\n\n\tlog.info(\"Mutating glyf/gvar tables\")\n\tgvar = varfont['gvar']\n\tglyf = varfont['glyf']\n\t# get list of glyph names in gvar sorted by component depth\n\tglyphnames = sorted(\n\t\tgvar.variations.keys(),\n\t\tkey=lambda name: (\n\t\t\tglyf[name].getCompositeMaxpValues(glyf).maxComponentDepth\n\t\t\tif glyf[name].isComposite() else 0,\n\t\t\tname))\n\tfor glyphname in glyphnames:\n\t\tvariations = gvar.variations[glyphname]\n\t\tcoordinates,_ = _GetCoordinates(varfont, glyphname)\n\t\torigCoords, endPts = None, None\n\t\tfor var in variations:\n\t\t\tscalar = supportScalar(loc, var.axes)\n\t\t\tif not scalar: continue\n\t\t\tdelta = var.coordinates\n\t\t\tif None in delta:\n\t\t\t\tif origCoords is None:\n\t\t\t\t\torigCoords,control = _GetCoordinates(varfont, glyphname)\n\t\t\t\t\tendPts = control[1] if control[0] >= 1 else list(range(len(control[1])))\n\t\t\t\tdelta = iup_delta(delta, origCoords, endPts)\n\t\t\tcoordinates += GlyphCoordinates(delta) * scalar\n\t\t_SetCoordinates(varfont, glyphname, coordinates)\n\n\tif 'cvar' in varfont:\n\t\tlog.info(\"Mutating cvt/cvar tables\")\n\t\tcvar = varfont['cvar']\n\t\tcvt = varfont['cvt ']\n\t\tdeltas = {}\n\t\tfor var in cvar.variations:\n\t\t\tscalar = supportScalar(loc, var.axes)\n\t\t\tif not scalar: continue\n\t\t\tfor i, c in enumerate(var.coordinates):\n\t\t\t\tif c is not None:\n\t\t\t\t\tdeltas[i] = deltas.get(i, 0) + scalar * c\n\t\tfor i, delta in deltas.items():\n\t\t\tcvt[i] += otRound(delta)\n\n\tif 'MVAR' in varfont:\n\t\tlog.info(\"Mutating MVAR table\")\n\t\tmvar = varfont['MVAR'].table\n\t\tvarStoreInstancer = VarStoreInstancer(mvar.VarStore, fvar.axes, loc)\n\t\trecords = mvar.ValueRecord\n\t\tfor rec in records:\n\t\t\tmvarTag = rec.ValueTag\n\t\t\tif mvarTag not in MVAR_ENTRIES:\n\t\t\t\tcontinue\n\t\t\ttableTag, itemName = MVAR_ENTRIES[mvarTag]\n\t\t\tdelta = otRound(varStoreInstancer[rec.VarIdx])\n\t\t\tif not delta:\n\t\t\t\tcontinue\n\t\t\tsetattr(varfont[tableTag], itemName,\n\t\t\t\tgetattr(varfont[tableTag], itemName) + delta)\n\n\tif 'GDEF' in varfont:\n\t\tlog.info(\"Mutating GDEF/GPOS/GSUB tables\")\n\t\tmerger = MutatorMerger(varfont, loc)\n\n\t\tlog.info(\"Building interpolated tables\")\n\t\tmerger.instantiate()\n\n\tif 'name' in varfont:\n\t\tlog.info(\"Pruning name table\")\n\t\texclude = {a.axisNameID for a in fvar.axes}\n\t\tfor i in fvar.instances:\n\t\t\texclude.add(i.subfamilyNameID)\n\t\t\texclude.add(i.postscriptNameID)\n\t\tvarfont['name'].names[:] = [\n\t\t\tn for n in varfont['name'].names\n\t\t\tif n.nameID not in exclude\n\t\t]\n\n\tlog.info(\"Removing variable tables\")\n\tfor tag in ('avar','cvar','fvar','gvar','HVAR','MVAR','VVAR','STAT'):\n\t\tif tag in varfont:\n\t\t\tdel varfont[tag]\n\n\treturn varfont\n\n\ndef main(args=None):\n\tfrom fontTools import configLogger\n\timport argparse\n\n\tparser = argparse.ArgumentParser(\n\t\t\"fonttools varLib.mutator\", description=\"Instantiate a variable font\")\n\tparser.add_argument(\n\t\t\"input\", metavar=\"INPUT.ttf\", help=\"Input variable TTF file.\")\n\tparser.add_argument(\n\t\t\"locargs\", metavar=\"AXIS=LOC\", nargs=\"*\",\n\t\thelp=\"List of space separated locations. A location consist in \"\n\t\t\"the name of a variation axis, followed by '=' and a number. E.g.: \"\n\t\t\" wght=700 wdth=80. The default is the location of the base master.\")\n\tparser.add_argument(\n\t\t\"-o\", \"--output\", metavar=\"OUTPUT.ttf\", default=None,\n\t\thelp=\"Output instance TTF file (default: INPUT-instance.ttf).\")\n\tlogging_group = parser.add_mutually_exclusive_group(required=False)\n\tlogging_group.add_argument(\n\t\t\"-v\", \"--verbose\", action=\"store_true\", help=\"Run more verbosely.\")\n\tlogging_group.add_argument(\n\t\t\"-q\", \"--quiet\", action=\"store_true\", help=\"Turn verbosity off.\")\n\toptions = parser.parse_args(args)\n\n\tvarfilename = options.input\n\toutfile = (\n\t\tos.path.splitext(varfilename)[0] + '-instance.ttf'\n\t\tif not options.output else options.output)\n\tconfigLogger(level=(\n\t\t\"DEBUG\" if options.verbose else\n\t\t\"ERROR\" if options.quiet else\n\t\t\"INFO\"))\n\n\tloc = {}\n\tfor arg in options.locargs:\n\t\ttry:\n\t\t\ttag, val = arg.split('=')\n\t\t\tassert len(tag) <= 4\n\t\t\tloc[tag.ljust(4)] = float(val)\n\t\texcept (ValueError, AssertionError):\n\t\t\tparser.error(\"invalid location argument format: %r\" % arg)\n\tlog.info(\"Location: %s\", loc)\n\n\tlog.info(\"Loading variable font\")\n\tvarfont = TTFont(varfilename)\n\n\tinstantiateVariableFont(varfont, loc, inplace=True)\n\n\tlog.info(\"Saving instance font %s\", outfile)\n\tvarfont.save(outfile)\n\n\nif __name__ == \"__main__\":\n\timport sys\n\tif len(sys.argv) > 1:\n\t\tsys.exit(main())\n\timport doctest\n\tsys.exit(doctest.testmod().failed)\n", "path": "Lib/fontTools/varLib/mutator.py"}], "after_files": [{"content": "\"\"\"\nInstantiate a variation font.  Run, eg:\n\n$ fonttools varLib.mutator ./NotoSansArabic-VF.ttf wght=140 wdth=85\n\"\"\"\nfrom __future__ import print_function, division, absolute_import\nfrom fontTools.misc.py23 import *\nfrom fontTools.misc.fixedTools import floatToFixedToFloat, otRound\nfrom fontTools.ttLib import TTFont\nfrom fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates\nfrom fontTools.varLib import _GetCoordinates, _SetCoordinates, _DesignspaceAxis\nfrom fontTools.varLib.models import supportScalar, normalizeLocation\nfrom fontTools.varLib.merger import MutatorMerger\nfrom fontTools.varLib.varStore import VarStoreInstancer\nfrom fontTools.varLib.mvar import MVAR_ENTRIES\nfrom fontTools.varLib.iup import iup_delta\nimport os.path\nimport logging\n\n\nlog = logging.getLogger(\"fontTools.varlib.mutator\")\n\n# map 'wdth' axis (1..200) to OS/2.usWidthClass (1..9), rounding to closest\nOS2_WIDTH_CLASS_VALUES = {}\npercents = [50.0, 62.5, 75.0, 87.5, 100.0, 112.5, 125.0, 150.0, 200.0]\nfor i, (prev, curr) in enumerate(zip(percents[:-1], percents[1:]), start=1):\n\thalf = (prev + curr) / 2\n\tOS2_WIDTH_CLASS_VALUES[half] = i\n\n\ndef instantiateVariableFont(varfont, location, inplace=False):\n\t\"\"\" Generate a static instance from a variable TTFont and a dictionary\n\tdefining the desired location along the variable font's axes.\n\tThe location values must be specified as user-space coordinates, e.g.:\n\n\t\t{'wght': 400, 'wdth': 100}\n\n\tBy default, a new TTFont object is returned. If ``inplace`` is True, the\n\tinput varfont is modified and reduced to a static font.\n\t\"\"\"\n\tif not inplace:\n\t\t# make a copy to leave input varfont unmodified\n\t\tstream = BytesIO()\n\t\tvarfont.save(stream)\n\t\tstream.seek(0)\n\t\tvarfont = TTFont(stream)\n\n\tfvar = varfont['fvar']\n\taxes = {a.axisTag:(a.minValue,a.defaultValue,a.maxValue) for a in fvar.axes}\n\tloc = normalizeLocation(location, axes)\n\tif 'avar' in varfont:\n\t\tmaps = varfont['avar'].segments\n\t\tloc = {k:_DesignspaceAxis._map(v, maps[k]) for k,v in loc.items()}\n\t# Quantize to F2Dot14, to avoid surprise interpolations.\n\tloc = {k:floatToFixedToFloat(v, 14) for k,v in loc.items()}\n\t# Location is normalized now\n\tlog.info(\"Normalized location: %s\", loc)\n\n\tlog.info(\"Mutating glyf/gvar tables\")\n\tgvar = varfont['gvar']\n\tglyf = varfont['glyf']\n\t# get list of glyph names in gvar sorted by component depth\n\tglyphnames = sorted(\n\t\tgvar.variations.keys(),\n\t\tkey=lambda name: (\n\t\t\tglyf[name].getCompositeMaxpValues(glyf).maxComponentDepth\n\t\t\tif glyf[name].isComposite() else 0,\n\t\t\tname))\n\tfor glyphname in glyphnames:\n\t\tvariations = gvar.variations[glyphname]\n\t\tcoordinates,_ = _GetCoordinates(varfont, glyphname)\n\t\torigCoords, endPts = None, None\n\t\tfor var in variations:\n\t\t\tscalar = supportScalar(loc, var.axes)\n\t\t\tif not scalar: continue\n\t\t\tdelta = var.coordinates\n\t\t\tif None in delta:\n\t\t\t\tif origCoords is None:\n\t\t\t\t\torigCoords,control = _GetCoordinates(varfont, glyphname)\n\t\t\t\t\tendPts = control[1] if control[0] >= 1 else list(range(len(control[1])))\n\t\t\t\tdelta = iup_delta(delta, origCoords, endPts)\n\t\t\tcoordinates += GlyphCoordinates(delta) * scalar\n\t\t_SetCoordinates(varfont, glyphname, coordinates)\n\n\tif 'cvar' in varfont:\n\t\tlog.info(\"Mutating cvt/cvar tables\")\n\t\tcvar = varfont['cvar']\n\t\tcvt = varfont['cvt ']\n\t\tdeltas = {}\n\t\tfor var in cvar.variations:\n\t\t\tscalar = supportScalar(loc, var.axes)\n\t\t\tif not scalar: continue\n\t\t\tfor i, c in enumerate(var.coordinates):\n\t\t\t\tif c is not None:\n\t\t\t\t\tdeltas[i] = deltas.get(i, 0) + scalar * c\n\t\tfor i, delta in deltas.items():\n\t\t\tcvt[i] += otRound(delta)\n\n\tif 'MVAR' in varfont:\n\t\tlog.info(\"Mutating MVAR table\")\n\t\tmvar = varfont['MVAR'].table\n\t\tvarStoreInstancer = VarStoreInstancer(mvar.VarStore, fvar.axes, loc)\n\t\trecords = mvar.ValueRecord\n\t\tfor rec in records:\n\t\t\tmvarTag = rec.ValueTag\n\t\t\tif mvarTag not in MVAR_ENTRIES:\n\t\t\t\tcontinue\n\t\t\ttableTag, itemName = MVAR_ENTRIES[mvarTag]\n\t\t\tdelta = otRound(varStoreInstancer[rec.VarIdx])\n\t\t\tif not delta:\n\t\t\t\tcontinue\n\t\t\tsetattr(varfont[tableTag], itemName,\n\t\t\t\tgetattr(varfont[tableTag], itemName) + delta)\n\n\tif 'GDEF' in varfont:\n\t\tlog.info(\"Mutating GDEF/GPOS/GSUB tables\")\n\t\tmerger = MutatorMerger(varfont, loc)\n\n\t\tlog.info(\"Building interpolated tables\")\n\t\tmerger.instantiate()\n\n\tif 'name' in varfont:\n\t\tlog.info(\"Pruning name table\")\n\t\texclude = {a.axisNameID for a in fvar.axes}\n\t\tfor i in fvar.instances:\n\t\t\texclude.add(i.subfamilyNameID)\n\t\t\texclude.add(i.postscriptNameID)\n\t\tvarfont['name'].names[:] = [\n\t\t\tn for n in varfont['name'].names\n\t\t\tif n.nameID not in exclude\n\t\t]\n\n\tif \"wght\" in location and \"OS/2\" in varfont:\n\t\tvarfont[\"OS/2\"].usWeightClass = otRound(\n\t\t\tmax(1, min(location[\"wght\"], 1000))\n\t\t)\n\tif \"wdth\" in location:\n\t\twdth = location[\"wdth\"]\n\t\tfor percent, widthClass in sorted(OS2_WIDTH_CLASS_VALUES.items()):\n\t\t\tif wdth < percent:\n\t\t\t\tvarfont[\"OS/2\"].usWidthClass = widthClass\n\t\t\t\tbreak\n\t\telse:\n\t\t\tvarfont[\"OS/2\"].usWidthClass = 9\n\tif \"slnt\" in location and \"post\" in varfont:\n\t\tvarfont[\"post\"].italicAngle = max(-90, min(location[\"slnt\"], 90))\n\n\tlog.info(\"Removing variable tables\")\n\tfor tag in ('avar','cvar','fvar','gvar','HVAR','MVAR','VVAR','STAT'):\n\t\tif tag in varfont:\n\t\t\tdel varfont[tag]\n\n\treturn varfont\n\n\ndef main(args=None):\n\tfrom fontTools import configLogger\n\timport argparse\n\n\tparser = argparse.ArgumentParser(\n\t\t\"fonttools varLib.mutator\", description=\"Instantiate a variable font\")\n\tparser.add_argument(\n\t\t\"input\", metavar=\"INPUT.ttf\", help=\"Input variable TTF file.\")\n\tparser.add_argument(\n\t\t\"locargs\", metavar=\"AXIS=LOC\", nargs=\"*\",\n\t\thelp=\"List of space separated locations. A location consist in \"\n\t\t\"the name of a variation axis, followed by '=' and a number. E.g.: \"\n\t\t\" wght=700 wdth=80. The default is the location of the base master.\")\n\tparser.add_argument(\n\t\t\"-o\", \"--output\", metavar=\"OUTPUT.ttf\", default=None,\n\t\thelp=\"Output instance TTF file (default: INPUT-instance.ttf).\")\n\tlogging_group = parser.add_mutually_exclusive_group(required=False)\n\tlogging_group.add_argument(\n\t\t\"-v\", \"--verbose\", action=\"store_true\", help=\"Run more verbosely.\")\n\tlogging_group.add_argument(\n\t\t\"-q\", \"--quiet\", action=\"store_true\", help=\"Turn verbosity off.\")\n\toptions = parser.parse_args(args)\n\n\tvarfilename = options.input\n\toutfile = (\n\t\tos.path.splitext(varfilename)[0] + '-instance.ttf'\n\t\tif not options.output else options.output)\n\tconfigLogger(level=(\n\t\t\"DEBUG\" if options.verbose else\n\t\t\"ERROR\" if options.quiet else\n\t\t\"INFO\"))\n\n\tloc = {}\n\tfor arg in options.locargs:\n\t\ttry:\n\t\t\ttag, val = arg.split('=')\n\t\t\tassert len(tag) <= 4\n\t\t\tloc[tag.ljust(4)] = float(val)\n\t\texcept (ValueError, AssertionError):\n\t\t\tparser.error(\"invalid location argument format: %r\" % arg)\n\tlog.info(\"Location: %s\", loc)\n\n\tlog.info(\"Loading variable font\")\n\tvarfont = TTFont(varfilename)\n\n\tinstantiateVariableFont(varfont, loc, inplace=True)\n\n\tlog.info(\"Saving instance font %s\", outfile)\n\tvarfont.save(outfile)\n\n\nif __name__ == \"__main__\":\n\timport sys\n\tif len(sys.argv) > 1:\n\t\tsys.exit(main())\n\timport doctest\n\tsys.exit(doctest.testmod().failed)\n", "path": "Lib/fontTools/varLib/mutator.py"}]}