all files / fontkit/src/ CmapProcessor.coffee

77.63% Statements 59/76
58.82% Branches 20/34
100% Functions 4/4
75% Lines 51/68
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   21×       21×   37×     12× 12× 12× 12×         327× 327×         197× 197× 197× 816×   816× 345× 471× 275×   196× 196× 128×   68× 68× 68× 68×   196×                   130× 130× 130× 1467× 1467×   1467× 728× 739× 609×   130× 130×                                   88× 88×                                              
class CmapProcessor
  constructor: (cmapTable) ->
    @_characterSet = null
    
    # find the unicode cmap
    # check for a 32-bit cmap first
    for cmap in cmapTable.tables
      # unicode or windows platform
      if (cmap.platformID is 0 and cmap.encodingID in [4, 6]) or (cmap.platformID is 3 and cmap.encodingID is 10)
         @cmap = cmap.table
         return
       
    # try "old" 16-bit cmap
    for cmap in cmapTable.tables
      Eif cmap.platformID is 0 or (cmap.platformID is 3 and cmap.encodingID is 1)
        @cmap = cmap.table
        return
      
    throw new Error "Could not find a unicode cmap"
    
  lookup: (codepoint) ->
    cmap = @cmap
    switch cmap.version
      when 0
        return cmap.codeMap.get(codepoint) or 0
        
      when 4
        min = 0
        max = cmap.segCount - 1
        while min <= max
          mid = (min + max) >> 1
          
          if codepoint < cmap.startCode.get(mid)
            max = mid - 1
          else if codepoint > cmap.endCode.get(mid)
            min = mid + 1
          else
            rangeOffset = cmap.idRangeOffset.get(mid)
            if rangeOffset is 0
              gid = codepoint + cmap.idDelta.get(mid)
            else
              index = rangeOffset / 2 + (codepoint - cmap.startCode.get(mid)) - (cmap.segCount - mid)
              gid = cmap.glyphIndexArray.get(index) or 0
              Eunless gid is 0
                gid += cmap.idDelta.get(mid)
                
            return gid & 0xffff
        
        return 0
        
      when 8
        throw new Error 'TODO: cmap format 8'
            
      when 6, 10
        return cmap.glyphIndices.get(codepoint - cmap.firstCode) or 0
        
      when 12, 13
        min = 0
        max = cmap.nGroups - 1
        while min <= max
          mid = (min + max) >> 1
          group = cmap.groups.get(mid)
          
          if codepoint < group.startCharCode
            max = mid - 1
          else if codepoint > group.endCharCode
            min = mid + 1
          else
            Eif cmap.version is 12
              return group.glyphID + (codepoint - group.startCharCode)
            else
              return group.glyphID
              
        return 0
        
      when 14
        throw new Error 'TODO: cmap format 14'
        
      else
        throw new Error 'Unknown cmap format ' + cmap.version
    
  getCharacterSet: ->
    if @_characterSet
      return @_characterSet
      
    cmap = @cmap
    switch cmap.version
      when 0
        @_characterSet = [0...cmap.codeMap.length]
          
      when 4
        res = []
        for tail, i in cmap.endCode.toArray()
          start = cmap.startCode.get(i)
          res.push [start..tail]...
          
        @_characterSet = res
      
      when 8
        throw new Error 'TODO: cmap format 8'
        
      when 6, 10
        @_characterSet = [cmap.firstCode...cmap.firstCode + cmap.glyphIndices.length]
        
      when 12, 13
        res = []
        for group in cmap.groups.toArray()
          res.push [group.startCharCode..group.endCharCode]...
          
        @_characterSet = res
        
      when 14
        throw new Error 'TODO: cmap format 14'
        
      else
        throw new Error 'Unknown cmap format ' + cmap.version
        
    return @_characterSet
 
module.exports = CmapProcessor