44
55import { JoiningGroup , JoiningType , JOINING_GROUP , JOINING_TYPE } from './arabic_joining.js' ;
66
7+ // A Kashida insertion point.
78export class Kashida {
89 constructor ( index , priority , max = null ) {
910 this . index = index ;
@@ -12,6 +13,8 @@ export class Kashida {
1213 }
1314}
1415
16+ // An array of Kashida insertion points. Makes sure that there are no more than
17+ // one Kashida at the same index.
1518class Kashidas extends Array {
1619 append ( kashida ) {
1720 if ( this . some ( k => k . index === kashida . index ) ) {
@@ -21,11 +24,14 @@ class Kashidas extends Array {
2124 }
2225}
2326
27+ // Supported Kashida algorithms.
2428export const Algorithm = {
2529 SIMPLE : 'simple' ,
2630 NASKH : 'naskh'
2731} ;
2832
33+ // Match any Kashida character that is not followed by a small alef or hamza
34+ // above.
2935const KASHIDA_RE = / ( \u0640 ) (? ! \u0670 | \u0654 ) / ;
3036
3137const ALEF = Object . keys ( JOINING_GROUP ) . filter ( c => JOINING_GROUP [ c ] === JoiningGroup . Alef ) . map ( c => String . fromCharCode ( c ) ) ;
@@ -50,6 +56,9 @@ function isArabicLetter(c) {
5056 return c . match ( / \p{ L} / u) && c . match ( / \p{ Script= Arab} / u) ;
5157} ;
5258
59+ // Get the next Arabic letter in a text string skipping combining marks, or None
60+ // if next base character is not an Arabic letter.If step is negative, it will
61+ // get the previous Arabic letter.
5362function getNextArabicLetter ( text , index , step = 1 ) {
5463 while ( index >= 0 && index < text . length ) {
5564 const c = text [ index ] ;
@@ -65,27 +74,50 @@ function getNextArabicLetter(text, index, step = 1) {
6574 return null ;
6675}
6776
77+ // Get the previous Arabic letter in a text string skipping combining marks, or
78+ // None if previous base character is not an Arabic letter.
6879function getPreviousArabicLetter ( text , index ) {
6980 return getNextArabicLetter ( text , index , - 1 ) ;
7081}
7182
83+ // Check if the character at the given index joins to the left.
7284export function joinsLeft ( word , index ) {
85+ // Get the current Letter, skipping combining marks.
7386 const c1 = getNextArabicLetter ( word , index ) ;
74- if ( ! c1 ) return false ;
75- if ( RIGHT_JOINING . includes ( c1 [ 0 ] ) ) return false ;
76- if ( ! getNextArabicLetter ( word , c1 [ 1 ] + 1 ) ) return false ;
87+ if ( ! c1 )
88+ return false ;
89+
90+ // If it is righ joining, then it does not join to the left.
91+ if ( RIGHT_JOINING . includes ( c1 [ 0 ] ) )
92+ return false ;
93+
94+ // Get the next Letter, skipping combining marks.
95+ if ( ! getNextArabicLetter ( word , c1 [ 1 ] + 1 ) )
96+ return false ;
97+
7798 return true ;
7899}
79100
101+ // Check if the character at the given index joins to the right.
80102export function joinsRight ( word , index ) {
103+ // Get the current Letter, skipping combining marks.
81104 const c1 = getPreviousArabicLetter ( word , index ) ;
82- if ( ! c1 ) return false ;
105+ if ( ! c1 )
106+ return false ;
107+
108+ // Get the previous Letter, skipping combining marks.
83109 const c2 = getPreviousArabicLetter ( word , c1 [ 1 ] - 1 ) ;
84- if ( ! c2 ) return false ;
85- if ( RIGHT_JOINING . includes ( c2 [ 0 ] ) ) return false ;
110+ if ( ! c2 )
111+ return false ;
112+
113+ // If it is righ joining, then it does not join to the left.
114+ if ( RIGHT_JOINING . includes ( c2 [ 0 ] ) )
115+ return false ;
116+
86117 return true ;
87118}
88119
120+ // Check if the character at the given index is a Lam Alef ligature.
89121function isLamAlef ( word , index ) {
90122 const c = word [ index ] ;
91123 if ( ALEF . includes ( c ) ) {
@@ -97,6 +129,9 @@ function isLamAlef(word, index) {
97129 return false ;
98130}
99131
132+ // Find Kashida insertion points in Arabic text using Microsoft's algorithm as
133+ // described in:
134+ // https://web.archive.org/web/20130308140133/microsoft.com/middleeast/msdn/JustifyingText-CSS.aspx
100135function findKashidaPointsSimple ( word ) {
101136 const kashidas = new Kashidas ( ) ;
102137
@@ -151,6 +186,12 @@ function findKashidaPointsSimple(word) {
151186 return kashidas ;
152187}
153188
189+ // Find Kashida insertion points in a word.
190+ //
191+ // Args:
192+ // word: A text word.
193+ // remove_existing_kashida: Remove existing Kashida characters.
194+ // algorithm: Kashida algorithm to use.
154195export function findKashidaPoints ( word , algorithm = Algorithm . SIMPLE , removeExistingKashida = true ) {
155196 if ( removeExistingKashida ) {
156197 word = word . replace ( KASHIDA_RE , '' ) ;
@@ -166,6 +207,13 @@ export function findKashidaPoints(word, algorithm = Algorithm.SIMPLE, removeExis
166207 return [ word , kashidas ] ;
167208}
168209
210+ // Insert kashida characters into a word.
211+ //
212+ // Args:
213+ // word: A text word.
214+ // kashidas: Kashida insertion points.
215+ // all_kashidas: Insert all possible Kashidas in a word, default is to
216+ // insert only the Kashida with the highest priority.
169217export function insertKashidas ( word , kashidas , allKashidas = false ) {
170218 if ( ! kashidas . length ) return word ;
171219
@@ -184,6 +232,14 @@ export function insertKashidas(word, kashidas, allKashidas = false) {
184232 return word ;
185233}
186234
235+ // Find possible Kashida points and insert them in a text string.
236+ //
237+ // Args:
238+ // text: A text string.
239+ // algorithm: Kashida algorithm to use.
240+ // remove_existing_kashida: Remove existing Kashida characters.
241+ // all_kashidas: Insert all possible Kashidas in a word, default is to
242+ // insert only the Kashida with the highest priority.
187243export function makeKashidaString ( text , algorithm = Algorithm . SIMPLE , removeExistingKashida = true , allKashidas = false ) {
188244 const words = text . split ( ' ' ) ;
189245 const ret = words . map ( word => {
0 commit comments