UNPKG

svg2ttf

Version:

Converts SVG font to TTF font

194 lines (159 loc) 5.12 kB
'use strict' // 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