use module for Arabic text shaping road labels on Chrome

This commit is contained in:
Nick Doiron
2019-03-06 22:43:11 -05:00
parent 0a1e497836
commit 7708d05b16
2 changed files with 23 additions and 120 deletions

View File

@@ -1,77 +1,16 @@
// see https://github.com/openstreetmap/iD/pull/3707
// https://gist.github.com/mapmeld/556b09ddec07a2044c76e1ef45f01c60
var chars = {
// madda above alef
1570: { initial: 'آ‎', isolated: 'ﺁ', medial: 'ﺁ', final: 'ﺂ' },
// hamza above and below alef
1571: { initial: 'أ', isolated: 'ﺃ', medial: '', final: 'ﺄ' },
// 1572 is ؤ
1573: { initial: 'إ', isolated: 'ﺇ', medial: '', final: 'ﺈ' },
// 1574 is ئ
1575: { initial: 'ا', isolated: 'ا', medial: '', final: '' },
1576: { initial: 'ﺑ', isolated: 'ﺏ', medial: 'ﺒ', final: 'ﺐ' },
// 1577 ة
1577: { initial: '', isolated: 'ة', medial: '', final: 'ﺔ' },
1578: { initial: 'ﺗ', isolated: 'ﺕ', medial: 'ﺘ', final: 'ﺖ' },
1579: { initial: 'ﺛ', isolated: 'ﺙ', medial: 'ﺜ', final: 'ﺚ' },
1580: { initial: 'ﺟ', isolated: 'ﺝ', medial: 'ﺠ', final: 'ﺞ' },
1581: { initial: 'ﺣ', isolated: 'ﺡ', medial: 'ﺤ', final: 'ﺢ' },
1582: { initial: 'ﺧ', isolated: 'ﺥ', medial: 'ﺨ', final: 'ﺦ' },
1583: { initial: 'ﺩ', isolated: 'ﺩ', medial: '', final: 'ﺪ' },
1584: { initial: 'ﺫ', isolated: 'ﺫ', medial: '', final: 'ﺬ' },
1585: { initial: 'ﺭ', isolated: 'ﺭ', medial: '', final: 'ﺮ' },
1586: { initial: 'ﺯ', isolated: 'ﺯ', medial: '', final: 'ﺰ' },
1688: { initial: 'ﮊ', isolated: 'ﮊ', medial: '', final: 'ﮋ' },
1587: { initial: 'ﺳ', isolated: 'ﺱ', medial: 'ﺴ', final: 'ﺲ' },
1588: { initial: 'ﺷ', isolated: 'ﺵ', medial: 'ﺸ', final: 'ﺶ' },
1589: { initial: 'ﺻ', isolated: 'ﺹ', medial: 'ﺼ', final: 'ﺺ' },
1590: { initial: 'ﺿ', isolated: 'ﺽ', medial: 'ﻀ', final: 'ﺾ' },
1591: { initial: 'ﻃ', isolated: 'ﻁ', medial: 'ﻄ', final: 'ﻂ' },
1592: { initial: 'ﻇ', isolated: 'ﻅ', medial: 'ﻈ', final: 'ﻆ' },
1593: { initial: 'ﻋ', isolated: 'ﻉ', medial: 'ﻌ', final: 'ﻊ' },
1594: { initial: 'ﻏ', isolated: 'ﻍ', medial: 'ﻐ', final: 'ﻎ' },
// 1595 ػ - may be very rare
1601: { initial: 'ﻓ', isolated: 'ﻑ', medial: 'ﻔ', final: 'ﻒ' },
1602: { initial: 'ﻗ', isolated: 'ﻕ', medial: 'ﻘ', final: 'ﻖ' },
1604: { initial: 'ﻟ', isolated: 'ﻝ', medial: 'ﻠ', final: 'ﻞ' },
1605: { initial: 'ﻣ', isolated: 'ﻡ', medial: 'ﻤ', final: 'ﻢ' },
1606: { initial: 'ﻧ', isolated: 'ﻥ', medial: 'ﻨ', final: 'ﻦ' },
1607: { initial: '', isolated: '', medial: '', final: '' },
1608: { initial: 'ﻭ', isolated: 'ﻭ', medial: '', final: 'ﻮ' },
// 1609 ى
1609: { initial: 'ﯨ', isolated: 'ﻯ', medial: 'ﯩ', final: 'ﻰ' },
// 1610 ي
1610: { initial: 'ﻳ', isolated: 'ﻱ', medial: 'ﻴ', final: 'ﻲ' },
// short vowel sounds / tashkil markings
1662: { initial: 'ﭘ', isolated: 'ﭖ', medial: 'ﭙ', final: 'ﭗ' },
1670: { initial: 'ﭼ', isolated: 'ﭺ', medial: 'ﭽ', final: 'ﭻ' },
1603: { initial: 'ﻛ', isolated: 'ﻙ', medial: 'ﻜ', final: 'ﻚ' },
1705: { initial: 'ﻛ', isolated: 'ﮎ', medial: 'ﻜ', final: 'ﮏ' },
1711: { initial: 'ﮔ', isolated: 'ﮒ', medial: 'ﮕ', final: 'ﮓ' },
1740: { initial: 'ﻳ', isolated: 'ﻯ', medial: 'ﻴ', final: 'ﻰ' },
5000: { initial: 'ﻻ', isolated: 'ﻻ', medial: '', final: 'ﻼ' }
};
import { BaselineSplitter, WordShaper } from 'alif-toolkit';
export var rtlRegex = /[\u0590-\u05FF\u0600-\u06FF\u0780-\u07BF]/;
export function fixRTLTextForSvg(inputText) {
var context = true;
var ret = '';
var rtlBuffer = [];
var arabicRegex = /[\u0600-\u06FF]/g;
var arabicTashkil = /[\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED]/;
var thaanaVowel = /[\u07A6-\u07B0]/;
var hebrewSign = /[\u0591-\u05bd\u05bf\u05c1-\u05c5\u05c7]/;
let ret = '', rtlBuffer = [];
let arabicRegex = /[\u0600-\u06FF]/g;
let arabicTashkil = /[\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED]/;
let thaanaVowel = /[\u07A6-\u07B0]/;
let hebrewSign = /[\u0591-\u05bd\u05bf\u05c1-\u05c5\u05c7]/;
if (!arabicRegex.test(inputText)) {
// Hebrew or Thaana RTL script
@@ -90,59 +29,22 @@ export function fixRTLTextForSvg(inputText) {
rtlBuffer = [];
}
}
ret += rtlBuffer.reverse().join('');
return ret;
} else {
for (var i = 0, l = inputText.length; i < l; i++) {
var code = inputText[i].charCodeAt(0);
var nextCode = inputText[i + 1] ? inputText[i + 1].charCodeAt(0) : 0;
if (!chars[code]) {
if (code === 32 && rtlBuffer.length) {
// whitespace
rtlBuffer = [rtlBuffer.reverse().join('') + ' '];
} else if (arabicTashkil.test(inputText[i]) && rtlBuffer.length) {
// tashkil mark
rtlBuffer[rtlBuffer.length - 1] += inputText[i];
} else {
// non-RTL character
ret += rtlBuffer.reverse().join('') + inputText[i];
rtlBuffer = [];
}
continue;
}
if (context) {
if (i === l - 1 || nextCode === 32) {
rtlBuffer.push(chars[code].isolated);
} else {
// special case for لا
if (code === 1604 && nextCode === 1575) {
rtlBuffer.push(chars[5000].initial);
i++;
context = true;
continue;
}
rtlBuffer.push(chars[code].initial);
}
} else {
if (i === l - 1 || nextCode === 32){
rtlBuffer.push(chars[code].final);
} else {
// special case for ﻼ
if (code === 1604 && nextCode === 1575){
rtlBuffer.push(chars[5000].final);
i++;
context = true;
continue;
}
if (chars[code].medial === ''){
rtlBuffer.push(chars[code].final);
} else {
rtlBuffer.push(chars[code].medial);
}
}
}
context = (chars[code].medial === '') || nextCode === 32;
}
let myWord = BaselineSplitter(WordShaper(inputText));
myWord.forEach((baseline) => {
if (baseline === ' ') {
ret = baseline + rtlBuffer.reverse().join('') + ret;
rtlBuffer = [];
} else if (baseline.match(/^\d+$/)) {
ret = rtlBuffer.reverse().join('') + ret + baseline;
rtlBuffer = [];
} else {
rtlBuffer.push(baseline);
}
});
ret = rtlBuffer.reverse().join('') + ret;
return ret;
}
ret += rtlBuffer.reverse().join('');
return ret;
}

View File

@@ -35,6 +35,7 @@
"@mapbox/togeojson": "0.16.0",
"@mapbox/vector-tile": "^1.3.1",
"@turf/bbox-clip": "^6.0.0",
"alif-toolkit": "^1.2.4",
"diacritics": "1.3.0",
"fast-json-stable-stringify": "2.0.0",
"lodash-es": "4.17.11",