/src/fribidi/lib/fribidi-arabic.c
Line | Count | Source |
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, see: <https://www.gnu.org/licenses/>. |
19 | | * |
20 | | * For licensing issues, contact <fribidi.license@gmail.com> or write to |
21 | | * Sharif FarsiWeb, Inc., PO Box 13445-389, Tehran, Iran. |
22 | | * |
23 | | * Author(s): |
24 | | * Behdad Esfahbod, 2005 |
25 | | */ |
26 | | |
27 | | #include "common.h" |
28 | | |
29 | | #ifdef HAVE_CONFIG_H |
30 | | # include <config.h> |
31 | | #endif |
32 | | |
33 | | #ifdef HAVE_STDLIB_H |
34 | | # include <stdlib.h> |
35 | | #endif |
36 | | |
37 | | |
38 | | #include <fribidi-arabic.h> |
39 | | #include <fribidi-unicode.h> |
40 | | |
41 | | |
42 | | typedef struct _PairMap { |
43 | | FriBidiChar pair[2], to; |
44 | | } PairMap; |
45 | | |
46 | | |
47 | 0 | #define FRIBIDI_ACCESS_SHAPE_TABLE(table,min,max,x,shape) (table), (min), (max) |
48 | | # define FRIBIDI_ACCESS_SHAPE_TABLE_REAL(table,min,max,x,shape) \ |
49 | 0 | (((x)<(min)||(x)>(max))?(x):(table)[(x)-(min)][(shape)]) |
50 | | |
51 | | #include "arabic-shaping.tab.i" |
52 | | #include "arabic-misc.tab.i" |
53 | | |
54 | | |
55 | | static void |
56 | | fribidi_shape_arabic_joining ( |
57 | | /* input */ |
58 | | const FriBidiChar table[][4], |
59 | | FriBidiChar min, |
60 | | FriBidiChar max, |
61 | | const FriBidiStrIndex len, |
62 | | const FriBidiArabicProp *ar_props, |
63 | | /* input and output */ |
64 | | FriBidiChar *str |
65 | | ) |
66 | 0 | { |
67 | 0 | register FriBidiStrIndex i; |
68 | |
|
69 | 0 | for (i = 0; i < len; i++) |
70 | 0 | if (FRIBIDI_ARAB_SHAPES(ar_props[i])) |
71 | 0 | str[i] = FRIBIDI_ACCESS_SHAPE_TABLE_REAL (table, min, max, str[i], FRIBIDI_JOIN_SHAPE (ar_props[i])); |
72 | 0 | } |
73 | | |
74 | | |
75 | | |
76 | | static int |
77 | | comp_PairMap (const void *pa, const void *pb) |
78 | 0 | { |
79 | 0 | PairMap *a = (PairMap *)pa; |
80 | 0 | PairMap *b = (PairMap *)pb; |
81 | |
|
82 | 0 | if (a->pair[0] != b->pair[0]) |
83 | 0 | return a->pair[0] < b->pair[0] ? -1 : +1; |
84 | 0 | else |
85 | 0 | return a->pair[1] < b->pair[1] ? -1 : |
86 | 0 | a->pair[1] > b->pair[1] ? +1 : |
87 | 0 | 0; |
88 | 0 | } |
89 | | |
90 | | static void * |
91 | | fribidi_bsearch (const void *key, const void *base, |
92 | | unsigned int nmemb, unsigned int size, |
93 | | int (*compar)(const void *_key, const void *_item)) |
94 | 0 | { |
95 | 0 | int min = 0, max = (int) nmemb - 1; |
96 | 0 | while (min <= max) |
97 | 0 | { |
98 | 0 | int mid = ((unsigned int) min + (unsigned int) max) / 2; |
99 | 0 | const void *p = (const void *) (((const char *) base) + (mid * size)); |
100 | 0 | int c = compar (key, p); |
101 | 0 | if (c < 0) |
102 | 0 | max = mid - 1; |
103 | 0 | else if (c > 0) |
104 | 0 | min = mid + 1; |
105 | 0 | else |
106 | 0 | return (void *) p; |
107 | 0 | } |
108 | 0 | return NULL; |
109 | 0 | } |
110 | | |
111 | | static FriBidiChar |
112 | | find_pair_match (const PairMap *table, int size, FriBidiChar first, FriBidiChar second) |
113 | 0 | { |
114 | 0 | PairMap *match; |
115 | 0 | PairMap x; |
116 | 0 | x.pair[0] = first; |
117 | 0 | x.pair[1] = second; |
118 | 0 | x.to = 0; |
119 | 0 | match = fribidi_bsearch (&x, table, size, sizeof (table[0]), comp_PairMap); |
120 | 0 | return match ? match->to : 0; |
121 | 0 | } |
122 | | |
123 | | #define PAIR_MATCH(table,len,first,second) \ |
124 | 0 | ((first)<(table[0].pair[0])||(first)>(table[len-1].pair[0])?0: \ |
125 | 0 | find_pair_match(table, len, first, second)) |
126 | | |
127 | | static void |
128 | | fribidi_shape_arabic_ligature ( |
129 | | /* input */ |
130 | | const PairMap *table, |
131 | | int size, |
132 | | const FriBidiLevel *embedding_levels, |
133 | | const FriBidiStrIndex len, |
134 | | /* input and output */ |
135 | | FriBidiArabicProp *ar_props, |
136 | | FriBidiChar *str |
137 | | ) |
138 | 0 | { |
139 | | /* TODO: This doesn't form ligatures for even-level Arabic text. |
140 | | * no big problem though. */ |
141 | 0 | register FriBidiStrIndex i; |
142 | |
|
143 | 0 | for (i = 0; i < len - 1; i++) { |
144 | 0 | register FriBidiChar c; |
145 | 0 | if (FRIBIDI_LEVEL_IS_RTL(embedding_levels[i]) && |
146 | 0 | embedding_levels[i] == embedding_levels[i+1] && |
147 | 0 | (c = PAIR_MATCH(table, size, str[i], str[i+1]))) |
148 | 0 | { |
149 | 0 | str[i] = FRIBIDI_CHAR_FILL; |
150 | 0 | FRIBIDI_SET_BITS(ar_props[i], FRIBIDI_MASK_LIGATURED); |
151 | 0 | str[i+1] = c; |
152 | 0 | } |
153 | 0 | } |
154 | 0 | } |
155 | | |
156 | | #define DO_LIGATURING(table, levels, len, ar_props, str) \ |
157 | 0 | fribidi_shape_arabic_ligature ((table), sizeof(table)/sizeof((table)[0]), levels, len, ar_props, str) |
158 | | |
159 | | #define DO_SHAPING(tablemacro, len, ar_props, str) \ |
160 | 0 | fribidi_shape_arabic_joining (tablemacro(,), len, ar_props, str); |
161 | | |
162 | | |
163 | | |
164 | | |
165 | | FRIBIDI_ENTRY void |
166 | | fribidi_shape_arabic ( |
167 | | /* input */ |
168 | | FriBidiFlags flags, |
169 | | const FriBidiLevel *embedding_levels, |
170 | | const FriBidiStrIndex len, |
171 | | /* input and output */ |
172 | | FriBidiArabicProp *ar_props, |
173 | | FriBidiChar *str |
174 | | ) |
175 | 0 | { |
176 | 0 | DBG ("in fribidi_shape_arabic"); |
177 | |
|
178 | 0 | if UNLIKELY |
179 | 0 | (len == 0 || !str) return; |
180 | | |
181 | 0 | DBG ("in fribidi_shape"); |
182 | |
|
183 | 0 | fribidi_assert (ar_props); |
184 | |
|
185 | 0 | if (FRIBIDI_TEST_BITS (flags, FRIBIDI_FLAG_SHAPE_ARAB_PRES)) |
186 | 0 | { |
187 | 0 | DO_SHAPING (FRIBIDI_GET_ARABIC_SHAPE_PRES, len, ar_props, str); |
188 | 0 | } |
189 | |
|
190 | 0 | if (FRIBIDI_TEST_BITS (flags, FRIBIDI_FLAG_SHAPE_ARAB_LIGA)) |
191 | 0 | { |
192 | 0 | DO_LIGATURING (mandatory_liga_table, embedding_levels, len, ar_props, str); |
193 | 0 | } |
194 | |
|
195 | 0 | if (FRIBIDI_TEST_BITS (flags, FRIBIDI_FLAG_SHAPE_ARAB_CONSOLE)) |
196 | 0 | { |
197 | 0 | DO_LIGATURING (console_liga_table, embedding_levels, len, ar_props, str); |
198 | 0 | DO_SHAPING (FRIBIDI_GET_ARABIC_SHAPE_NSM, len, ar_props, str); |
199 | 0 | } |
200 | 0 | } |
201 | | |
202 | | /* Editor directions: |
203 | | * Local Variables: |
204 | | * mode: c |
205 | | * c-basic-offset: 2 |
206 | | * indent-tabs-mode: t |
207 | | * tab-width: 8 |
208 | | * End: |
209 | | * vim: textwidth=78: autoindent: cindent: shiftwidth=2: tabstop=8: |
210 | | */ |