opentype.js
Version:
OpenType font parser
137 lines (114 loc) • 4.4 kB
JavaScript
// The `fvar` table stores font variation axes and instances.
// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6fvar.html
import check from '../check';
import parse from '../parse';
import table from '../table';
function addName(name, names) {
const nameString = JSON.stringify(name);
let nameID = 256;
for (let nameKey in names) {
let n = parseInt(nameKey);
if (!n || n < 256) {
continue;
}
if (JSON.stringify(names[nameKey]) === nameString) {
return n;
}
if (nameID <= n) {
nameID = n + 1;
}
}
names[nameID] = name;
return nameID;
}
function makeFvarAxis(n, axis, names) {
const nameID = addName(axis.name, names);
return [
{name: 'tag_' + n, type: 'TAG', value: axis.tag},
{name: 'minValue_' + n, type: 'FIXED', value: axis.minValue << 16},
{name: 'defaultValue_' + n, type: 'FIXED', value: axis.defaultValue << 16},
{name: 'maxValue_' + n, type: 'FIXED', value: axis.maxValue << 16},
{name: 'flags_' + n, type: 'USHORT', value: 0},
{name: 'nameID_' + n, type: 'USHORT', value: nameID}
];
}
function parseFvarAxis(data, start, names) {
const axis = {};
const p = new parse.Parser(data, start);
axis.tag = p.parseTag();
axis.minValue = p.parseFixed();
axis.defaultValue = p.parseFixed();
axis.maxValue = p.parseFixed();
p.skip('uShort', 1); // reserved for flags; no values defined
axis.name = names[p.parseUShort()] || {};
return axis;
}
function makeFvarInstance(n, inst, axes, names) {
const nameID = addName(inst.name, names);
const fields = [
{name: 'nameID_' + n, type: 'USHORT', value: nameID},
{name: 'flags_' + n, type: 'USHORT', value: 0}
];
for (let i = 0; i < axes.length; ++i) {
const axisTag = axes[i].tag;
fields.push({
name: 'axis_' + n + ' ' + axisTag,
type: 'FIXED',
value: inst.coordinates[axisTag] << 16
});
}
return fields;
}
function parseFvarInstance(data, start, axes, names) {
const inst = {};
const p = new parse.Parser(data, start);
inst.name = names[p.parseUShort()] || {};
p.skip('uShort', 1); // reserved for flags; no values defined
inst.coordinates = {};
for (let i = 0; i < axes.length; ++i) {
inst.coordinates[axes[i].tag] = p.parseFixed();
}
return inst;
}
function makeFvarTable(fvar, names) {
const result = new table.Table('fvar', [
{name: 'version', type: 'ULONG', value: 0x10000},
{name: 'offsetToData', type: 'USHORT', value: 0},
{name: 'countSizePairs', type: 'USHORT', value: 2},
{name: 'axisCount', type: 'USHORT', value: fvar.axes.length},
{name: 'axisSize', type: 'USHORT', value: 20},
{name: 'instanceCount', type: 'USHORT', value: fvar.instances.length},
{name: 'instanceSize', type: 'USHORT', value: 4 + fvar.axes.length * 4}
]);
result.offsetToData = result.sizeOf();
for (let i = 0; i < fvar.axes.length; i++) {
result.fields = result.fields.concat(makeFvarAxis(i, fvar.axes[i], names));
}
for (let j = 0; j < fvar.instances.length; j++) {
result.fields = result.fields.concat(makeFvarInstance(j, fvar.instances[j], fvar.axes, names));
}
return result;
}
function parseFvarTable(data, start, names) {
const p = new parse.Parser(data, start);
const tableVersion = p.parseULong();
check.argument(tableVersion === 0x00010000, 'Unsupported fvar table version.');
const offsetToData = p.parseOffset16();
// Skip countSizePairs.
p.skip('uShort', 1);
const axisCount = p.parseUShort();
const axisSize = p.parseUShort();
const instanceCount = p.parseUShort();
const instanceSize = p.parseUShort();
const axes = [];
for (let i = 0; i < axisCount; i++) {
axes.push(parseFvarAxis(data, start + offsetToData + i * axisSize, names));
}
const instances = [];
const instanceStart = start + offsetToData + axisCount * axisSize;
for (let j = 0; j < instanceCount; j++) {
instances.push(parseFvarInstance(data, instanceStart + j * instanceSize, axes, names));
}
return {axes: axes, instances: instances};
}
export default { make: makeFvarTable, parse: parseFvarTable };