internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: fonttools/fonttools

Issue:
Remove MacOS module in macCreatorType
utils.macCreatorType uses either the `xattr` module or the `MacOS` module to do its thing. But the MacOS module has been removed from Python 3.x. If we only support 3.x, we should remove the `MacOS`-related code.


Discussion:
Agreed.
Additionally if the `xattr` module is not available then `setMacCreatorAndType` *silently* fails.
> Additionally if the xattr module is not available then setMacCreatorAndType silently fails.

This is intentional. It's a legacy thing, that is nice to have still around, but nothing more than that.

Resolving patch:
diff --git a/Lib/fontTools/misc/macCreatorType.py b/Lib/fontTools/misc/macCreatorType.py
--- a/Lib/fontTools/misc/macCreatorType.py
+++ b/Lib/fontTools/misc/macCreatorType.py
@@ -4,10 +4,6 @@
 	import xattr
 except ImportError:
 	xattr = None
-try:
-	import MacOS
-except ImportError:
-	MacOS = None
 
 
 def _reverseString(s):
@@ -36,17 +32,7 @@ def getMacCreatorAndType(path):
 			fileType = Tag(finderInfo[:4])
 			fileCreator = Tag(finderInfo[4:8])
 			return fileCreator, fileType
-	if MacOS is not None:
-		fileCreator, fileType = MacOS.GetCreatorAndType(path)
-		if sys.version_info[:2] < (2, 7) and sys.byteorder == "little":
-			# work around bug in MacOS.GetCreatorAndType() on intel:
-			# http://bugs.python.org/issue1594
-			# (fixed with Python 2.7)
-			fileCreator = _reverseString(fileCreator)
-			fileType = _reverseString(fileType)
-		return fileCreator, fileType
-	else:
-		return None, None
+	return None, None
 
 
 def setMacCreatorAndType(path, fileCreator, fileType):
@@ -67,5 +53,3 @@ def setMacCreatorAndType(path, fileCreator, fileType):
 			raise TypeError('arg must be string of 4 chars')
 		finderInfo = pad(bytesjoin([fileType, fileCreator]), 32)
 		xattr.setxattr(path, 'com.apple.FinderInfo', finderInfo)
-	if MacOS is not None:
-		MacOS.SetCreatorAndType(path, fileCreator, fileType)


Test patch:
Repository: fonttools/fonttools

Issue:
[ttLib] maxp.maxComponentDepth calculation is sometimes wrong
I need to double check, but so far this is my conclusion:

`glyph.getCompositeMaxpValues()` sometimes returns a value that is too high. The logic of that function is flawed.

(This bug is 21 years old and predates version control, and is therefore all mine.)

I will write a test case and a fix.


Discussion:

Resolving patch:
diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
--- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py
+++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py
@@ -650,6 +650,7 @@ def getCompositeMaxpValues(self, glyfTable, maxComponentDepth=1):
 		assert self.isComposite()
 		nContours = 0
 		nPoints = 0
+		initialMaxComponentDepth = maxComponentDepth
 		for compo in self.components:
 			baseGlyph = glyfTable[compo.glyphName]
 			if baseGlyph.numberOfContours == 0:
@@ -657,8 +658,9 @@ def getCompositeMaxpValues(self, glyfTable, maxComponentDepth=1):
 			elif baseGlyph.numberOfContours > 0:
 				nP, nC = baseGlyph.getMaxpValues()
 			else:
-				nP, nC, maxComponentDepth = baseGlyph.getCompositeMaxpValues(
-						glyfTable, maxComponentDepth + 1)
+				nP, nC, componentDepth = baseGlyph.getCompositeMaxpValues(
+						glyfTable, initialMaxComponentDepth + 1)
+				maxComponentDepth = max(maxComponentDepth, componentDepth)
 			nPoints = nPoints + nP
 			nContours = nContours + nC
 		return CompositeMaxpValues(nPoints, nContours, maxComponentDepth)


