/ node_modules / opentype.js / src / types.js
types.js
  1  // Data types used in the OpenType font file.
  2  // All OpenType fonts use Motorola-style byte ordering (Big Endian)
  3  
  4  import check from './check';
  5  
  6  const LIMIT16 = 32768; // The limit at which a 16-bit number switches signs == 2^15
  7  const LIMIT32 = 2147483648; // The limit at which a 32-bit number switches signs == 2 ^ 31
  8  
  9  /**
 10   * @exports opentype.decode
 11   * @class
 12   */
 13  const decode = {};
 14  /**
 15   * @exports opentype.encode
 16   * @class
 17   */
 18  const encode = {};
 19  /**
 20   * @exports opentype.sizeOf
 21   * @class
 22   */
 23  const sizeOf = {};
 24  
 25  // Return a function that always returns the same value.
 26  function constant(v) {
 27      return function() {
 28          return v;
 29      };
 30  }
 31  
 32  // OpenType data types //////////////////////////////////////////////////////
 33  
 34  /**
 35   * Convert an 8-bit unsigned integer to a list of 1 byte.
 36   * @param {number}
 37   * @returns {Array}
 38   */
 39  encode.BYTE = function(v) {
 40      check.argument(v >= 0 && v <= 255, 'Byte value should be between 0 and 255.');
 41      return [v];
 42  };
 43  /**
 44   * @constant
 45   * @type {number}
 46   */
 47  sizeOf.BYTE = constant(1);
 48  
 49  /**
 50   * Convert a 8-bit signed integer to a list of 1 byte.
 51   * @param {string}
 52   * @returns {Array}
 53   */
 54  encode.CHAR = function(v) {
 55      return [v.charCodeAt(0)];
 56  };
 57  
 58  /**
 59   * @constant
 60   * @type {number}
 61   */
 62  sizeOf.CHAR = constant(1);
 63  
 64  /**
 65   * Convert an ASCII string to a list of bytes.
 66   * @param {string}
 67   * @returns {Array}
 68   */
 69  encode.CHARARRAY = function(v) {
 70      const b = [];
 71      for (let i = 0; i < v.length; i += 1) {
 72          b[i] = v.charCodeAt(i);
 73      }
 74  
 75      return b;
 76  };
 77  
 78  /**
 79   * @param {Array}
 80   * @returns {number}
 81   */
 82  sizeOf.CHARARRAY = function(v) {
 83      return v.length;
 84  };
 85  
 86  /**
 87   * Convert a 16-bit unsigned integer to a list of 2 bytes.
 88   * @param {number}
 89   * @returns {Array}
 90   */
 91  encode.USHORT = function(v) {
 92      return [(v >> 8) & 0xFF, v & 0xFF];
 93  };
 94  
 95  /**
 96   * @constant
 97   * @type {number}
 98   */
 99  sizeOf.USHORT = constant(2);
