aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/evas/src/lib/engines/common/evas_text_utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/evas/src/lib/engines/common/evas_text_utils.c')
-rw-r--r--libraries/evas/src/lib/engines/common/evas_text_utils.c472
1 files changed, 472 insertions, 0 deletions
diff --git a/libraries/evas/src/lib/engines/common/evas_text_utils.c b/libraries/evas/src/lib/engines/common/evas_text_utils.c
new file mode 100644
index 0000000..a00df91
--- /dev/null
+++ b/libraries/evas/src/lib/engines/common/evas_text_utils.c
@@ -0,0 +1,472 @@
1#include "evas_common.h"
2#include "evas_font_private.h"
3#include "evas_text_utils.h"
4#include "language/evas_bidi_utils.h"
5#include "language/evas_language_utils.h"
6#include "evas_font_ot.h"
7
8void
9evas_common_text_props_bidi_set(Evas_Text_Props *props,
10 Evas_BiDi_Paragraph_Props *bidi_par_props, size_t start)
11{
12#ifdef BIDI_SUPPORT
13 props->bidi.dir = (evas_bidi_is_rtl_char(
14 bidi_par_props,
15 0,
16 start)) ? EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
17#else
18 (void) start;
19 (void) bidi_par_props;
20 props->bidi.dir = EVAS_BIDI_DIRECTION_LTR;
21#endif
22}
23
24void
25evas_common_text_props_script_set(Evas_Text_Props *props, Evas_Script_Type scr)
26{
27 props->script = scr;
28}
29
30void
31evas_common_text_props_content_copy_and_ref(Evas_Text_Props *dst,
32 const Evas_Text_Props *src)
33{
34 memcpy(dst, src, sizeof(Evas_Text_Props));
35 evas_common_text_props_content_ref(dst);
36}
37
38void
39evas_common_text_props_content_ref(Evas_Text_Props *props)
40{
41 /* No content in this case */
42 if (!props->info)
43 return;
44
45 props->info->refcount++;
46}
47
48void
49evas_common_text_props_content_unref(Evas_Text_Props *props)
50{
51 /* No content in this case */
52 if (!props->info)
53 return;
54
55 if (--(props->info->refcount) == 0)
56 {
57 if (props->info->glyph)
58 free(props->info->glyph);
59#ifdef OT_SUPPORT
60 if (props->info->ot)
61 free(props->info->ot);
62#endif
63 free(props->info);
64 props->info = NULL;
65 }
66}
67
68static int
69_evas_common_text_props_cluster_move(const Evas_Text_Props *props, int pos,
70 Eina_Bool right)
71{
72 int prop_pos = evas_common_text_props_index_find(props, pos);
73 if (!right && (prop_pos > 0))
74 {
75#ifdef OT_SUPPORT
76 return props->info->ot[props->start + prop_pos - 1].source_cluster -
77 props->text_offset;
78#else
79 return props->start + prop_pos - 1 - props->text_offset;
80#endif
81 }
82 else if (right && (prop_pos < (int) (props->len - 1)))
83 {
84#ifdef OT_SUPPORT
85 return props->info->ot[props->start + prop_pos + 1].source_cluster -
86 props->text_offset;
87#else
88 return props->start + prop_pos + 1 - props->text_offset;
89#endif
90 }
91
92 return pos;
93}
94
95EAPI int
96evas_common_text_props_cluster_next(const Evas_Text_Props *props, int pos)
97{
98 Eina_Bool right;
99 /* Move right if we are in a non-rtl text */
100 right = (props->bidi.dir != EVAS_BIDI_DIRECTION_RTL);
101 return _evas_common_text_props_cluster_move(props, pos, right);
102}
103
104EAPI int
105evas_common_text_props_cluster_prev(const Evas_Text_Props *props, int pos)
106{
107 Eina_Bool right;
108 /* Move right if we are in an rtl text */
109 right = (props->bidi.dir == EVAS_BIDI_DIRECTION_RTL);
110 return _evas_common_text_props_cluster_move(props, pos, right);
111}
112
113/* Returns the index of the logical char in the props. */
114EAPI int
115evas_common_text_props_index_find(const Evas_Text_Props *props, int _cutoff)
116{
117#ifdef OT_SUPPORT
118 Evas_Font_OT_Info *ot_info;
119 int min = 0;
120 int max = props->len - 1;
121 int mid;
122
123 _cutoff += props->text_offset;
124 ot_info = props->info->ot + props->start;
125 /* Should get us closer to the right place. */
126 if ((min <= _cutoff) && (_cutoff <= max))
127 mid = _cutoff;
128 else
129 mid = (min + max) / 2;
130
131 if (props->bidi.dir == EVAS_BIDI_DIRECTION_RTL)
132 {
133 /* Monotonic in a descending order */
134 do
135 {
136 if (_cutoff > (int) ot_info[mid].source_cluster)
137 max = mid - 1;
138 else if (_cutoff < (int) ot_info[mid].source_cluster)
139 min = mid + 1;
140 else
141 break;
142
143 mid = (min + max) / 2;
144 }
145 while (min <= max);
146 }
147 else
148 {
149 /* Monotonic in an ascending order */
150 do
151 {
152 if (_cutoff < (int) ot_info[mid].source_cluster)
153 max = mid - 1;
154 else if (_cutoff > (int) ot_info[mid].source_cluster)
155 min = mid + 1;
156 else
157 break;
158
159 mid = (min + max) / 2;
160 }
161 while (min <= max);
162 }
163
164 /* If we didn't find, abort */
165 if (min > max)
166 return -1;
167
168 ot_info += mid;
169 if (props->bidi.dir == EVAS_BIDI_DIRECTION_RTL)
170 {
171 /* Walk to the last one of the same cluster */
172 for ( ; mid < (int) props->len ; mid++, ot_info++)
173 {
174 if (ot_info->source_cluster != (size_t) _cutoff)
175 break;
176 }
177 mid--;
178 }
179 else
180 {
181 /* Walk to the last one of the same cluster */
182 for ( ; mid >= 0 ; mid--, ot_info--)
183 {
184 if (ot_info->source_cluster != (size_t) _cutoff)
185 break;
186 }
187 mid++;
188 }
189
190 return mid;
191#else
192 return _cutoff;
193 (void) props;
194#endif
195}
196
197/* Won't work in the middle of ligatures, assumes cutoff < len.
198 * Also won't work in the middle of indic words, should handle that in a
199 * smart way. */
200EAPI void
201evas_common_text_props_split(Evas_Text_Props *base,
202 Evas_Text_Props *ext, int _cutoff)
203{
204 size_t cutoff;
205
206 /* Translate text cutoff pos to string object cutoff point */
207#ifdef OT_SUPPORT
208 _cutoff = evas_common_text_props_index_find(base, _cutoff);
209
210 if (_cutoff >= 0)
211 {
212 cutoff = (size_t) _cutoff;
213 }
214 else
215 {
216 ERR("Couldn't find the cutoff position. Is it inside a cluster?");
217 return;
218 }
219#else
220 cutoff = (size_t) _cutoff;
221#endif
222
223 evas_common_text_props_content_copy_and_ref(ext, base);
224 if (base->bidi.dir == EVAS_BIDI_DIRECTION_RTL)
225 {
226 ext->start = base->start;
227 ext->len = cutoff + 1;
228 base->start = base->start + ext->len;
229 base->len = base->len - ext->len;
230
231#ifdef OT_SUPPORT
232 ext->text_offset =
233 ext->info->ot[ext->start + ext->len - 1].source_cluster;
234#else
235 ext->text_offset = base->text_offset + base->len;
236#endif
237 }
238 else
239 {
240 ext->start = base->start + cutoff;
241 ext->len = base->len - cutoff;
242 base->len = cutoff;
243
244#ifdef OT_SUPPORT
245 ext->text_offset = ext->info->ot[ext->start].source_cluster;
246#else
247 ext->text_offset = base->text_offset + base->len;
248#endif
249 }
250 ext->text_len = base->text_len - (ext->text_offset - base->text_offset);
251 base->text_len = (ext->text_offset - base->text_offset);
252}
253
254/* Won't work in the middle of ligatures */
255EAPI void
256evas_common_text_props_merge(Evas_Text_Props *item1,
257 const Evas_Text_Props *item2)
258{
259 if (item1->info != item2->info)
260 {
261 ERR("tried merge back items that weren't together in the first place.");
262 return;
263 }
264 if (item1->bidi.dir == EVAS_BIDI_DIRECTION_RTL)
265 {
266 item1->start = item2->start;
267 }
268
269 item1->len += item2->len;
270 item1->text_len += item2->text_len;
271}
272
273EAPI Eina_Bool
274evas_common_text_props_content_create(void *_fi, const Eina_Unicode *text,
275 Evas_Text_Props *text_props, const Evas_BiDi_Paragraph_Props *par_props,
276 size_t par_pos, int len)
277{
278 RGBA_Font_Int *fi = (RGBA_Font_Int *) _fi;
279
280 if (text_props->info)
281 {
282 evas_common_text_props_content_unref(text_props);
283 }
284 if (len == 0)
285 {
286 text_props->info = NULL;
287 text_props->start = text_props->len = text_props->text_offset = 0;
288 }
289 text_props->info = calloc(1, sizeof(Evas_Text_Props_Info));
290
291 text_props->font_instance = fi;
292
293 evas_common_font_int_reload(fi);
294 if (fi->src->current_size != fi->size)
295 {
296 FTLOCK();
297 FT_Activate_Size(fi->ft.size);
298 FTUNLOCK();
299 fi->src->current_size = fi->size;
300 }
301
302#ifdef OT_SUPPORT
303 size_t char_index;
304 Evas_Font_Glyph_Info *gl_itr;
305 Evas_Coord pen_x = 0, adjust_x = 0;
306 (void) par_props;
307 (void) par_pos;
308
309 evas_common_font_ot_populate_text_props(text, text_props, len);
310
311 gl_itr = text_props->info->glyph;
312 for (char_index = 0 ; char_index < text_props->len ; char_index++)
313 {
314 FT_UInt idx;
315 RGBA_Font_Glyph *fg;
316 Eina_Bool is_replacement = EINA_FALSE;
317 /* If we got a malformed index, show the replacement char instead */
318 if (gl_itr->index == 0)
319 {
320 gl_itr->index = evas_common_get_char_index(fi, REPLACEMENT_CHAR);
321 is_replacement = EINA_TRUE;
322 }
323 idx = gl_itr->index;
324 LKL(fi->ft_mutex);
325 fg = evas_common_font_int_cache_glyph_get(fi, idx);
326 if (!fg)
327 {
328 LKU(fi->ft_mutex);
329 continue;
330 }
331 LKU(fi->ft_mutex);
332
333 gl_itr->x_bear = fg->glyph_out->left;
334 gl_itr->width = fg->glyph_out->bitmap.width;
335 /* text_props->info->glyph[char_index].advance =
336 * text_props->info->glyph[char_index].index =
337 * already done by the ot function */
338 if (EVAS_FONT_CHARACTER_IS_INVISIBLE(
339 text[text_props->info->ot[char_index].source_cluster]))
340 {
341 gl_itr->index = 0;
342 /* Reduce the current advance */
343 if (gl_itr > text_props->info->glyph)
344 {
345 adjust_x -= gl_itr->pen_after - (gl_itr - 1)->pen_after;
346 }
347 else
348 {
349 adjust_x -= gl_itr->pen_after;
350 }
351 }
352 else
353 {
354 if (is_replacement)
355 {
356 /* Update the advance accordingly */
357 adjust_x += (pen_x + (fg->glyph->advance.x >> 16)) -
358 gl_itr->pen_after;
359 }
360 pen_x = gl_itr->pen_after;
361 }
362 gl_itr->pen_after += adjust_x;
363
364 fi = text_props->font_instance;
365 gl_itr++;
366 }
367#else
368 /* We are walking the string in visual ordering */
369 Evas_Font_Glyph_Info *gl_itr;
370 Eina_Bool use_kerning;
371 FT_UInt prev_index;
372 FT_Face pface = NULL;
373 Evas_Coord pen_x = 0;
374 int adv_d, i;
375#if !defined(OT_SUPPORT) && defined(BIDI_SUPPORT)
376 Eina_Unicode *base_str = NULL;
377 if (text_props->bidi.dir == EVAS_BIDI_DIRECTION_RTL)
378 {
379 text = base_str = eina_unicode_strndup(text, len);
380 evas_bidi_shape_string(base_str, par_props, par_pos, len);
381 }
382#else
383 (void) par_props;
384 (void) par_pos;
385#endif
386
387 FTLOCK();
388 use_kerning = FT_HAS_KERNING(fi->src->ft.face);
389 FTUNLOCK();
390 prev_index = 0;
391
392 i = len;
393 text_props->info->glyph = calloc(len,
394 sizeof(Evas_Font_Glyph_Info));
395
396 if (text_props->bidi.dir == EVAS_BIDI_DIRECTION_RTL)
397 {
398 text += len - 1;
399 adv_d = -1;
400 }
401 else
402 {
403 adv_d = 1;
404 }
405
406 gl_itr = text_props->info->glyph;
407 for ( ; i > 0 ; gl_itr++, text += adv_d, i--)
408 {
409 FT_UInt idx;
410 RGBA_Font_Glyph *fg;
411 int _gl, kern;
412 Evas_Coord adv;
413 _gl = *text;
414 if (_gl == 0) break;
415
416 idx = evas_common_get_char_index(fi, _gl);
417 if (idx == 0)
418 {
419 idx = evas_common_get_char_index(fi, REPLACEMENT_CHAR);
420 }
421
422 LKL(fi->ft_mutex);
423 fg = evas_common_font_int_cache_glyph_get(fi, idx);
424 if (!fg)
425 {
426 LKU(fi->ft_mutex);
427 continue;
428 }
429 kern = 0;
430
431 if ((use_kerning) && (prev_index) && (idx) &&
432 (pface == fi->src->ft.face))
433 {
434 if (evas_common_font_query_kerning(fi, prev_index, idx, &kern))
435 {
436 pen_x += kern;
437 (gl_itr - 1)->pen_after +=
438 EVAS_FONT_ROUND_26_6_TO_INT(kern);
439 }
440 }
441
442 pface = fi->src->ft.face;
443 LKU(fi->ft_mutex);
444
445 gl_itr->index = idx;
446 gl_itr->x_bear = fg->glyph_out->left;
447 adv = fg->glyph->advance.x >> 10;
448 gl_itr->width = fg->glyph_out->bitmap.width;
449
450 if (EVAS_FONT_CHARACTER_IS_INVISIBLE(_gl))
451 {
452 gl_itr->index = 0;
453 }
454 else
455 {
456 pen_x += adv;
457 }
458
459 gl_itr->pen_after = EVAS_FONT_ROUND_26_6_TO_INT(pen_x);
460
461 prev_index = idx;
462 }
463 text_props->len = len;
464# if !defined(OT_SUPPORT) && defined(BIDI_SUPPORT)
465 if (base_str)
466 free(base_str);
467# endif
468#endif
469 text_props->text_len = len;
470 text_props->info->refcount = 1;
471 return EINA_TRUE;
472}