Code coverage report for fontkit/src/cff/CFFDict.coffee

Statements: 95.45% (84 / 88)      Branches: 88.57% (31 / 35)      Functions: 100% (7 / 7)      Lines: 96.15% (75 / 78)      Ignored: none     

All files » fontkit/src/cff/ » CFFDict.coffee
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 1271 1 1   1   3 3 54 54   1 372 3 9 369 72   297   191       106   1 633 8 24 625 101 524 320 204   204 204         57 57 57     57       57 1094 1094 363 113   363 363   363 363   731     57 802   57     69           69   69 1050 1050   549 549 1233   549 549   69 40   69     8             8   8 128 128   60 60 117   60 60 81   8 8 3 3   8   1  
isEqual = require 'deep-equal'
r = require 'restructure'
CFFOperand = require './CFFOperand'
 
class CFFDict
  constructor: (@ops = []) ->
    @fields = {}
    for field in @ops
      key = if Array.isArray(field[0]) then field[0][0] << 8 | field[0][1] else field[0]
      @fields[key] = field
      
  decodeOperands = (type, stream, ret, operands) ->
    if Array.isArray(type)
      for op, i in operands
        decodeOperands type[i], stream, ret, [op]
    else if type.decode?
      type.decode(stream, ret, operands)
    else
      switch type
        when 'number', 'offset', 'sid'
          operands[0]
        when 'boolean'
          !!operands[0]
        else
          operands
          
  encodeOperands = (type, stream, ctx, operands) ->
    if Array.isArray(type)
      for op, i in operands
        encodeOperands(type[i], stream, ctx, op)[0]
    else if type.encode?
      type.encode(stream, operands, ctx)
    else if typeof operands is 'number'
      [operands]
    else Iif typeof operands is 'boolean'
      [+operands]
    else Eif Array.isArray(operands)
      operands
    else
      [operands]
              
  decode: (stream, parent) ->
    end = stream.pos + parent.length
    ret = {}
    operands = []
    
    # define hidden properties    
    Object.defineProperties ret,
      parent:         { value: parent }
      _startOffset:   { value: stream.pos }
    
    while stream.pos < end
      b = stream.readUInt8()
      if b <= 21
        if b is 12
          b = (b << 8) | stream.readUInt8()
          
        field = @fields[b]
        Ithrow new Error "Unknown operator " + b unless field
        
        ret[field[1]] = decodeOperands field[2], stream, ret, operands
        operands = []
      else
        operands.push CFFOperand.decode(stream, b)
        
    # fill in defaults
    for key, field of @fields
      ret[field[1]] ?= field[3]
        
    return ret
    
  size: (dict, parent, includePointers = true) ->
    ctx = 
      parent: parent
      val: dict
      pointerSize: 0
      startOffset: parent.startOffset or 0
    
    len = 0
        
    for k, field of @fields
      val = dict[field[1]]
      continue if not val? or isEqual val, field[3]
      
      operands = encodeOperands field[2], null, ctx, val
      for op in operands
        len += CFFOperand.size op
        
      key = if Array.isArray(field[0]) then field[0] else [field[0]]
      len += key.length
      
    if includePointers
      len += ctx.pointerSize
        
    return len
    
  encode: (stream, dict, parent) ->
    ctx = 
      pointers: []
      startOffset: stream.pos
      parent: parent
      val: dict
      pointerSize: 0
      
    ctx.pointerOffset = stream.pos + @size(dict, ctx, false)
    
    for field in @ops
      val = dict[field[1]]
      continue if not val? or isEqual val, field[3]
              
      operands = encodeOperands field[2], stream, ctx, val
      for op in operands
        CFFOperand.encode stream, op
        
      key = if Array.isArray(field[0]) then field[0] else [field[0]]
      for op in key
        stream.writeUInt8 op
        
    i = 0
    while i < ctx.pointers.length
      ptr = ctx.pointers[i++]
      ptr.type.encode(stream, ptr.val, ptr.parent)
        
    return
    
module.exports = CFFDict