From 05de20071a43f7dac9903835f479d57e863521f7 Mon Sep 17 00:00:00 2001
From: Jonas Jenwald <jonas.jenwald@gmail.com>
Date: Sat, 27 Feb 2021 14:20:43 +0100
Subject: [PATCH] Modernize some of the code in `src/core/cmap.js` by using
 classes and async/await

This converts a couple of our old "classes" to proper ECMAScript classes, and replaces a lot of manual Promise-wrapping with async/await instead.
---
 src/core/cmap.js | 174 +++++++++++++++++++++--------------------------
 1 file changed, 79 insertions(+), 95 deletions(-)

diff --git a/src/core/cmap.js b/src/core/cmap.js
index a6abeaab5..dbf083bf9 100644
--- a/src/core/cmap.js
+++ b/src/core/cmap.js
@@ -479,20 +479,21 @@ var BinaryCMapReader = (function BinaryCMapReaderClosure() {
   var MAX_NUM_SIZE = 16;
   var MAX_ENCODED_NUM_SIZE = 19; // ceil(MAX_NUM_SIZE * 7 / 8)
 
-  function BinaryCMapStream(data) {
-    this.buffer = data;
-    this.pos = 0;
-    this.end = data.length;
-    this.tmpBuf = new Uint8Array(MAX_ENCODED_NUM_SIZE);
-  }
+  class BinaryCMapStream {
+    constructor(data) {
+      this.buffer = data;
+      this.pos = 0;
+      this.end = data.length;
+      this.tmpBuf = new Uint8Array(MAX_ENCODED_NUM_SIZE);
+    }
 
-  BinaryCMapStream.prototype = {
     readByte() {
       if (this.pos >= this.end) {
         return -1;
       }
       return this.buffer[this.pos++];
-    },
+    }
+
     readNumber() {
       var n = 0;
       var last;
@@ -505,15 +506,18 @@ var BinaryCMapReader = (function BinaryCMapReaderClosure() {
         n = (n << 7) | (b & 0x7f);
       } while (!last);
       return n;
-    },
+    }
+
     readSigned() {
       var n = this.readNumber();
       return n & 1 ? ~(n >>> 1) : n >>> 1;
-    },
+    }
+
     readHex(num, size) {
       num.set(this.buffer.subarray(this.pos, this.pos + size + 1));
       this.pos += size + 1;
-    },
+    }
+
     readHexNumber(num, size) {
       var last;
       var stack = this.tmpBuf,
@@ -539,7 +543,8 @@ var BinaryCMapReader = (function BinaryCMapReaderClosure() {
         buffer >>= 8;
         bufferSize -= 8;
       }
-    },
+    }
+
     readHexSigned(num, size) {
       this.readHexNumber(num, size);
       var sign = num[size] & 1 ? 255 : 0;
@@ -548,7 +553,8 @@ var BinaryCMapReader = (function BinaryCMapReaderClosure() {
         c = ((c & 1) << 8) | num[i];
         num[i] = (c >> 1) ^ sign;
       }
-    },
+    }
+
     readString() {
       var len = this.readNumber();
       var s = "";
@@ -556,11 +562,12 @@ var BinaryCMapReader = (function BinaryCMapReaderClosure() {
         s += String.fromCharCode(this.readNumber());
       }
       return s;
-    },
-  };
+    }
+  }
 
-  function processBinaryCMap(data, cMap, extend) {
-    return new Promise(function (resolve, reject) {
+  // eslint-disable-next-line no-shadow
+  class BinaryCMapReader {
+    async process(data, cMap, extend) {
       var stream = new BinaryCMapStream(data);
       var header = stream.readByte();
       cMap.vertical = !!(header & 1);
@@ -592,7 +599,7 @@ var BinaryCMapReader = (function BinaryCMapReaderClosure() {
         var dataSize = b & 15;
 
         if (dataSize + 1 > MAX_NUM_SIZE) {
-          throw new Error("processBinaryCMap: Invalid dataSize.");
+          throw new Error("BinaryCMapReader.process: Invalid dataSize.");
         }
 
         var ucs2DataSize = 1;
@@ -730,26 +737,17 @@ var BinaryCMapReader = (function BinaryCMapReaderClosure() {
             }
             break;
           default:
-            reject(new Error("processBinaryCMap: Unknown type: " + type));
-            return;
+            throw new Error(`BinaryCMapReader.process - unknown type: ${type}`);
         }
       }
 
       if (useCMap) {
-        resolve(extend(useCMap));
-        return;
+        return extend(useCMap);
       }
-      resolve(cMap);
-    });
+      return cMap;
+    }
   }
 
-  // eslint-disable-next-line no-shadow
-  function BinaryCMapReader() {}
-
-  BinaryCMapReader.prototype = {
-    process: processBinaryCMap,
-  };
-
   return BinaryCMapReader;
 })();
 
@@ -902,7 +900,7 @@ var CMapFactory = (function CMapFactoryClosure() {
     }
   }
 
