Как кодировать операнды CFF2 в соответствии со спецификацией OpenType в JavaScript - PullRequest
0 голосов
/ 22 января 2019

Я смотрю на спецификации и задаюсь вопросом, как преобразовать то, что они говорят, в реальный код.Как кодируются эти целые числа и вещественные числа (дополнительная информация здесь ).

var encodings = {
  // each encoding should return an array of 8-bit values.
  number: function(value){
    if (parseInt(value) !== value) return encodings.real(value)
    if (integer >= -107 && integer <= 107) return encodings.integer1(value)
    if (integer >= 108 && integer <= 1131) return encodings.integer2a(value)
    if (integer >= -1131 && integer <= -108) return encodings.integer2b(value)
    if (integer >= -32768 && integer <= 32767) return encodings.integer3(value)
    return encodings.integer5(value)
  },

  // 1 32 to 246 -107 to +107  b0 – 139
  // 0 8b    
  // 100 ef    
  // -100  27   
  integer1: function(integer){

  },

  // 2 247 to 250  +108 to +1131 (b0 – 247) * 256 + b1 + 108
  integer2a: function(integer){

  },

  // 2 251 to 254  -1131 to -108 -(b0 – 251) * 256 – b1 – 108
  integer2b: function(integer){

  },

  // 3 28  -32768 to +32767  b1 << 8 | b2
  integer3: function(integer){

  },

  // 5 29  -(2^31) to +(2^31 – 1)  b1 << 24 | b2 << 16 | b3 << 8 | b4
  integer5: function(integer){

  },

  // 0 to 9  0 to 9    
  // a . (decimal point)   
  // b E   
  // c E–    
  // d <reserved>    
  // e – (minus)   
  // f end of number
  real: function(real){

  }
}

console.log(encodings.number(0))
console.log(encodings.number(100))
console.log(encodings.number(-100))
console.log(encodings.number(1000))
console.log(encodings.number(10000))
console.log(encodings.number(-10000))
console.log(encodings.number(100000))
console.log(encodings.number(-100000))

// 1000  fa 7c   
// 10000 1c 27 10    
// -10000  1c d8 f0    
// 100000  1d 00 01 86 a0    
// -100000 1d ff fe 79 60

Я хочу закодировать, скажем, первое, напримерэто после прочтения спецификации:

  integer1: function(integer){
    return integer - 139
  }

Но это приведет к неправильному выводу в соответствии с их примерами.Так что я не совсем понимаю, как их кодировать.Интересно, можно ли показать, как реализовать их, быстро объяснив, как вы это поняли / что означает реализация, если она не очевидна, или как реализация была получена из спецификации.

Вот фрагмент фактическогокод, который я использовал для вычисления закодированных значений:

