1 #include "evas_font_private.h" /* for Frame-Queuing support */
2
3 #define VAR_SEQ_SAFE(x) (((x)<run_end) ? VAR_SEQ(*(x)) : 0)
4
5 /* FIXME: Check coverage according to the font and not by actually loading */
6 /**
7 * @internal
8 * Find the end of a run according to font coverage, and return the base script
9 * font and the current wanted font.
10 *
11 * @param[in] fn the font to use.
12 * @param script_fi The base font instance to be used with the script. If NULL, then it's calculated and returned in this variable, if not NULL, it's used and not modified.
13 * @param[out] cur_fi The font instance found for the current run.
14 * @param[in] script the base script
15 * @param[in] text the text to work on.
16 * @param[in] run_let the current run len, i.e "search limit".
17 * @return length of the run found.
18 */
19 EAPI int
evas_common_font_query_run_font_end_get(RGBA_Font * fn,RGBA_Font_Int ** script_fi,RGBA_Font_Int ** cur_fi,Evas_Script_Type script EINA_UNUSED,const Eina_Unicode * text,int run_len)20 evas_common_font_query_run_font_end_get(RGBA_Font *fn, RGBA_Font_Int **script_fi, RGBA_Font_Int **cur_fi, Evas_Script_Type script EINA_UNUSED, const Eina_Unicode *text, int run_len)
21 {
22 RGBA_Font_Int *fi = NULL;
23 const Eina_Unicode *run_end = text + run_len;
24 const Eina_Unicode *itr;
25
26 /* If there's no current script_fi, find it first */
27 if (!*script_fi)
28 {
29 fi = fn->fonts->data;
30
31 *script_fi = fi;
32 }
33 else
34 {
35 fi = *script_fi;
36 }
37
38 /* Find the longest run of the same font starting from the start position
39 * and update cur_fi accordingly. */
40 Eina_Unicode variation_sequence = 0;
41 itr = text;
42 while (itr < run_end)
43 {
44 RGBA_Font_Int *tmp_fi;
45 /* Itr will end up being the first of the next run */
46 for ( ; itr < run_end ; itr++)
47 {
48 /* 0x1F is the last ASCII contral char, just a hack in
49 * the meanwhile. */
50 if (*itr <= 0x1F)
51 continue;
52
53 /* Skip searching font for INHERITED script unicodes.
54 * It is meaningful when only it comes after other unicodes from same font. */
55 if (evas_common_language_char_script_get(*itr) == EVAS_SCRIPT_INHERITED)
56 continue;
57
58 variation_sequence = VAR_SEQ_SAFE(itr+1);
59
60 /* Break if either it's not in the font, or if it is in the
61 * script's font. */
62 if (!evas_common_get_char_index(fi, *itr, variation_sequence))
63 break;
64
65 if (fi != *script_fi)
66 {
67 if (evas_common_get_char_index(*script_fi, *itr, variation_sequence))
68 break;
69 }
70 }
71
72 /* Abort if we reached the end */
73 if (itr == run_end)
74 break;
75
76 /* If the script font doesn't fit even one char, find a new font. */
77 if (itr == text)
78 {
79 /* If we can find a font, use it. Otherwise, find the first
80 * char the run of chars that can't be rendered until the first
81 * one that can. */
82 Eina_Unicode variation_sequence = VAR_SEQ_SAFE(itr+1);
83 if (evas_common_font_glyph_search(fn, &tmp_fi, *itr, variation_sequence, EVAS_FONT_SEARCH_OPTION_NONE))
84 {
85 fi = tmp_fi;
86 }
87 else if ( (variation_sequence == VARIATION_TEXT_PRESENTATION) && evas_common_font_glyph_search(fn, &tmp_fi, *itr, 0, EVAS_FONT_SEARCH_OPTION_SKIP_COLOR))
88 {
89 /* If we can not find unicode with variation sequence, then we will
90 Search and find for non color glyph because variation sequence is Text
91 */
92 fi = tmp_fi;
93 }
94 else if ( variation_sequence && evas_common_font_glyph_search(fn, &tmp_fi, *itr, 0, EVAS_FONT_SEARCH_OPTION_NONE))
95 {
96 /* If we can not find unicode with variation sequence, then we will
97 Search and find glyph without the variation sequence
98 */
99 fi = tmp_fi;
100 }
101 else
102 {
103 itr++;
104 /* Go through all the chars that can't be rendered with any
105 * font */
106 for ( ; itr < run_end ; itr++)
107 {
108 if(VAR_SEQ_SAFE(itr))
109 continue;
110
111 Eina_Unicode variation_sequence = VAR_SEQ_SAFE(itr+1);
112 tmp_fi = fi;
113 if (evas_common_get_char_index(fi, *itr, variation_sequence) ||
114 evas_common_font_glyph_search(fn, &tmp_fi, *itr, variation_sequence, EVAS_FONT_SEARCH_OPTION_NONE))
115 {
116 fi = tmp_fi;
117 break;
118 }
119 }
120
121 /* If we found a renderable character and the found font
122 * can render the replacement char, continue, otherwise
123 * find a font most suitable for the replacement char and
124 * break */
125 if ((itr == run_end) ||
126 !evas_common_get_char_index(fi, REPLACEMENT_CHAR, 0))
127 {
128 evas_common_font_glyph_search(fn, &fi, REPLACEMENT_CHAR, 0, EVAS_FONT_SEARCH_OPTION_NONE);
129 break;
130 }
131 }
132 itr++;
133 }
134 else
135 {
136 /* If this char is not renderable by any font, but the replacement
137 * char can be rendered using the currentfont, continue this
138 * run. */
139 Eina_Unicode variation_sequence = VAR_SEQ_SAFE(itr+1);
140
141 if (!evas_common_font_glyph_search(fn, &tmp_fi, *itr, variation_sequence, EVAS_FONT_SEARCH_OPTION_NONE) &&
142 (variation_sequence ? !evas_common_font_glyph_search(fn, &tmp_fi, *itr, 0, EVAS_FONT_SEARCH_OPTION_NONE) : 1) &&
143 evas_common_get_char_index(fi, REPLACEMENT_CHAR, 0))
144 {
145 itr++;
146 }
147 else
148 {
149 /* Done, we did as much as possible */
150 break;
151 }
152 }
153 }
154
155 if (fi)
156 *cur_fi = fi;
157 else
158 *cur_fi = *script_fi;
159
160 return itr - text;
161 }
162
163 /**
164 * @internal
165 * Calculate the kerning between "left" and "right.
166 *
167 * @param fi the font instance to use
168 * @param left the left glyph index
169 * @param right the right glyph index
170 * @param[out] kerning the kerning calculated.
171 * @return FALSE on error, TRUE on success.
172 */
173 EAPI int
evas_common_font_query_kerning(RGBA_Font_Int * fi,FT_UInt left,FT_UInt right,int * kerning)174 evas_common_font_query_kerning(RGBA_Font_Int *fi, FT_UInt left, FT_UInt right,
175 int *kerning)
176 {
177 int *result;
178 FT_Vector delta;
179 int key[2];
180 int hash;
181 int error = 1;
182
183 key[0] = left;
184 key[1] = right;
185 hash = eina_hash_int32(&left, sizeof (int)) ^ eina_hash_int32(&right, sizeof (int));
186
187 result = eina_hash_find_by_hash(fi->kerning, key, sizeof (int) * 2, hash);
188 if (result)
189 {
190 *kerning = result[2];
191 goto on_correct;
192 }
193
194 /* NOTE: ft2 seems to have a bug. and sometimes returns bizarre
195 * values to kern by - given same font, same size and same
196 * prev_index and index. auto/bytecode or none hinting doesn't
197 * matter */
198 evas_common_font_int_reload(fi);
199 FTLOCK();
200 if (FT_Get_Kerning(fi->src->ft.face,
201 left, right,
202 FT_KERNING_DEFAULT, &delta) == 0)
203 {
204 int *push;
205
206 FTUNLOCK();
207 *kerning = delta.x;
208
209 push = malloc(sizeof (int) * 3);
210 if (!push) return 1;
211
212 push[0] = left;
213 push[1] = right;
214 push[2] = *kerning;
215
216 eina_hash_direct_add_by_hash(fi->kerning, push, sizeof(int) * 2, hash, push);
217
218 goto on_correct;
219 }
220
221 FTUNLOCK();
222 error = 0;
223
224 on_correct:
225 return error;
226 }
227
228 /**
229 * @internal
230 * Calculate the inset of the text. Inset is the difference between the pen
231 * position of the first char in the string, and the first pixel drawn.
232 * (can be negative).
233 *
234 * @param fn the font set to use.
235 * @param text_props the string object.
236 * @return the calculated inset.
237 */
238 EAPI int
evas_common_font_query_inset(RGBA_Font * fn EINA_UNUSED,const Evas_Text_Props * text_props)239 evas_common_font_query_inset(RGBA_Font *fn EINA_UNUSED, const Evas_Text_Props *text_props)
240 {
241 if (!text_props->len) return 0;
242 return text_props->info->glyph[text_props->start].x_bear;
243 }
244
245 /**
246 * @internal
247 * Calculate the right inset of the text. This is the difference between the
248 * pen position of the glyph after the last glyph in the text, and the last
249 * pixel drawn in the text (essentially "advance - width" of the last char).
250 *
251 * @param fn the font set to use.
252 * @param text_props the string object.
253 * @return the calculated inset.
254 *
255 * @see evas_common_font_query_inset()
256 */
257 EAPI int
evas_common_font_query_right_inset(RGBA_Font * fn EINA_UNUSED,const Evas_Text_Props * text_props)258 evas_common_font_query_right_inset(RGBA_Font *fn EINA_UNUSED, const Evas_Text_Props *text_props)
259 {
260 const Evas_Font_Glyph_Info *gli;
261 if (!text_props->len) return 0;
262 gli = text_props->info->glyph + text_props->start + text_props->len - 1;
263
264 /* If the last char is a whitespace, we use the advance as the size,
265 * so the right_inset is 0. */
266 if (gli->width == 0)
267 return 0;
268
269 return ((gli > text_props->info->glyph) ?
270 gli->pen_after - (gli - 1)->pen_after : gli->pen_after) -
271 (gli->width + gli->x_bear
272 #ifdef OT_SUPPORT
273 + EVAS_FONT_ROUND_26_6_TO_INT(EVAS_FONT_OT_X_OFF_GET(
274 text_props->info->ot[text_props->start + text_props->len - 1]))
275 #endif
276 );
277 }
278
279 /**
280 * @internal
281 * Calculate the ascent/descent of a run. This is different from
282 * evas_common_font_[max]_ascent/descent_get because this one returns the
283 * actual sizee (i.e including accents), and not just what the font reports.
284 *
285 * @param fn the font set to use.
286 * @param text_props the string object.
287 * @param[out] ascent the calculated ascent
288 * @param[out] descent the calculated descent
289 */
290 EAPI void
evas_common_font_ascent_descent_get(RGBA_Font * fn,const Evas_Text_Props * text_props,int * ascent,int * descent)291 evas_common_font_ascent_descent_get(RGBA_Font *fn, const Evas_Text_Props *text_props, int *ascent, int *descent)
292 {
293 int asc = 0, desc = 0;
294 int max_asc, max_desc;
295
296 max_asc = evas_common_font_ascent_get(fn);
297 max_desc = evas_common_font_descent_get(fn);
298
299 /* FIXME: Not supported yet!!! */
300 desc = max_desc;
301
302 EVAS_FONT_WALK_TEXT_INIT();
303 EVAS_FONT_WALK_TEXT_START()
304 {
305 EVAS_FONT_WALK_TEXT_WORK();
306 if (!EVAS_FONT_WALK_IS_VISIBLE) continue;
307
308 if ((EVAS_FONT_WALK_PEN_Y + EVAS_FONT_WALK_Y_OFF + EVAS_FONT_WALK_Y_BEAR) > asc)
309 {
310 asc = EVAS_FONT_WALK_PEN_Y + EVAS_FONT_WALK_Y_OFF + EVAS_FONT_WALK_Y_BEAR;
311 }
312 }
313 EVAS_FONT_WALK_TEXT_END();
314
315 if (ascent) *ascent = (asc > max_asc) ? asc : max_asc;
316 if (descent) *descent = (desc < max_desc) ? desc : max_desc;
317 }
318
319 /**
320 * @internal
321 * Calculate the size of the string (width and height).
322 * The width is the disntance between the first pen position and the last pixel
323 * drawn.
324 * The height is the max ascent+descent of the font.
325 *
326 * @param fn the font set to use.
327 * @param text_props the string object.
328 * @param[out] w the calculated width
329 * @param[out] h the calculated height
330 */
331 EAPI void
evas_common_font_query_size(RGBA_Font * fn,const Evas_Text_Props * text_props,int * w,int * h)332 evas_common_font_query_size(RGBA_Font *fn, const Evas_Text_Props *text_props, int *w, int *h)
333 {
334 Evas_Coord ret_w = 0;
335
336 if (text_props->len > 0)
337 {
338 size_t off = text_props->start + text_props->len - 1;
339 const Evas_Font_Glyph_Info *first_glyph = text_props->info->glyph +
340 text_props->start;
341 const Evas_Font_Glyph_Info *last_glyph = text_props->info->glyph + off;;
342
343 const Evas_Font_Glyph_Info *glyph = last_glyph;
344 size_t cluster = 0;
345 size_t cur_cluster = 0;
346
347 #ifdef OT_SUPPORT
348 Evas_Font_OT_Info *ot = text_props->info->ot + off;
349 cluster = ot->source_cluster;
350 cur_cluster = ot->source_cluster;
351 #endif
352
353 do
354 {
355 Evas_Coord cur_w = 0;
356 if (text_props->len > 1)
357 {
358 cur_w = glyph[-1].pen_after;
359 if (text_props->start > 0)
360 cur_w -= first_glyph[-1].pen_after;
361 }
362 cur_w += glyph->width + glyph->x_bear;
363 #ifdef OT_SUPPORT
364 cur_w += EVAS_FONT_ROUND_26_6_TO_INT(EVAS_FONT_OT_X_OFF_GET(*ot));
365
366 cur_cluster = ot->source_cluster;
367 ot--;
368 #else
369 cur_cluster = cluster + 1; /* Change cluster manually for no OT */
370 #endif
371 glyph--;
372
373 if (cur_w > ret_w)
374 {
375 ret_w = cur_w;
376 }
377 }
378 while ((glyph > first_glyph) && (cur_cluster == cluster));
379 }
380
381 if (w) *w = ret_w;
382 if (h) *h = evas_common_font_max_ascent_get(fn) + evas_common_font_max_descent_get(fn);
383 }
384
385 /**
386 * @internal
387 * Calculate the advance of the string. Advance is the distance between the
388 * first pen position and the pen position after the string.
389 *
390 * @param fn the font set to use.
391 * @param text_props the string object.
392 * @param[out] h_adv the calculated horizontal advance.
393 * @param[out] v_adv the calculated vertical advance.
394 */
395 EAPI void
evas_common_font_query_advance(RGBA_Font * fn,const Evas_Text_Props * text_props,int * h_adv,int * v_adv)396 evas_common_font_query_advance(RGBA_Font *fn, const Evas_Text_Props *text_props, int *h_adv, int *v_adv)
397 {
398 Evas_Coord ret_adv = 0;
399 if (text_props->len > 0)
400 {
401 // RGBA_Font_Int *fi = text_props->font_instance;
402 const Evas_Font_Glyph_Info *glyph = text_props->info->glyph +
403 text_props->start;
404 const Evas_Font_Glyph_Info *last_glyph = glyph + text_props->len - 1;
405 ret_adv = last_glyph->pen_after;
406 if (text_props->start > 0)
407 ret_adv -= glyph[-1].pen_after;
408
409 #if 0
410 /* Runtime slant adjustment. */
411 if (fi->runtime_rend & FONT_REND_SLANT)
412 {
413 RGBA_Font_Glyph *fg =
414 evas_common_font_int_cache_glyph_get(fi, last_glyph->index);
415 if (!fg->glyph_out) evas_common_font_int_cache_glyph_render(fg);
416 ret_adv += fg->glyph_out->bitmap.rows * _EVAS_FONT_SLANT_TAN;
417 }
418 #endif
419 }
420
421 if (h_adv) *h_adv = ret_adv;
422 if (v_adv) *v_adv = evas_common_font_get_line_advance(fn);
423 }
424
425 /**
426 * @internal
427 * Query the coordinates of the char at position pos. If the position is at the
428 * end of the string (i.e where the finishing null would be) it returns the
429 * coordinates of the position right after the last char. This is either on
430 * the left or on the right of the string, depending on BiDi direction. Returned
431 * width in this case is 0. It returns the x of the leftmost pixel drawn.
432 *
433 * @param fn the font set to use.
434 * @param text_props the string object.
435 * @param pos the position of the char in the string object (not actual position in the string object, but the position of the source character).
436 * @param[out] cx the calculated x - CAN BE NULL
437 * @param[out] cy the calculated y - CAN BE NULL
438 * @param[out] cw the calculated width - CAN BE NULL
439 * @param[out] ch the calculated height - CAN BE NULL
440 * @return TRUE on success, FALSE otherwise.
441 *
442 * @see evas_common_font_query_pen_coords()
443 */
444 EAPI int
evas_common_font_query_char_coords(RGBA_Font * fn,const Evas_Text_Props * text_props,int pos,int * cx,int * cy,int * cw,int * ch)445 evas_common_font_query_char_coords(RGBA_Font *fn, const Evas_Text_Props *text_props, int pos, int *cx, int *cy, int *cw, int *ch)
446 {
447 int asc, desc;
448 size_t position = 0;
449 int ret_val = 0;
450 EVAS_FONT_WALK_TEXT_INIT();
451
452 asc = evas_common_font_max_ascent_get(fn);
453 desc = evas_common_font_max_descent_get(fn);
454
455 position = pos;
456 /* If it's the null, choose location according to the direction. */
457 if (position == text_props->text_len)
458 {
459 /* if it's rtl then the location is the left of the string,
460 * otherwise, the right. */
461 #ifdef BIDI_SUPPORT
462 if (text_props->bidi_dir == EVAS_BIDI_DIRECTION_RTL)
463 {
464 if (cx) *cx = 0;
465 if (ch) *ch = asc + desc;
466 }
467 else
468 #endif
469 {
470 evas_common_font_query_advance(fn, text_props, cx, ch);
471 }
472 if (cy) *cy = 0;
473 if (cw) *cw = 0;
474 ret_val = 1;
475 goto end;
476 }
477
478 Evas_Coord cluster_start = 0, last_end = 0;
479 int prev_cluster = -1;
480 int found = 0, items = 1, item_pos = 1;
481 int last_is_visible = 0;
482 EVAS_FONT_WALK_TEXT_START()
483 {
484 EVAS_FONT_WALK_TEXT_WORK();
485
486 if (prev_cluster != (int) EVAS_FONT_WALK_POS)
487 {
488 if (found)
489 {
490 break;
491 }
492 else
493 {
494 cluster_start = EVAS_FONT_WALK_PEN_X +
495 EVAS_FONT_WALK_X_OFF +
496 EVAS_FONT_WALK_X_BEAR;
497 }
498 }
499 last_is_visible = EVAS_FONT_WALK_IS_VISIBLE;
500 last_end = EVAS_FONT_WALK_PEN_X + EVAS_FONT_WALK_X_OFF +
501 EVAS_FONT_WALK_X_BEAR + EVAS_FONT_WALK_WIDTH;
502 /* we need to see if the char at the visual position is the char wanted */
503 if ((text_props->bidi_dir == EVAS_BIDI_DIRECTION_LTR) &&
504 (EVAS_FONT_WALK_POS <= (size_t) position) &&
505 ((((size_t) position) < EVAS_FONT_WALK_POS_NEXT) ||
506 (EVAS_FONT_WALK_IS_LAST)))
507 {
508 found = 1;
509 #ifdef OT_SUPPORT
510 items = evas_common_font_ot_cluster_size_get(text_props,
511 char_index);
512 #endif
513 item_pos = position - EVAS_FONT_WALK_POS + 1;
514 }
515 else if ((text_props->bidi_dir == EVAS_BIDI_DIRECTION_RTL) &&
516 ((EVAS_FONT_WALK_POS_PREV > (size_t) position) ||
517 (EVAS_FONT_WALK_IS_FIRST)) &&
518 (((size_t) position) >= EVAS_FONT_WALK_POS))
519 {
520 found = 1;
521 #ifdef OT_SUPPORT
522 items = evas_common_font_ot_cluster_size_get(text_props,
523 char_index);
524 #endif
525 item_pos = items - (position - EVAS_FONT_WALK_POS);
526 }
527
528 prev_cluster = EVAS_FONT_WALK_POS;
529 }
530 EVAS_FONT_WALK_TEXT_END();
531 if (found)
532 {
533 Evas_Coord cluster_w;
534 cluster_w = last_end - cluster_start;
535 if (cy) *cy = -asc;
536 if (ch) *ch = asc + desc;
537 if (last_is_visible)
538 {
539 if (cx) *cx = cluster_start +
540 (cluster_w / items) *
541 (item_pos - 1);
542 if (cw) *cw = (cluster_w / items);
543 }
544 else
545 {
546 if (cx) *cx = cluster_start;
547 if (cw) *cw = 0;
548 }
549 ret_val = 1;
550 goto end;
551 }
552 end:
553
554 return ret_val;
555 }
556
557 /**
558 * @internal
559 * Query the coordinates of the char at position pos. If the position is at the
560 * end of the string (i.e where the finishing null would be) it returns the
561 * coordinates of the position right after the last char. This is either on
562 * the left or on the right of the string, depending on BiDi direction. Returned
563 * advance in this case is 0.
564 *
565 * This is the same as evas_common_font_query_char_coords() except that the
566 * advance of the character is returned instead of the width and the pen
567 * position is returned instead of the actual pixel position.
568 *
569 * @param fn the font set to use.
570 * @param text_props the string object.
571 * @param pos the position of the char in the string object (not actual position in the string object, but the position of the source character).
572 * @param[out] cpenx the calculated x - CAN BE NULL
573 * @param[out] cy the calculated y - CAN BE NULL
574 * @param[out] cadv the calculated advance - CAN BE NULL
575 * @param[out] ch the calculated height - CAN BE NULL
576 * @return TRUE on success, FALSE otherwise.
577 *
578 * @see evas_common_font_query_char_coords()
579 */
580 EAPI int
evas_common_font_query_pen_coords(RGBA_Font * fn,const Evas_Text_Props * text_props,int pos,int * cpen_x,int * cy,int * cadv,int * ch)581 evas_common_font_query_pen_coords(RGBA_Font *fn, const Evas_Text_Props *text_props, int pos, int *cpen_x, int *cy, int *cadv, int *ch)
582 {
583 int asc, desc;
584 size_t position;
585 int ret_val = 0;
586 EVAS_FONT_WALK_TEXT_INIT();
587
588 asc = evas_common_font_max_ascent_get(fn);
589 desc = evas_common_font_max_descent_get(fn);
590
591 position = pos;
592 /* If it's the null, choose location according to the direction. */
593 if (position == text_props->text_len)
594 {
595 /* if it's rtl then the location is the left of the string,
596 * otherwise, the right. */
597 #ifdef BIDI_SUPPORT
598 if (text_props->bidi_dir == EVAS_BIDI_DIRECTION_RTL)
599 {
600 if (cpen_x) *cpen_x = 0;
601 if (ch) *ch = asc + desc;
602 }
603 else
604 #endif
605 {
606 evas_common_font_query_advance(fn, text_props, cpen_x, ch);
607 }
608 if (cy) *cy = 0;
609 if (cadv) *cadv = 0;
610 ret_val = 1;
611 goto end;
612 }
613 Evas_Coord cluster_start = 0;
614 int prev_cluster = -1;
615 int found = 0, items = 1, item_pos = 1;
616 int last_is_visible = 0;
617 EVAS_FONT_WALK_TEXT_START()
618 {
619 EVAS_FONT_WALK_TEXT_WORK();
620
621 if (prev_cluster != (int) EVAS_FONT_WALK_POS)
622 {
623 if (found)
624 {
625 break;
626 }
627 else
628 {
629 cluster_start = EVAS_FONT_WALK_PEN_X;
630 }
631 }
632 last_is_visible = EVAS_FONT_WALK_IS_VISIBLE;
633
634 if ((text_props->bidi_dir == EVAS_BIDI_DIRECTION_LTR) &&
635 (EVAS_FONT_WALK_POS <= (size_t) position) &&
636 ((((size_t) position) < EVAS_FONT_WALK_POS_NEXT) ||
637 (EVAS_FONT_WALK_IS_LAST)))
638 {
639 found = 1;
640 #ifdef OT_SUPPORT
641 items = evas_common_font_ot_cluster_size_get(text_props,
642 char_index);
643 #endif
644 item_pos = position - EVAS_FONT_WALK_POS + 1;
645 }
646 else if ((text_props->bidi_dir == EVAS_BIDI_DIRECTION_RTL) &&
647 ((EVAS_FONT_WALK_POS_PREV > (size_t) position) ||
648 (EVAS_FONT_WALK_IS_FIRST)) &&
649 (((size_t) position) >= EVAS_FONT_WALK_POS))
650 {
651 found = 1;
652 #ifdef OT_SUPPORT
653 items = evas_common_font_ot_cluster_size_get(text_props,
654 char_index);
655 #endif
656 item_pos = items - (position - EVAS_FONT_WALK_POS);
657 }
658
659 prev_cluster = EVAS_FONT_WALK_POS;
660 }
661 EVAS_FONT_WALK_TEXT_END();
662
663 if (found)
664 {
665 Evas_Coord cluster_adv;
666 cluster_adv = EVAS_FONT_WALK_PEN_X - cluster_start;
667 if (cy) *cy = -asc;
668 if (ch) *ch = asc + desc;
669 if (last_is_visible)
670 {
671 if (cpen_x) *cpen_x = cluster_start +
672 (cluster_adv / items) *
673 (item_pos - 1);
674 if (cadv) *cadv = (cluster_adv / items);
675 }
676 else
677 {
678 if (cpen_x) *cpen_x = EVAS_FONT_WALK_PEN_X;
679 if (cadv) *cadv = 0;
680 }
681 ret_val = 1;
682 goto end;
683 }
684 end:
685
686 return ret_val;
687 }
688
689 /**
690 * @internal
691 * Find the character at a specific x, y coordinates and return it's position
692 * in the text (not in the text object, but in the source text). Also calculate
693 * the char's geometry.
694 *
695 * @param fn the font set to use.
696 * @param text_props the string object.
697 * @param x the x to look at.
698 * @param y the y to look at.
699 * @param[out] cx the calculated x - CAN BE NULL
700 * @param[out] cy the calculated y - CAN BE NULL
701 * @param[out] cw the calculated width - CAN BE NULL
702 * @param[out] ch the calculated height - CAN BE NULL
703 * @return the position found, -1 on failure.
704 */
705 EAPI int
evas_common_font_query_char_at_coords(RGBA_Font * fn,const Evas_Text_Props * text_props,int x,int y,int * cx,int * cy,int * cw,int * ch)706 evas_common_font_query_char_at_coords(RGBA_Font *fn, const Evas_Text_Props *text_props, int x, int y, int *cx, int *cy, int *cw, int *ch)
707 {
708 int asc, desc;
709 int ret_val = -1;
710 EVAS_FONT_WALK_TEXT_INIT();
711
712 asc = evas_common_font_max_ascent_get(fn);
713 desc = evas_common_font_max_descent_get(fn);
714 Evas_Coord cluster_start = 0;
715 int prev_cluster = -1;
716 int found = 0, items = 1;
717 EVAS_FONT_WALK_TEXT_START()
718 {
719 EVAS_FONT_WALK_TEXT_WORK();
720 if (prev_cluster != (int) EVAS_FONT_WALK_POS)
721 {
722 if (found)
723 {
724 break;
725 }
726 else
727 {
728 cluster_start = EVAS_FONT_WALK_PEN_X;
729 }
730 }
731
732 if (!EVAS_FONT_WALK_IS_VISIBLE) continue;
733
734 /* we need to see if the char at the visual position is the char,
735 * we check that by checking if it's before the current pen
736 * position and the next */
737 if ((x >= EVAS_FONT_WALK_PEN_X) &&
738 (x <= (EVAS_FONT_WALK_PEN_X_AFTER)) && (y >= -asc) && (y <= desc))
739 {
740 #ifdef OT_SUPPORT
741 items = evas_common_font_ot_cluster_size_get(text_props,
742 char_index);
743 #endif
744 found = 1;
745 }
746
747 prev_cluster = EVAS_FONT_WALK_POS;
748 }
749 EVAS_FONT_WALK_TEXT_END();
750 if (found)
751 {
752 int item_pos;
753 Evas_Coord cx_it, cw_it, cmid;
754 Evas_Coord cluster_adv;
755 cluster_adv = EVAS_FONT_WALK_PEN_X - cluster_start;
756
757 if (text_props->bidi_dir == EVAS_BIDI_DIRECTION_LTR)
758 {
759 double part;
760 part = (double) cluster_adv / items;
761 item_pos = (int) ((x - cluster_start) / part);
762 }
763 else
764 {
765 double part;
766 part = (double) cluster_adv / items;
767 item_pos = items - ((int) ((x - cluster_start) / part)) - 1;
768 }
769
770 cx_it = EVAS_FONT_WALK_PEN_X + ((cluster_adv / items) * (item_pos - 1));
771 cw_it = (cluster_adv / items);
772
773 if (cx) *cx = cx_it;
774 if (cy) *cy = -asc;
775 if (cw) *cw = cw_it;
776 if (ch) *ch = asc + desc;
777 ret_val = prev_cluster + item_pos;
778
779 /* Check, if x coord points to RIGHT half part of LTR char
780 * or to LEFT half char of RTL char. If so, increment found position */
781 cmid = cx_it + (cw_it / 2);
782 if (text_props->bidi_dir == EVAS_BIDI_DIRECTION_LTR)
783 {
784 if (x > cmid)
785 {
786 ret_val++;
787 }
788 }
789 else
790 {
791 if (x < cmid)
792 {
793 ret_val++;
794 }
795 }
796
797 goto end;
798 }
799 end:
800
801 return ret_val;
802 }
803
804 /**
805 * @internal
806 * Find one after the last character that fits until the boundaries set by x
807 * and y. I.e find the first char that doesn't fit.
808 * This LOGICALLY walks the string. This is needed for wrapping for example
809 * where we want the first part to be the first logical part.
810 *
811 * @param fn the font set to use.
812 * @param text_props the string object.
813 * @param x the x boundary.
814 * @param y the y boundary.
815 * @param width_offset the additional width only for allowing glyph's.
816 * @return the position found, -1 on failure.
817 */
818 EAPI int
evas_common_font_query_last_up_to_pos(RGBA_Font * fn,const Evas_Text_Props * text_props,int x,int y,int width_offset)819 evas_common_font_query_last_up_to_pos(RGBA_Font *fn, const Evas_Text_Props *text_props, int x, int y, int width_offset)
820 {
821 int asc, desc;
822 int ret=-1;
823
824 asc = evas_common_font_max_ascent_get(fn);
825 desc = evas_common_font_max_descent_get(fn);
826
827 #ifdef BIDI_SUPPORT
828 if (text_props->bidi_dir == EVAS_BIDI_DIRECTION_RTL)
829 {
830 Evas_Font_Glyph_Info *gli = NULL;
831 Evas_Coord full_adv = 0, pen_x = 0, start_pen = 0;
832 int i;
833
834 if ((text_props->info) && (text_props->len > 0))
835 {
836 gli = text_props->info->glyph + text_props->start;
837 full_adv = gli[text_props->len - 1].pen_after;
838 if (text_props->start > 0)
839 {
840 start_pen = gli[-1].pen_after;
841 full_adv -= start_pen;
842 }
843
844 gli += text_props->len - 1;
845
846 for (i = text_props->len - 1 ; i >= 0 ; i--, gli--)
847 {
848 pen_x = full_adv - (gli->pen_after - start_pen);
849 /* If invisible, skip */
850 if (gli->index == 0) continue;
851
852 /* FIXME: Should we care glyph's width for RTL?
853 I think if width+x_bear/advance stacked from left side,
854 we don't need to care glyph's width to find linebreak position
855 or ellipsis position.
856 Even if (x < (pen_x + gli->x_bear + gli->width)))) is removed,
857 the whole test suite is passed.
858 */
859 if ((x >= pen_x) &&
860 (((i == 0) && (x <= full_adv)) ||
861 (x < (full_adv - (gli[-1].pen_after - start_pen)) ||
862 (x < (pen_x + gli->x_bear + gli->width)))) &&
863 (y >= -asc) && (y <= desc))
864 {
865 #ifdef OT_SUPPORT
866 ret = EVAS_FONT_OT_POS_GET(
867 text_props->info->ot[text_props->start + i]) -
868 text_props->text_offset;
869 #else
870 ret = text_props->text_len - i - 1;
871 #endif
872 goto end;
873 }
874 }
875 }
876 }
877 else
878 #endif
879 {
880 EVAS_FONT_WALK_TEXT_INIT();
881 /* When text is not rtl, visual direction = logical direction */
882 EVAS_FONT_WALK_TEXT_START()
883 {
884 EVAS_FONT_WALK_TEXT_WORK();
885 if (!EVAS_FONT_WALK_IS_VISIBLE) continue;
886
887 if ((x >= EVAS_FONT_WALK_PEN_X) &&
888 ((x < (EVAS_FONT_WALK_PEN_X_AFTER)) ||
889 (x + width_offset < (EVAS_FONT_WALK_PEN_X +
890 _glyph_itr->x_bear + _glyph_itr->width))) &&
891 (y >= -asc) && (y <= desc))
892 {
893 ret = EVAS_FONT_WALK_POS;
894 goto end;
895 }
896 }
897 EVAS_FONT_WALK_TEXT_END();
898 }
899
900 end:
901
902 return ret;
903 }
904
905