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 (key: &x, base: table, nmemb: size, size: sizeof (table[0]), compar: 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 | */ |
212 | |