Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add local eslint rule rule for banning concat and new performance test #690

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions eslint-local-rules.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,30 @@
'use strict';

module.exports = {
'ban-concat': {
meta: {
type: 'suggestion',
schema: [],
},
create(context) {
return {
CallExpression(node) {
if (
(
node.callee.property &&
node.callee.property.name === 'concat' &&
node.callee?.object?.name !== 'Buffer'
) || (
node.callee?.object?.property?.name === 'concat' &&
node.callee?.object?.object?.type === 'ArrayExpression'
)
) {
context.report({node, message: 'Use obj.push(...data) instead of obj = obj.concat(data)'})
}
},
}
},
},
'ban-foreach': {
meta: {
type: 'suggestion',
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -87,6 +87,7 @@
"error",
"always"
],
"local-rules/ban-concat": 2,
"local-rules/ban-foreach": 2,
"local-rules/import-extensions": 2
}
3 changes: 1 addition & 2 deletions src/bidi.js
Original file line number Diff line number Diff line change
@@ -101,8 +101,7 @@ Bidi.prototype.registerFeatures = function (script, tags) {
if (!Object.prototype.hasOwnProperty.call(this.featuresTags, script)) {
this.featuresTags[script] = supportedTags;
} else {
this.featuresTags[script] =
this.featuresTags[script].concat(supportedTags);
this.featuresTags[script].push(...supportedTags);
}
};

3 changes: 2 additions & 1 deletion src/features/arab/arabicPresentationForms.js
Original file line number Diff line number Diff line change
@@ -12,7 +12,8 @@ import applySubstitution from '../applySubstitution.js';
* @param {ContextParams} charContextParams context params of a char
*/
function willConnectPrev(charContextParams) {
let backtrack = [].concat(charContextParams.backtrack);
let backtrack = [...charContextParams.backtrack];

for (let i = backtrack.length - 1; i >= 0; i--) {
const prevChar = backtrack[i];
const isolated = isIsolatedArabicChar(prevChar);
4 changes: 2 additions & 2 deletions src/features/featureQuery.js
Original file line number Diff line number Diff line change
@@ -128,7 +128,7 @@ function chainingSubstitutionFormat3(contextParams, subtable) {
subtable.lookaheadCoverage, lookaheadParams
);
// BACKTRACK LOOKUP //
let backtrackContext = [].concat(contextParams.backtrack);
let backtrackContext = [...contextParams.backtrack];
backtrackContext.reverse();
while (backtrackContext.length && isTashkeelArabicChar(backtrackContext[0].char)) {
backtrackContext.shift();
@@ -375,7 +375,7 @@ FeatureQuery.prototype.lookupFeature = function (query) {
`for script '${query.script}'.`
);
const lookups = this.getFeatureLookups(feature);
const substitutions = [].concat(contextParams.context);
const substitutions = [...contextParams.context];
for (let l = 0; l < lookups.length; l++) {
const lookupTable = lookups[l];
const subtables = this.getLookupSubtables(lookupTable);
3 changes: 1 addition & 2 deletions src/path.js
Original file line number Diff line number Diff line change
@@ -76,8 +76,7 @@ function optimizeCommands(commands) {
}
}
}
commands = [].concat.apply([], subpaths); // flatten again
return commands;
return [...subpaths.flat()]; // flatten again
}

/**
14 changes: 9 additions & 5 deletions src/substitution.js
Original file line number Diff line number Diff line change
@@ -161,7 +161,7 @@ Substitution.prototype.getLigatures = function(feature, script, language) {
for (let k = 0; k < ligSet.length; k++) {
const lig = ligSet[k];
ligatures.push({
sub: [startGlyph].concat(lig.components),
sub: [startGlyph, ...lig.components],
by: lig.ligGlyph
});
}
@@ -309,15 +309,19 @@ Substitution.prototype.getFeature = function(feature, script, language) {
switch (feature) {
case 'aalt':
case 'salt':
return this.getSingle(feature, script, language)
.concat(this.getAlternates(feature, script, language));
return [
...this.getSingle(feature, script, language),
...this.getAlternates(feature, script, language)
];
case 'dlig':
case 'liga':
case 'rlig':
return this.getLigatures(feature, script, language);
case 'ccmp':
return this.getMultiple(feature, script, language)
.concat(this.getLigatures(feature, script, language));
return [
...this.getMultiple(feature, script, language),
...this.getLigatures(feature, script, language)
];
case 'stch':
return this.getMultiple(feature, script, language);
}
98 changes: 49 additions & 49 deletions src/table.js
Original file line number Diff line number Diff line change
@@ -85,7 +85,7 @@ function recordList(itemName, records, itemCallback) {
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));
fields.push(...itemCallback(records[i], i));
}
return fields;
}
@@ -101,21 +101,19 @@ function recordList(itemName, records, itemCallback) {
*/
function Coverage(coverageTable) {
if (coverageTable.format === 1) {
Table.call(this, 'coverageTable',
[{name: 'coverageFormat', type: 'USHORT', value: 1}]
.concat(ushortList('glyph', coverageTable.glyphs))
);
Table.call(this, 'coverageTable', [
{name: 'coverageFormat', type: 'USHORT', value: 1},
...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, i) {
return [
{name: 'startGlyphID' + i, type: 'USHORT', value: RangeRecord.start},
{name: 'endGlyphID' + i, type: 'USHORT', value: RangeRecord.end},
{name: 'startCoverageIndex' + i, type: 'USHORT', value: RangeRecord.index},
];
}))
);
Table.call(this, 'coverageTable', [
{name: 'coverageFormat', type: 'USHORT', value: 2},
...recordList('rangeRecord', coverageTable.ranges, (RangeRecord, i) => [
{name: 'startGlyphID' + i, type: 'USHORT', value: RangeRecord.start},
{name: 'endGlyphID' + i, type: 'USHORT', value: RangeRecord.end},
{name: 'startCoverageIndex' + i, type: 'USHORT', value: RangeRecord.index},
])
]);
} else {
check.assert(false, 'Coverage format must be 1 or 2.');
}
@@ -134,19 +132,22 @@ function ScriptList(scriptListTable) {
{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)))}
];
})))}
];
{name: 'reqFeatureIndex', type: 'USHORT', value: defaultLangSys.reqFeatureIndex},
...ushortList('featureIndex', defaultLangSys.featureIndexes)
])},
...recordList('langSys', script.langSysRecords, (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},
...ushortList('featureIndex', langSys.featureIndexes)
])}
];
})
])}
];
})
);
}
@@ -168,8 +169,9 @@ function FeatureList(featureListTable) {
{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)))}
];
...ushortList('lookupListIndex', feature.lookupListIndexes)
])}
];
})
);
}
@@ -190,8 +192,9 @@ function LookupList(lookupListTable, subtableMakers) {
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)));
{name: 'lookupFlag', type: 'USHORT', value: lookupTable.lookupFlag},
...tableList('subtable', lookupTable.subtables, subtableCallback)
]);
}));
}
LookupList.prototype = Object.create(Table.prototype);
@@ -209,24 +212,21 @@ LookupList.prototype.constructor = LookupList;
*/
function ClassDef(classDefTable) {
if (classDefTable.format === 1) {
Table.call(this, 'classDefTable',
[
{name: 'classFormat', type: 'USHORT', value: 1},
{name: 'startGlyphID', type: 'USHORT', value: classDefTable.startGlyph}
]
.concat(ushortList('glyph', classDefTable.classes))
);
Table.call(this, 'classDefTable', [
{name: 'classFormat', type: 'USHORT', value: 1},
{name: 'startGlyphID', type: 'USHORT', value: classDefTable.startGlyph},
...ushortList('glyph', classDefTable.classes)
]);
} else if (classDefTable.format === 2) {
Table.call(this, 'classDefTable',
[{name: 'classFormat', type: 'USHORT', value: 2}]
.concat(recordList('rangeRecord', classDefTable.ranges, function(RangeRecord, i) {
return [
{name: 'startGlyphID' + i, type: 'USHORT', value: RangeRecord.start},
{name: 'endGlyphID' + i, type: 'USHORT', value: RangeRecord.end},
{name: 'class' + i, type: 'USHORT', value: RangeRecord.classId},
];
}))
);
Table.call(this, 'classDefTable', [
{name: 'classFormat', type: 'USHORT', value: 2},
...recordList('rangeRecord', classDefTable.ranges, (RangeRecord, i) => [
{name: 'startGlyphID' + i, type: 'USHORT', value: RangeRecord.start},
{name: 'endGlyphID' + i, type: 'USHORT', value: RangeRecord.end},
{name: 'class' + i, type: 'USHORT', value: RangeRecord.classId},
])
]);

} else {
check.assert(false, 'Class format must be 1 or 2.');
}
6 changes: 3 additions & 3 deletions src/tables/avar.js
Original file line number Diff line number Diff line change
@@ -20,10 +20,10 @@ function makeAvarSegmentMap(n, axis) {
let axisValueMaps = [];
for (let i = 0; i < axis.axisValueMaps.length; i++) {
const valueMap = makeAvarAxisValueMap(`${n}_${i}`, axis.axisValueMaps[i]);
axisValueMaps = axisValueMaps.concat(valueMap.fields);
axisValueMaps.push(...valueMap.fields);
}

returnTable.fields = returnTable.fields.concat(axisValueMaps);
returnTable.fields.push(...axisValueMaps);

return returnTable;
}
@@ -40,7 +40,7 @@ function makeAvarTable(avar, fvar) {

for (let i = 0; i < avar.axisSegmentMaps.length; i++) {
const axisRecord = makeAvarSegmentMap(i, avar.axisSegmentMaps[i]);
result.fields = result.fields.concat(axisRecord.fields);
result.fields.push(...axisRecord.fields);
}

return result;
4 changes: 2 additions & 2 deletions src/tables/fvar.js
Original file line number Diff line number Diff line change
@@ -99,11 +99,11 @@ function makeFvarTable(fvar, names) {
result.offsetToData = result.sizeOf();

for (let i = 0; i < fvar.axes.length; i++) {
result.fields = result.fields.concat(makeFvarAxis(i, fvar.axes[i], names));
result.fields.push(...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));
result.fields.push(...makeFvarInstance(j, fvar.instances[j], fvar.axes, names));
}

return result;
2 changes: 1 addition & 1 deletion src/tables/glyf.js
Original file line number Diff line number Diff line change
@@ -290,7 +290,7 @@ function buildPath(glyphs, glyph) {
transform.dy = firstPt.y - secondPt.y;
transformedPoints = transformPoints(componentGlyph.points, transform);
}
glyph.points = glyph.points.concat(transformedPoints);
glyph.points.push(...transformedPoints);
}
}
}
168 changes: 93 additions & 75 deletions src/tables/gsub.js
Original file line number Diff line number Diff line change
@@ -224,8 +224,9 @@ subtableMakers[1] = function makeLookup1(subtable) {
} else if (subtable.substFormat === 2) {
return new table.Table('substitutionTable', [
{name: 'substFormat', type: 'USHORT', value: 2},
{name: 'coverage', type: 'TABLE', value: new table.Coverage(subtable.coverage)}
].concat(table.ushortList('substitute', subtable.substitute)));
{name: 'coverage', type: 'TABLE', value: new table.Coverage(subtable.coverage)},
...table.ushortList('substitute', subtable.substitute)
]);
}
check.fail('Lookup type 1 substFormat must be 1 or 2.');
};
@@ -234,84 +235,95 @@ subtableMakers[2] = function makeLookup2(subtable) {
check.assert(subtable.substFormat === 1, 'Lookup type 2 substFormat must be 1.');
return new table.Table('substitutionTable', [
{name: 'substFormat', type: 'USHORT', value: 1},
{name: 'coverage', type: 'TABLE', value: new table.Coverage(subtable.coverage)}
].concat(table.tableList('seqSet', subtable.sequences, function(sequenceSet) {
return new table.Table('sequenceSetTable', table.ushortList('sequence', sequenceSet));
})));
{name: 'coverage', type: 'TABLE', value: new table.Coverage(subtable.coverage)},
...table.tableList('seqSet', subtable.sequences, sequenceSet =>
new table.Table('sequenceSetTable', table.ushortList('sequence', sequenceSet))
)
]);
};

