Tizen 2.1 base
[framework/osp/uifw.git] / src / graphics / FGrp_FontBidiManager.cpp
1 //
2 // Open Service Platform
3 // Copyright (c) 2012-2013 Samsung Electronics Co., Ltd.
4 //
5 // Licensed under the Flora License, Version 1.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://floralicense.org/license/
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an AS IS BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17
18 /*
19  * @file        FGrp_FontBidiManager.cpp
20  * @brief       This is the cpp file for _FontMemoryManager class.
21  *
22  */
23
24 #include <new>
25 #include <memory>
26
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "FGrp_FontBidiManager.h"
31
32 namespace
33 {
34 typedef wchar_t Eina_Unicode;
35
36 typedef FriBidiCharType EvasBiDiCharType;
37 typedef FriBidiLevel EvasBiDiLevel;
38 typedef FriBidiParType EvasBiDiParType;
39
40 #define EVAS_BIDI_PARAGRAPH_NEUTRAL FRIBIDI_PAR_ON
41 #define EVAS_BIDI_PARAGRAPH_LTR     FRIBIDI_PAR_LTR
42 #define EVAS_BIDI_PARAGRAPH_RTL     FRIBIDI_PAR_RTL
43 #define EVAS_BIDI_PARAGRAPH_WLTR    FRIBIDI_PAR_WLTR
44 #define EVAS_BIDI_PARAGRAPH_WRTL    FRIBIDI_PAR_WRTL
45
46 #define EVAS_BIDI_PARAGRAPH_DIRECTION_IS_RTL(x)       \
47    (((x) &&                                \
48      ((x->direction == EVAS_BIDI_PARAGRAPH_RTL) ||   \
49       (x->direction == EVAS_BIDI_PARAGRAPH_WRTL))) ?   \
50     EINA_TRUE : EINA_FALSE)
51
52 typedef struct _Evas_BiDi_Paragraph_Props {
53    EvasBiDiCharType  *char_types; /* BiDi char types */
54    EvasBiDiLevel     *embedding_levels; /* BiDi embedding levels */
55    int                refcount; /* The number of references to this object */
56    EvasBiDiParType    direction;
57 } Evas_BiDi_Paragraph_Props;
58
59 ////////////////////////////////////////////////////////////////////////////////
60
61 typedef int Eina_Bool;
62
63 #define EINA_TRUE 1
64 #define EINA_FALSE 0
65
66 #define BIDILOCK()
67 #define BIDIUNLOCK()
68
69 #define _SAFE_FREE(x) \
70    do {               \
71       if (x)          \
72         {             \
73            free(x);   \
74            x = NULL;  \
75         }             \
76      } while(0)
77
78 Eina_Bool
79 evas_bidi_is_rtl_str(const Eina_Unicode *str)
80 {
81    EvasBiDiCharType type;
82
83    if (!str)
84       return EINA_FALSE;
85
86    for ( ; *str ; str++)
87      {
88         type = fribidi_get_bidi_type((FriBidiChar) *str);
89         if (FRIBIDI_IS_LETTER(type) && FRIBIDI_IS_RTL(type))
90           {
91              return EINA_TRUE;
92           }
93      }
94    return EINA_FALSE;
95 }
96
97 int
98 eina_unicode_strlen(const Eina_Unicode *str)
99 {
100         if (str)
101         {
102                 const Eina_Unicode* pEnd = str;
103
104                 while (*pEnd++)
105                         ;
106
107                 return (pEnd - str - 1);
108         }
109         else
110         {
111                 return 0;
112         }
113 }
114
115 void
116 evas_bidi_paragraph_props_clean(Evas_BiDi_Paragraph_Props *bidi_props)
117 {
118    _SAFE_FREE(bidi_props->embedding_levels);
119    _SAFE_FREE(bidi_props->char_types);
120 }
121
122 Evas_BiDi_Paragraph_Props *
123 evas_bidi_paragraph_props_new(void)
124 {
125    Evas_BiDi_Paragraph_Props *ret;
126    ret = (Evas_BiDi_Paragraph_Props *)calloc(1, sizeof(Evas_BiDi_Paragraph_Props));
127    ret->direction = EVAS_BIDI_PARAGRAPH_NEUTRAL;
128    ret->refcount = 1;
129
130    return ret;
131 }
132
133 void
134 evas_bidi_paragraph_props_unref(Evas_BiDi_Paragraph_Props *bidi_props)
135 {
136    if (!bidi_props) return;
137    BIDILOCK();
138
139    if (--bidi_props->refcount == 0)
140      {
141         evas_bidi_paragraph_props_clean(bidi_props);
142         free(bidi_props);
143      }
144    BIDIUNLOCK();
145 }
146
147 ////////////////
148
149 Evas_BiDi_Paragraph_Props *
150 evas_bidi_paragraph_props_get(const Eina_Unicode *eina_ustr, size_t len, int *segment_idxs)
151 {
152    Evas_BiDi_Paragraph_Props *bidi_props = NULL;
153    EvasBiDiCharType *char_types = NULL;
154    EvasBiDiLevel *embedding_levels = NULL;
155    const FriBidiChar *ustr;
156    FriBidiChar *base_ustr = NULL;
157
158    if (!eina_ustr)
159       return NULL;
160
161
162    if (!evas_bidi_is_rtl_str(eina_ustr)) /* No need to handle bidi */
163      {
164         len = -1;
165         goto cleanup;
166      }
167
168    len = eina_unicode_strlen(eina_ustr);
169    /* The size of fribidichar s different than eina_unicode, convert */
170 #ifdef EVAS_FRIBIDI_EINA_UNICODE_UNEQUAL
171    base_ustr = calloc(len + 1, sizeof(FriBidiChar));
172    base_ustr = _evas_bidi_unicode_to_fribidichar(base_ustr, eina_ustr);
173    ustr = base_ustr;
174 #else
175    ustr = (const FriBidiChar *) eina_ustr;
176 #endif
177
178    bidi_props = evas_bidi_paragraph_props_new();
179
180    /* Prep work for reordering */
181    char_types = (EvasBiDiCharType *) malloc(sizeof(EvasBiDiCharType) * len);
182    if (!char_types)
183       {
184          len = -2;
185          goto cleanup;
186       }
187    fribidi_get_bidi_types(ustr, len, char_types);
188
189    embedding_levels = (EvasBiDiLevel *)malloc(sizeof(EvasBiDiLevel) * len);
190    if (!embedding_levels)
191      {
192         len = -2;
193         goto cleanup;
194      }
195
196    if (segment_idxs)
197      {
198         size_t pos = 0;
199         int *itr;
200         EvasBiDiLevel base_level = 0;
201         EvasBiDiParType direction;
202
203         for (itr = segment_idxs ; *itr > 0 ; itr++)
204           {
205              direction = EVAS_BIDI_PARAGRAPH_NEUTRAL;
206              if (!fribidi_get_par_embedding_levels(char_types + pos,
207                       *itr - pos,
208                       &direction,
209                       embedding_levels + pos))
210                {
211                   len = -2;
212                   goto cleanup;
213                }
214
215              /* Only on the first run */
216              if (itr == segment_idxs)
217                {
218                   bidi_props->direction = direction;
219                   /* adjust base_level to be 1 for rtl paragraphs, and 0 for
220                    * ltr paragraphs. */
221                   base_level =
222                      EVAS_BIDI_PARAGRAPH_DIRECTION_IS_RTL(bidi_props) ? 1 : 0;
223                }
224
225              /* We want those chars at the override points to be on the base
226               * level and we also remove -2 cause we later increment them,
227               * just for simpler code paths */
228              embedding_levels[*itr] = base_level - 2;
229              pos = *itr + 1;
230           }
231
232         direction = EVAS_BIDI_PARAGRAPH_NEUTRAL;
233         if (!fribidi_get_par_embedding_levels(char_types + pos,
234                  len - pos,
235                  &direction,
236                  embedding_levels + pos))
237           {
238              len = -2;
239              goto cleanup;
240           }
241
242         /* Increment all levels by 2 to emulate embedding. */
243           {
244              EvasBiDiLevel *bitr = embedding_levels, *end;
245              end = bitr + len;
246              for ( ; bitr < end ; bitr++)
247                {
248                   *bitr += 2;
249                }
250           }
251      }
252    else
253      {
254         if (!fribidi_get_par_embedding_levels(char_types, len,
255                  &bidi_props->direction, embedding_levels))
256           {
257              len = -2;
258              goto cleanup;
259           }
260      }
261
262
263    /* clean up */
264    if (bidi_props->embedding_levels)
265      {
266         free(bidi_props->embedding_levels);
267      }
268    bidi_props->embedding_levels = embedding_levels;
269
270    /* clean up */
271
272    if (bidi_props->char_types)
273      {
274         free(bidi_props->char_types);
275      }
276    bidi_props->char_types = char_types;
277
278    if (base_ustr) free(base_ustr);
279
280
281    return bidi_props;
282
283 /* Cleanup */
284 cleanup:
285    if (char_types) free(char_types);
286    if (embedding_levels) free(embedding_levels);
287    if (base_ustr) free(base_ustr);
288    if (bidi_props) evas_bidi_paragraph_props_unref(bidi_props); /* Clean up the bidi props */
289    return NULL;
290 }
291
292 } // namespace
293
294 namespace
295 {
296
297 //?? arbitrary
298 typedef int EvasBiDiStrIndex;
299
300 Eina_Bool
301 evas_bidi_props_reorder_line(Eina_Unicode *eina_ustr, size_t start, size_t len, const Evas_BiDi_Paragraph_Props *props, EvasBiDiStrIndex **_v_to_l)
302 {
303    EvasBiDiStrIndex *v_to_l = NULL;
304    FriBidiChar *ustr = NULL, *base_ustr = NULL;
305
306    if (!props)
307      return EINA_FALSE;
308
309    if (eina_ustr)
310      {
311         /* The size of fribidichar is different than eina_unicode, convert */
312 #ifdef EVAS_FRIBIDI_EINA_UNICODE_UNEQUAL
313         base_ustr = ustr = calloc(len + 1, sizeof(FriBidiChar));
314         ustr = _evas_bidi_unicode_to_fribidichar(ustr, eina_ustr);
315 #else
316         ustr = (FriBidiChar *) eina_ustr;
317 #endif
318      }
319
320
321    if (_v_to_l) {
322       size_t i;
323       v_to_l = *_v_to_l = (EvasBiDiStrIndex *) calloc(len, sizeof(EvasBiDiStrIndex));
324       if (!v_to_l)
325         {
326            goto error;
327         }
328       /* init the array for fribidi */
329       for (i = 0 ; i < len ; i++)
330         {
331            v_to_l[i] = i;
332         }
333    }
334
335      {
336         EvasBiDiLevel *emb_lvl;
337         emb_lvl = (EvasBiDiLevel *) malloc((start + len) * sizeof(EvasBiDiLevel));
338         memcpy(emb_lvl, props->embedding_levels,
339               (start + len) * sizeof(EvasBiDiLevel));
340         /* We pass v_to_l - start, because fribidi assumes start is the offset
341          * from the start of v_to_l as well, not just the props. */
342         if (!fribidi_reorder_line (FRIBIDI_FLAGS_DEFAULT, props->char_types,
343                  len, start, props->direction, emb_lvl, ustr, v_to_l - start))
344           {
345              free(emb_lvl);
346              goto error;
347           }
348         free(emb_lvl);
349      }
350
351
352    /* The size of fribidichar is different than eina_unicode, convert */
353 #ifdef EVAS_FRIBIDI_EINA_UNICODE_UNEQUAL
354    _evas_bidi_fribidichar_to_unicode(eina_ustr, base_ustr);
355    free(base_ustr);
356 #endif
357    return EINA_FALSE;
358 /* ERROR HANDLING */
359 error:
360    if (base_ustr) free(base_ustr);
361    _SAFE_FREE(v_to_l);
362    return EINA_TRUE;
363 }
364
365 }
366
367 ////////////////////////////////////////////////////////////////////////////////
368
369 namespace Tizen { namespace Graphics
370 {
371
372 _FontBidiProps::_FontBidiProps(const wchar_t* pText, int length)
373         : pEventText(0)
374         , pCharType(0)
375         , pEmbeddingLevels(0)
376         , baseDirection(FRIBIDI_PAR_ON)
377         , isRtl(false)
378 {
379         Evas_BiDi_Paragraph_Props* pEvasBidiProps = evas_bidi_paragraph_props_get(pText, length, 0);
380
381         if (pEvasBidiProps)
382         {
383                 this->pEventText = (FriBidiChar *)pText;
384                 this->pCharType = pEvasBidiProps->char_types;
385                 this->pEmbeddingLevels = pEvasBidiProps->embedding_levels;
386                 this->baseDirection = pEvasBidiProps->direction;
387                 this->isRtl = true;
388
389                 evas_bidi_paragraph_props_unref(pEvasBidiProps);
390         }
391 }
392
393 }} // Tizen::Graphics