100  
101  /**
102   * Convert a 16-bit signed integer to a list of 2 bytes.
103   * @param {number}
104   * @returns {Array}
105   */
106  encode.SHORT = function(v) {
107      // Two's complement
108      if (v >= LIMIT16) {
109          v = -(2 * LIMIT16 - v);
110      }
111  
112      return [(v >> 8) & 0xFF, v & 0xFF];
113  };
114  
115  /**
116   * @constant
117   * @type {number}
118   */
119  sizeOf.SHORT = constant(2);
120  
121  /**
122   * Convert a 24-bit unsigned integer to a list of 3 bytes.
123   * @param {number}
124   * @returns {Array}
125   */
126  encode.UINT24 = function(v) {
127      return [(v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF];
128  };
129  
130  /**
131   * @constant
132   * @type {number}
133   */
134  sizeOf.UINT24 = constant(3);
135  
136  /**
137   * Convert a 32-bit unsigned integer to a list of 4 bytes.
138   * @param {number}
139   * @returns {Array}
140   */
141  encode.ULONG = function(v) {
142      return [(v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF];
143  };
144  
145  /**
146   * @constant
147   * @type {number}
148   */
149  sizeOf.ULONG = constant(4);
150  
151  /**
152   * Convert a 32-bit unsigned integer to a list of 4 bytes.
153   * @param {number}
154   * @returns {Array}
155   */
156  encode.LONG = function(v) {
157      // Two's complement
158      if (v >= LIMIT32) {
159          v = -(2 * LIMIT32 - v);
160      }
161  
162      return [(v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF];
163  };
164  
165  /**
166   * @constant
167   * @type {number}
168   */
169  sizeOf.LONG = constant(4);
170  
171  encode.FIXED = encode.ULONG;
172  sizeOf.FIXED = sizeOf.ULONG;
173  
174  encode.FWORD = encode.SHORT;
175  sizeOf.FWORD = sizeOf.SHORT;
176  
177  encode.UFWORD = encode.USHORT;
178  sizeOf.UFWORD = sizeOf.USHORT;
179  
180  /**
181   * Convert a 32-bit Apple Mac timestamp integer to a list of 8 bytes, 64-bit timestamp.
182   * @param {number}
183   * @returns {Array}
184   */
185  encode.LONGDATETIME = function(v) {
186      return [0, 0, 0, 0, (v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF];
187  };
188  
189  /**
190   * @constant
191   * @type {number}
192   */
193  sizeOf.LONGDATETIME = constant(8);
194  
195  /**
196   * Convert a 4-char tag to a list of 4 bytes.
197   * @param {string}
198   * @returns {Array}
199   */
200  encode.TAG = function(v) {
201      check.argument(v.length === 4, 'Tag should be exactly 4 ASCII characters.');
202      return [v.charCodeAt(0),
203              v.charCodeAt(1),
204              v.charCodeAt(2),
205              v.charCodeAt(3)];
206  };
207  
208  /**
209   * @constant
210   * @type {number}
211   */
212  sizeOf.TAG = constant(4);
213  
214  // CFF data types ///////////////////////////////////////////////////////////
215  
216  encode.Card8 = encode.BYTE;
217  sizeOf.Card8 = sizeOf.BYTE;
218  
219  encode.Card16 = encode.USHORT;
220  sizeOf.Card16 = sizeOf.USHORT;
221  
222  encode.OffSize = encode.BYTE;
223  sizeOf.OffSize = sizeOf.BYTE;
224  
225  encode.SID = encode.USHORT;
226  sizeOf.SID = sizeOf.USHORT;
227  
228  // Convert a numeric operand or charstring number to a variable-size list of bytes.
229  /**
230   * Convert a numeric operand or charstring number to a variable-size list of bytes.
231   * @param {number}
232   * @returns {Array}
233   */
234  encode.NUMBER = function(v) {
235      if (v >= -107 && v <= 107) {
236          return [v + 139];
237      } else if (v >= 108 && v <= 1131) {
238          v = v - 108;
239          return [(v >> 8) + 247, v & 0xFF];
240      } else if (v >= -1131 && v <= -108) {
241          v = -v - 108;
242          return [(v >> 8) + 251, v & 0xFF];
243      } else if (v >= -32768 && v <= 32767) {
244          return encode.NUMBER16(v);
245      } else {
246          return encode.NUMBER32(v);
247      }
248  };
249  
250  /**
251   * @param {number}
252   * @returns {number}
253   */
254  sizeOf.NUMBER = function(v) {
255      return encode.NUMBER(v).length;
256  };
257  
258  /**
259   * Convert a signed number between -32768 and +32767 to a three-byte value.
260   * This ensures we always use three bytes, but is not the most compact format.
261   * @param {number}
262   * @returns {Array}
263   */
264  encode.NUMBER16 = function(v) {
265      return [28, (v >> 8) & 0xFF, v & 0xFF];
266  };
267  
268  /**
269   * @constant
270   * @type {number}
271   */
272  sizeOf.NUMBER16 = constant(3);
273  
274  /**
275   * Convert a signed number between -(2^31) and +(2^31-1) to a five-byte value.
276   * This is useful if you want to be sure you always use four bytes,
277   * at the expense of wasting a few bytes for smaller numbers.
278   * @param {number}
279   * @returns {Array}
280   */
281  encode.NUMBER32 = function(v) {
282      return [29, (v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF];
283  };
284  
285  /**
286   * @constant
287   * @type {number}
288   */
289  sizeOf.NUMBER32 = constant(5);
290  
291  /**
292   * @param {number}
293   * @returns {Array}
294   */
295  encode.REAL = function(v) {
296      let value = v.toString();
297  
298      // Some numbers use an epsilon to encode the value. (e.g. JavaScript will store 0.0000001 as 1e-7)
299      // This code converts it back to a number without the epsilon.
300      const m = /\.(\d*?)(?:9{5,20}|0{5,20})\d{0,2}(?:e(.+)|$)/.exec(value);
301      if (m) {
302          const epsilon = parseFloat('1e' + ((m[2] ? +m[2] : 0) + m[1].length));
303          value = (Math.round(v * epsilon) / epsilon).toString();
304      }
305  
306      let nibbles = '';
307      for (let i = 0, ii = value.length; i < ii; i += 1) {
308          const c = value[i];
309          if (c === 'e') {
310              nibbles += value[++i] === '-' ? 'c' : 'b';
311          } else if (c === '.') {
312              nibbles += 'a';
313          } else if (c === '-') {
314              nibbles += 'e';
315          } else {
316              nibbles += c;
317          }
318      }
319  
320      nibbles += (nibbles.length & 1) ? 'f' : 'ff';
321      const out = [30];
322      for (let i = 0, ii = nibbles.length; i < ii; i += 2) {
323          out.push(parseInt(nibbles.substr(i, 2), 16));
324      }
325  
326      return out;
327  };
328  
329  /**
330   * @param {number}
331   * @returns {number}
332   */
333  sizeOf.REAL = function(v) {
334      return encode.REAL(v).length;
335  };
336  
337  encode.NAME = encode.CHARARRAY;
338  sizeOf.NAME = sizeOf.CHARARRAY;
339  
340  encode.STRING = encode.CHARARRAY;
341  sizeOf.STRING = sizeOf.CHARARRAY;
342  
343  /**
344   * @param {DataView} data
345   * @param {number} offset
346   * @param {number} numBytes
347   * @returns {string}
348   */
349  decode.UTF8 = function(data, offset, numBytes) {
350      const codePoints = [];
351      const numChars = numBytes;
352      for (let j = 0; j < numChars; j++, offset += 1) {
353          codePoints[j] = data.getUint8(offset);
354      }
355  
356      return String.fromCharCode.apply(null, codePoints);
357  };
358  
359  /**
360   * @param {DataView} data
361   * @param {number} offset
362   * @param {number} numBytes
363   * @returns {string}
364   */
365  decode.UTF16 = function(data, offset, numBytes) {
366      const codePoints = [];
367      const numChars = numBytes / 2;
368      for (let j = 0; j < numChars; j++, offset += 2) {
369          codePoints[j] = data.getUint16(offset);
370      }
371  
372      return String.fromCharCode.apply(null, codePoints);
373  };
374  
375  /**
376   * Convert a JavaScript string to UTF16-BE.
377   * @param {string}
378   * @returns {Array}
379   */
380  encode.UTF16 = function(v) {
381      const b = [];
382      for (let i = 0; i < v.length; i += 1) {
383          const codepoint = v.charCodeAt(i);
384          b[b.length] = (codepoint >> 8) & 0xFF;
385          b[b.length] = codepoint & 0xFF;
386      }
387  
388      return b;
389  };
390  
391  /**
392   * @param {string}
393   * @returns {number}
394   */
395  sizeOf.UTF16 = function(v) {
396      return v.length * 2;
397  };
398  
399  // Data for converting old eight-bit Macintosh encodings to Unicode.
400  // This representation is optimized for decoding; encoding is slower
401  // and needs more memory. The assumption is that all opentype.js users
402  // want to open fonts, but saving a font will be comparatively rare
403  // so it can be more expensive. Keyed by IANA character set name.
404  //
405  // Python script for generating these strings:
406  //
407  //     s = u''.join([chr(c).decode('mac_greek') for c in range(128, 256)])
408  //     print(s.encode('utf-8'))
409  /**
410   * @private
411   */
412  const eightBitMacEncodings = {
413      'x-mac-croatian':  // Python: 'mac_croatian'
414      'ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®Š™´¨≠ŽØ∞±≤≥∆µ∂∑∏š∫ªºΩžø' +
415      '¿¡¬√ƒ≈ƫȅ ÀÃÕŒœĐ—“”‘’÷◊©⁄€‹›Æ»–·‚„‰ÂćÁčÈÍÎÏÌÓÔđÒÚÛÙıˆ˜¯πË˚¸Êæˇ',
416      'x-mac-cyrillic':  // Python: 'mac_cyrillic'
417      'АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ†°Ґ£§•¶І®©™Ђђ≠Ѓѓ∞±≤≥іµґЈЄєЇїЉљЊњ' +
418      'јЅ¬√ƒ≈∆«»… ЋћЌќѕ–—“”‘’÷„ЎўЏџ№Ёёяабвгдежзийклмнопрстуфхцчшщъыьэю',
419      'x-mac-gaelic': // http://unicode.org/Public/MAPPINGS/VENDORS/APPLE/GAELIC.TXT
420      'ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ÆØḂ±≤≥ḃĊċḊḋḞḟĠġṀæø' +
421      'ṁṖṗɼƒſṠ«»… ÀÃÕŒœ–—“”‘’ṡẛÿŸṪ€‹›Ŷŷṫ·Ỳỳ⁊ÂÊÁËÈÍÎÏÌÓÔ♣ÒÚÛÙıÝýŴŵẄẅẀẁẂẃ',
422      'x-mac-greek':  // Python: 'mac_greek'
423      'Ĺ²É³ÖÜ΅àâä΄¨çéèê룙î‰ôö¦€ùûü†ΓΔΘΛΞΠß®©ΣΪ§≠°·Α±≤≥¥ΒΕΖΗΙΚΜΦΫΨΩ' +
424      'άΝ¬ΟΡ≈Τ«»… ΥΧΆΈœ–―“”‘’÷ΉΊΌΎέήίόΏύαβψδεφγηιξκλμνοπώρστθωςχυζϊϋΐΰ\u00AD',
425      'x-mac-icelandic':  // Python: 'mac_iceland'
426      'ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûüݰ¢£§•¶ß®©™´¨≠ÆØ∞±≤≥¥µ∂∑∏π∫ªºΩæø' +
427      '¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸ⁄€ÐðÞþý·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ',
428      'x-mac-inuit': // http://unicode.org/Public/MAPPINGS/VENDORS/APPLE/INUIT.TXT
429      'ᐃᐄᐅᐆᐊᐋᐱᐲᐳᐴᐸᐹᑉᑎᑏᑐᑑᑕᑖᑦᑭᑮᑯᑰᑲᑳᒃᒋᒌᒍᒎᒐᒑ°ᒡᒥᒦ•¶ᒧ®©™ᒨᒪᒫᒻᓂᓃᓄᓅᓇᓈᓐᓯᓰᓱᓲᓴᓵᔅᓕᓖᓗ' +
430      'ᓘᓚᓛᓪᔨᔩᔪᔫᔭ… ᔮᔾᕕᕖᕗ–—“”‘’ᕘᕙᕚᕝᕆᕇᕈᕉᕋᕌᕐᕿᖀᖁᖂᖃᖄᖅᖏᖐᖑᖒᖓᖔᖕᙱᙲᙳᙴᙵᙶᖖᖠᖡᖢᖣᖤᖥᖦᕼŁł',
431      'x-mac-ce':  // Python: 'mac_latin2'
432      'ÄĀāÉĄÖÜáąČäčĆć鏟ĎíďĒēĖóėôöõúĚěü†°Ę£§•¶ß®©™ę¨≠ģĮįĪ≤≥īĶ∂∑łĻļĽľĹĺŅ' +
433      'ņѬ√ńŇ∆«»… ňŐÕőŌ–—“”‘’÷◊ōŔŕŘ‹›řŖŗŠ‚„šŚśÁŤťÍŽžŪÓÔūŮÚůŰűŲųÝýķŻŁżĢˇ',
434      macintosh:  // Python: 'mac_roman'
435      'ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ÆØ∞±≤≥¥µ∂∑∏π∫ªºΩæø' +
436      '¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸ⁄€‹›fifl‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ',
437      'x-mac-romanian':  // Python: 'mac_romanian'
438      'ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ĂȘ∞±≤≥¥µ∂∑∏π∫ªºΩăș' +
439      '¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸ⁄€‹›Țț‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙıˆ˜¯˘˙˚¸˝˛ˇ',
440      'x-mac-turkish':  // Python: 'mac_turkish'
441      'ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü†°¢£§•¶ß®©™´¨≠ÆØ∞±≤≥¥µ∂∑∏π∫ªºΩæø' +
442      '¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷◊ÿŸĞğİıŞş‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙˆ˜¯˘˙˚¸˝˛ˇ'
443  };
444  
445  /**
446   * Decodes an old-style Macintosh string. Returns either a Unicode JavaScript
447   * string, or 'undefined' if the encoding is unsupported. For example, we do
448   * not support Chinese, Japanese or Korean because these would need large
449   * mapping tables.
450   * @param {DataView} dataView
451   * @param {number} offset
452   * @param {number} dataLength
453   * @param {string} encoding
454   * @returns {string}
455   */
456  decode.MACSTRING = function(dataView, offset, dataLength, encoding) {
457      const table = eightBitMacEncodings[encoding];
458      if (table === undefined) {
459          return undefined;
460      }
461  
462      let result = '';
463      for (let i = 0; i < dataLength; i++) {
464          const c = dataView.getUint8(offset + i);
465          // In all eight-bit Mac encodings, the characters 0x00..0x7F are
466          // mapped to U+0000..U+007F; we only need to look up the others.
467          if (c <= 0x7F) {
468              result += String.fromCharCode(c);
469          } else {
470              result += table[c & 0x7F];
471          }
472      }
473  
474      return result;
475  };
476  
477  // Helper function for encode.MACSTRING. Returns a dictionary for mapping
478  // Unicode character codes to their 8-bit MacOS equivalent. This table
479  // is not exactly a super cheap data structure, but we do not care because
480  // encoding Macintosh strings is only rarely needed in typical applications.
481  const macEncodingTableCache = typeof WeakMap === 'function' && new WeakMap();
482  let macEncodingCacheKeys;
483  const getMacEncodingTable = function (encoding) {
484      // Since we use encoding as a cache key for WeakMap, it has to be
485      // a String object and not a literal. And at least on NodeJS 2.10.1,
486      // WeakMap requires that the same String instance is passed for cache hits.
487      if (!macEncodingCacheKeys) {
488          macEncodingCacheKeys = {};
489          for (let e in eightBitMacEncodings) {
490              /*jshint -W053 */  // Suppress "Do not use String as a constructor."
491              macEncodingCacheKeys[e] = new String(e);
492          }
493      }
494  
495      const cacheKey = macEncodingCacheKeys[encoding];
496      if (cacheKey === undefined) {
497          return undefined;
498      }
499  
500      // We can't do "if (cache.has(key)) {return cache.get(key)}" here:
501      // since garbage collection may run at any time, it could also kick in
502      // between the calls to cache.has() and cache.get(). In that case,
503      // we would return 'undefined' even though we do support the encoding.
504      if (macEncodingTableCache) {
505          const cachedTable = macEncodingTableCache.get(cacheKey);
506          if (cachedTable !== undefined) {
507              return cachedTable;
508          }
509      }
510  
511      const decodingTable = eightBitMacEncodings[encoding];
512      if (decodingTable === undefined) {
513          return undefined;
514      }
515  
516      const encodingTable = {};
517      for (let i = 0; i < decodingTable.length; i++) {
518          encodingTable[decodingTable.charCodeAt(i)] = i + 0x80;
519      }
520  
521      if (macEncodingTableCache) {
522          macEncodingTableCache.set(cacheKey, encodingTable);
523      }
524  
525      return encodingTable;
526  };
527  
528  /**
529   * Encodes an old-style Macintosh string. Returns a byte array upon success.
530   * If the requested encoding is unsupported, or if the input string contains
531   * a character that cannot be expressed in the encoding, the function returns
532   * 'undefined'.
533   * @param {string} str
534   * @param {string} encoding
535   * @returns {Array}
536   */
537  encode.MACSTRING = function(str, encoding) {
538      const table = getMacEncodingTable(encoding);
539      if (table === undefined) {
540          return undefined;
541      }
542  
543      const result = [];
544      for (let i = 0; i < str.length; i++) {
545          let c = str.charCodeAt(i);
546  
547          // In all eight-bit Mac encodings, the characters 0x00..0x7F are
548          // mapped to U+0000..U+007F; we only need to look up the others.
549          if (c >= 0x80) {
550              c = table[c];
551              if (c === undefined) {
552                  // str contains a Unicode character that cannot be encoded
553                  // in the requested encoding.
554                  return undefined;
555              }
556          }
557          result[i] = c;
558          // result.push(c);
559      }
560  
561      return result;
562  };
563  
564  /**
565   * @param {string} str
566   * @param {string} encoding
567   * @returns {number}
568   */
569  sizeOf.MACSTRING = function(str, encoding) {
570      const b = encode.MACSTRING(str, encoding);
571      if (b !== undefined) {
572          return b.length;
573      } else {
574          return 0;
575      }
576  };
577  
578  // Helper for encode.VARDELTAS
579  function isByteEncodable(value) {
580      return value >= -128 && value <= 127;
581  }
582  
583  // Helper for encode.VARDELTAS
584  function encodeVarDeltaRunAsZeroes(deltas, pos, result) {
585      let runLength = 0;
586      const numDeltas = deltas.length;
587      while (pos < numDeltas && runLength < 64 && deltas[pos] === 0) {
588          ++pos;
589          ++runLength;
590      }
591      result.push(0x80 | (runLength - 1));
592      return pos;
593  }
594  
595  // Helper for encode.VARDELTAS
596  function encodeVarDeltaRunAsBytes(deltas, offset, result) {
597      let runLength = 0;
598      const numDeltas = deltas.length;
599      let pos = offset;
600      while (pos < numDeltas && runLength < 64) {
601          const value = deltas[pos];
602          if (!isByteEncodable(value)) {
603              break;
604          }
605  
606          // Within a byte-encoded run of deltas, a single zero is best
607          // stored literally as 0x00 value. However, if we have two or
608          // more zeroes in a sequence, it is better to start a new run.
609          // Fore example, the sequence of deltas [15, 15, 0, 15, 15]
610          // becomes 6 bytes (04 0F 0F 00 0F 0F) when storing the zero
611          // within the current run, but 7 bytes (01 0F 0F 80 01 0F 0F)
612          // when starting a new run.
613          if (value === 0 && pos + 1 < numDeltas && deltas[pos + 1] === 0) {
614              break;
615          }
616  
617          ++pos;
618          ++runLength;
619      }
620      result.push(runLength - 1);
621      for (let i = offset; i < pos; ++i) {
622          result.push((deltas[i] + 256) & 0xff);
623      }
624      return pos;
625  }
626  
627  // Helper for encode.VARDELTAS
628  function encodeVarDeltaRunAsWords(deltas, offset, result) {
629      let runLength = 0;
630      const numDeltas = deltas.length;
631      let pos = offset;
632      while (pos < numDeltas && runLength < 64) {
633          const value = deltas[pos];
634  
635          // Within a word-encoded run of deltas, it is easiest to start
636          // a new run (with a different encoding) whenever we encounter
637          // a zero value. For example, the sequence [0x6666, 0, 0x7777]
638          // needs 7 bytes when storing the zero inside the current run
639          // (42 66 66 00 00 77 77), and equally 7 bytes when starting a
640          // new run (40 66 66 80 40 77 77).
641          if (value === 0) {
642              break;
643          }
644  
645          // Within a word-encoded run of deltas, a single value in the
646          // range (-128..127) should be encoded within the current run
647          // because it is more compact. For example, the sequence
648          // [0x6666, 2, 0x7777] becomes 7 bytes when storing the value
649          // literally (42 66 66 00 02 77 77), but 8 bytes when starting
650          // a new run (40 66 66 00 02 40 77 77).
651          if (isByteEncodable(value) && pos + 1 < numDeltas && isByteEncodable(deltas[pos + 1])) {
652              break;
653          }
654  
655          ++pos;
656          ++runLength;
657      }
658      result.push(0x40 | (runLength - 1));
659      for (let i = offset; i < pos; ++i) {
660          const val = deltas[i];
661          result.push(((val + 0x10000) >> 8) & 0xff, (val + 0x100) & 0xff);
662      }
663      return pos;
664  }
665  
666  /**
667   * Encode a list of variation adjustment deltas.
668   *
669   * Variation adjustment deltas are used in ‘gvar’ and ‘cvar’ tables.
670   * They indicate how points (in ‘gvar’) or values (in ‘cvar’) get adjusted
671   * when generating instances of variation fonts.
672   *
673   * @see https://www.microsoft.com/typography/otspec/gvar.htm
674   * @see https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6gvar.html
675   * @param {Array}
676   * @return {Array}
677   */
678  encode.VARDELTAS = function(deltas) {
679      let pos = 0;
680      const result = [];
681      while (pos < deltas.length) {
682          const value = deltas[pos];
683          if (value === 0) {
684              pos = encodeVarDeltaRunAsZeroes(deltas, pos, result);
685          } else if (value >= -128 && value <= 127) {
686              pos = encodeVarDeltaRunAsBytes(deltas, pos, result);
687          } else {
688              pos = encodeVarDeltaRunAsWords(deltas, pos, result);
689          }
690      }
691      return result;
692  };
693  
694  // Convert a list of values to a CFF INDEX structure.
695  // The values should be objects containing name / type / value.
696  /**
697   * @param {Array} l
698   * @returns {Array}
699   */
700  encode.INDEX = function(l) {
701      //var offset, offsets, offsetEncoder, encodedOffsets, encodedOffset, data,
702      //    i, v;
703      // Because we have to know which data type to use to encode the offsets,
704      // we have to go through the values twice: once to encode the data and
705      // calculate the offsets, then again to encode the offsets using the fitting data type.
706      let offset = 1; // First offset is always 1.
707      const offsets = [offset];
708      const data = [];
709      for (let i = 0; i < l.length; i += 1) {
710          const v = encode.OBJECT(l[i]);
711          Array.prototype.push.apply(data, v);
712          offset += v.length;
713          offsets.push(offset);
714      }
715  
716      if (data.length === 0) {
717          return [0, 0];
718      }
719  
720      const encodedOffsets = [];
721      const offSize = (1 + Math.floor(Math.log(offset) / Math.log(2)) / 8) | 0;
722      const offsetEncoder = [undefined, encode.BYTE, encode.USHORT, encode.UINT24, encode.ULONG][offSize];
723      for (let i = 0; i < offsets.length; i += 1) {
724          const encodedOffset = offsetEncoder(offsets[i]);
725          Array.prototype.push.apply(encodedOffsets, encodedOffset);
726      }
727  
728      return Array.prototype.concat(encode.Card16(l.length),
729                             encode.OffSize(offSize),
730                             encodedOffsets,
731                             data);
732  };
733  
734  /**
735   * @param {Array}
736   * @returns {number}
737   */
738  sizeOf.INDEX = function(v) {
739      return encode.INDEX(v).length;
740  };
741  
742  /**
743   * Convert an object to a CFF DICT structure.
744   * The keys should be numeric.
745   * The values should be objects containing name / type / value.
746   * @param {Object} m
747   * @returns {Array}
748   */
749  encode.DICT = function(m) {
750      let d = [];
751      const keys = Object.keys(m);
752      const length = keys.length;
753  
754      for (let i = 0; i < length; i += 1) {
755          // Object.keys() return string keys, but our keys are always numeric.
756          const k = parseInt(keys[i], 0);
757          const v = m[k];
758          // Value comes before the key.
759          d = d.concat(encode.OPERAND(v.value, v.type));
760          d = d.concat(encode.OPERATOR(k));
761      }
762  
763      return d;
764  };
765  
766  /**
767   * @param {Object}
768   * @returns {number}
769   */
770  sizeOf.DICT = function(m) {
771      return encode.DICT(m).length;
772  };
773  
774  /**
775   * @param {number}
776   * @returns {Array}
777   */
778  encode.OPERATOR = function(v) {
779      if (v < 1200) {
780          return [v];
781      } else {
782          return [12, v - 1200];
783      }
784  };
785  
786  /**
787   * @param {Array} v
788   * @param {string}
789   * @returns {Array}
790   */
791  encode.OPERAND = function(v, type) {
792      let d = [];
793      if (Array.isArray(type)) {
794          for (let i = 0; i < type.length; i += 1) {
795              check.argument(v.length === type.length, 'Not enough arguments given for type' + type);
796              d = d.concat(encode.OPERAND(v[i], type[i]));
797          }
798      } else {
799          if (type === 'SID') {
800              d = d.concat(encode.NUMBER(v));
801          } else if (type === 'offset') {
802              // We make it easy for ourselves and always encode offsets as
803              // 4 bytes. This makes offset calculation for the top dict easier.
804              d = d.concat(encode.NUMBER32(v));
805          } else if (type === 'number') {
806              d = d.concat(encode.NUMBER(v));
807          } else if (type === 'real') {
808              d = d.concat(encode.REAL(v));
809          } else {
810              throw new Error('Unknown operand type ' + type);
811              // FIXME Add support for booleans
812          }
813      }
814  
815      return d;
816  };
817  
818  encode.OP = encode.BYTE;
819  sizeOf.OP = sizeOf.BYTE;
820  
821  // memoize charstring encoding using WeakMap if available
822  const wmm = typeof WeakMap === 'function' && new WeakMap();
823  
824  /**
825   * Convert a list of CharString operations to bytes.
826   * @param {Array}
827   * @returns {Array}
828   */
829  encode.CHARSTRING = function(ops) {
830      // See encode.MACSTRING for why we don't do "if (wmm && wmm.has(ops))".
831      if (wmm) {
832          const cachedValue = wmm.get(ops);
833          if (cachedValue !== undefined) {
834              return cachedValue;
835          }
836      }
837  
838      let d = [];
839      const length = ops.length;
840  
841      for (let i = 0; i < length; i += 1) {
842          const op = ops[i];
843          d = d.concat(encode[op.type](op.value));
844      }
845  
846      if (wmm) {
847          wmm.set(ops, d);
848      }
849  
850      return d;
851  };
852  
853  /**
854   * @param {Array}
855   * @returns {number}
856   */
857  sizeOf.CHARSTRING = function(ops) {
858      return encode.CHARSTRING(ops).length;
859  };
860  
861  // Utility functions ////////////////////////////////////////////////////////
862  
863  /**
864   * Convert an object containing name / type / value to bytes.
865   * @param {Object}
866   * @returns {Array}
867   */
868  encode.OBJECT = function(v) {
869      const encodingFunction = encode[v.type];
870      check.argument(encodingFunction !== undefined, 'No encoding function for type ' + v.type);
871      return encodingFunction(v.value);
872  };
873  
874  /**
875   * @param {Object}
876   * @returns {number}
877   */
878  sizeOf.OBJECT = function(v) {
879      const sizeOfFunction = sizeOf[v.type];
880      check.argument(sizeOfFunction !== undefined, 'No sizeOf function for type ' + v.type);
881      return sizeOfFunction(v.value);
882  };
883  
884  /**
885   * Convert a table object to bytes.
886   * A table contains a list of fields containing the metadata (name, type and default value).
887   * The table itself has the field values set as attributes.
888   * @param {opentype.Table}
889   * @returns {Array}
890   */
891  encode.TABLE = function(table) {
892      let d = [];
893      const length = table.fields.length;
894      const subtables = [];
895      const subtableOffsets = [];
896  
897      for (let i = 0; i < length; i += 1) {
898          const field = table.fields[i];
899          const encodingFunction = encode[field.type];
900          check.argument(encodingFunction !== undefined, 'No encoding function for field type ' + field.type + ' (' + field.name + ')');
901          let value = table[field.name];
902          if (value === undefined) {
903              value = field.value;
904          }
905  
906          const bytes = encodingFunction(value);
907  
908          if (field.type === 'TABLE') {
909              subtableOffsets.push(d.length);
910              d = d.concat([0, 0]);
911              subtables.push(bytes);
912          } else {
913              d = d.concat(bytes);
914          }
915      }
916  
917      for (let i = 0; i < subtables.length; i += 1) {
918          const o = subtableOffsets[i];
919          const offset = d.length;
920          check.argument(offset < 65536, 'Table ' + table.tableName + ' too big.');
921          d[o] = offset >> 8;
922          d[o + 1] = offset & 0xff;
923          d = d.concat(subtables[i]);
924      }
925  
926      return d;
927  };
928  
929  /**
930   * @param {opentype.Table}
931   * @returns {number}
932   */
933  sizeOf.TABLE = function(table) {
934      let numBytes = 0;
935      const length = table.fields.length;
936  
937      for (let i = 0; i < length; i += 1) {
938          const field = table.fields[i];
939          const sizeOfFunction = sizeOf[field.type];
940          check.argument(sizeOfFunction !== undefined, 'No sizeOf function for field type ' + field.type + ' (' + field.name + ')');
941          let value = table[field.name];
942          if (value === undefined) {
943              value = field.value;
944          }
945  
946          numBytes += sizeOfFunction(value);
947  
948          // Subtables take 2 more bytes for offsets.
949          if (field.type === 'TABLE') {
950              numBytes += 2;
951          }
952      }
953  
954      return numBytes;
955  };
956  
957  encode.RECORD = encode.TABLE;
958  sizeOf.RECORD = sizeOf.TABLE;
959  
960  // Merge in a list of bytes.
961  encode.LITERAL = function(v) {
962      return v;
963  };
964  
965  sizeOf.LITERAL = function(v) {
966      return v.length;
967  };
968  
969  export { decode, encode, sizeOf };