diff --git a/src/core/cff_parser.js b/src/core/cff_parser.js
index 20b794305..8335b8fcc 100644
--- a/src/core/cff_parser.js
+++ b/src/core/cff_parser.js
@@ -413,26 +413,7 @@ var CFFParser = (function CFFParserClosure() {
       var names = [];
       for (var i = 0, ii = index.count; i < ii; ++i) {
         var name = index.get(i);
-        // OTS doesn't allow names to be over 127 characters.
-        var length = Math.min(name.length, 127);
-        var data = [];
-        // OTS also only permits certain characters in the name.
-        for (var j = 0; j < length; ++j) {
-          var c = name[j];
-          if (j === 0 && c === 0) {
-            data[j] = c;
-            continue;
-          }
-          if ((c < 33 || c > 126) || c === 91 /* [ */ || c === 93 /* ] */ ||
-              c === 40 /* ( */ || c === 41 /* ) */ || c === 123 /* { */ ||
-              c === 125 /* } */ || c === 60 /* < */ || c === 62 /* > */ ||
-              c === 47 /* / */ || c === 37 /* % */ || c === 35 /* # */) {
-            data[j] = 95;
-            continue;
-          }
-          data[j] = c;
-        }
-        names.push(bytesToString(data));
+        names.push(bytesToString(name));
       }
       return names;
     },
@@ -1413,7 +1394,27 @@ var CFFCompiler = (function CFFCompilerClosure() {
     compileNameIndex: function CFFCompiler_compileNameIndex(names) {
       var nameIndex = new CFFIndex();
       for (var i = 0, ii = names.length; i < ii; ++i) {
-        nameIndex.add(stringToBytes(names[i]));
+        var name = names[i];
+        // OTS doesn't allow names to be over 127 characters.
+        var length = Math.min(name.length, 127);
+        var sanitizedName = new Array(length);
+        for (var j = 0; j < length; j++) {
+          // OTS requires chars to be between a range and not certain other
+          // chars.
+          var char = name[j];
+          if (char < '!' || char > '~' || char === '[' || char === ']' ||
+              char === '(' || char === ')' || char === '{' || char === '}' ||
+              char === '<' || char === '>' || char === '/' || char === '%') {
+            char = '_';
+          }
+          sanitizedName[j] = char;
+        }
+        sanitizedName = sanitizedName.join('');
+
+        if (sanitizedName === '') {
+          sanitizedName = 'Bad_Font_Name';
+        }
+        nameIndex.add(stringToBytes(sanitizedName));
       }
       return this.compileIndex(nameIndex);
     },
diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore
index 07539309b..58efe926c 100644
--- a/test/pdfs/.gitignore
+++ b/test/pdfs/.gitignore
@@ -116,6 +116,7 @@
 !issue4630.pdf
 !issue4909.pdf
 !issue5084.pdf
+!issue8960_reduced.pdf
 !issue5202.pdf
 !issue5280.pdf
 !issue5677.pdf
diff --git a/test/pdfs/issue8960_reduced.pdf b/test/pdfs/issue8960_reduced.pdf
new file mode 100644
index 000000000..4c1f90ac2
Binary files /dev/null and b/test/pdfs/issue8960_reduced.pdf differ
diff --git a/test/test_manifest.json b/test/test_manifest.json
index 41947059e..d9637a7f9 100644
--- a/test/test_manifest.json
+++ b/test/test_manifest.json
@@ -773,6 +773,13 @@
        "link": false,
        "type": "eq"
     },
+    {  "id": "issue8960_reduced",
+       "file": "pdfs/issue8960_reduced.pdf",
+       "md5": "12ccf71307f4b5bd4148d5f985ffde07",
+       "rounds": 1,
+       "link": false,
+       "type": "eq"
+    },
     {  "id": "issue5954",
        "file": "pdfs/issue5954.pdf",
        "md5": "4f60ec0d9bbeec845b681242b8982361",
diff --git a/test/unit/cff_parser_spec.js b/test/unit/cff_parser_spec.js
index ee5b8fcbe..3a2101025 100644
--- a/test/unit/cff_parser_spec.js
+++ b/test/unit/cff_parser_spec.js
@@ -14,7 +14,7 @@
  */
 
 import {
-  CFFCompiler, CFFIndex, CFFParser, CFFStrings
+  CFFCompiler, CFFParser, CFFStrings
 } from '../../src/core/cff_parser';
 import { SEAC_ANALYSIS_ENABLED } from '../../src/core/fonts';
 import { Stream } from '../../src/core/stream';
@@ -88,23 +88,6 @@ describe('CFFParser', function() {
     expect(names[0]).toEqual('ABCDEF+Times-Roman');
   });
 
-  it('sanitizes name index', function() {
-    var index = new CFFIndex();
-    index.add(['['.charCodeAt(0), 'a'.charCodeAt(0)]);
-
-    var names = parser.parseNameIndex(index);
-    expect(names).toEqual(['_a']);
-
-    index = new CFFIndex();
-    var longName = [];
-    for (var i = 0; i < 129; i++) {
-      longName.push(0);
-    }
-    index.add(longName);
-    names = parser.parseNameIndex(index);
-    expect(names[0].length).toEqual(127);
-  });
-
   it('parses string index', function() {
     var strings = cff.strings;
     expect(strings.count).toEqual(3);
@@ -368,6 +351,16 @@ describe('CFFParser', function() {
 });
 
 describe('CFFCompiler', function() {
+
+  function testParser(bytes) {
+    bytes = new Uint8Array(bytes);
+    return new CFFParser({
+      getBytes: () => {
+        return bytes;
+      },
+    }, {}, SEAC_ANALYSIS_ENABLED);
+  }
+
   it('encodes integers', function() {
     var c = new CFFCompiler();
     // all the examples from the spec
@@ -388,5 +381,24 @@ describe('CFFCompiler', function() {
     expect(c.encodeFloat(5e-11)).toEqual([0x1e, 0x5c, 0x11, 0xff]);
   });
 
+  it('sanitizes name index', function() {
+    var c = new CFFCompiler();
+    var nameIndexCompiled = c.compileNameIndex(['[a']);
+    var parser = testParser(nameIndexCompiled);
+    var nameIndex = parser.parseIndex(0);
+    var names = parser.parseNameIndex(nameIndex.obj);
+    expect(names).toEqual(['_a']);
+
+    var longName = '';
+    for (var i = 0; i < 129; i++) {
+      longName += '_';
+    }
+    nameIndexCompiled = c.compileNameIndex([longName]);
+    parser = testParser(nameIndexCompiled);
+    nameIndex = parser.parseIndex(0);
+    names = parser.parseNameIndex(nameIndex.obj);
+    expect(names[0].length).toEqual(127);
+  });
+
   // TODO a lot more compiler tests
 });