svg2ttf
Version:
Converts SVG font to TTF font
90 lines (80 loc) • 4.05 kB
JavaScript
// See documentation here: http://www.microsoft.com/typography/otspec/os2.htm
var _ = require('lodash')
var identifier = require('../utils.js').identifier
var ByteBuffer = require('microbuffer')
// get first glyph unicode
function getFirstCharIndex (font) {
return Math.max(0, Math.min(0xffff, Math.abs(_.minBy(Object.keys(font.codePoints), function (point) {
return parseInt(point, 10)
}))))
}
// get last glyph unicode
function getLastCharIndex (font) {
return Math.max(0, Math.min(0xffff, Math.abs(_.maxBy(Object.keys(font.codePoints), function (point) {
return parseInt(point, 10)
}))))
}
// OpenType spec: https://docs.microsoft.com/en-us/typography/opentype/spec/os2
function createOS2Table (font) {
// use at least 2 for ligatures and kerning
var maxContext = font.ligatures.map(function (l) {
return l.unicode.length
}).reduce(function (a, b) {
return Math.max(a, b)
}, 2)
var buf = new ByteBuffer(96)
// Version 5 is not supported in the Android 5 browser.
buf.writeUint16(4) // version
buf.writeInt16(font.avgWidth) // xAvgCharWidth
buf.writeUint16(font.weightClass) // usWeightClass
buf.writeUint16(font.widthClass) // usWidthClass
buf.writeInt16(font.fsType) // fsType
buf.writeInt16(font.ySubscriptXSize) // ySubscriptXSize
buf.writeInt16(font.ySubscriptYSize) // ySubscriptYSize
buf.writeInt16(font.ySubscriptXOffset) // ySubscriptXOffset
buf.writeInt16(font.ySubscriptYOffset) // ySubscriptYOffset
buf.writeInt16(font.ySuperscriptXSize) // ySuperscriptXSize
buf.writeInt16(font.ySuperscriptYSize) // ySuperscriptYSize
buf.writeInt16(font.ySuperscriptXOffset) // ySuperscriptXOffset
buf.writeInt16(font.ySuperscriptYOffset) // ySuperscriptYOffset
buf.writeInt16(font.yStrikeoutSize) // yStrikeoutSize
buf.writeInt16(font.yStrikeoutPosition) // yStrikeoutPosition
buf.writeInt16(font.familyClass) // sFamilyClass
buf.writeUint8(font.panose.familyType) // panose.bFamilyType
buf.writeUint8(font.panose.serifStyle) // panose.bSerifStyle
buf.writeUint8(font.panose.weight) // panose.bWeight
buf.writeUint8(font.panose.proportion) // panose.bProportion
buf.writeUint8(font.panose.contrast) // panose.bContrast
buf.writeUint8(font.panose.strokeVariation) // panose.bStrokeVariation
buf.writeUint8(font.panose.armStyle) // panose.bArmStyle
buf.writeUint8(font.panose.letterform) // panose.bLetterform
buf.writeUint8(font.panose.midline) // panose.bMidline
buf.writeUint8(font.panose.xHeight) // panose.bXHeight
// TODO: This field is used to specify the Unicode blocks or ranges based on the 'cmap' table.
buf.writeUint32(0) // ulUnicodeRange1
buf.writeUint32(0) // ulUnicodeRange2
buf.writeUint32(0) // ulUnicodeRange3
buf.writeUint32(0) // ulUnicodeRange4
buf.writeUint32(identifier('PfEd')) // achVendID, equal to PfEd
buf.writeUint16(font.fsSelection) // fsSelection
buf.writeUint16(getFirstCharIndex(font)) // usFirstCharIndex
buf.writeUint16(getLastCharIndex(font)) // usLastCharIndex
buf.writeInt16(font.ascent) // sTypoAscender
buf.writeInt16(font.descent) // sTypoDescender
buf.writeInt16(font.lineGap) // lineGap
// Enlarge win acscent/descent to avoid clipping
// WinAscent - WinDecent should at least be equal to TypoAscender - TypoDescender + TypoLineGap:
// https://www.high-logic.com/font-editor/fontcreator/tutorials/font-metrics-vertical-line-spacing
buf.writeInt16(Math.max(font.yMax, font.ascent + font.lineGap)) // usWinAscent
buf.writeInt16(-Math.min(font.yMin, font.descent)) // usWinDescent
buf.writeInt32(1) // ulCodePageRange1, Latin 1
buf.writeInt32(0) // ulCodePageRange2
buf.writeInt16(font.xHeight) // sxHeight
buf.writeInt16(font.capHeight) // sCapHeight
buf.writeUint16(0) // usDefaultChar, pointing to missing glyph (always id=0)
buf.writeUint16(0) // usBreakChar, code=32 isn't guaranteed to be a space in icon fonts
buf.writeUint16(maxContext) // usMaxContext, use at least 2 for ligatures and kerning
return buf
}
module.exports = createOS2Table