Imported Upstream version 1.0.10
[platform/upstream/fribidi.git] / lib / fribidi-arabic.c
1 /* fribidi-arabic.c - Arabic shaping
2  *
3  * Copyright (C) 2005  Behdad Esfahbod
4  *
5  * This file is part of GNU FriBidi.
6  * 
7  * GNU FriBidi is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * as published by the Free Software Foundation; either version 2.1
10  * of the License, or (at your option) any later version.
11  * 
12  * GNU FriBidi is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Lesser General Public License for more details.
16  * 
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with GNU FriBidi; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  * 
21  * For licensing issues, contact <fribidi.license@gmail.com> or write to
22  * Sharif FarsiWeb, Inc., PO Box 13445-389, Tehran, Iran.
23  *
24  * Author(s):
25  *   Behdad Esfahbod, 2005
26  */
27
28 #include "common.h"
29
30 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
33
34 #ifdef HAVE_STDLIB_H
35 # include <stdlib.h>
36 #endif
37
38
39 #include <fribidi-arabic.h>
40 #include <fribidi-unicode.h>
41
42
43 typedef struct _PairMap {
44   FriBidiChar pair[2], to;
45 } PairMap;
46
47
48 #define FRIBIDI_ACCESS_SHAPE_TABLE(table,min,max,x,shape) (table), (min), (max)
49 # define FRIBIDI_ACCESS_SHAPE_TABLE_REAL(table,min,max,x,shape) \
50         (((x)<(min)||(x)>(max))?(x):(table)[(x)-(min)][(shape)])
51
52 #include "arabic-shaping.tab.i"
53 #include "arabic-misc.tab.i"
54
55
56 static void
57 fribidi_shape_arabic_joining (
58   /* input */
59   const FriBidiChar table[][4],
60   FriBidiChar min,
61   FriBidiChar max,
62   const FriBidiStrIndex len,
63   const FriBidiArabicProp *ar_props,
64   /* input and output */
65   FriBidiChar *str
66 )
67 {
68   register FriBidiStrIndex i;
69
70   for (i = 0; i < len; i++)
71     if (FRIBIDI_ARAB_SHAPES(ar_props[i]))
72       str[i] = FRIBIDI_ACCESS_SHAPE_TABLE_REAL (table, min, max, str[i], FRIBIDI_JOIN_SHAPE (ar_props[i]));
73 }
74
75
76
77 static int
78 comp_PairMap (const void *pa, const void *pb)
79 {
80   PairMap *a = (PairMap *)pa;
81   PairMap *b = (PairMap *)pb;
82
83   if (a->pair[0] != b->pair[0])
84     return a->pair[0] < b->pair[0] ? -1 : +1;
85   else
86     return a->pair[1] < b->pair[1] ? -1 :
87            a->pair[1] > b->pair[1] ? +1 :
88            0;
89 }
90
91 static void *
92 fribidi_bsearch (const void *key, const void *base,
93                  unsigned int nmemb, unsigned int size,
94                  int (*compar)(const void *_key, const void *_item))
95 {
96   int min = 0, max = (int) nmemb - 1;
97   while (min <= max)
98   {
99     int mid = ((unsigned int) min + (unsigned int) max) / 2;
100     const void *p = (const void *) (((const char *) base) + (mid * size));
101     int c = compar (key, p);
102     if (c < 0)
103       max = mid - 1;
104     else if (c > 0)
105       min = mid + 1;
106     else
107       return (void *) p;
108   }
109   return NULL;
110 }
111
112 static FriBidiChar
113 find_pair_match (const PairMap *table, int size, FriBidiChar first, FriBidiChar second)
114 {
115   PairMap *match;
116   PairMap x;
117   x.pair[0] = first;
118   x.pair[1] = second;
119   x.to = 0;
120   match = fribidi_bsearch (&x, table, size, sizeof (table[0]), comp_PairMap);
121   return match ? match->to : 0;
122 }
123
124 #define PAIR_MATCH(table,len,first,second) \
125         ((first)<(table[0].pair[0])||(first)>(table[len-1].pair[0])?0: \
126          find_pair_match(table, len, first, second))
127
128 static void
129 fribidi_shape_arabic_ligature (
130   /* input */
131   const PairMap *table,
132   int size,
133   const FriBidiLevel *embedding_levels,
134   const FriBidiStrIndex len,
135   /* input and output */
136   FriBidiArabicProp *ar_props,
137   FriBidiChar *str
138 )
139 {
140   /* TODO: This doesn't form ligatures for even-level Arabic text.
141    * no big problem though. */
142   register FriBidiStrIndex i;
143
144   for (i = 0; i < len - 1; i++) {
145     register FriBidiChar c;
146     if (FRIBIDI_LEVEL_IS_RTL(embedding_levels[i]) &&
147         embedding_levels[i] == embedding_levels[i+1] &&
148         (c = PAIR_MATCH(table, size, str[i], str[i+1])))
149       {
150         str[i] = FRIBIDI_CHAR_FILL;
151         FRIBIDI_SET_BITS(ar_props[i], FRIBIDI_MASK_LIGATURED);
152         str[i+1] = c;
153       }
154   }
155 }
156
157 #define DO_LIGATURING(table, levels, len, ar_props, str) \
158         fribidi_shape_arabic_ligature ((table), sizeof(table)/sizeof((table)[0]), levels, len, ar_props, str)
159
160 #define DO_SHAPING(tablemacro, len, ar_props, str) \
161         fribidi_shape_arabic_joining (tablemacro(,), len, ar_props, str);
162         
163
164
165
166 FRIBIDI_ENTRY void
167 fribidi_shape_arabic (
168   /* input */
169   FriBidiFlags flags,
170   const FriBidiLevel *embedding_levels,
171   const FriBidiStrIndex len,
172   /* input and output */
173   FriBidiArabicProp *ar_props,
174   FriBidiChar *str
175 )
176 {
177   DBG ("in fribidi_shape_arabic");
178
179   if UNLIKELY
180     (len == 0 || !str) return;
181
182   DBG ("in fribidi_shape");
183
184   fribidi_assert (ar_props);
185
186   if (FRIBIDI_TEST_BITS (flags, FRIBIDI_FLAG_SHAPE_ARAB_PRES))
187     {
188       DO_SHAPING (FRIBIDI_GET_ARABIC_SHAPE_PRES, len, ar_props, str);
189     }
190
191   if (FRIBIDI_TEST_BITS (flags, FRIBIDI_FLAG_SHAPE_ARAB_LIGA))
192     {
193       DO_LIGATURING (mandatory_liga_table, embedding_levels, len, ar_props, str);
194     }
195
196   if (FRIBIDI_TEST_BITS (flags, FRIBIDI_FLAG_SHAPE_ARAB_CONSOLE))
197     {
198       DO_LIGATURING (console_liga_table, embedding_levels, len, ar_props, str);
199       DO_SHAPING (FRIBIDI_GET_ARABIC_SHAPE_NSM, len, ar_props, str);
200     }
201 }
202
203 /* Editor directions:
204  * Local Variables:
205  *   mode: c
206  *   c-basic-offset: 2
207  *   indent-tabs-mode: t
208  *   tab-width: 8
209  * End:
210  * vim: textwidth=78: autoindent: cindent: shiftwidth=2: tabstop=8:
211  */