Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 65fdd2f4e8
Fetching contributors…

Cannot retrieve contributors at this time

file 189 lines (149 sloc) 7.201 kb
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 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
Table = require '../table'
Data = require '../../data'

class CmapTable extends Table
    parse: (data) ->
        data.pos = @offset
        
        @version = data.readUInt16()
        tableCount = data.readUInt16()
        @tables = []
        @unicode = null
        
        for i in [0...tableCount]
            entry = new CmapEntry(data, @offset)
            @tables.push entry
            @unicode ?= entry if entry.isUnicode
            
        return true
        
    @encode: (charmap, encoding = 'macroman') ->
        result = CmapEntry.encode(charmap, encoding)
        table = new Data
        
        table.writeUInt16 0 # version
        table.writeUInt16 1 # tableCount
        
        result.table = table.data.concat(result.subtable)
        return result
            
class CmapEntry
    constructor: (data, offset) ->
        @platformID = data.readUInt16()
        @encodingID = data.readShort()
        @offset = offset + data.readInt()
        
        data.pos = @offset
        @format = data.readUInt16()
        @length = data.readUInt16()
        @language = data.readUInt16()
        
        @isUnicode = (@platformID is 3 and @encodingID is 1 and @format is 4) or @platformID is 0 and @format is 4
        
        @codeMap = {}
        switch @format
            when 0
                for i in [0...256]
                    @codeMap[i] = data.readByte()
            
            when 4
                segCountX2 = data.readUInt16()
                segCount = segCountX2 / 2
                
                data.pos += 6 # skip searching hints
                endCode = (data.readUInt16() for i in [0...segCount])
                data.pos += 2 # skip reserved value
                
                startCode = (data.readUInt16() for i in [0...segCount])
                idDelta = (data.readUInt16() for i in [0...segCount])
                idRangeOffset = (data.readUInt16() for i in [0...segCount])
                
                count = @length - data.pos + @offset
                glyphIds = (data.readUInt16() for i in [0...count])
                
                for tail, i in endCode
                    start = startCode[i]
                    for code in [start..tail]
                        if idRangeOffset[i] is 0
                            glyphId = code + idDelta[i]
                        else
                            index = idRangeOffset[i] / 2 + (code - start) - (segCount - i)
                            glyphId = glyphIds[index] or 0
                            glyphId += idDelta[i] if glyphId isnt 0
                            
                        @codeMap[code] = glyphId & 0xFFFF
                        
    @encode: (charmap, encoding) ->
        subtable = new Data
        codes = Object.keys(charmap).sort (a, b) -> a - b
        
        switch encoding
            when 'macroman'
                id = 0
                indexes = (0 for i in [0...256])
                map = { 0: 0 }
                codeMap = {}
                
                for code in codes
                    map[charmap[code]] ?= ++id
                    codeMap[code] =
                        old: charmap[code]
                        new: map[charmap[code]]
                        
                    indexes[code] = map[charmap[code]]
                    
                subtable.writeUInt16 1 # platformID
                subtable.writeUInt16 0 # encodingID
                subtable.writeUInt32 12 # offset
                subtable.writeUInt16 0 # format
                subtable.writeUInt16 262 # length
                subtable.writeUInt16 0 # language
                subtable.write indexes # glyph indexes
                
                result =
                    charMap: codeMap
                    subtable: subtable.data
                    maxGlyphID: id + 1
                
            when 'unicode'
                startCodes = []
                endCodes = []
                nextID = 0
                map = {}
                charMap = {}
                last = diff = null
                
                for code in codes
                    old = charmap[code]
                    map[old] ?= ++nextID
                    charMap[code] =
                        old: old
                        new: map[old]
                    
                    delta = map[old] - code
                    if not last? or delta isnt diff
                        endCodes.push last if last
                        startCodes.push code
                        diff = delta

                    last = code
                    
                endCodes.push last if last
                endCodes.push 0xFFFF
                startCodes.push 0xFFFF
                
                segCount = startCodes.length
                segCountX2 = segCount * 2
                searchRange = 2 * Math.pow(2, Math.floor(Math.log(segCount) / Math.LN2))
                entrySelector = Math.log(segCount) / Math.LN2
                rangeShift = 2 * segCount - searchRange
                
                deltas = []
                rangeOffsets = []
                glyphIDs = []
                
                for startCode, i in startCodes
                    endCode = endCodes[i]
                    
                    if startCode is 0xFFFF
                        deltas.push 1
                        rangeOffsets.push 0
                        break
                        
                    startGlyph = charMap[startCode].new
                    if startCode - startGlyph >= 0x8000
                        deltas.push 0
                        rangeOffsets.push 2 * (glyphIDs.length + segCount - i)
                        
                        for code in [startCode..endCode]
                            glyphIDs.push charMap[code].new
                            
                    else
                        deltas.push startGlyph - startCode
                        rangeOffsets.push 0
                                                
                subtable.writeUInt16 3 # platformID
                subtable.writeUInt16 1 # encodingID
                subtable.writeUInt32 12 # offset
                subtable.writeUInt16 4 # format
                subtable.writeUInt16 16 + segCount * 8 + glyphIDs.length * 2 # length
                subtable.writeUInt16 0 # language
                subtable.writeUInt16 segCountX2
                subtable.writeUInt16 searchRange
                subtable.writeUInt16 entrySelector
                subtable.writeUInt16 rangeShift
                
                subtable.writeUInt16 code for code in endCodes
                subtable.writeUInt16 0 # reserved value
                subtable.writeUInt16 code for code in startCodes
                
                subtable.writeUInt16 delta for delta in deltas
                subtable.writeUInt16 offset for offset in rangeOffsets
                subtable.writeUInt16 id for id in glyphIDs
                
                result =
                    charMap: charMap
                    subtable: subtable.data
                    maxGlyphID: nextID + 1
        
module.exports = CmapTable
Something went wrong with that request. Please try again.