-  function parseCMap(cMap, lexer, fetchBuiltInCMap, useCMap) {
+  async function parseCMap(cMap, lexer, fetchBuiltInCMap, useCMap) {
     var previous;
     var embeddedUseCMap;
     objLoop: while (true) {
@@ -960,74 +958,59 @@ var CMapFactory = (function CMapFactoryClosure() {
     if (useCMap) {
       return extendCMap(cMap, fetchBuiltInCMap, useCMap);
     }
-    return Promise.resolve(cMap);
+    return cMap;
   }
 
-  function extendCMap(cMap, fetchBuiltInCMap, useCMap) {
-    return createBuiltInCMap(useCMap, fetchBuiltInCMap).then(function (
-      newCMap
-    ) {
-      cMap.useCMap = newCMap;
-      // If there aren't any code space ranges defined clone all the parent ones
-      // into this cMap.
-      if (cMap.numCodespaceRanges === 0) {
-        var useCodespaceRanges = cMap.useCMap.codespaceRanges;
-        for (var i = 0; i < useCodespaceRanges.length; i++) {
-          cMap.codespaceRanges[i] = useCodespaceRanges[i].slice();
-        }
-        cMap.numCodespaceRanges = cMap.useCMap.numCodespaceRanges;
+  async function extendCMap(cMap, fetchBuiltInCMap, useCMap) {
+    cMap.useCMap = await createBuiltInCMap(useCMap, fetchBuiltInCMap);
+    // If there aren't any code space ranges defined clone all the parent ones
+    // into this cMap.
+    if (cMap.numCodespaceRanges === 0) {
+      var useCodespaceRanges = cMap.useCMap.codespaceRanges;
+      for (var i = 0; i < useCodespaceRanges.length; i++) {
+        cMap.codespaceRanges[i] = useCodespaceRanges[i].slice();
+      }
+      cMap.numCodespaceRanges = cMap.useCMap.numCodespaceRanges;
+    }
+    // Merge the map into the current one, making sure not to override
+    // any previously defined entries.
+    cMap.useCMap.forEach(function (key, value) {
+      if (!cMap.contains(key)) {
+        cMap.mapOne(key, cMap.useCMap.lookup(key));
       }
-      // Merge the map into the current one, making sure not to override
-      // any previously defined entries.
-      cMap.useCMap.forEach(function (key, value) {
-        if (!cMap.contains(key)) {
-          cMap.mapOne(key, cMap.useCMap.lookup(key));
-        }
-      });
-
-      return cMap;
     });
+
+    return cMap;
   }
 
-  function createBuiltInCMap(name, fetchBuiltInCMap) {
+  async function createBuiltInCMap(name, fetchBuiltInCMap) {
     if (name === "Identity-H") {
-      return Promise.resolve(new IdentityCMap(false, 2));
+      return new IdentityCMap(false, 2);
     } else if (name === "Identity-V") {
-      return Promise.resolve(new IdentityCMap(true, 2));
+      return new IdentityCMap(true, 2);
     }
     if (!BUILT_IN_CMAPS.includes(name)) {
-      return Promise.reject(new Error("Unknown CMap name: " + name));
+      throw new Error("Unknown CMap name: " + name);
     }
     if (!fetchBuiltInCMap) {
-      return Promise.reject(
-        new Error("Built-in CMap parameters are not provided.")
-      );
+      throw new Error("Built-in CMap parameters are not provided.");
     }
 
-    return fetchBuiltInCMap(name).then(function (data) {
-      var cMapData = data.cMapData,
-        compressionType = data.compressionType;
-      var cMap = new CMap(true);
+    const { cMapData, compressionType } = await fetchBuiltInCMap(name);
+    var cMap = new CMap(true);
 
-      if (compressionType === CMapCompressionType.BINARY) {
-        return new BinaryCMapReader().process(
-          cMapData,
-          cMap,
-          function (useCMap) {
-            return extendCMap(cMap, fetchBuiltInCMap, useCMap);
-          }
-        );
-      }
-      if (compressionType === CMapCompressionType.NONE) {
-        var lexer = new Lexer(new Stream(cMapData));
-        return parseCMap(cMap, lexer, fetchBuiltInCMap, null);
-      }
-      return Promise.reject(
-        new Error(
-          "TODO: Only BINARY/NONE CMap compression is currently supported."
-        )
-      );
-    });
+    if (compressionType === CMapCompressionType.BINARY) {
+      return new BinaryCMapReader().process(cMapData, cMap, useCMap => {
+        return extendCMap(cMap, fetchBuiltInCMap, useCMap);
+      });
+    }
+    if (compressionType === CMapCompressionType.NONE) {
+      var lexer = new Lexer(new Stream(cMapData));
+      return parseCMap(cMap, lexer, fetchBuiltInCMap, null);
+    }
+    throw new Error(
+      "TODO: Only BINARY/NONE CMap compression is currently supported."
+    );
   }
 
   return {
@@ -1039,16 +1022,17 @@ var CMapFactory = (function CMapFactoryClosure() {
       if (isName(encoding)) {
         return createBuiltInCMap(encoding.name, fetchBuiltInCMap);
       } else if (isStream(encoding)) {
-        var cMap = new CMap();
-        var lexer = new Lexer(encoding);
-        return parseCMap(cMap, lexer, fetchBuiltInCMap, useCMap).then(function (
-          parsedCMap
-        ) {
-          if (parsedCMap.isIdentityCMap) {
-            return createBuiltInCMap(parsedCMap.name, fetchBuiltInCMap);
-          }
-          return parsedCMap;
-        });
+        const parsedCMap = await parseCMap(
+          /* cMap = */ new CMap(),
+          /* lexer = */ new Lexer(encoding),
+          fetchBuiltInCMap,
+          useCMap
+        );
+
+        if (parsedCMap.isIdentityCMap) {
+          return createBuiltInCMap(parsedCMap.name, fetchBuiltInCMap);
+        }
+        return parsedCMap;
       }
       throw new Error("Encoding required.");
     },