160 lines
3.8 KiB
JavaScript
160 lines
3.8 KiB
JavaScript
|
var HEADER = [66, 77, 70]
|
||
|
|
||
|
module.exports = function readBMFontBinary(buf) {
|
||
|
if (buf.length < 6)
|
||
|
throw new Error('invalid buffer length for BMFont')
|
||
|
|
||
|
var header = HEADER.every(function(byte, i) {
|
||
|
return buf.readUInt8(i) === byte
|
||
|
})
|
||
|
|
||
|
if (!header)
|
||
|
throw new Error('BMFont missing BMF byte header')
|
||
|
|
||
|
var i = 3
|
||
|
var vers = buf.readUInt8(i++)
|
||
|
if (vers > 3)
|
||
|
throw new Error('Only supports BMFont Binary v3 (BMFont App v1.10)')
|
||
|
|
||
|
var target = { kernings: [], chars: [] }
|
||
|
for (var b=0; b<5; b++)
|
||
|
i += readBlock(target, buf, i)
|
||
|
return target
|
||
|
}
|
||
|
|
||
|
function readBlock(target, buf, i) {
|
||
|
if (i > buf.length-1)
|
||
|
return 0
|
||
|
|
||
|
var blockID = buf.readUInt8(i++)
|
||
|
var blockSize = buf.readInt32LE(i)
|
||
|
i += 4
|
||
|
|
||
|
switch(blockID) {
|
||
|
case 1:
|
||
|
target.info = readInfo(buf, i)
|
||
|
break
|
||
|
case 2:
|
||
|
target.common = readCommon(buf, i)
|
||
|
break
|
||
|
case 3:
|
||
|
target.pages = readPages(buf, i, blockSize)
|
||
|
break
|
||
|
case 4:
|
||
|
target.chars = readChars(buf, i, blockSize)
|
||
|
break
|
||
|
case 5:
|
||
|
target.kernings = readKernings(buf, i, blockSize)
|
||
|
break
|
||
|
}
|
||
|
return 5 + blockSize
|
||
|
}
|
||
|
|
||
|
function readInfo(buf, i) {
|
||
|
var info = {}
|
||
|
info.size = buf.readInt16LE(i)
|
||
|
|
||
|
var bitField = buf.readUInt8(i+2)
|
||
|
info.smooth = (bitField >> 7) & 1
|
||
|
info.unicode = (bitField >> 6) & 1
|
||
|
info.italic = (bitField >> 5) & 1
|
||
|
info.bold = (bitField >> 4) & 1
|
||
|
|
||
|
//fixedHeight is only mentioned in binary spec
|
||
|
if ((bitField >> 3) & 1)
|
||
|
info.fixedHeight = 1
|
||
|
|
||
|
info.charset = buf.readUInt8(i+3) || ''
|
||
|
info.stretchH = buf.readUInt16LE(i+4)
|
||
|
info.aa = buf.readUInt8(i+6)
|
||
|
info.padding = [
|
||
|
buf.readInt8(i+7),
|
||
|
buf.readInt8(i+8),
|
||
|
buf.readInt8(i+9),
|
||
|
buf.readInt8(i+10)
|
||
|
]
|
||
|
info.spacing = [
|
||
|
buf.readInt8(i+11),
|
||
|
buf.readInt8(i+12)
|
||
|
]
|
||
|
info.outline = buf.readUInt8(i+13)
|
||
|
info.face = readStringNT(buf, i+14)
|
||
|
return info
|
||
|
}
|
||
|
|
||
|
function readCommon(buf, i) {
|
||
|
var common = {}
|
||
|
common.lineHeight = buf.readUInt16LE(i)
|
||
|
common.base = buf.readUInt16LE(i+2)
|
||
|
common.scaleW = buf.readUInt16LE(i+4)
|
||
|
common.scaleH = buf.readUInt16LE(i+6)
|
||
|
common.pages = buf.readUInt16LE(i+8)
|
||
|
var bitField = buf.readUInt8(i+10)
|
||
|
common.packed = 0
|
||
|
common.alphaChnl = buf.readUInt8(i+11)
|
||
|
common.redChnl = buf.readUInt8(i+12)
|
||
|
common.greenChnl = buf.readUInt8(i+13)
|
||
|
common.blueChnl = buf.readUInt8(i+14)
|
||
|
return common
|
||
|
}
|
||
|
|
||
|
function readPages(buf, i, size) {
|
||
|
var pages = []
|
||
|
var text = readNameNT(buf, i)
|
||
|
var len = text.length+1
|
||
|
var count = size / len
|
||
|
for (var c=0; c<count; c++) {
|
||
|
pages[c] = buf.slice(i, i+text.length).toString('utf8')
|
||
|
i += len
|
||
|
}
|
||
|
return pages
|
||
|
}
|
||
|
|
||
|
function readChars(buf, i, blockSize) {
|
||
|
var chars = []
|
||
|
|
||
|
var count = blockSize / 20
|
||
|
for (var c=0; c<count; c++) {
|
||
|
var char = {}
|
||
|
var off = c*20
|
||
|
char.id = buf.readUInt32LE(i + 0 + off)
|
||
|
char.x = buf.readUInt16LE(i + 4 + off)
|
||
|
char.y = buf.readUInt16LE(i + 6 + off)
|
||
|
char.width = buf.readUInt16LE(i + 8 + off)
|
||
|
char.height = buf.readUInt16LE(i + 10 + off)
|
||
|
char.xoffset = buf.readInt16LE(i + 12 + off)
|
||
|
char.yoffset = buf.readInt16LE(i + 14 + off)
|
||
|
char.xadvance = buf.readInt16LE(i + 16 + off)
|
||
|
char.page = buf.readUInt8(i + 18 + off)
|
||
|
char.chnl = buf.readUInt8(i + 19 + off)
|
||
|
chars[c] = char
|
||
|
}
|
||
|
return chars
|
||
|
}
|
||
|
|
||
|
function readKernings(buf, i, blockSize) {
|
||
|
var kernings = []
|
||
|
var count = blockSize / 10
|
||
|
for (var c=0; c<count; c++) {
|
||
|
var kern = {}
|
||
|
var off = c*10
|
||
|
kern.first = buf.readUInt32LE(i + 0 + off)
|
||
|
kern.second = buf.readUInt32LE(i + 4 + off)
|
||
|
kern.amount = buf.readInt16LE(i + 8 + off)
|
||
|
kernings[c] = kern
|
||
|
}
|
||
|
return kernings
|
||
|
}
|
||
|
|
||
|
function readNameNT(buf, offset) {
|
||
|
var pos=offset
|
||
|
for (; pos<buf.length; pos++) {
|
||
|
if (buf[pos] === 0x00)
|
||
|
break
|
||
|
}
|
||
|
return buf.slice(offset, pos)
|
||
|
}
|
||
|
|
||
|
function readStringNT(buf, offset) {
|
||
|
return readNameNT(buf, offset).toString('utf8')
|
||
|
}
|