pdfkit/lib/font/tables/cmap.coffee
2014-03-23 12:25:09 -07:00

189 lines
5.6 KiB
CoffeeScript

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(Math.log(segCount) / Math.LN2, 2)
entrySelector = Math.log(searchRange / 2) / Math.LN2
rangeShift = 2 * segCount - searchRange
deltas = []
rangeOffsets = []
glyphIDs = []
for startCode, i in startCodes
endCode = endCodes[i]
if startCode is 0xFFFF
deltas.push 0
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