aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/evas/src/lib/engines/common/language/evas_bidi_utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/evas/src/lib/engines/common/language/evas_bidi_utils.c')
-rw-r--r--libraries/evas/src/lib/engines/common/language/evas_bidi_utils.c645
1 files changed, 0 insertions, 645 deletions
diff --git a/libraries/evas/src/lib/engines/common/language/evas_bidi_utils.c b/libraries/evas/src/lib/engines/common/language/evas_bidi_utils.c
deleted file mode 100644
index 002cfbe..0000000
--- a/libraries/evas/src/lib/engines/common/language/evas_bidi_utils.c
+++ /dev/null
@@ -1,645 +0,0 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#include <string.h>
6#include <stdlib.h>
7
8#include "evas_common.h"
9#include "evas_bidi_utils.h"
10
11#include "evas_font_private.h"
12
13#ifdef BIDI_SUPPORT
14#include <fribidi/fribidi.h>
15/**
16 * @internal
17 * @addtogroup Evas_Utils
18 *
19 * @{
20 */
21/**
22 * @internal
23 * @addtogroup Evas_BiDi
24 *
25 * @{
26 */
27
28/**
29 * @internal
30 * @def _SAFE_FREE(x)
31 * checks if x is not NULL, if it's not, it's freed and set to NULL.
32 */
33#define _SAFE_FREE(x) \
34 do { \
35 if (x) \
36 { \
37 free(x); \
38 x = NULL; \
39 } \
40 } while(0)
41
42#if SIZEOF_FRIBIDICHAR != SIZEOF_EINA_UNICODE
43# define EVAS_FRIBIDI_EINA_UNICODE_UNEQUAL
44#endif
45
46#ifdef EVAS_FRIBIDI_EINA_UNICODE_UNEQUAL
47/* Convert bidichar to eina_unicode assume both are valid pointers */
48static Eina_Unicode *
49_evas_bidi_fribidichar_to_unicode(Eina_Unicode *dest, const FriBidiChar *src)
50{
51 Eina_Unicode *ret = dest;
52
53 while (*src)
54 *dest++ = *src++;
55 *dest = 0;
56 return ret;
57}
58
59/* Convert eina_unicode to bidi_char assume both are valid pointers */
60static FriBidiChar *
61_evas_bidi_unicode_to_fribidichar(FriBidiChar *dest, const Eina_Unicode *src)
62{
63 FriBidiChar *ret = dest;
64
65 while (*src)
66 *dest++ = *src++;
67 *dest = 0;
68 return ret;
69}
70#endif
71
72/**
73 * @internal
74 * Checks if the string has RTL characters.
75 *
76 * @param str The string to be checked
77 * @return #EINA_TRUE if true, #EINA_FALSE otherwise.
78 */
79Eina_Bool
80evas_bidi_is_rtl_str(const Eina_Unicode *str)
81{
82 EvasBiDiCharType type;
83
84 if (!str)
85 return EINA_FALSE;
86
87 for ( ; *str ; str++)
88 {
89 type = fribidi_get_bidi_type((FriBidiChar) *str);
90 if (FRIBIDI_IS_LETTER(type) && FRIBIDI_IS_RTL(type))
91 {
92 return EINA_TRUE;
93 }
94 }
95 return EINA_FALSE;
96}
97
98/**
99 * @internal
100 * Shapes the string ustr according to the bidi properties.
101 *
102 * @param str The string to shape
103 * @param bidi_props the bidi props to shaped according.
104 * @param start the start of the string to shape (offset in bidi_props)
105 * @param len the length of th string.
106 * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
107 */
108EAPI Eina_Bool
109evas_bidi_shape_string(Eina_Unicode *eina_ustr, const Evas_BiDi_Paragraph_Props *bidi_props, size_t start, size_t len)
110{
111 FriBidiChar *ustr, *base_ustr = NULL;
112
113 if (!bidi_props)
114 return EINA_FALSE;
115
116 /* The size of fribidichar is different than eina_unicode, convert */
117#ifdef EVAS_FRIBIDI_EINA_UNICODE_UNEQUAL
118 base_ustr = ustr = calloc(len + 1, sizeof(FriBidiChar));
119 ustr = _evas_bidi_unicode_to_fribidichar(ustr, eina_ustr);
120#else
121 (void) base_ustr;
122 ustr = (FriBidiChar *) eina_ustr;
123#endif
124
125
126 EvasBiDiJoiningType *join_types = NULL;
127 join_types = (EvasBiDiJoiningType *) malloc(sizeof(EvasBiDiJoiningType) * len);
128 if (!join_types)
129 {
130 return EINA_FALSE;
131 }
132 fribidi_get_joining_types(ustr, len, join_types);
133
134 fribidi_join_arabic(bidi_props->char_types + start, len,
135 bidi_props->embedding_levels + start, join_types);
136
137
138 /* Actually modify the string */
139 fribidi_shape(FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC,
140 bidi_props->embedding_levels + start, len, join_types, ustr);
141
142 if (join_types) free(join_types);
143
144 /* Convert back */
145#ifdef EVAS_FRIBIDI_EINA_UNICODE_UNEQUAL
146 eina_ustr = _evas_bidi_fribidichar_to_unicode(eina_ustr, ustr);
147 if (base_ustr) free(base_ustr);
148#endif
149 return EINA_TRUE;
150}
151
152/**
153 * @internal
154 * Return a -1 terminated array of the indexes of the delimiters (passed in
155 * delim) found in the string. This result should be used with par_props_get.
156 *
157 * @param str The string to parse
158 * @param delim a list of delimiters to work with.
159 * @return returns a -1 terminated array of indexes according to positions of the delimiters found. NULL if there were none.
160 */
161int *
162evas_bidi_segment_idxs_get(const Eina_Unicode *str, const char *delim)
163{
164 Eina_Unicode *udelim;
165 const Eina_Unicode *str_base = str;
166 int *ret, *tmp_ret;
167 int ret_idx = 0, ret_len = 10; /* arbitrary choice */
168 udelim = eina_unicode_utf8_to_unicode(delim, NULL);
169 ret = malloc(ret_len * sizeof(int));
170 for ( ; *str ; str++)
171 {
172 const Eina_Unicode *del;
173 for (del = udelim ; *del ; del++)
174 {
175 if (*str == *del)
176 {
177 if (ret_idx >= ret_len)
178 {
179 /* arbitrary choice */
180 ret_len += 20;
181 tmp_ret = realloc(ret, ret_len * sizeof(int));
182 if (!tmp_ret)
183 {
184 free(ret);
185 return NULL;
186 }
187 }
188 ret[ret_idx++] = str - str_base;
189 break;
190 }
191 }
192 }
193 free(udelim);
194
195 /* If no indexes were found return NULL */
196 if (ret_idx == 0)
197 {
198 free(ret);
199 return NULL;
200 }
201
202 ret[ret_idx] = -1;
203 tmp_ret = realloc(ret, (ret_idx + 1) * sizeof(int));
204
205 return (tmp_ret) ? tmp_ret : ret;
206}
207
208/**
209 * @internal
210 * Allocates bidi properties according to ustr. First checks to see if the
211 * passed has rtl chars, if not, it returns NULL.
212 *
213 * Assumes all the segment_idxs are either -1 or legal, and > 0 indexes.
214 * Also assumes that the characters at the override points are of weak/neutral
215 * bidi type, otherwise unexpected results may occur.
216 *
217 * @param ustr The string to update according to.
218 * @param len The length of the string
219 * @param segment_idxs A -1 terminated array of points to start a new bidi analysis at (used for section high level bidi overrides). - NULL means none.
220 * @return returns allocated paragraph props on success, NULL otherwise.
221 */
222Evas_BiDi_Paragraph_Props *
223evas_bidi_paragraph_props_get(const Eina_Unicode *eina_ustr, size_t len,
224 int *segment_idxs)
225{
226 Evas_BiDi_Paragraph_Props *bidi_props = NULL;
227 EvasBiDiCharType *char_types = NULL;
228 EvasBiDiLevel *embedding_levels = NULL;
229 const FriBidiChar *ustr;
230 FriBidiChar *base_ustr = NULL;
231
232 if (!eina_ustr)
233 return NULL;
234
235
236 if (!evas_bidi_is_rtl_str(eina_ustr)) /* No need to handle bidi */
237 {
238 len = -1;
239 goto cleanup;
240 }
241
242 len = eina_unicode_strlen(eina_ustr);
243 /* The size of fribidichar s different than eina_unicode, convert */
244#ifdef EVAS_FRIBIDI_EINA_UNICODE_UNEQUAL
245 base_ustr = calloc(len + 1, sizeof(FriBidiChar));
246 base_ustr = _evas_bidi_unicode_to_fribidichar(base_ustr, eina_ustr);
247 ustr = base_ustr;
248#else
249 ustr = (const FriBidiChar *) eina_ustr;
250#endif
251
252 bidi_props = evas_bidi_paragraph_props_new();
253
254 /* Prep work for reordering */
255 char_types = (EvasBiDiCharType *) malloc(sizeof(EvasBiDiCharType) * len);
256 if (!char_types)
257 {
258 len = -2;
259 goto cleanup;
260 }
261 fribidi_get_bidi_types(ustr, len, char_types);
262
263 embedding_levels = (EvasBiDiLevel *)malloc(sizeof(EvasBiDiLevel) * len);
264 if (!embedding_levels)
265 {
266 len = -2;
267 goto cleanup;
268 }
269
270 if (segment_idxs)
271 {
272 size_t pos = 0;
273 int *itr;
274 EvasBiDiLevel base_level = 0;
275 EvasBiDiParType direction;
276
277 for (itr = segment_idxs ; *itr > 0 ; itr++)
278 {
279 direction = EVAS_BIDI_PARAGRAPH_NEUTRAL;
280 if (!fribidi_get_par_embedding_levels(char_types + pos,
281 *itr - pos,
282 &direction,
283 embedding_levels + pos))
284 {
285 len = -2;
286 goto cleanup;
287 }
288
289 /* Only on the first run */
290 if (itr == segment_idxs)
291 {
292 bidi_props->direction = direction;
293 /* adjust base_level to be 1 for rtl paragraphs, and 0 for
294 * ltr paragraphs. */
295 base_level =
296 EVAS_BIDI_PARAGRAPH_DIRECTION_IS_RTL(bidi_props) ? 1 : 0;
297 }
298
299 /* We want those chars at the override points to be on the base
300 * level and we also remove -2 cause we later increment them,
301 * just for simpler code paths */
302 embedding_levels[*itr] = base_level - 2;
303 pos = *itr + 1;
304 }
305
306 direction = EVAS_BIDI_PARAGRAPH_NEUTRAL;
307 if (!fribidi_get_par_embedding_levels(char_types + pos,
308 len - pos,
309 &direction,
310 embedding_levels + pos))
311 {
312 len = -2;
313 goto cleanup;
314 }
315
316 /* Increment all levels by 2 to emulate embedding. */
317 {
318 EvasBiDiLevel *bitr = embedding_levels, *end;
319 end = bitr + len;
320 for ( ; bitr < end ; bitr++)
321 {
322 *bitr += 2;
323 }
324 }
325 }
326 else
327 {
328 if (!fribidi_get_par_embedding_levels(char_types, len,
329 &bidi_props->direction, embedding_levels))
330 {
331 len = -2;
332 goto cleanup;
333 }
334 }
335
336
337 /* clean up */
338 if (bidi_props->embedding_levels)
339 {
340 free(bidi_props->embedding_levels);
341 }
342 bidi_props->embedding_levels = embedding_levels;
343
344 /* clean up */
345
346 if (bidi_props->char_types)
347 {
348 free(bidi_props->char_types);
349 }
350 bidi_props->char_types = char_types;
351
352 if (base_ustr) free(base_ustr);
353
354
355 return bidi_props;
356
357/* Cleanup */
358cleanup:
359 if (char_types) free(char_types);
360 if (embedding_levels) free(embedding_levels);
361 if (base_ustr) free(base_ustr);
362 if (bidi_props) evas_bidi_paragraph_props_unref(bidi_props); /* Clean up the bidi props */
363 return NULL;
364}
365
366/**
367 * @internal
368 * Copies dst to src and refs (doesn't copy) the paragraph props.
369 *
370 * @param src the props to copy
371 * @param dst the props to copy to.
372 */
373void
374evas_bidi_props_copy_and_ref(const Evas_BiDi_Props *src, Evas_BiDi_Props *dst)
375{
376 dst->dir = src->dir;
377}
378
379/**
380 * @internal
381 * Reorders ustr according to the bidi props.
382 *
383 * @param ustr the string to reorder. - Null is ok, will just populate the map.
384 * @param start the start of the line
385 * @param len the length of the line
386 * @param props the paragraph props to reorder according to
387 * @param _v_to_l The visual to logical map to populate - if NULL it won't populate it.
388 * @return #EINA_FALSE on success, #EINA_TRUE on error.
389 */
390Eina_Bool
391evas_bidi_props_reorder_line(Eina_Unicode *eina_ustr, size_t start, size_t len, const Evas_BiDi_Paragraph_Props *props, EvasBiDiStrIndex **_v_to_l)
392{
393 EvasBiDiStrIndex *v_to_l = NULL;
394 FriBidiChar *ustr = NULL, *base_ustr = NULL;
395
396 if (!props)
397 return EINA_FALSE;
398
399 if (eina_ustr)
400 {
401 /* The size of fribidichar is different than eina_unicode, convert */
402#ifdef EVAS_FRIBIDI_EINA_UNICODE_UNEQUAL
403 base_ustr = ustr = calloc(len + 1, sizeof(FriBidiChar));
404 ustr = _evas_bidi_unicode_to_fribidichar(ustr, eina_ustr);
405#else
406 ustr = (FriBidiChar *) eina_ustr;
407#endif
408 }
409
410
411 if (_v_to_l) {
412 size_t i;
413 v_to_l = *_v_to_l = calloc(len, sizeof(EvasBiDiStrIndex));
414 if (!v_to_l)
415 {
416 goto error;
417 }
418 /* init the array for fribidi */
419 for (i = 0 ; i < len ; i++)
420 {
421 v_to_l[i] = i;
422 }
423 }
424
425 {
426 EvasBiDiLevel *emb_lvl;
427 emb_lvl = malloc((start + len) * sizeof(EvasBiDiLevel));
428 memcpy(emb_lvl, props->embedding_levels,
429 (start + len) * sizeof(EvasBiDiLevel));
430 /* We pass v_to_l - start, because fribidi assumes start is the offset
431 * from the start of v_to_l as well, not just the props. */
432 if (!fribidi_reorder_line (FRIBIDI_FLAGS_DEFAULT, props->char_types,
433 len, start, props->direction, emb_lvl, ustr, v_to_l - start))
434 {
435 free(emb_lvl);
436 goto error;
437 }
438 free(emb_lvl);
439 }
440
441
442 /* The size of fribidichar is different than eina_unicode, convert */
443#ifdef EVAS_FRIBIDI_EINA_UNICODE_UNEQUAL
444 _evas_bidi_fribidichar_to_unicode(eina_ustr, base_ustr);
445 free(base_ustr);
446#endif
447 return EINA_FALSE;
448/* ERROR HANDLING */
449error:
450 if (base_ustr) free(base_ustr);
451 _SAFE_FREE(v_to_l);
452 return EINA_TRUE;
453}
454
455/**
456 * @internal
457 * Returns the end of the current run of text
458 *
459 * @param bidi_props the paragraph properties
460 * @param start where to start looking from
461 * @param len the length of the string
462 * @return the position of the end of the run (offset from
463 * bidi_props->props->start), 0 when there is no end (i.e all the text)
464 */
465int
466evas_bidi_end_of_run_get(const Evas_BiDi_Paragraph_Props *bidi_props,
467 size_t start, int len)
468{
469 EvasBiDiLevel *i;
470 EvasBiDiLevel base;
471
472 if (!bidi_props || (len <= 0))
473 return 0;
474
475 i = bidi_props->embedding_levels + start;
476 base = *i;
477 for ( ; (len > 0) && (base == *i) ; len--, i++)
478 ;
479
480 if (len == 0)
481 {
482 return 0;
483 }
484 return i - (bidi_props->embedding_levels + start);
485}
486
487/**
488 * @internal
489 * Returns the visual string index from the logical string index.
490 *
491 * @param v_to_l the visual to logical map
492 * @param len the length of the map.
493 * @param position the position to convert.
494 * @return on success the visual position, on failure the same position.
495 */
496EvasBiDiStrIndex
497evas_bidi_position_logical_to_visual(EvasBiDiStrIndex *v_to_l, int len, EvasBiDiStrIndex position)
498{
499 int i;
500 EvasBiDiStrIndex *ind;
501 if (position >= len || !v_to_l)
502 return position;
503
504 for (i = 0, ind = v_to_l ; i < len ; i++, ind++)
505 {
506 if (*ind == position)
507 {
508 return i;
509 }
510 }
511 return position;
512}
513
514/**
515 * @internal
516 * Returns the reversed pos of the index.
517 *
518 * @param dir the direction of the string
519 * @param len the length of the map.
520 * @param position the position to convert.
521 * @return on success the visual position, on failure the same position.
522 */
523EvasBiDiStrIndex
524evas_bidi_position_reverse(const Evas_BiDi_Props *props, int len, EvasBiDiStrIndex position)
525{
526 if (!props || position >= len)
527 return position;
528
529 return (props->dir == EVAS_BIDI_DIRECTION_RTL) ? (len - 1) - position : position;
530}
531
532/**
533 * @internal
534 * Checks if the char is rtl oriented. I.e even a neutral char can become rtl
535 * if surrounded by rtl chars.
536 *
537 * @param bidi_props The bidi paragraph properties
538 * @param start the base position
539 * @param index the offset from the base position.
540 * @return #EINA_TRUE if true, #EINA_FALSE otherwise.
541 */
542Eina_Bool
543evas_bidi_is_rtl_char(const Evas_BiDi_Paragraph_Props *bidi_props, size_t start, EvasBiDiStrIndex ind)
544{
545 if(!bidi_props || ind < 0)
546 return EINA_FALSE;
547 return (FRIBIDI_IS_RTL(
548 bidi_props->embedding_levels[ind + start]))
549 ? EINA_TRUE : EINA_FALSE;
550}
551
552Evas_BiDi_Paragraph_Props *
553evas_bidi_paragraph_props_new(void)
554{
555 Evas_BiDi_Paragraph_Props *ret;
556 ret = calloc(1, sizeof(Evas_BiDi_Paragraph_Props));
557 ret->direction = EVAS_BIDI_PARAGRAPH_NEUTRAL;
558 ret->refcount = 1;
559
560 return ret;
561}
562
563/**
564 * @internal
565 * Refs the bidi props.
566 *
567 * @param bidi_props the props to ref.
568 */
569Evas_BiDi_Paragraph_Props *
570evas_bidi_paragraph_props_ref(Evas_BiDi_Paragraph_Props *bidi_props)
571{
572 if (!bidi_props) return NULL;
573 BIDILOCK();
574
575 bidi_props->refcount++;
576 BIDIUNLOCK();
577 return bidi_props;
578}
579
580/**
581 * @internal
582 * Unrefs and potentially frees the props.
583 *
584 * @param bidi_props the properties to unref
585 */
586void
587evas_bidi_paragraph_props_unref(Evas_BiDi_Paragraph_Props *bidi_props)
588{
589 if (!bidi_props) return;
590 BIDILOCK();
591
592 if (--bidi_props->refcount == 0)
593 {
594 evas_bidi_paragraph_props_clean(bidi_props);
595 free(bidi_props);
596 }
597 BIDIUNLOCK();
598}
599
600
601/**
602 * @internal
603 * Cleans the paragraph properties.
604 *
605 * @param bidi_props the properties to clean.
606 */
607void
608evas_bidi_paragraph_props_clean(Evas_BiDi_Paragraph_Props *bidi_props)
609{
610 _SAFE_FREE(bidi_props->embedding_levels);
611 _SAFE_FREE(bidi_props->char_types);
612}
613
614/**
615 * @internal
616 * Cleans the bidi properties.
617 *
618 * @param bidi_props the properties to clean.
619 */
620void
621evas_bidi_props_clean(Evas_BiDi_Props *bidi_props)
622{
623 if (!bidi_props) return;
624 bidi_props->dir = EVAS_BIDI_DIRECTION_NEUTRAL;
625}
626/**
627 * @}
628 */
629/**
630 * @}
631 */
632#endif
633
634#if 0
635/* Good for debugging */
636static void
637dump_levels(Eina_Unicode *ustr, EvasBiDiLevel *emb)
638{
639 for ( ; *ustr ; ustr++, emb++)
640 {
641 printf("%lc %d\n", *ustr, *emb);
642 }
643}
644#endif
645