diff options
Diffstat (limited to '')
-rw-r--r-- | libraries/evas/src/lib/engines/common/evas_text_utils.c | 472 |
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 | |||
8 | void | ||
9 | evas_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 | |||
24 | void | ||
25 | evas_common_text_props_script_set(Evas_Text_Props *props, Evas_Script_Type scr) | ||
26 | { | ||
27 | props->script = scr; | ||
28 | } | ||
29 | |||
30 | void | ||
31 | evas_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 | |||
38 | void | ||
39 | evas_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 | |||
48 | void | ||
49 | evas_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 | |||
68 | static 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 | |||
95 | EAPI int | ||
96 | evas_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 | |||
104 | EAPI int | ||
105 | evas_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. */ | ||
114 | EAPI int | ||
115 | evas_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. */ | ||
200 | EAPI void | ||
201 | evas_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 */ | ||
255 | EAPI void | ||
256 | evas_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 | |||
273 | EAPI Eina_Bool | ||
274 | evas_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 | } | ||