'use strict'; // 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;