Test patch:
diff --git a/Tests/ttLib/tables/_g_l_y_f_test.py b/Tests/ttLib/tables/_g_l_y_f_test.py
--- a/Tests/ttLib/tables/_g_l_y_f_test.py
+++ b/Tests/ttLib/tables/_g_l_y_f_test.py
@@ -471,6 +471,30 @@ def test_getCoordinates(self):
             ]
         )
 
+    def test_getCompositeMaxpValues(self):
+        # https://github.com/fonttools/fonttools/issues/2044
+        glyphSet = {}
+        pen = TTGlyphPen(glyphSet)  # empty non-composite glyph
+        glyphSet["fraction"] = pen.glyph()
+        glyphSet["zero.numr"] = pen.glyph()
+        pen = TTGlyphPen(glyphSet)
+        pen.addComponent("zero.numr", (1, 0, 0, 1, 0, 0))
+        glyphSet["zero.dnom"] = pen.glyph()
+        pen = TTGlyphPen(glyphSet)
+        pen.addComponent("zero.numr", (1, 0, 0, 1, 0, 0))
+        pen.addComponent("fraction", (1, 0, 0, 1, 0, 0))
+        pen.addComponent("zero.dnom", (1, 0, 0, 1, 0, 0))
+        glyphSet["percent"] = pen.glyph()
+        pen = TTGlyphPen(glyphSet)
+        pen.addComponent("zero.numr", (1, 0, 0, 1, 0, 0))
+        pen.addComponent("fraction", (1, 0, 0, 1, 0, 0))
+        pen.addComponent("zero.dnom", (1, 0, 0, 1, 0, 0))
+        pen.addComponent("zero.dnom", (1, 0, 0, 1, 0, 0))
+        glyphSet["perthousand"] = pen.glyph()
+        assert glyphSet["zero.dnom"].getCompositeMaxpValues(glyphSet)[2] == 1
+        assert glyphSet["percent"].getCompositeMaxpValues(glyphSet)[2] == 2
+        assert glyphSet["perthousand"].getCompositeMaxpValues(glyphSet)[2] == 2
+
 
 class GlyphComponentTest:
Repository: fonttools/fonttools

Issue:
[varLib] Possible bug in varStore.py?
I am trying to learn how VarStores work, and am running into a bit of source code that looks off, even though I'm not sure I fully understand what's going on there.

Compare this fragment:
https://github.com/fonttools/fonttools/blob/e4b0486b31a50c368a794bb20692903ee55313e5/Lib/fontTools/varLib/varStore.py#L71-L73

With this:
https://github.com/fonttools/fonttools/blob/e4b0486b31a50c368a794bb20692903ee55313e5/Lib/fontTools/varLib/varStore.py#L107-L111

Shouldn't the former also test for `0xFFFF` instead of `0xFFF`?
[varLib] Possible bug in varStore.py?
I am trying to learn how VarStores work, and am running into a bit of source code that looks off, even though I'm not sure I fully understand what's going on there.

Compare this fragment:
https://github.com/fonttools/fonttools/blob/e4b0486b31a50c368a794bb20692903ee55313e5/Lib/fontTools/varLib/varStore.py#L71-L73

With this:
https://github.com/fonttools/fonttools/blob/e4b0486b31a50c368a794bb20692903ee55313e5/Lib/fontTools/varLib/varStore.py#L107-L111

Shouldn't the former also test for `0xFFFF` instead of `0xFFF`?


Discussion:
yeah, looks like a typo to me
ItemVariationData.ItemCount is a uint16 so there can be max `0xFFFF` items (deltaset rows) per VarData subtable
yeah, looks like a typo to me
ItemVariationData.ItemCount is a uint16 so there can be max `0xFFFF` items (deltaset rows) per VarData subtable

