svg2ttf
Version:
Converts SVG font to TTF font
194 lines (159 loc) • 5.12 kB
JavaScript
// See documentation here: http://www.microsoft.com/typography/otspec/glyf.htm
var _ = require('lodash')
var ByteBuffer = require('microbuffer')
function getFlags (glyph) {
var result = []
_.forEach(glyph.ttfContours, function (contour) {
_.forEach(contour, function (point) {
var flag = point.onCurve ? 1 : 0
if (point.x === 0) {
flag += 16
} else {
if (point.x >= -0xFF && point.x <= 0xFF) {
flag += 2 // the corresponding x-coordinate is 1 byte long
}
if (point.x > 0 && point.x <= 0xFF) {
flag += 16 // If x-Short Vector is set, this bit describes the sign of the value, with 1 equalling positive and 0 negative
}
}
if (point.y === 0) {
flag += 32
} else {
if (point.y >= -0xFF && point.y <= 0xFF) {
flag += 4 // the corresponding y-coordinate is 1 byte long
}
if (point.y > 0 && point.y <= 0xFF) {
flag += 32 // If y-Short Vector is set, this bit describes the sign of the value, with 1 equalling positive and 0 negative.
}
}
result.push(flag)
})
})
return result
}
// repeating flags can be packed
function compactFlags (flags) {
var result = []
var prevFlag = -1
var firstRepeat = false
_.forEach(flags, function (flag) {
if (prevFlag === flag) {
if (firstRepeat) {
result[result.length - 1] += 8 // current flag repeats previous one, need to set 3rd bit of previous flag and set 1 to the current one
result.push(1)
firstRepeat = false
} else {
result[result.length - 1]++ // when flag is repeating second or more times, we need to increase the last flag value
}
} else {
firstRepeat = true
prevFlag = flag
result.push(flag)
}
})
return result
}
function getCoords (glyph, coordName) {
var result = []
_.forEach(glyph.ttfContours, function (contour) {
result.push.apply(result, _.map(contour, coordName))
})
return result
}
function compactCoords (coords) {
return _.filter(coords, function (coord) {
return coord !== 0
})
}
// calculates length of glyph data in GLYF table
function glyphDataSize (glyph) {
// Ignore glyphs without outlines. These will get a length of zero in the “loca” table
if (!glyph.contours.length) {
return 0
}
var result = 12 // glyph fixed properties
result += glyph.contours.length * 2 // add contours
_.forEach(glyph.ttf_x, function (x) {
// add 1 or 2 bytes for each coordinate depending of its size
result += ((x >= -0xFF && x <= 0xFF)) ? 1 : 2
})
_.forEach(glyph.ttf_y, function (y) {
// add 1 or 2 bytes for each coordinate depending of its size
result += ((y >= -0xFF && y <= 0xFF)) ? 1 : 2
})
// Add flags length to glyph size.
result += glyph.ttf_flags.length
if (result % 4 !== 0) { // glyph size must be divisible by 4.
result += 4 - result % 4
}
return result
}
function tableSize (font) {
var result = 0
_.forEach(font.glyphs, function (glyph) {
glyph.ttf_size = glyphDataSize(glyph)
result += glyph.ttf_size
})
font.ttf_glyph_size = result // sum of all glyph lengths
return result
}
function createGlyfTable (font) {
_.forEach(font.glyphs, function (glyph) {
glyph.ttf_flags = getFlags(glyph)
glyph.ttf_flags = compactFlags(glyph.ttf_flags)
glyph.ttf_x = getCoords(glyph, 'x')
glyph.ttf_x = compactCoords(glyph.ttf_x)
glyph.ttf_y = getCoords(glyph, 'y')
glyph.ttf_y = compactCoords(glyph.ttf_y)
})
var buf = new ByteBuffer(tableSize(font))
_.forEach(font.glyphs, function (glyph) {
// Ignore glyphs without outlines. These will get a length of zero in the “loca” table
if (!glyph.contours.length) {
return
}
var offset = buf.tell()
buf.writeInt16(glyph.contours.length) // numberOfContours
buf.writeInt16(glyph.xMin) // xMin
buf.writeInt16(glyph.yMin) // yMin
buf.writeInt16(glyph.xMax) // xMax
buf.writeInt16(glyph.yMax) // yMax
// Array of end points
var endPtsOfContours = -1
var ttfContours = glyph.ttfContours
_.forEach(ttfContours, function (contour) {
endPtsOfContours += contour.length
buf.writeInt16(endPtsOfContours)
})
buf.writeInt16(0) // instructionLength, is not used here
// Array of flags
_.forEach(glyph.ttf_flags, function (flag) {
buf.writeInt8(flag)
})
// Array of X relative coordinates
_.forEach(glyph.ttf_x, function (x) {
if (x >= -0xFF && x <= 0xFF) {
buf.writeUint8(Math.abs(x))
} else {
buf.writeInt16(x)
}
})
// Array of Y relative coordinates
_.forEach(glyph.ttf_y, function (y) {
if (y >= -0xFF && y <= 0xFF) {
buf.writeUint8(Math.abs(y))
} else {
buf.writeInt16(y)
}
})
var tail = (buf.tell() - offset) % 4
if (tail !== 0) { // glyph size must be divisible by 4.
for (; tail < 4; tail++) {
buf.writeUint8(0)
}
}
})
return buf
}
module.exports = createGlyfTable