84 lines
3.0 KiB
Plaintext
84 lines
3.0 KiB
Plaintext
|
/**
|
||
|
* Apply Arabic presentation forms to a range of tokens
|
||
|
*/
|
||
|
|
||
|
import { ContextParams } from '../../tokenizer';
|
||
|
import { isIsolatedArabicChar, isTashkeelArabicChar } from '../../char';
|
||
|
import { SubstitutionAction } from '../featureQuery';
|
||
|
import applySubstitution from '../applySubstitution';
|
||
|
|
||
|
/**
|
||
|
* Check if a char can be connected to it's preceding char
|
||
|
* @param {ContextParams} charContextParams context params of a char
|
||
|
*/
|
||
|
function willConnectPrev(charContextParams) {
|
||
|
let backtrack = [].concat(charContextParams.backtrack);
|
||
|
for (let i = backtrack.length - 1; i >= 0; i--) {
|
||
|
const prevChar = backtrack[i];
|
||
|
const isolated = isIsolatedArabicChar(prevChar);
|
||
|
const tashkeel = isTashkeelArabicChar(prevChar);
|
||
|
if (!isolated && !tashkeel) return true;
|
||
|
if (isolated) return false;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if a char can be connected to it's proceeding char
|
||
|
* @param {ContextParams} charContextParams context params of a char
|
||
|
*/
|
||
|
function willConnectNext(charContextParams) {
|
||
|
if (isIsolatedArabicChar(charContextParams.current)) return false;
|
||
|
for (let i = 0; i < charContextParams.lookahead.length; i++) {
|
||
|
const nextChar = charContextParams.lookahead[i];
|
||
|
const tashkeel = isTashkeelArabicChar(nextChar);
|
||
|
if (!tashkeel) return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Apply arabic presentation forms to a list of tokens
|
||
|
* @param {ContextRange} range a range of tokens
|
||
|
*/
|
||
|
function arabicPresentationForms(range) {
|
||
|
const script = 'arab';
|
||
|
const tags = this.featuresTags[script];
|
||
|
const tokens = this.tokenizer.getRangeTokens(range);
|
||
|
if (tokens.length === 1) return;
|
||
|
let contextParams = new ContextParams(
|
||
|
tokens.map(token => token.getState('glyphIndex')
|
||
|
), 0);
|
||
|
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);
|
||
|
let CONNECT = 0; // 2 bits 00 (10: can connect next) (01: can connect prev)
|
||
|
if (willConnectPrev(charContextParams)) CONNECT |= 1;
|
||
|
if (willConnectNext(charContextParams)) CONNECT |= 2;
|
||
|
let tag;
|
||
|
switch (CONNECT) {
|
||
|
case 1: (tag = 'fina'); break;
|
||
|
case 2: (tag = 'init'); break;
|
||
|
case 3: (tag = 'medi'); break;
|
||
|
}
|
||
|
if (tags.indexOf(tag) === -1) return;
|
||
|
let substitutions = this.query.lookupFeature({
|
||
|
tag, script, contextParams
|
||
|
});
|
||
|
if (substitutions instanceof Error) return console.info(substitutions.message);
|
||
|
substitutions.forEach((action, index) => {
|
||
|
if (action instanceof SubstitutionAction) {
|
||
|
applySubstitution(action, tokens, index);
|
||
|
contextParams.context[index] = action.substitution;
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
export default arabicPresentationForms;
|
||
|
export { arabicPresentationForms };
|