Resolving patch:
diff --git a/Lib/fontTools/varLib/varStore.py b/Lib/fontTools/varLib/varStore.py
--- a/Lib/fontTools/varLib/varStore.py
+++ b/Lib/fontTools/varLib/varStore.py
@@ -68,7 +68,7 @@ def _add_VarData(self):
 			self._outer = varDataIdx
 			self._data = self._store.VarData[varDataIdx]
 			self._cache = self._varDataCaches[key]
-			if len(self._data.Item) == 0xFFF:
+			if len(self._data.Item) == 0xFFFF:
 				# This is full.  Need new one.
 				varDataIdx = None
 


Test patch:
Repository: fonttools/fonttools

Issue:
file created too early in TTFont.save()
If an exception occurs during compilation, an empty file remains. This isn't so bad per se, but when overwriting the original file this destroys the original, which is rather nasty. It can probably be fixed by moving the file creation block to after the SFNTWriter is done.



Discussion:
Or maybe create the output in a tmp file and only rename it if compilation was successful, and remove it otherwise.

In general the case of output and input filenames being equal is dangerous, so I wouldn't mind if we disallow that.


Resolving patch:
diff --git a/Lib/fontTools/ttLib/ttFont.py b/Lib/fontTools/ttLib/ttFont.py
--- a/Lib/fontTools/ttLib/ttFont.py
+++ b/Lib/fontTools/ttLib/ttFont.py
@@ -161,22 +161,17 @@ def save(self, file, reorderTables=True):
 			if self.lazy and self.reader.file.name == file:
 				raise TTLibError(
 					"Can't overwrite TTFont when 'lazy' attribute is True")
-			closeStream = True
-			file = open(file, "wb")
+			createStream = True
 		else:
 			# assume "file" is a writable file object
-			closeStream = False
+			createStream = False
 
 		tmp = BytesIO()
 
 		writer_reordersTables = self._save(tmp)
 
