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