var api = {
  type: {
    NUMBER: {
      make: function(v) {
        if (v >= -107 && v <= 107) {
          return [v - 139];
        } else if (v >= 108 && v <= 1131) {
          v = v - 108;
          return [(v >> 8), v & 0xFF];
        } else if (v >= -1131 && v <= -108) {
          v = -v - 108;
          return [(v >> 8), v & 0xFF];
        } else if (v >= -32768 && v <= 32767) {
          return api.type.NUMBER16.make(v);
        } else {
          return api.type.NUMBER32.make(v);
        }
      }
    },

    NUMBER16: {
      make: function(v) {
        return [28, (v >> 8) & 0xFF, v & 0xFF];
      }
    },

    NUMBER32: {
      make: function(v) {
        return [29, (v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF];
      }
    },

    REAL: {
      make: function(v) {
        let value = v.toString();

        // Some numbers use an epsilon to encode the value. (e.g. JavaScript will store 0.0000001 as 1e-7)
        // This code converts it back to a number without the epsilon.
        var m = /\.(\d*?)(?:9{5,20}|0{5,20})\d{0,2}(?:e(.+)|$)/.exec(value);
        if (m) {
          var epsilon = parseFloat('1e' + ((m[2] ? +m[2] : 0) + m[1].length));
          value = (Math.round(v * epsilon) / epsilon).toString();
        }

        let nibbles = '';
        for (let i = 0, ii = value.length; i < ii; i += 1) {
          var c = value[i];
          if (c === 'e') {
            nibbles += value[++i] === '-' ? 'c' : 'b';
          } else if (c === '.') {
            nibbles += 'a';
          } else if (c === '-') {
            nibbles += 'e';
          } else {
            nibbles += c;
          }
        }

        nibbles += (nibbles.length & 1) ? 'f' : 'ff';
        var out = [30];
        for (let i = 0, ii = nibbles.length; i < ii; i += 2) {
          out.push(parseInt(nibbles.substr(i, 2), 16));
        }

        return out;
      }
    },

    DICT: {
      make: function(m) {
        let d = [];
        var keys = Object.keys(m);
        var length = keys.length;

        for (let i = 0; i < length; i += 1) {
          // Object.keys() return string keys, but our keys are always numeric.
          var k = keys[i]
          var v = m[k];
          var op = v.op
          // Value comes before the key.
          d = d.concat(api.type.OPERAND.make(v.value, v.type));
          d = d.concat(api.type.OPERATOR.make(op));
        }

        return d;
      }
    },

    OPERATOR: {
      make: function(v) {
        if (v < 1200) {
          return [v];
        } else {
          return [12, v - 1200];
        }
      }
    },

    OPERAND: {
      make: function(v, type) {
        let d = [];
        if (Array.isArray(type)) {
          for (let i = 0; i < type.length; i += 1) {
            d = d.concat(api.type.OPERAND.make(v[i], type[i]))
          }
        } else {
          if (type === 'SID') {
            d = d.concat(api.type.NUMBER.make(v))
          } else if (type === 'offset') {
            d = d.concat(api.type.NUMBER32.make(v))
          } else if (type === 'number') {
            d = d.concat(api.type.NUMBER.make(v))
          } else if (type === 'real') {
            d = d.concat(api.type.REAL.make(v))
          } else if (type === 'DELTA') {
            d = d.concat(api.type.DELTA.make(v))
          } else {
            throw new Error('Unknown operand type ' + type)
          }
        }

        return d;
      }
    }
  }
}

Следуя спецификации, я попытался изменить реализацию Number на то, что казалось более логичным, но он выдает неправильные выходные значения:

NUMBER: {
  make: function(v) {
    if (v >= -107 && v <= 107) {
      return [v - 139];
    } else if (v >= 108 && v <= 1131) {
      // v = v - 108;
      var b0 = v & 0xFF
      var b1 = (v >> 8) & 0xFF
      var v = (b0 - 247) * 256 + (b1 + 108)
      return [(v >> 8), v & 0xFF];
    } else if (v >= -1131 && v <= -108) {
        // v = -v - 108;
        var b0 = v & 0xFF
        var b1 = (v >> 8) & 0xFF
        v = -(b0 - 251) * 256 - b1 - 108
      return [(v >> 8), v & 0xFF];
    } else if (v >= -32768 && v <= 32767) {
      return api.type.NUMBER16.make(v);
    } else {
      return api.type.NUMBER32.make(v);
    }
  }
},

NUMBER16: {
  make: function(v) {
    var b0 = v & 0xFF
    var b1 = (v >> 8) & 0xFF
    var b2 = (v >> 16) & 0xFF
    v = b1 << 8 | b2
    return [28, (v >> 8) & 0xFF, v & 0xFF];
  }
},

NUMBER32: {
  make: function(v) {
    var b0 = v & 0xFF
    var b1 = (v >> 8) & 0xFF
    var b2 = (v >> 16) & 0xFF
    var b3 = (v >> 24) & 0xFF
    var b4 = (v >> 32) & 0xFF
    v = b1 << 24 | b2 << 16 | b3 << 8 | b4
    return [29, (v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF];
  }
}

Так что я не уверен, как на самом деле рассчитать значения.Если окажется, что исходная форма в приведенном мной примере кода верна, если вы не возражаете, объясните, как она была получена из спецификации, что было бы полезно, потому что я не вижу связи.

...