astro-ghostcms/.pnpm-store/v3/files/ac/5bee1023645b14ce074f9461a26...

216 lines
7.5 KiB
Plaintext
Raw Normal View History

2024-02-14 14:10:47 +00:00
// Table metadata
import check from './check';
import { encode, sizeOf } from './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 (let i = 0; i < fields.length; i += 1) {
const field = fields[i];
this[field.name] = field.value;
}
}
this.tableName = tableName;
this.fields = fields;
if (options) {
const optionKeys = Object.keys(options);
for (let i = 0; i < optionKeys.length; i += 1) {
const k = optionKeys[i];
const 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 encode.TABLE(this);
};
/**
* Get the size of the table.
* @return {number}
*/
Table.prototype.sizeOf = function() {
return sizeOf.TABLE(this);
};
/**
* @private
*/
function ushortList(itemName, list, count) {
if (count === undefined) {
count = list.length;
}
const fields = new Array(list.length + 1);
fields[0] = {name: itemName + 'Count', type: 'USHORT', value: count};
for (let 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) {
const count = records.length;
const fields = new Array(count + 1);
fields[0] = {name: itemName + 'Count', type: 'USHORT', value: count};
for (let 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) {
const count = records.length;
let fields = [];
fields[0] = {name: itemName + 'Count', type: 'USHORT', value: count};
for (let 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.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) {
const script = scriptRecord.script;
let defaultLangSys = script.defaultLangSys;
check.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) {
const 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) {
const 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) {
let subtableCallback = subtableMakers[lookupTable.lookupType];
check.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;
// Record = same as Table, but inlined (a Table has an offset and its data is further in the stream)
// Don't use offsets inside Records (probable bug), only in Tables.
export default {
Table,
Record: Table,
Coverage,
ScriptList,
FeatureList,
LookupList,
ushortList,
tableList,
recordList,
};