Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into patch-1
Browse files Browse the repository at this point in the history
Connum authored Mar 10, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
2 parents 7ca0b2d + 66c9899 commit 71a9022
Showing 13 changed files with 183 additions and 82 deletions.
43 changes: 43 additions & 0 deletions eslint-local-rules.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict';

module.exports = {
'ban-foreach': {
meta: {
type: 'suggestion',
schema: [],
},
create(context) {
return {
CallExpression(node) {
if (node.callee.property && node.callee.property.name === 'forEach') {
context.report({node, message: 'Use for() loops instead of .forEach()'})
}
},
}
},
},
'import-extensions': {
meta: {
type: 'problem',
schema: []
},
create(context) {
const checkImportPath = (node) => {
const importPath = node.source.value;
const isRelative = importPath.startsWith('.') || importPath.startsWith('/');
const extensionMissing = require('path').extname(importPath) === '';
if (!isRelative || !extensionMissing) {
return;
}
context.report({
node: node.source,
message: 'Import paths require a file extension to work in browser module context'
});
};

return {
ImportDeclaration: checkImportPath
};
},
}
};
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -54,6 +54,7 @@
"es2021": true,
"node": true
},
"plugins": ["eslint-plugin-local-rules"],
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 2018,
@@ -78,7 +79,12 @@
"semi": [
"error",
"always"
]
],
"local-rules/ban-foreach": 2,
"local-rules/import-extensions": 2
}
},
"dependencies": {
"eslint-plugin-local-rules": "^1.3.2"
}
}
31 changes: 18 additions & 13 deletions src/bidi.js
Original file line number Diff line number Diff line change
@@ -78,14 +78,15 @@ function tokenizeText() {
*/
function reverseArabicSentences() {
const ranges = this.tokenizer.getContextRanges('arabicSentence');
ranges.forEach(range => {
for(let i = 0; i < ranges.length; i++) {
const range = ranges[i];
let rangeTokens = this.tokenizer.getRangeTokens(range);
this.tokenizer.replaceRange(
range.startIndex,
range.endOffset,
rangeTokens.reverse()
);
});
}
}

/**
@@ -153,9 +154,10 @@ function applyArabicPresentationForms() {
if (!Object.prototype.hasOwnProperty.call(this.featuresTags, script)) return;
checkGlyphIndexStatus.call(this);
const ranges = this.tokenizer.getContextRanges('arabicWord');
ranges.forEach(range => {
for(let i = 0; i < ranges.length; i++) {
const range = ranges[i];
arabicPresentationForms.call(this, range);
});
}
}

/**
@@ -165,9 +167,10 @@ function applyArabicRequireLigatures() {
if (!this.hasFeatureEnabled('arab', 'rlig')) return;
checkGlyphIndexStatus.call(this);
const ranges = this.tokenizer.getContextRanges('arabicWord');
ranges.forEach(range => {
for(let i = 0; i < ranges.length; i++) {
const range = ranges[i];
arabicRequiredLigatures.call(this, range);
});
}
}

/**
@@ -177,16 +180,18 @@ function applyLatinLigatures() {
if (!this.hasFeatureEnabled('latn', 'liga')) return;
checkGlyphIndexStatus.call(this);
const ranges = this.tokenizer.getContextRanges('latinWord');
ranges.forEach(range => {
for(let i = 0; i < ranges.length; i++) {
const range = ranges[i];
latinLigature.call(this, range);
});
}
}

function applyUnicodeVariationSequences() {
const ranges = this.tokenizer.getContextRanges('unicodeVariationSequence');
ranges.forEach(range => {
for(let i = 0; i < ranges.length; i++) {
const range = ranges[i];
unicodeVariationSequences.call(this, range);
});
}
}

/**
@@ -195,12 +200,12 @@ function applyUnicodeVariationSequences() {
function applyThaiFeatures() {
checkGlyphIndexStatus.call(this);
const ranges = this.tokenizer.getContextRanges('thaiWord');
ranges.forEach(range => {
for(let i = 0; i < ranges.length; i++) {
const range = ranges[i];
if (this.hasFeatureEnabled('thai', 'liga')) thaiLigatures.call(this, range);
if (this.hasFeatureEnabled('thai', 'rlig')) thaiRequiredLigatures.call(this, range);
if (this.hasFeatureEnabled('thai', 'ccmp')) thaiGlyphComposition.call(this, range);
});

}
}

/**
7 changes: 4 additions & 3 deletions src/features/applySubstitution.js
Original file line number Diff line number Diff line change
@@ -27,10 +27,11 @@ function singleSubstitutionFormat2(action, tokens, index) {
* @param {number} index token index
*/
function chainingSubstitutionFormat3(action, tokens, index) {
action.substitution.forEach((subst, offset) => {
const token = tokens[index + offset];
for(let i = 0; i < action.substitution.length; i++) {
const subst = action.substitution[i];
const token = tokens[index + i];
token.setState(action.tag, subst);
});
}
}

/**
27 changes: 16 additions & 11 deletions src/features/arab/arabicPresentationForms.js
Original file line number Diff line number Diff line change
@@ -52,10 +52,11 @@ function arabicPresentationForms(range) {
const charContextParams = new ContextParams(
tokens.map(token => token.char
), 0);
tokens.forEach((token, index) => {
if (isTashkeelArabicChar(token.char)) return;
contextParams.setCurrentIndex(index);
charContextParams.setCurrentIndex(index);
for(let i = 0; i < tokens.length; i++) {
const token = tokens[i];
if (isTashkeelArabicChar(token.char)) continue;
contextParams.setCurrentIndex(i);
charContextParams.setCurrentIndex(i);
let CONNECT = 0; // 2 bits 00 (10: can connect next) (01: can connect prev)
if (willConnectPrev(charContextParams)) CONNECT |= 1;
if (willConnectNext(charContextParams)) CONNECT |= 2;
@@ -65,18 +66,22 @@ function arabicPresentationForms(range) {
case 2: (tag = 'init'); break;
case 3: (tag = 'medi'); break;
}
if (tags.indexOf(tag) === -1) return;
if (tags.indexOf(tag) === -1) continue;
let substitutions = this.query.lookupFeature({
tag, script, contextParams
});
if (substitutions instanceof Error) return console.info(substitutions.message);
substitutions.forEach((action, index) => {
if (substitutions instanceof Error) {
console.info(substitutions.message);
continue;
}
for(let j = 0; j < substitutions.length; j++) {
const action = substitutions[j];
if (action instanceof SubstitutionAction) {
applySubstitution(action, tokens, index);
contextParams.context[index] = action.substitution;
applySubstitution(action, tokens, j);
contextParams.context[j] = action.substitution;
}
});
});
}
}
}

export default arabicPresentationForms;
11 changes: 6 additions & 5 deletions src/features/arab/arabicRequiredLigatures.js
Original file line number Diff line number Diff line change
@@ -26,18 +26,19 @@ function arabicRequiredLigatures(range) {
const script = 'arab';
let tokens = this.tokenizer.getRangeTokens(range);
let contextParams = getContextParams(tokens);
contextParams.context.forEach((glyphIndex, index) => {
for (let index = 0; index < contextParams.context.length; index++) {
contextParams.setCurrentIndex(index);
let substitutions = this.query.lookupFeature({
tag: 'rlig', script, contextParams
});
if (substitutions.length) {
substitutions.forEach(
action => applySubstitution(action, tokens, index)
);
for(let i = 0; i < substitutions.length; i++) {
const action = substitutions[i];
applySubstitution(action, tokens, index);
}
contextParams = getContextParams(tokens);
}
});
}
}

export default arabicRequiredLigatures;
11 changes: 6 additions & 5 deletions src/features/latn/latinLigatures.js
Original file line number Diff line number Diff line change
@@ -26,18 +26,19 @@ function latinLigature(range) {
const script = 'latn';
let tokens = this.tokenizer.getRangeTokens(range);
let contextParams = getContextParams(tokens);
contextParams.context.forEach((glyphIndex, index) => {
for(let index = 0; index < contextParams.context.length; index++) {
contextParams.setCurrentIndex(index);
let substitutions = this.query.lookupFeature({
tag: 'liga', script, contextParams
});
if (substitutions.length) {
substitutions.forEach(
action => applySubstitution(action, tokens, index)
);
for(let i = 0; i < substitutions.length; i++) {
const action = substitutions[i];
applySubstitution(action, tokens, index);
}
contextParams = getContextParams(tokens);
}
});
}
}

export default latinLigature;
11 changes: 6 additions & 5 deletions src/features/thai/thaiGlyphComposition.js
Original file line number Diff line number Diff line change
@@ -23,18 +23,19 @@ function thaiGlyphComposition(range) {
const script = 'thai';
let tokens = this.tokenizer.getRangeTokens(range);
let contextParams = getContextParams(tokens, 0);
contextParams.context.forEach((glyphIndex, index) => {
for(let index = 0; index < contextParams.context.length; index++) {
contextParams.setCurrentIndex(index);
let substitutions = this.query.lookupFeature({
tag: 'ccmp', script, contextParams
});
if (substitutions.length) {
substitutions.forEach(
action => applySubstitution(action, tokens, index)
);
for(let i = 0; i < substitutions.length; i++) {
const action = substitutions[i];
applySubstitution(action, tokens, index);
}
contextParams = getContextParams(tokens, index);
}
});
}
}

export default thaiGlyphComposition;
14 changes: 9 additions & 5 deletions src/features/thai/thaiLigatures.js
Original file line number Diff line number Diff line change
@@ -5,6 +5,9 @@
import { ContextParams } from '../../tokenizer.js';
import applySubstitution from '../applySubstitution.js';

// @TODO: use commonFeatureUtils.js for reduction of code duplication
// once #564 has been merged.

/**
* Update context params
* @param {any} tokens a list of tokens
@@ -23,18 +26,19 @@ function thaiLigatures(range) {
const script = 'thai';
let tokens = this.tokenizer.getRangeTokens(range);
let contextParams = getContextParams(tokens, 0);
contextParams.context.forEach((glyphIndex, index) => {
for(let index = 0; index < contextParams.context.length; index++) {
contextParams.setCurrentIndex(index);
let substitutions = this.query.lookupFeature({
tag: 'liga', script, contextParams
});
if (substitutions.length) {
substitutions.forEach(
action => applySubstitution(action, tokens, index)
);
for(let i = 0; i < substitutions.length; i++) {
const action = substitutions[i];
applySubstitution(action, tokens, index);
}
contextParams = getContextParams(tokens, index);
}
});
}
}

export default thaiLigatures;
14 changes: 9 additions & 5 deletions src/features/thai/thaiRequiredLigatures.js
Original file line number Diff line number Diff line change
@@ -5,6 +5,9 @@
import { ContextParams } from '../../tokenizer.js';
import applySubstitution from '../applySubstitution.js';

// @TODO: use commonFeatureUtils.js for reduction of code duplication
// once #564 has been merged.

/**
* Update context params
* @param {any} tokens a list of tokens
@@ -23,18 +26,19 @@ function thaiRequiredLigatures(range) {
const script = 'thai';
let tokens = this.tokenizer.getRangeTokens(range);
let contextParams = getContextParams(tokens, 0);
contextParams.context.forEach((glyphIndex, index) => {
for(let index = 0; index < contextParams.context.length; index++) {
contextParams.setCurrentIndex(index);
let substitutions = this.query.lookupFeature({
tag: 'rlig', script, contextParams
});
if (substitutions.length) {
substitutions.forEach(
action => applySubstitution(action, tokens, index)
);
for(let i = 0; i < substitutions.length; i++) {
const action = substitutions[i];
applySubstitution(action, tokens, index);
}
contextParams = getContextParams(tokens, index);
}
});
}
}

export default thaiRequiredLigatures;
48 changes: 29 additions & 19 deletions src/tables/gsub.js
Original file line number Diff line number Diff line change
@@ -282,11 +282,12 @@ subtableMakers[5] = function makeLookup5(subtable) {
// ("glyphCount" in the spec) comes before seqLookupCount
[tableData[0], tableData[1]] = [tableData[1], tableData[0]];

sequenceRule.lookupRecords.forEach((record, i) => {
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});
});
}
return new table.Table('sequenceRuleTable', tableData);
}));
})));
@@ -302,12 +303,12 @@ subtableMakers[5] = function makeLookup5(subtable) {
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));

classSeqRule.lookupRecords.forEach((record, i) => {
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});
});
}
return new table.Table('classSeqRuleTable', tableData);
}));
})));
@@ -318,15 +319,17 @@ subtableMakers[5] = function makeLookup5(subtable) {

tableData.push({name: 'inputGlyphCount', type: 'USHORT', value: subtable.coverages.length});
tableData.push({name: 'substitutionCount', type: 'USHORT', value: subtable.lookupRecords.length});
subtable.coverages.forEach((coverage, i) => {
for(let i = 0; i < subtable.coverages.length; i++) {
const coverage = subtable.coverages[i];
tableData.push({name: 'inputCoverage' + i, type: 'TABLE', value: new table.Coverage(coverage)});
});
}

subtable.lookupRecords.forEach((record, i) => {
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});
});
}

let returnTable = new table.Table('contextualSubstitutionTable', tableData);

@@ -348,11 +351,12 @@ subtableMakers[6] = function makeLookup6(subtable) {
.concat(table.ushortList('lookaheadGlyph', chainRule.lookahead, chainRule.lookahead.length))
.concat(table.ushortList('substitution', [], chainRule.lookupRecords.length));

chainRule.lookupRecords.forEach((record, i) => {
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});
});
}
return new table.Table('chainRuleTable', tableData);
}));
})));
@@ -365,24 +369,30 @@ subtableMakers[6] = function makeLookup6(subtable) {
];

tableData.push({name: 'backtrackGlyphCount', type: 'USHORT', value: subtable.backtrackCoverage.length});
subtable.backtrackCoverage.forEach((coverage, i) => {
for(let i = 0; i < subtable.backtrackCoverage.length; i++) {
const coverage = subtable.backtrackCoverage[i];
tableData.push({name: 'backtrackCoverage' + i, type: 'TABLE', value: new table.Coverage(coverage)});
});
}
tableData.push({name: 'inputGlyphCount', type: 'USHORT', value: subtable.inputCoverage.length});
subtable.inputCoverage.forEach((coverage, i) => {

for(let i = 0; i < subtable.inputCoverage.length; i++) {
const coverage = subtable.inputCoverage[i];
tableData.push({name: 'inputCoverage' + i, type: 'TABLE', value: new table.Coverage(coverage)});
});
}
tableData.push({name: 'lookaheadGlyphCount', type: 'USHORT', value: subtable.lookaheadCoverage.length});
subtable.lookaheadCoverage.forEach((coverage, i) => {

for(let i = 0; i < subtable.lookaheadCoverage.length; i++) {
const coverage = subtable.lookaheadCoverage[i];
tableData.push({name: 'lookaheadCoverage' + i, type: 'TABLE', value: new table.Coverage(coverage)});
});
}

tableData.push({name: 'substitutionCount', type: 'USHORT', value: subtable.lookupRecords.length});
subtable.lookupRecords.forEach((record, i) => {
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});
});
}

let returnTable = new table.Table('chainContextTable', tableData);

27 changes: 17 additions & 10 deletions src/tokenizer.js
Original file line number Diff line number Diff line change
@@ -10,6 +10,8 @@ function Token(char) {
this.char = char;
this.state = {};
this.activeState = null;


}

/**
@@ -79,29 +81,32 @@ function initializeCoreEvents(events) {
'replaceToken', 'replaceRange', 'composeRUD', 'updateContextsRanges'
];

coreEvents.forEach(eventId => {
for(let i = 0; i < coreEvents.length; i++) {
const eventId = coreEvents[i];
Object.defineProperty(this.events, eventId, {
value: new Event(eventId)
});
});
}

if (events) {
coreEvents.forEach(eventId => {
for(let i = 0; i < coreEvents.length; i++) {
const eventId = coreEvents[i];
const event = events[eventId];
if (typeof event === 'function') {
this.events[eventId].subscribe(event);
}
});
}
}
const requiresContextUpdate = [
'insertToken', 'removeToken', 'removeRange',
'replaceToken', 'replaceRange', 'composeRUD'
];
requiresContextUpdate.forEach(eventId => {
for(let i = 0; i < requiresContextUpdate.length; i++) {
const eventId = requiresContextUpdate[i];
this.events[eventId].subscribe(
this.updateContextsRanges
);
});
}
}

/**
@@ -374,9 +379,10 @@ Tokenizer.prototype.on = function(eventName, eventHandler) {
Tokenizer.prototype.dispatch = function(eventName, args) {
const event = this.events[eventName];
if (event instanceof Event) {
event.subscribers.forEach(subscriber => {
for(let i = 0; i < event.subscribers.length; i++) {
const subscriber = event.subscribers[i];
subscriber.apply(this, args || []);
});
}
}
};

@@ -480,7 +486,8 @@ Tokenizer.prototype.setEndOffset = function (offset, contextName) {
*/
Tokenizer.prototype.runContextCheck = function(contextParams) {
const index = contextParams.index;
this.contextCheckers.forEach(contextChecker => {
for(let i = 0; i < this.contextCheckers.length; i++) {
const contextChecker = this.contextCheckers[i];
let contextName = contextChecker.contextName;
let openRange = this.getContext(contextName).openRange;
if (!openRange && contextChecker.checkStart(contextParams)) {
@@ -493,7 +500,7 @@ Tokenizer.prototype.runContextCheck = function(contextParams) {
const range = this.setEndOffset(offset, contextName);
this.dispatch('contextEnd', [contextName, range]);
}
});
}
};

/**

0 comments on commit 71a9022

Please sign in to comment.