subtableMakers[3] = function makeLookup3(subtable) {
check.assert(subtable.substFormat === 1, 'Lookup type 3 substFormat must be 1.');
return new table.Table('substitutionTable', [
{name: 'substFormat', type: 'USHORT', value: 1},
{name: 'coverage', type: 'TABLE', value: new table.Coverage(subtable.coverage)}
].concat(table.tableList('altSet', subtable.alternateSets, function(alternateSet) {
return new table.Table('alternateSetTable', table.ushortList('alternate', alternateSet));
})));
{name: 'coverage', type: 'TABLE', value: new table.Coverage(subtable.coverage)},
...table.tableList('altSet', subtable.alternateSets, alternateSet =>
new table.Table('alternateSetTable', table.ushortList('alternate', alternateSet))
)
]);
};

subtableMakers[4] = function makeLookup4(subtable) {
check.assert(subtable.substFormat === 1, 'Lookup type 4 substFormat must be 1.');
return new table.Table('substitutionTable', [
{name: 'substFormat', type: 'USHORT', value: 1},
{name: 'coverage', type: 'TABLE', value: new table.Coverage(subtable.coverage)}
].concat(table.tableList('ligSet', subtable.ligatureSets, function(ligatureSet) {
return new table.Table('ligatureSetTable', table.tableList('ligature', ligatureSet, function(ligature) {
return new table.Table('ligatureTable',
[{name: 'ligGlyph', type: 'USHORT', value: ligature.ligGlyph}]
.concat(table.ushortList('component', ligature.components, ligature.components.length + 1))
);
}));
})));
{name: 'coverage', type: 'TABLE', value: new table.Coverage(subtable.coverage)},
...table.tableList('ligSet', subtable.ligatureSets, ligatureSet =>
new table.Table('ligatureSetTable', table.tableList('ligature', ligatureSet, ligature =>
new table.Table('ligatureTable', [
{name: 'ligGlyph', type: 'USHORT', value: ligature.ligGlyph},
...table.ushortList('component', ligature.components, ligature.components.length + 1)
])
))
)
]);
};

subtableMakers[5] = function makeLookup5(subtable) {
if (subtable.substFormat === 1) {
return new table.Table('contextualSubstitutionTable', [
{name: 'substFormat', type: 'USHORT', value: subtable.substFormat},
{name: 'coverage', type: 'TABLE', value: new table.Coverage(subtable.coverage)}
].concat(table.tableList('sequenceRuleSet', subtable.ruleSets, function(sequenceRuleSet) {
if (!sequenceRuleSet) {
return new table.Table('NULL', null);
}
return new table.Table('sequenceRuleSetTable', table.tableList('sequenceRule', sequenceRuleSet, function(sequenceRule) {
let tableData = table.ushortList('seqLookup', [], sequenceRule.lookupRecords.length)
.concat(table.ushortList('inputSequence', sequenceRule.input, sequenceRule.input.length + 1));

// swap the first two elements, because inputSequenceCount
// ("glyphCount" in the spec) comes before seqLookupCount
[tableData[0], tableData[1]] = [tableData[1], tableData[0]];

for(let i = 0; i < sequenceRule.lookupRecords.length; i++) {
const record = sequenceRule.lookupRecords[i];
tableData = tableData
.concat({name: 'sequenceIndex' + i, type: 'USHORT', value: record.sequenceIndex})
.concat({name: 'lookupListIndex' + i, type: 'USHORT', value: record.lookupListIndex});
{name: 'coverage', type: 'TABLE', value: new table.Coverage(subtable.coverage)},
...table.tableList('sequenceRuleSet', subtable.ruleSets, sequenceRuleSet => {
if (!sequenceRuleSet) {
return new table.Table('NULL', null);
}
return new table.Table('sequenceRuleTable', tableData);
}));
})));
return new table.Table('sequenceRuleSetTable', table.tableList('sequenceRule', sequenceRuleSet, sequenceRule => {
let tableData = [
...table.ushortList('seqLookup', [], sequenceRule.lookupRecords.length),
...table.ushortList('inputSequence', sequenceRule.input, sequenceRule.input.length + 1)
];

// swap the first two elements, because inputSequenceCount
// ("glyphCount" in the spec) comes before seqLookupCount
[tableData[0], tableData[1]] = [tableData[1], tableData[0]];

for (let i = 0; i < sequenceRule.lookupRecords.length; i++) {
const record = sequenceRule.lookupRecords[i];
tableData.push(
{name: 'sequenceIndex' + i, type: 'USHORT', value: record.sequenceIndex},
{name: 'lookupListIndex' + i, type: 'USHORT', value: record.lookupListIndex}
);
}
return new table.Table('sequenceRuleTable', tableData);
}));
})
]);
} else if (subtable.substFormat === 2) {
return new table.Table('contextualSubstitutionTable', [
{name: 'substFormat', type: 'USHORT', value: subtable.substFormat},
{name: 'coverage', type: 'TABLE', value: new table.Coverage(subtable.coverage)},
{name: 'classDef', type: 'TABLE', value: new table.ClassDef(subtable.classDef)}
].concat(table.tableList('classSeqRuleSet', subtable.classSets, function(classSeqRuleSet) {
if (!classSeqRuleSet) {
return new table.Table('NULL', null);
}
return new table.Table('classSeqRuleSetTable', table.tableList('classSeqRule', classSeqRuleSet, function(classSeqRule) {
let tableData = table.ushortList('classes', classSeqRule.classes, classSeqRule.classes.length + 1)
.concat(table.ushortList('seqLookupCount', [], classSeqRule.lookupRecords.length));
for(let i = 0; i < classSeqRule.lookupRecords.length; i++) {
const record = classSeqRule.lookupRecords[i];
tableData = tableData
.concat({name: 'sequenceIndex' + i, type: 'USHORT', value: record.sequenceIndex})
.concat({name: 'lookupListIndex' + i, type: 'USHORT', value: record.lookupListIndex});
{name: 'classDef', type: 'TABLE', value: new table.ClassDef(subtable.classDef)},
...table.tableList('classSeqRuleSet', subtable.classSets, classSeqRuleSet => {
if (!classSeqRuleSet) {
return new table.Table('NULL', null);
}
return new table.Table('classSeqRuleTable', tableData);
}));
})));
return new table.Table('classSeqRuleSetTable', table.tableList('classSeqRule', classSeqRuleSet, classSeqRule => {
let tableData = [
...table.ushortList('classes', classSeqRule.classes, classSeqRule.classes.length + 1),
...table.ushortList('seqLookupCount', [], classSeqRule.lookupRecords.length)
];
for (let i = 0; i < classSeqRule.lookupRecords.length; i++) {
const record = classSeqRule.lookupRecords[i];
tableData.push(
{name: 'sequenceIndex' + i, type: 'USHORT', value: record.sequenceIndex},
{name: 'lookupListIndex' + i, type: 'USHORT', value: record.lookupListIndex}
);
}
return new table.Table('classSeqRuleTable', tableData);
}));
})
]);
} else if (subtable.substFormat === 3) {
let tableData = [
{name: 'substFormat', type: 'USHORT', value: subtable.substFormat},
@@ -326,9 +338,10 @@ subtableMakers[5] = function makeLookup5(subtable) {

for(let i = 0; i < subtable.lookupRecords.length; i++) {
const record = subtable.lookupRecords[i];
tableData = tableData
.concat({name: 'sequenceIndex' + i, type: 'USHORT', value: record.sequenceIndex})
.concat({name: 'lookupListIndex' + i, type: 'USHORT', value: record.lookupListIndex});
tableData.push(
{name: 'sequenceIndex' + i, type: 'USHORT', value: record.sequenceIndex},
{name: 'lookupListIndex' + i, type: 'USHORT', value: record.lookupListIndex}
);
}

let returnTable = new table.Table('contextualSubstitutionTable', tableData);
@@ -344,22 +357,26 @@ subtableMakers[6] = function makeLookup6(subtable) {
let returnTable = new table.Table('chainContextTable', [
{name: 'substFormat', type: 'USHORT', value: subtable.substFormat},
{name: 'coverage', type: 'TABLE', value: new table.Coverage(subtable.coverage)}
].concat(table.tableList('chainRuleSet', subtable.chainRuleSets, function(chainRuleSet) {
return new table.Table('chainRuleSetTable', table.tableList('chainRule', chainRuleSet, function(chainRule) {
let tableData = table.ushortList('backtrackGlyph', chainRule.backtrack, chainRule.backtrack.length)
.concat(table.ushortList('inputGlyph', chainRule.input, chainRule.input.length + 1))
.concat(table.ushortList('lookaheadGlyph', chainRule.lookahead, chainRule.lookahead.length))
.concat(table.ushortList('substitution', [], chainRule.lookupRecords.length));

for(let i = 0; i < chainRule.lookupRecords.length; i++) {
]);
returnTable.fields.push(...table.tableList('chainRuleSet', subtable.chainRuleSets, chainRuleSet =>
new table.Table('chainRuleSetTable', table.tableList('chainRule', chainRuleSet, chainRule => {
let tableData = [
...table.ushortList('backtrackGlyph', chainRule.backtrack, chainRule.backtrack.length),
...table.ushortList('inputGlyph', chainRule.input, chainRule.input.length + 1),
...table.ushortList('lookaheadGlyph', chainRule.lookahead, chainRule.lookahead.length),
...table.ushortList('substitution', [], chainRule.lookupRecords.length)
];

for (let i = 0; i < chainRule.lookupRecords.length; i++) {
const record = chainRule.lookupRecords[i];
tableData = tableData
.concat({name: 'sequenceIndex' + i, type: 'USHORT', value: record.sequenceIndex})
.concat({name: 'lookupListIndex' + i, type: 'USHORT', value: record.lookupListIndex});
tableData.push(
{name: 'sequenceIndex' + i, type: 'USHORT', value: record.sequenceIndex},
{name: 'lookupListIndex' + i, type: 'USHORT', value: record.lookupListIndex}
);
}
return new table.Table('chainRuleTable', tableData);
}));
})));
}))
));
return returnTable;
} else if (subtable.substFormat === 2) {
check.assert(false, 'lookup type 6 format 2 is not yet supported.');
@@ -389,9 +406,10 @@ subtableMakers[6] = function makeLookup6(subtable) {
tableData.push({name: 'substitutionCount', type: 'USHORT', value: subtable.lookupRecords.length});
for(let i = 0; i < subtable.lookupRecords.length; i++) {
const record = subtable.lookupRecords[i];
tableData = tableData
.concat({name: 'sequenceIndex' + i, type: 'USHORT', value: record.sequenceIndex})
.concat({name: 'lookupListIndex' + i, type: 'USHORT', value: record.lookupListIndex});
tableData.push(
{name: 'sequenceIndex' + i, type: 'USHORT', value: record.sequenceIndex},
{name: 'lookupListIndex' + i, type: 'USHORT', value: record.lookupListIndex}
);
}

let returnTable = new table.Table('chainContextTable', tableData);
3 changes: 1 addition & 2 deletions src/tables/sfnt.js
Original file line number Diff line number Diff line change
@@ -104,8 +104,7 @@ function makeSfntTable(tables) {
}
});

sfnt.fields = sfnt.fields.concat(recordFields);
sfnt.fields = sfnt.fields.concat(tableFields);
sfnt.fields.push(...recordFields, ...tableFields);
return sfnt;
}

11 changes: 5 additions & 6 deletions src/tables/stat.js
Original file line number Diff line number Diff line change
@@ -180,10 +180,10 @@ axisValueMakers[4] = function axisValueMaker4(n, table) {
];

for (let i = 0; i < table.axisValues.length; i++) {
returnFields = returnFields.concat([
returnFields.push(
{name: `format${n}axisIndex${i}`, type: 'USHORT', value: table.axisValues[i].axisIndex},
{name: `format${n}value${i}`, type: 'FLOAT', value: table.axisValues[i].value},
]);
);
}

return returnFields;
@@ -222,7 +222,7 @@ function makeSTATTable(STAT) {
for (let i = 0; i < STAT.axes.length; i++) {
const axisRecord = makeSTATAxisRecord(i, STAT.axes[i]);
result.offsetToAxisValueOffsets += axisRecord.sizeOf();
result.fields = result.fields.concat(axisRecord.fields);
result.fields.push(...axisRecord.fields);
}

const axisValueOffsets = [];
@@ -237,11 +237,10 @@ function makeSTATTable(STAT) {
value: axisValueTableOffset
});
axisValueTableOffset += axisValueTable.sizeOf();
axisValueTables = axisValueTables.concat(axisValueTable.fields);
axisValueTables.push(...axisValueTable.fields);
}

result.fields = result.fields.concat(axisValueOffsets);
result.fields = result.fields.concat(axisValueTables);
result.fields.push(...axisValueOffsets, ...axisValueTables);

return result;
}
16 changes: 8 additions & 8 deletions src/tokenizer.js
Original file line number Diff line number Diff line change
@@ -154,7 +154,7 @@ Tokenizer.prototype.inboundIndex = function(index) {
Tokenizer.prototype.composeRUD = function (RUDs) {
const silent = true;
const state = RUDs.map(RUD => (
this[RUD[0]].apply(this, RUD.slice(1).concat(silent))
this[RUD[0]].apply(this, [...RUD.slice(1), silent])
));
const hasFAILObject = obj => (
typeof obj === 'object' &&
@@ -181,7 +181,7 @@ Tokenizer.prototype.replaceRange = function (startIndex, offset, tokens, silent)
const isTokenType = tokens.every(token => token instanceof Token);
if (!isNaN(startIndex) && this.inboundIndex(startIndex) && isTokenType) {
const replaced = this.tokens.splice.apply(
this.tokens, [startIndex, offset].concat(tokens)
this.tokens, [startIndex, offset, ...tokens]
);
if (!silent) this.dispatch('replaceToken', [startIndex, offset, tokens]);
return [replaced, tokens];
@@ -246,8 +246,8 @@ Tokenizer.prototype.insertToken = function (tokens, index, silent) {
);
if (tokenType) {
this.tokens.splice.apply(
this.tokens, [index, 0].concat(tokens)
);
this.tokens, [index, 0, ...tokens]
);
if (!silent) this.dispatch('insertToken', [tokens, index]);
return tokens;
} else {
@@ -420,10 +420,10 @@ Tokenizer.prototype.registerContextChecker = function(contextName, contextStartC
*/
Tokenizer.prototype.getRangeTokens = function(range) {
const endIndex = range.startIndex + range.endOffset;
return [].concat(
this.tokens
.slice(range.startIndex, endIndex)
);
return [
...this.tokens.slice(range.startIndex, endIndex)
];
};

/**
10 changes: 6 additions & 4 deletions src/types.js
Original file line number Diff line number Diff line change
@@ -753,10 +753,12 @@ encode.INDEX = function(l) {
Array.prototype.push.apply(encodedOffsets, encodedOffset);
}

return Array.prototype.concat(encode.Card16(l.length),
encode.OffSize(offSize),
encodedOffsets,
data);
return [
...encode.Card16(l.length),
...encode.OffSize(offSize),
...encodedOffsets,
...data
];
};

/**
6 changes: 6 additions & 0 deletions test/fonts/LICENSE
Original file line number Diff line number Diff line change
@@ -26,6 +26,12 @@ Jomhuria-Regular.ttf
SIL Open Font License, Version 1.1.
https://www.fontsquirrel.com/license/jomhuria

notosanssc-bold.ttf
NotoSansThai-Medium-Testing-v1.ttf
Copyright 2012 Google Inc. All Rights Reserved.
SIL Open Font License v1.10
https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL

OpenMojiCOLORv0-subset.ttf
All emojis designed by OpenMoji – the open-source emoji and icon project.
Creative Commons Share Alike License 4.0 (CC BY-SA 4.0)
Binary file added test/fonts/notosanssc-bold.ttf
Binary file not shown.
17 changes: 17 additions & 0 deletions test/performance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import assert from 'assert';
import { parse } from '../src/opentype.js';
import { readFileSync } from 'fs';
import { performance } from 'perf_hooks';
const loadSync = (url, opt) => parse(readFileSync(url), opt);

describe('performance tests', function() {
const notoSansSC = loadSync('./test/fonts/notosanssc-bold.ttf');

it('should not take too long to use toArrayBuffer() on large fonts', function() {
const start = performance.now();
const time = performance.now() - start;
notoSansSC.toArrayBuffer();
assert(time < 16000, true);
}).timeout(16000);
});


Unchanged files with check annotations Beta

*/
fillPalette(palette, colors = [], _colorCount = this.cpal().numPaletteEntries) {
palette = Number.isInteger(palette) ? this.get(palette, 'raw') : palette;
return Object.assign(Array(_colorCount).fill(this.defaultValue), this.toCPALcolor(palette).concat(this.toCPALcolor(colors)));

Check failure on line 87 in src/palettes.js

GitHub Actions / build

Use obj.push(...data) instead of obj = obj.concat(data)
}
/**