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 };