-		if (reorderTables is None or writer_reordersTables or
+		if not (reorderTables is None or writer_reordersTables or
 				(reorderTables is False and self.reader is None)):
-			# don't reorder tables and save as is
-			file.write(tmp.getvalue())
-			tmp.close()
-		else:
 			if reorderTables is False:
 				# sort tables using the original font's order
 				tableOrder = list(self.reader.keys())
@@ -186,12 +181,17 @@ def save(self, file, reorderTables=True):
 			tmp.flush()
 			tmp2 = BytesIO()
 			reorderFontTables(tmp, tmp2, tableOrder)
-			file.write(tmp2.getvalue())
 			tmp.close()
-			tmp2.close()
+			tmp = tmp2
+
+		if createStream:
+			# "file" is a path
+			with open(file, "wb") as file:
+				file.write(tmp.getvalue())
+		else:
+			file.write(tmp.getvalue())
 
-		if closeStream:
-			file.close()
+		tmp.close()
 
 	def _save(self, file, tableCache=None):
 		"""Internal function, to be shared by save() and TTCollection.save()"""


Test patch:
Repository: fonttools/fonttools

Issue:
[ttLib] font.importXML() always sets sfntVersion from imported TTX
When using `ttx -m` or `font.importXML()`, the font's `sfntVersion` is unconditionally taken from the imported TTX. That is correct if we're doing a full import (starting with an empty TTFont) but is dubious when importing some tables into an existing font. Importing (say) a name table from an .otf into an existing .ttf will set `sfntValue` to "OTTO", which is incorrect for TTF.

Perhaps we should not set `sfntVersion` if `importXML()` is called on a non-empty font.


Discussion:
(I only found this because for better or for worse, FreeType rejects "OTTO" fonts that don't have a CFF or CFF2 table.)
Because of this I have a bunch of duplicate TTX files where the only difference is the sfntVersion so I can apply one to the OTF and one to the TTF. It would be nice if I could use a single file for both. 
> not set sfntVersion if importXML() is called on a non-empty font.

yes, makes sense

Resolving patch:
diff --git a/Lib/fontTools/misc/xmlReader.py b/Lib/fontTools/misc/xmlReader.py
--- a/Lib/fontTools/misc/xmlReader.py
+++ b/Lib/fontTools/misc/xmlReader.py
@@ -93,11 +93,12 @@ def _startElementHandler(self, name, attrs):
 		if not stackSize:
 			if name != "ttFont":
 				raise TTXParseError("illegal root tag: %s" % name)
-			sfntVersion = attrs.get("sfntVersion")
-			if sfntVersion is not None:
-				if len(sfntVersion) != 4:
-					sfntVersion = safeEval('"' + sfntVersion + '"')
-				self.ttFont.sfntVersion = sfntVersion
+			if self.ttFont.reader is None and not self.ttFont.tables:
+				sfntVersion = attrs.get("sfntVersion")
+				if sfntVersion is not None:
+					if len(sfntVersion) != 4:
+						sfntVersion = safeEval('"' + sfntVersion + '"')
+					self.ttFont.sfntVersion = sfntVersion
 			self.contentStack.append([])
 		elif stackSize == 1:
 			if subFile is not None:


Test patch:
diff --git a/Tests/ttLib/ttFont_test.py b/Tests/ttLib/ttFont_test.py
--- a/Tests/ttLib/ttFont_test.py
+++ b/Tests/ttLib/ttFont_test.py
@@ -46,3 +46,35 @@ def test_registerCustomTableClassStandardName():
         assert font[TABLETAG].compile(font) == b"\x04\x05\x06"
     finally:
         unregisterCustomTableClass(TABLETAG)
+
+
+ttxTTF = r"""<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.9.0">
+  <hmtx>
+    <mtx name=".notdef" width="300" lsb="0"/>
+  </hmtx>
+</ttFont>
+"""
+
+
+ttxOTF = """<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="OTTO" ttLibVersion="4.9.0">
+  <hmtx>
+    <mtx name=".notdef" width="300" lsb="0"/>
+  </hmtx>
+</ttFont>
+"""
+
+
+def test_sfntVersionFromTTX():
+    # https://github.com/fonttools/fonttools/issues/2370
+    font = TTFont()
+    assert font.sfntVersion == "\x00\x01\x00\x00"
+    ttx = io.StringIO(ttxOTF)
+    # Font is "empty", TTX file will determine sfntVersion
+    font.importXML(ttx)
+    assert font.sfntVersion == "OTTO"
+    ttx = io.StringIO(ttxTTF)
+    # Font is not "empty", sfntVersion in TTX file will be ignored
+    font.importXML(ttx)
+    assert font.sfntVersion == "OTTO"
Repository: fonttools/fonttools

Issue:
[otlLib] `buildCoverage` does not remove duplicate glyphs
If a class in a feature file includes duplicate glyphs, `buildCoverage` is called on a list that includes duplicate glyphs. `buildCoverage` sorts the input glyphs but does not remove duplicates.
```pycon
>>> import fontTools.otlLib.builder
>>> fontTools.otlLib.builder.buildCoverage(['A', 'B', 'A'], {'A': 1, 'B': 2}).glyphs
['A', 'A', 'B']
```
[The spec](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-table) says “A Coverage table defines a unique index value, the _Coverage Index_, for each covered glyph”, but a glyph listed multiple times won’t have a unique index.

This can cause a problem when using the OpenType Sanitizer: if a CoverageFormat1 table lists more glyphs than are declared in 'maxp', OTS reports an error.


Discussion:
It’s at least consistent with what Adobe’s `makeotf` does, which also doesn’t remove guplicates.

Resolving patch:
diff --git a/Lib/fontTools/otlLib/builder.py b/Lib/fontTools/otlLib/builder.py
--- a/Lib/fontTools/otlLib/builder.py
+++ b/Lib/fontTools/otlLib/builder.py
@@ -52,7 +52,7 @@ def buildCoverage(glyphs, glyphMap):
     if not glyphs:
         return None
     self = ot.Coverage()
-    self.glyphs = sorted(glyphs, key=glyphMap.__getitem__)
+    self.glyphs = sorted(set(glyphs), key=glyphMap.__getitem__)
     return self
 
 


Test patch:
diff --git a/Tests/otlLib/builder_test.py b/Tests/otlLib/builder_test.py
--- a/Tests/otlLib/builder_test.py
+++ b/Tests/otlLib/builder_test.py
@@ -204,7 +204,7 @@ def test_buildComponentRecord_None(self):
         assert builder.buildComponentRecord(None) is None
 
     def test_buildCoverage(self):
-        cov = builder.buildCoverage({"two", "four"}, {"two": 2, "four": 4})
+        cov = builder.buildCoverage(("two", "four", "two"), {"two": 2, "four": 4})
         assert getXML(cov.toXML) == [
             "<Coverage>",
             '  <Glyph value="two"/>',
Repository: fonttools/fonttools

Issue:
[feaLib] "fonttools feaLib" should error out, not continue
If there's a parse/build error when using the feaLib command line tool, we currently do this:

https://github.com/fonttools/fonttools/blob/445108f735b22a5ca37f669808d47906d024fe24/Lib/fontTools/feaLib/__main__.py#L69-L73

i.e. we save the font anyway and exit with status code 0.

My Makefiles and I think this is a terrible idea, and I would like to change it. Any objections / thoughts?




Discussion:

Resolving patch:
diff --git a/Lib/fontTools/feaLib/__main__.py b/Lib/fontTools/feaLib/__main__.py
--- a/Lib/fontTools/feaLib/__main__.py
+++ b/Lib/fontTools/feaLib/__main__.py
@@ -70,6 +70,7 @@ def main(args=None):
         if options.traceback:
             raise
         log.error(e)
+        sys.exit(1)
     font.save(output_font)
 
 


Test patch:
Repository: fonttools/fonttools

Issue:
[instancer] OTS Warning when no AxisValues are left after instancing
When all axes are instantiated, ots warns like this:

   `WARNING: STAT: Unexpected non-zero offsetToAxisValueOffsets`

PR coming up.


Discussion:

Resolving patch:
diff --git a/Lib/fontTools/varLib/instancer/__init__.py b/Lib/fontTools/varLib/instancer/__init__.py
--- a/Lib/fontTools/varLib/instancer/__init__.py
+++ b/Lib/fontTools/varLib/instancer/__init__.py
@@ -1024,8 +1024,11 @@ def instantiateSTAT(varfont, axisLimits):
 
     log.info("Instantiating STAT table")
     newAxisValueTables = axisValuesFromAxisLimits(stat, axisLimits)
-    stat.AxisValueArray.AxisValue = newAxisValueTables
-    stat.AxisValueCount = len(stat.AxisValueArray.AxisValue)
+    stat.AxisValueCount = len(newAxisValueTables)
+    if stat.AxisValueCount:
+        stat.AxisValueArray.AxisValue = newAxisValueTables
+    else:
+        stat.AxisValueArray = None
 
 
 def axisValuesFromAxisLimits(stat, axisLimits):


Test patch:
Repository: fonttools/fonttools

Issue:
designspaceLib isinstance fails for custom descriptors
in Designspace editor we have custom descriptor objects that works nicely with NSObjects 

https://github.com/fonttools/fonttools/blob/main/Lib/fontTools/designspaceLib/__init__.py#L1448-L1452

patch:

hasattr(axisObject, "values") --> DiscreteAxisDescriptor
hasattr(axisObject, "minimum") and hasattr(axisObject, "maximum") --> AxisDescriptor




Discussion:
Agreed. Interface checking is better than class checking.
Agreed, I'll open a PR with the fixes.
Can we add `AxisLabelDescriptor` to `__all__` in https://github.com/fonttools/fonttools/blob/feb4e75469910d239fb72d80bbc979a121a6f87c/Lib/fontTools/designspaceLib/__init__.py#L24

Resolving patch:
diff --git a/Lib/fontTools/designspaceLib/__init__.py b/Lib/fontTools/designspaceLib/__init__.py
--- a/Lib/fontTools/designspaceLib/__init__.py
+++ b/Lib/fontTools/designspaceLib/__init__.py
@@ -22,9 +22,20 @@
 """
 
 __all__ = [
-    'DesignSpaceDocumentError', 'DesignSpaceDocument', 'SourceDescriptor',
-    'InstanceDescriptor', 'AxisDescriptor', 'RuleDescriptor', 'BaseDocReader',
-    'BaseDocWriter'
+    'AxisDescriptor',
+    'AxisLabelDescriptor',
+    'BaseDocReader',
+    'BaseDocWriter',
+    'DesignSpaceDocument',
+    'DesignSpaceDocumentError',
+    'DiscreteAxisDescriptor',
+    'InstanceDescriptor',
+    'LocationLabelDescriptor',
+    'RangeAxisSubsetDescriptor',
+    'RuleDescriptor',
+    'SourceDescriptor',
+    'ValueAxisSubsetDescriptor',
+    'VariableFontDescriptor',
 ]
 
 # ElementTree allows to find namespace-prefixed elements, but not attributes


Test patch:
Repository: fonttools/fonttools

Issue:
[feaLib] FeatureLibError: TypeError
This line fails with `TypeError: __init__() missing 1 required positional argument: 'location'`

https://github.com/fonttools/fonttools/blob/02a669ed6313d5479c0e6b176973cefa112cd32b/Lib/fontTools/feaLib/builder.py#L234

`location` is not an optional arg to `FeatureLibError`


Discussion:

Resolving patch:
diff --git a/Lib/fontTools/feaLib/builder.py b/Lib/fontTools/feaLib/builder.py
--- a/Lib/fontTools/feaLib/builder.py
+++ b/Lib/fontTools/feaLib/builder.py
@@ -231,7 +231,7 @@ def build(self, tables=None, debug=False):
             elif "GDEF" in self.font:
                 del self.font["GDEF"]
         elif self.varstorebuilder:
-            raise FeatureLibError("Must save GDEF when compiling a variable font")
+            raise FeatureLibError("Must save GDEF when compiling a variable font", None)
         if "BASE" in tables:
             base = self.buildBASE()
             if base:


Test patch:
Repository: fonttools/fonttools

Issue:
[feaLib.builder] Irrelevant "Must save GDEF when compiling a variable font" error when adding GSUB-only to VF
feaLib.builder insists on building GDEF, even if GPOS is not requested, for example if I only want to add GSUB to a VF.

I think the `elif self.varstorebuilder:` line below should read `elif "GPOS" in tables and self.varstorebuilder:`

https://github.com/fonttools/fonttools/blob/4139d8914abe788cc396dcb6ccc7c9cdb365ee89/Lib/fontTools/feaLib/builder.py#L227-L234

See also https://github.com/fonttools/fonttools/discussions/2690#discussioncomment-3131706


Discussion:
Even then it's possible that the GPOS not have any variations...
Maybe this would be more useful behavior:

```python
    gdef = self.buildGDEF():  # unconditionally
    if "GDEF" in tables:
        if gdef:
            self.font["GDEF"] = gdef
        elif "GDEF" in self.font: 
            del self.font["GDEF"]
    elif gdef:
        log.warning("GDEF is not requested, but is needed")
```
But why build if explicitly not requested?
The idea of this test is to prevent mistakes of this form:
- use variable .fea for GPOS
- request GPOS, but not GDEF
- without GDEF the variable GPOS won't work, so should error (or at least warn)

The current test is too strict, and one way (the easiest way?) to find out whether GDEF is in fact needed (so we can error or warn if it is not requested) is to simply build it.

If there's a different way to do this that avoids a pointless error as well as addresses Behdad's concern, we can consider of course.
I see, ok.

Regarding your proposed change in https://github.com/fonttools/fonttools/issues/2694#issuecomment-1183534216, should you not check both `if gdef and self.varstorebuilder`, rather than just `elif gdef:`?

```python
    elif gdef and self.varstorebuilder:
        log.warning("GDEF is not requested, but is needed when compiling variable font")
```
actually nevermind ignore my last comment, your suggested code is correct already. Please send a PR, thanks!
Ok, thanks!

Resolving patch:
diff --git a/Lib/fontTools/feaLib/builder.py b/Lib/fontTools/feaLib/builder.py
--- a/Lib/fontTools/feaLib/builder.py
+++ b/Lib/fontTools/feaLib/builder.py
@@ -224,14 +224,14 @@ def build(self, tables=None, debug=False):
                 del self.font[tag]
         if any(tag in self.font for tag in ("GPOS", "GSUB")) and "OS/2" in self.font:
             self.font["OS/2"].usMaxContext = maxCtxFont(self.font)
+        gdef = self.buildGDEF()  # build unconditionally so we can warn below
         if "GDEF" in tables:
-            gdef = self.buildGDEF()
             if gdef:
                 self.font["GDEF"] = gdef
             elif "GDEF" in self.font:
                 del self.font["GDEF"]
-        elif self.varstorebuilder:
-            raise FeatureLibError("Must save GDEF when compiling a variable font", None)
+        elif gdef:
+            log.warning("GDEF is not requested, but is needed")
         if "BASE" in tables:
             base = self.buildBASE()
             if base:


Test patch:
Repository: fonttools/fonttools

Issue:
subsetter always drops CPAL if there's no COLR even if SVG may be using it
currently in prune_post_subset, we unconditionally drop CPAL table whenever COLR is not in the font because we assume that COLR is the only table that uses CPAL and if it's no longer there, it's because subset step has emptied it so CPAL pruning returns False and the whole CPAL is removed.
However OT-SVG table can also make use of CPAL as explained in the OT spec [here](https://docs.microsoft.com/en-us/typography/opentype/spec/svg#color-palettes). nanoemoji (via its maximum_color tool) can now create OT-SVG tables with CPAL support from a COLR/CPAL font that contains multiple palettes (https://github.com/googlefonts/nanoemoji/pull/431).
I've never seen such a font in the wild before to be honest but now they exist, and even [work in Safari on iOS16 and macOS Ventura](https://github.com/googlefonts/nanoemoji/issues/422#issuecomment-1236984979)).

Ideally we would check if SVG is present and parse the SVG documents and collect all the used palette entry indices and prune CPAL accordingly (simiarly to how we currently do with COLR), but parsing SVG is too much pain so I'm leaning towards always keeping CPAL (and not even attempt at pruning unused palette entry indices) whenever the font also contains "SVG " table.


Discussion:

Resolving patch:
diff --git a/Lib/fontTools/subset/__init__.py b/Lib/fontTools/subset/__init__.py
--- a/Lib/fontTools/subset/__init__.py
+++ b/Lib/fontTools/subset/__init__.py
@@ -2207,6 +2207,14 @@ def subset_glyphs(self, s):
 
 @_add_method(ttLib.getTableClass('CPAL'))
 def prune_post_subset(self, font, options):
+	# Keep whole "CPAL" if "SVG " is present as it may be referenced by the latter
+	# via 'var(--color{palette_entry_index}, ...)' CSS color variables.
+	# For now we just assume this is the case by the mere presence of "SVG " table,
+	# for parsing SVG to collect all the used indices is too much work...
+	# TODO(anthrotype): Do The Right Thing (TM).
+	if "SVG " in font:
+		return True
+
 	colr = font.get("COLR")
 	if not colr:  # drop CPAL if COLR was subsetted to empty
 		return False


Test patch: