Repository: fonttools/fonttools

Issue:
Subsetter and CFF CharStrings
I've been banging my head against a Windows error with some fonts that have been made with the subsetter: turns out that it is removing the CharString entries that are marked raw="1". This is for the <Subrs> entry. I don't know why these are getting dumped as raw — that's the next thing to figure out, but removing them breaks the font in Windows.


Discussion:
They are raw because as far as subsetter sees, they are unreferenced and as such cannot be decoded. Because they are unreferenced, they are removed during unused subroutine removal. Do you have a minimal test case you can attach?
@behdad actually, I can simplify this much more: in this case (again, no idea why this is coming up raw, but it is), the subsetter leaves
```
        <Subrs>
          <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
        </Subrs>
```
in the CFF table. Taking that out and recompiling with TTX makes the font work.
@behdad I'll email a test case for you on this.
FYI, AFDKO's subroutinizer genarates some unused subroutines. See https://github.com/adobe-type-tools/afdko/issues/173.
@mashabow That's good to know, thank you.
So to recap, you're claiming that the subsetter removing charstrings that are marked "raw" in ttx dump (because unused) will break the font in Windows?

> I'll email a test case for you on this.

@benkiel could I get that one as well? thanks.
@anthrotype No, the subsetter isn't removing things because they are raw (afaik) but because the glyphs they reference aren't in the font anymore. Instead of checking the length of the subrs to see if there is anything in there and then writing, fontTools writes the subrs regardless of length. If empty, font won't install in Windows.

This is best a check outside of the subsetter, so that fontTools doesn't write an empty subrs. I know that @behdad prefers that fontTools just writes what's there, vaild or not, so if this falls into that case, then it should be a check in the subsetter.

Email me a good email address and I'll send the test case.
Just updating this, as of 3.21.0.dev0, empty subrs are still being written into the font when subset.

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
@@ -2407,12 +2407,27 @@ def prune_post_subset(self, options):
             for subr in subrs.items:
                 subr.subset_subroutines (local_subrs, font.GlobalSubrs)
 
+        # Delete local SubrsIndex if empty
+        if hasattr(font, 'FDSelect'):
+            for fd in font.FDArray:
+                _delete_empty_subrs(fd.Private)
+        else:
+            _delete_empty_subrs(font.Private)
+
         # Cleanup
         for subrs in all_subrs:
             del subrs._used, subrs._old_bias, subrs._new_bias
 
     return True
 
+
+def _delete_empty_subrs(private_dict):
+    if hasattr(private_dict, 'Subrs') and not private_dict.Subrs:
+        if 'Subrs' in private_dict.rawDict:
+            del private_dict.rawDict['Subrs']
+        del private_dict.Subrs
+
+
 @_add_method(ttLib.getTableClass('cmap'))
 def closure_glyphs(self, s):
     tables = [t for t in self.tables if t.isUnicode()]


Test patch:
diff --git a/Tests/subset/data/expect_desubroutinize_CFF.ttx b/Tests/subset/data/expect_desubroutinize_CFF.ttx
--- a/Tests/subset/data/expect_desubroutinize_CFF.ttx
+++ b/Tests/subset/data/expect_desubroutinize_CFF.ttx
@@ -33,9 +33,6 @@
         <initialRandomSeed value="0"/>
         <defaultWidthX value="267"/>
         <nominalWidthX value="448"/>
-        <Subrs>
-          <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
-        </Subrs>
       </Private>
       <CharStrings>
         <CharString name=".notdef">
diff --git a/Tests/subset/data/expect_no_hinting_desubroutinize_CFF.ttx b/Tests/subset/data/expect_no_hinting_desubroutinize_CFF.ttx
--- a/Tests/subset/data/expect_no_hinting_desubroutinize_CFF.ttx
+++ b/Tests/subset/data/expect_no_hinting_desubroutinize_CFF.ttx
@@ -32,9 +32,6 @@
         <initialRandomSeed value="0"/>
         <defaultWidthX value="267"/>
         <nominalWidthX value="448"/>
-        <Subrs>
-          <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
-        </Subrs>
       </Private>
       <CharStrings>
         <CharString name=".notdef">
diff --git a/Tests/subset/data/expect_no_notdef_outline_cid.ttx b/Tests/subset/data/expect_no_notdef_outline_cid.ttx
--- a/Tests/subset/data/expect_no_notdef_outline_cid.ttx
+++ b/Tests/subset/data/expect_no_notdef_outline_cid.ttx
@@ -43,9 +43,6 @@
             <initialRandomSeed value="0"/>
             <defaultWidthX value="1000"/>
             <nominalWidthX value="107"/>
-            <Subrs>
-              <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
-            </Subrs>
           </Private>
         </FontDict>
       </FDArray>
diff --git a/Tests/subset/data/expect_no_notdef_outline_otf.ttx b/Tests/subset/data/expect_no_notdef_outline_otf.ttx
--- a/Tests/subset/data/expect_no_notdef_outline_otf.ttx
+++ b/Tests/subset/data/expect_no_notdef_outline_otf.ttx
@@ -33,9 +33,6 @@
         <initialRandomSeed value="0"/>
         <defaultWidthX value="500"/>
         <nominalWidthX value="300"/>
-        <Subrs>
-          <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
-        </Subrs>
       </Private>
       <CharStrings>
         <CharString name=".notdef">
diff --git a/Tests/subset/data/expect_notdef_width_cid.ttx b/Tests/subset/data/expect_notdef_width_cid.ttx
--- a/Tests/subset/data/expect_notdef_width_cid.ttx
+++ b/Tests/subset/data/expect_notdef_width_cid.ttx
@@ -38,9 +38,6 @@
             <initialRandomSeed value="0"/>
             <defaultWidthX value="602"/>
             <nominalWidthX value="630"/>
-            <Subrs>
-              <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
-            </Subrs>
           </Private>
         </FontDict>
         <FontDict index="1">
@@ -56,9 +53,6 @@
             <initialRandomSeed value="0"/>
             <defaultWidthX value="1000"/>
             <nominalWidthX value="107"/>
-            <Subrs>
-              <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
-            </Subrs>
           </Private>
         </FontDict>
       </FDArray>