fonteditor-core
Version:
fonts (ttf, woff, woff2, eot, svg, otf) parse, write, transform, glyph adjust.
322 lines (269 loc) • 11.1 kB
JavaScript
/**
* @file OS/2表
* @author mengke01(kekee000@gmail.com)
*
* http://www.microsoft.com/typography/otspec/os2.htm
*/
import table from './table';
import struct from './struct';
export default table.create(
'OS/2',
[
['version', struct.Uint16],
['xAvgCharWidth', struct.Int16],
['usWeightClass', struct.Uint16],
['usWidthClass', struct.Uint16],
['fsType', struct.Uint16],
['ySubscriptXSize', struct.Uint16],
['ySubscriptYSize', struct.Uint16],
['ySubscriptXOffset', struct.Uint16],
['ySubscriptYOffset', struct.Uint16],
['ySuperscriptXSize', struct.Uint16],
['ySuperscriptYSize', struct.Uint16],
['ySuperscriptXOffset', struct.Uint16],
['ySuperscriptYOffset', struct.Uint16],
['yStrikeoutSize', struct.Uint16],
['yStrikeoutPosition', struct.Uint16],
['sFamilyClass', struct.Uint16],
// Panose
['bFamilyType', struct.Uint8],
['bSerifStyle', struct.Uint8],
['bWeight', struct.Uint8],
['bProportion', struct.Uint8],
['bContrast', struct.Uint8],
['bStrokeVariation', struct.Uint8],
['bArmStyle', struct.Uint8],
['bLetterform', struct.Uint8],
['bMidline', struct.Uint8],
['bXHeight', struct.Uint8],
// unicode range
['ulUnicodeRange1', struct.Uint32],
['ulUnicodeRange2', struct.Uint32],
['ulUnicodeRange3', struct.Uint32],
['ulUnicodeRange4', struct.Uint32],
// char 4
['achVendID', struct.String, 4],
['fsSelection', struct.Uint16],
['usFirstCharIndex', struct.Uint16],
['usLastCharIndex', struct.Uint16],
['sTypoAscender', struct.Int16],
['sTypoDescender', struct.Int16],
['sTypoLineGap', struct.Int16],
['usWinAscent', struct.Uint16],
['usWinDescent', struct.Uint16],
// version 0 above 39
['ulCodePageRange1', struct.Uint32],
['ulCodePageRange2', struct.Uint32],
// version 1 above 41
['sxHeight', struct.Int16],
['sCapHeight', struct.Int16],
['usDefaultChar', struct.Uint16],
['usBreakChar', struct.Uint16],
['usMaxContext', struct.Uint16]
// version 2,3,4 above 46
],
{
read(reader, ttf) {
const format = reader.readUint16(this.offset);
let struct = this.struct;
// format2
if (format === 0) {
struct = struct.slice(0, 39);
}
else if (format === 1) {
struct = struct.slice(0, 41);
}
const OS2Head = table.create('os2head', struct);
const tbl = new OS2Head(this.offset).read(reader, ttf);
// 补齐其他version的字段
const os2Fields = {
ulCodePageRange1: 1,
ulCodePageRange2: 0,
sxHeight: 0,
sCapHeight: 0,
usDefaultChar: 0,
usBreakChar: 32,
usMaxContext: 0
};
return Object.assign(os2Fields, tbl);
},
size(ttf) {
// 更新其他表的统计信息
// header
let xMin = 16384;
let yMin = 16384;
let xMax = -16384;
let yMax = -16384;
// hhea
let advanceWidthMax = -1;
let minLeftSideBearing = 16384;
let minRightSideBearing = 16384;
let xMaxExtent = -16384;
// os2 count
let xAvgCharWidth = 0;
let usFirstCharIndex = 0x10FFFF;
let usLastCharIndex = -1;
// maxp
let maxPoints = 0;
let maxContours = 0;
let maxCompositePoints = 0;
let maxCompositeContours = 0;
let maxSizeOfInstructions = 0;
let maxComponentElements = 0;
let glyfNotEmpty = 0; // 非空glyf
const hinting = ttf.writeOptions ? ttf.writeOptions.hinting : false;
// 计算instructions和functiondefs
if (hinting) {
if (ttf.cvt) {
maxSizeOfInstructions = Math.max(maxSizeOfInstructions, ttf.cvt.length);
}
if (ttf.prep) {
maxSizeOfInstructions = Math.max(maxSizeOfInstructions, ttf.prep.length);
}
if (ttf.fpgm) {
maxSizeOfInstructions = Math.max(maxSizeOfInstructions, ttf.fpgm.length);
}
}
ttf.glyf.forEach((glyf) => {
// 统计control point信息
if (glyf.compound) {
let compositeContours = 0;
let compositePoints = 0;
glyf.glyfs.forEach((g) => {
const cglyf = ttf.glyf[g.glyphIndex];
if (!cglyf) {
return;
}
compositeContours += cglyf.contours ? cglyf.contours.length : 0;
if (cglyf.contours && cglyf.contours.length) {
cglyf.contours.forEach((contour) => {
compositePoints += contour.length;
});
}
});
maxComponentElements = Math.max(maxComponentElements, glyf.glyfs.length);
maxCompositePoints = Math.max(maxCompositePoints, compositePoints);
maxCompositeContours = Math.max(maxCompositeContours, compositeContours);
}
// 简单图元
else if (glyf.contours && glyf.contours.length) {
maxContours = Math.max(maxContours, glyf.contours.length);
let points = 0;
glyf.contours.forEach((contour) => {
points += contour.length;
});
maxPoints = Math.max(maxPoints, points);
}
if (hinting && glyf.instructions) {
maxSizeOfInstructions = Math.max(maxSizeOfInstructions, glyf.instructions.length);
}
// 统计边界信息
if (null != glyf.xMin && glyf.xMin < xMin) {
xMin = glyf.xMin;
}
if (null != glyf.yMin && glyf.yMin < yMin) {
yMin = glyf.yMin;
}
if (null != glyf.xMax && glyf.xMax > xMax) {
xMax = glyf.xMax;
}
if (null != glyf.yMax && glyf.yMax > yMax) {
yMax = glyf.yMax;
}
advanceWidthMax = Math.max(advanceWidthMax, glyf.advanceWidth);
minLeftSideBearing = Math.min(minLeftSideBearing, glyf.leftSideBearing);
if (null != glyf.xMax) {
minRightSideBearing = Math.min(minRightSideBearing, glyf.advanceWidth - glyf.xMax);
xMaxExtent = Math.max(xMaxExtent, glyf.xMax);
}
if (null != glyf.advanceWidth) {
xAvgCharWidth += glyf.advanceWidth;
glyfNotEmpty++;
}
let unicodes = glyf.unicode;
if (typeof glyf.unicode === 'number') {
unicodes = [glyf.unicode];
}
if (Array.isArray(unicodes)) {
unicodes.forEach((unicode) => {
if (unicode !== 0xFFFF) {
usFirstCharIndex = Math.min(usFirstCharIndex, unicode);
usLastCharIndex = Math.max(usLastCharIndex, unicode);
}
});
}
});
// 重新设置version 4
ttf['OS/2'].version = 0x4;
ttf['OS/2'].achVendID = (ttf['OS/2'].achVendID + ' ').slice(0, 4);
ttf['OS/2'].xAvgCharWidth = xAvgCharWidth / (glyfNotEmpty || 1);
ttf['OS/2'].ulUnicodeRange2 = 268435456;
ttf['OS/2'].usFirstCharIndex = usFirstCharIndex;
ttf['OS/2'].usLastCharIndex = usLastCharIndex;
// rewrite hhea
ttf.hhea.version = ttf.hhea.version || 0x1;
ttf.hhea.advanceWidthMax = advanceWidthMax;
ttf.hhea.minLeftSideBearing = minLeftSideBearing;
ttf.hhea.minRightSideBearing = minRightSideBearing;
ttf.hhea.xMaxExtent = xMaxExtent;
// rewrite head
ttf.head.version = ttf.head.version || 0x1;
ttf.head.lowestRecPPEM = ttf.head.lowestRecPPEM || 0x8;
ttf.head.xMin = xMin;
ttf.head.yMin = yMin;
ttf.head.xMax = xMax;
ttf.head.yMax = yMax;
// head rewrite
if (ttf.support.head) {
const {xMin, yMin, xMax, yMax} = ttf.support.head;
if (xMin != null) {
ttf.head.xMin = xMin;
}
if (yMin != null) {
ttf.head.yMin = yMin;
}
if (xMax != null) {
ttf.head.xMax = xMax;
}
if (yMax != null) {
ttf.head.yMax = yMax;
}
}
// hhea rewrite
if (ttf.support.hhea) {
const {advanceWidthMax, xMaxExtent, minLeftSideBearing, minRightSideBearing} = ttf.support.hhea;
if (advanceWidthMax != null) {
ttf.hhea.advanceWidthMax = advanceWidthMax;
}
if (xMaxExtent != null) {
ttf.hhea.xMaxExtent = xMaxExtent;
}
if (minLeftSideBearing != null) {
ttf.hhea.minLeftSideBearing = minLeftSideBearing;
}
if (minRightSideBearing != null) {
ttf.hhea.minRightSideBearing = minRightSideBearing;
}
}
// 这里根据存储的maxp来设置新的maxp,避免重复计算maxp
ttf.maxp = ttf.maxp || {};
ttf.support.maxp = {
version: 1.0,
numGlyphs: ttf.glyf.length,
maxPoints,
maxContours,
maxCompositePoints,
maxCompositeContours,
maxZones: ttf.maxp.maxZones || 0,
maxTwilightPoints: ttf.maxp.maxTwilightPoints || 0,
maxStorage: ttf.maxp.maxStorage || 0,
maxFunctionDefs: ttf.maxp.maxFunctionDefs || 0,
maxStackElements: ttf.maxp.maxStackElements || 0,
maxSizeOfInstructions,
maxComponentElements,
maxComponentDepth: maxComponentElements ? 1 : 0
};
return table.size.call(this, ttf);
}
}
);