opentype.features.js
Version:
Library to list opentype fonts optional features.
189 lines (188 loc) • 7.67 kB
JavaScript
"use strict";
// Table metadata
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var check_1 = __importDefault(require("./check"));
var types_1 = require("./types");
/**
* @exports opentype.Table
* @class
* @param {string} tableName
* @param {Array} fields
* @param {Object} options
* @constructor
*/
function Table(tableName, fields, options) {
// For coverage tables with coverage format 2, we do not want to add the coverage data directly to the table object,
// as this will result in wrong encoding order of the coverage data on serialization to bytes.
// The fallback of using the field values directly when not present on the table is handled in types.encode.TABLE() already.
if (fields.length && (fields[0].name !== 'coverageFormat' || fields[0].value === 1)) {
for (var i = 0; i < fields.length; i += 1) {
var field = fields[i];
this[field.name] = field.value;
}
}
this.tableName = tableName;
this.fields = fields;
if (options) {
var optionKeys = Object.keys(options);
for (var i = 0; i < optionKeys.length; i += 1) {
var k = optionKeys[i];
var v = options[k];
if (this[k] !== undefined) {
this[k] = v;
}
}
}
}
/**
* Encodes the table and returns an array of bytes
* @return {Array}
*/
Table.prototype.encode = function () {
return types_1.encode.TABLE(this);
};
/**
* Get the size of the table.
* @return {number}
*/
Table.prototype.sizeOf = function () {
return types_1.sizeOf.TABLE(this);
};
/**
* @private
*/
function ushortList(itemName, list, count) {
if (count === undefined) {
count = list.length;
}
var fields = new Array(list.length + 1);
fields[0] = { name: itemName + 'Count', type: 'USHORT', value: count };
for (var i = 0; i < list.length; i++) {
fields[i + 1] = { name: itemName + i, type: 'USHORT', value: list[i] };
}
return fields;
}
/**
* @private
*/
function tableList(itemName, records, itemCallback) {
var count = records.length;
var fields = new Array(count + 1);
fields[0] = { name: itemName + 'Count', type: 'USHORT', value: count };
for (var i = 0; i < count; i++) {
fields[i + 1] = { name: itemName + i, type: 'TABLE', value: itemCallback(records[i], i) };
}
return fields;
}
/**
* @private
*/
function recordList(itemName, records, itemCallback) {
var count = records.length;
var fields = [];
fields[0] = { name: itemName + 'Count', type: 'USHORT', value: count };
for (var i = 0; i < count; i++) {
fields = fields.concat(itemCallback(records[i], i));
}
return fields;
}
// Common Layout Tables
/**
* @exports opentype.Coverage
* @class
* @param {opentype.Table}
* @constructor
* @extends opentype.Table
*/
function Coverage(coverageTable) {
if (coverageTable.format === 1) {
Table.call(this, 'coverageTable', [{ name: 'coverageFormat', type: 'USHORT', value: 1 }]
.concat(ushortList('glyph', coverageTable.glyphs)));
}
else if (coverageTable.format === 2) {
Table.call(this, 'coverageTable', [{ name: 'coverageFormat', type: 'USHORT', value: 2 }]
.concat(recordList('rangeRecord', coverageTable.ranges, function (RangeRecord) {
return [
{ name: 'startGlyphID', type: 'USHORT', value: RangeRecord.start },
{ name: 'endGlyphID', type: 'USHORT', value: RangeRecord.end },
{ name: 'startCoverageIndex', type: 'USHORT', value: RangeRecord.index },
];
})));
}
else {
check_1.default.assert(false, 'Coverage format must be 1 or 2.');
}
}
Coverage.prototype = Object.create(Table.prototype);
Coverage.prototype.constructor = Coverage;
function ScriptList(scriptListTable) {
Table.call(this, 'scriptListTable', recordList('scriptRecord', scriptListTable, function (scriptRecord, i) {
var script = scriptRecord.script;
var defaultLangSys = script.defaultLangSys;
check_1.default.assert(!!defaultLangSys, 'Unable to write GSUB: script ' + scriptRecord.tag + ' has no default language system.');
return [
{ name: 'scriptTag' + i, type: 'TAG', value: scriptRecord.tag },
{ name: 'script' + i, type: 'TABLE', value: new Table('scriptTable', [
{ name: 'defaultLangSys', type: 'TABLE', value: new Table('defaultLangSys', [
{ name: 'lookupOrder', type: 'USHORT', value: 0 },
{ name: 'reqFeatureIndex', type: 'USHORT', value: defaultLangSys.reqFeatureIndex }
]
.concat(ushortList('featureIndex', defaultLangSys.featureIndexes))) }
].concat(recordList('langSys', script.langSysRecords, function (langSysRecord, i) {
var langSys = langSysRecord.langSys;
return [
{ name: 'langSysTag' + i, type: 'TAG', value: langSysRecord.tag },
{ name: 'langSys' + i, type: 'TABLE', value: new Table('langSys', [
{ name: 'lookupOrder', type: 'USHORT', value: 0 },
{ name: 'reqFeatureIndex', type: 'USHORT', value: langSys.reqFeatureIndex }
].concat(ushortList('featureIndex', langSys.featureIndexes))) }
];
}))) }
];
}));
}
ScriptList.prototype = Object.create(Table.prototype);
ScriptList.prototype.constructor = ScriptList;
/**
* @exports opentype.FeatureList
* @class
* @param {opentype.Table}
* @constructor
* @extends opentype.Table
*/
function FeatureList(featureListTable) {
Table.call(this, 'featureListTable', recordList('featureRecord', featureListTable, function (featureRecord, i) {
var feature = featureRecord.feature;
return [
{ name: 'featureTag' + i, type: 'TAG', value: featureRecord.tag },
{ name: 'feature' + i, type: 'TABLE', value: new Table('featureTable', [
{ name: 'featureParams', type: 'USHORT', value: feature.featureParams },
].concat(ushortList('lookupListIndex', feature.lookupListIndexes))) }
];
}));
}
FeatureList.prototype = Object.create(Table.prototype);
FeatureList.prototype.constructor = FeatureList;
/**
* @exports opentype.LookupList
* @class
* @param {opentype.Table}
* @param {Object}
* @constructor
* @extends opentype.Table
*/
function LookupList(lookupListTable, subtableMakers) {
Table.call(this, 'lookupListTable', tableList('lookup', lookupListTable, function (lookupTable) {
var subtableCallback = subtableMakers[lookupTable.lookupType];
check_1.default.assert(!!subtableCallback, 'Unable to write GSUB lookup type ' + lookupTable.lookupType + ' tables.');
return new Table('lookupTable', [
{ name: 'lookupType', type: 'USHORT', value: lookupTable.lookupType },
{ name: 'lookupFlag', type: 'USHORT', value: lookupTable.lookupFlag }
].concat(tableList('subtable', lookupTable.subtables, subtableCallback)));
}));
}
LookupList.prototype = Object.create(Table.prototype);
LookupList.prototype.constructor = LookupList;