1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2
3 #include "vt_bidi.h"
4
5 #include <string.h> /* memset */
6 #include <ctype.h> /* isalpha */
7 #include <pobl/bl_debug.h>
8 #include <pobl/bl_mem.h> /* alloca/realloc/free */
9 #include <fribidi.h>
10
11 #if 0
12 #define __DEBUG
13 #endif
14
15 #define DIR_LTR_MARK 0x200e
16 #define DIR_RTL_MARK 0x200f
17
18 /* --- global functions --- */
19
vt_bidi_new(void)20 vt_bidi_state_t vt_bidi_new(void) {
21 vt_bidi_state_t state;
22
23 if ((state = malloc(sizeof(*state))) == NULL) {
24 return NULL;
25 }
26
27 state->visual_order = NULL;
28 state->size = 0;
29 state->rtl_state = 0;
30 state->bidi_mode = BIDI_NORMAL_MODE;
31
32 return state;
33 }
34
vt_bidi_destroy(vt_bidi_state_t state)35 int vt_bidi_destroy(vt_bidi_state_t state) {
36 free(state->visual_order);
37 free(state);
38
39 return 1;
40 }
41
42 /*
43 * Don't call this functions with type_p == FRIBIDI_TYPE_ON and size == cur_pos.
44 */
log2vis(FriBidiChar * str,u_int size,FriBidiCharType * type_p,vt_bidi_mode_t bidi_mode,FriBidiStrIndex * order,u_int cur_pos,int append)45 static void log2vis(FriBidiChar *str, u_int size, FriBidiCharType *type_p, vt_bidi_mode_t bidi_mode,
46 FriBidiStrIndex *order, u_int cur_pos, int append) {
47 FriBidiCharType type;
48 u_int pos;
49
50 if (size > cur_pos) {
51 if (bidi_mode == BIDI_NORMAL_MODE) {
52 type = FRIBIDI_TYPE_ON;
53 } else if (bidi_mode == BIDI_ALWAYS_RIGHT) {
54 type = FRIBIDI_TYPE_RTL;
55 } else /* if (bidi_mode == BIDI_ALWAYS_LEFT) */ {
56 type = FRIBIDI_TYPE_LTR;
57 }
58
59 fribidi_log2vis(str + cur_pos, size - cur_pos, &type, NULL, order + cur_pos, NULL, NULL);
60
61 if (*type_p == FRIBIDI_TYPE_ON) {
62 *type_p = type;
63 }
64 } else {
65 /*
66 * This functions is never called if type_p == FRIBIDI_TYPE_ON and
67 * size == cur_pos.
68 */
69 type = *type_p;
70 }
71
72 if (*type_p == FRIBIDI_TYPE_LTR) {
73 if (type == FRIBIDI_TYPE_RTL) {
74 /*
75 * (Logical) "LLL/RRRNNN " ('/' is a separator)
76 * ^-> endsp
77 * => (Visual) "LLL/ NNNRRR" => "LLL/NNNRRR "
78 */
79
80 u_int endsp_num;
81
82 for (pos = size; pos > cur_pos; pos--) {
83 if (str[pos - 1] != ' ') {
84 break;
85 }
86
87 order[pos - 1] = pos - 1;
88 }
89
90 endsp_num = size - pos;
91
92 for (pos = cur_pos; pos < size - endsp_num; pos++) {
93 order[pos] = order[pos] + cur_pos - endsp_num;
94 }
95 } else if (cur_pos > 0) {
96 for (pos = cur_pos; pos < size; pos++) {
97 order[pos] += cur_pos;
98 }
99 }
100
101 if (append) {
102 order[size] = size;
103 }
104 } else /* if (*type_p == FRIBIDI_TYPE_RTL) */ {
105 if (cur_pos > 0) {
106 for (pos = 0; pos < cur_pos; pos++) {
107 order[pos] += (size - cur_pos);
108 }
109 }
110
111 if (type == FRIBIDI_TYPE_LTR) {
112 /*
113 * (Logical) "RRRNNN/LLL " ('/' is a separator)
114 * ^-> endsp
115 * => (Visual) "LLL /NNNRRR" => " LLL/NNNRRR"
116 */
117
118 u_int endsp_num;
119
120 for (pos = size; pos > cur_pos; pos--) {
121 if (str[pos - 1] != ' ') {
122 break;
123 }
124
125 order[pos - 1] = size - pos;
126 }
127
128 endsp_num = size - pos;
129 for (pos = cur_pos; pos < size - endsp_num; pos++) {
130 order[pos] += endsp_num;
131 }
132 }
133
134 if (append) {
135 for (pos = 0; pos < size; pos++) {
136 order[pos]++;
137 }
138
139 order[size] = 0;
140 }
141 }
142 }
143
log2log(FriBidiStrIndex * order,u_int cur_pos,u_int size)144 static void log2log(FriBidiStrIndex *order, u_int cur_pos, u_int size) {
145 u_int pos;
146
147 for (pos = cur_pos; pos < size; pos++) {
148 order[pos] = pos;
149 }
150 }
151
vt_bidi(vt_bidi_state_t state,vt_char_t * src,u_int size,vt_bidi_mode_t bidi_mode,const char * separators)152 int vt_bidi(vt_bidi_state_t state, vt_char_t *src, u_int size, vt_bidi_mode_t bidi_mode,
153 const char *separators) {
154 FriBidiChar *fri_src;
155 FriBidiCharType fri_type;
156 FriBidiStrIndex *fri_order;
157 u_int cur_pos;
158 ef_charset_t cs;
159 u_int32_t code;
160 u_int count;
161 int ret;
162
163 state->rtl_state = 0;
164
165 if (size == 0) {
166 state->size = 0;
167
168 return 0;
169 }
170
171 if ((fri_src = alloca(sizeof(FriBidiChar) * size)) == NULL) {
172 #ifdef DEBUG
173 bl_warn_printf(BL_DEBUG_TAG " alloca() failed.\n");
174 #endif
175
176 return 0;
177 }
178
179 if ((fri_order = alloca(sizeof(FriBidiStrIndex) * size)) == NULL) {
180 #ifdef DEBUG
181 bl_warn_printf(BL_DEBUG_TAG " alloca() failed.\n");
182 #endif
183
184 return 0;
185 }
186
187 fri_type = FRIBIDI_TYPE_ON;
188
189 if (bidi_mode == BIDI_ALWAYS_RIGHT) {
190 SET_HAS_RTL(state);
191 }
192
193 for (count = 0, cur_pos = 0; count < size; count++) {
194 cs = vt_char_cs(&src[count]);
195 code = vt_char_code(&src[count]);
196
197 if (cs == US_ASCII) {
198 if (!isalpha(code)) {
199 if (vt_get_picture_char(&src[count])) {
200 fri_src[count] = 'a';
201 } else if (separators && strchr(separators, code)) {
202 if (HAS_RTL(state)) {
203 log2vis(fri_src, count, &fri_type, bidi_mode, fri_order, cur_pos, 1);
204 } else {
205 fri_type = FRIBIDI_TYPE_LTR;
206 log2log(fri_order, cur_pos, count + 1);
207 }
208
209 cur_pos = count + 1;
210 } else {
211 fri_src[count] = code;
212 }
213 } else {
214 fri_src[count] = code;
215 }
216 } else if (cs == ISO10646_UCS4_1) {
217 if (0x2500 <= code && code <= 0x259f) {
218 goto decsp;
219 } else {
220 fri_src[count] = code;
221
222 if (!HAS_RTL(state) && (fribidi_get_type(fri_src[count]) & FRIBIDI_MASK_RTL)) {
223 SET_HAS_RTL(state);
224 }
225 }
226 } else if (cs == DEC_SPECIAL) {
227 decsp:
228 bidi_mode = BIDI_ALWAYS_LEFT;
229
230 if (HAS_RTL(state)) {
231 log2vis(fri_src, count, &fri_type, bidi_mode, fri_order, cur_pos, 1);
232 } else {
233 log2log(fri_order, cur_pos, count + 1);
234 }
235
236 cur_pos = count + 1;
237 } else if (IS_ISCII(cs)) {
238 return -2; /* iscii */
239 } else {
240 #ifdef __DEBUG
241 bl_debug_printf(BL_DEBUG_TAG " %x is not ucs.\n", cs);
242 #endif
243
244 /*
245 * Regarded as NEUTRAL character.
246 */
247 fri_src[count] = ' ';
248 }
249 }
250
251 if (HAS_RTL(state)) {
252 log2vis(fri_src, size, &fri_type, bidi_mode, fri_order, cur_pos, 0);
253
254 count = 0;
255
256 if (state->size != size) {
257 void *p;
258
259 if (!(p = realloc(state->visual_order, sizeof(u_int16_t) * size))) {
260 #ifdef DEBUG
261 bl_warn_printf(BL_DEBUG_TAG " realloc() failed.\n");
262 #endif
263
264 state->size = 0;
265
266 return 0;
267 }
268
269 state->visual_order = p;
270 state->size = size;
271
272 ret = 2; /* order is changed */
273 } else {
274 ret = 1; /* order is not changed */
275
276 for (; count < size; count++) {
277 if (state->visual_order[count] != fri_order[count]) {
278 ret = 2; /* order_is_changed */
279 break;
280 }
281 }
282 }
283
284 for (; count < size; count++) {
285 state->visual_order[count] = fri_order[count];
286 }
287
288 #ifdef __DEBUG
289 bl_msg_printf("utf8 text => \n");
290 for (count = 0; count < size; count++) {
291 bl_msg_printf("%.4x ", fri_src[count]);
292 }
293 bl_msg_printf("\n");
294
295 bl_msg_printf("visual order => ");
296 for (count = 0; count < size; count++) {
297 bl_msg_printf("%.2d ", state->visual_order[count]);
298 }
299 bl_msg_printf("\n");
300 #endif
301
302 #ifdef DEBUG
303 for (count = 0; count < size; count++) {
304 if (state->visual_order[count] >= size) {
305 bl_warn_printf(BL_DEBUG_TAG " visual order(%d) of %d is illegal.\n",
306 state->visual_order[count], count);
307
308 bl_msg_printf("returned order => ");
309 for (count = 0; count < size; count++) {
310 bl_msg_printf("%d ", state->visual_order[count]);
311 }
312 bl_msg_printf("\n");
313
314 abort();
315 }
316 }
317 #endif
318 } else {
319 state->size = 0;
320
321 return -1; /* ot layout */
322 }
323
324 if (fri_type == FRIBIDI_TYPE_RTL) {
325 SET_BASE_RTL(state);
326 }
327
328 state->bidi_mode = bidi_mode;
329
330 return ret;
331 }
332
vt_bidi_copy(vt_bidi_state_t dst,vt_bidi_state_t src,int optimize)333 int vt_bidi_copy(vt_bidi_state_t dst, vt_bidi_state_t src, int optimize) {
334 u_int16_t *p;
335
336 if (optimize && !HAS_RTL(src)) {
337 vt_bidi_destroy(dst);
338
339 return -1;
340 } else if (src->size == 0) {
341 free(dst->visual_order);
342 p = NULL;
343 } else if ((p = realloc(dst->visual_order, sizeof(u_int16_t) * src->size))) {
344 memcpy(p, src->visual_order, sizeof(u_int16_t) * src->size);
345 } else {
346 return 0;
347 }
348
349 dst->visual_order = p;
350 dst->size = src->size;
351 dst->rtl_state = src->rtl_state;
352 dst->bidi_mode = src->bidi_mode;
353
354 return 1;
355 }
356
vt_bidi_reset(vt_bidi_state_t state)357 int vt_bidi_reset(vt_bidi_state_t state) {
358 state->size = 0;
359
360 return 1;
361 }
362
vt_bidi_get_mirror_char(u_int32_t ch)363 u_int32_t vt_bidi_get_mirror_char(u_int32_t ch) {
364 FriBidiChar mirror;
365
366 if (fribidi_get_mirror_char(ch, &mirror)) {
367 return mirror;
368 } else {
369 return 0;
370 }
371 }
372
vt_is_rtl_char(u_int32_t ch)373 int vt_is_rtl_char(u_int32_t ch) {
374 return (fribidi_get_bidi_type(ch) & FRIBIDI_MASK_RTL) == FRIBIDI_MASK_RTL;
375 }
376