2011-02-01 04:17:52 -08:00
|
|
|
#include "evas_common.h"
|
|
|
|
#include "evas_font_private.h"
|
2011-01-30 02:36:39 -08:00
|
|
|
#include "evas_text_utils.h"
|
|
|
|
#include "language/evas_bidi_utils.h"
|
|
|
|
#include "language/evas_language_utils.h"
|
|
|
|
#include "evas_font_ot.h"
|
|
|
|
|
2011-02-16 08:36:16 -08:00
|
|
|
/* Used for showing "malformed" or missing chars */
|
|
|
|
#define REPLACEMENT_CHAR 0xFFFD
|
|
|
|
|
2011-01-30 02:36:39 -08:00
|
|
|
void
|
|
|
|
evas_common_text_props_bidi_set(Evas_Text_Props *props,
|
|
|
|
Evas_BiDi_Paragraph_Props *bidi_par_props, size_t start)
|
|
|
|
{
|
|
|
|
#ifdef BIDI_SUPPORT
|
|
|
|
props->bidi.dir = (evas_bidi_is_rtl_char(
|
|
|
|
bidi_par_props,
|
|
|
|
0,
|
|
|
|
start)) ? EVAS_BIDI_DIRECTION_RTL : EVAS_BIDI_DIRECTION_LTR;
|
|
|
|
#else
|
|
|
|
(void) start;
|
|
|
|
(void) bidi_par_props;
|
|
|
|
props->bidi.dir = EVAS_BIDI_DIRECTION_LTR;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
evas_common_text_props_script_set(Evas_Text_Props *props,
|
|
|
|
const Eina_Unicode *str)
|
|
|
|
{
|
|
|
|
props->script = evas_common_language_script_type_get(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
evas_common_text_props_content_copy_and_ref(Evas_Text_Props *dst,
|
|
|
|
const Evas_Text_Props *src)
|
|
|
|
{
|
|
|
|
memcpy(dst, src, sizeof(Evas_Text_Props));
|
2011-01-30 02:43:34 -08:00
|
|
|
evas_common_text_props_content_ref(dst);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
evas_common_text_props_content_ref(Evas_Text_Props *props)
|
|
|
|
{
|
2011-02-10 07:29:13 -08:00
|
|
|
/* No content in this case */
|
|
|
|
if (!props->info)
|
2011-02-06 00:31:46 -08:00
|
|
|
return;
|
|
|
|
|
2011-02-01 04:17:52 -08:00
|
|
|
props->info->refcount++;
|
2011-01-30 02:36:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
evas_common_text_props_content_unref(Evas_Text_Props *props)
|
|
|
|
{
|
2011-02-10 07:29:13 -08:00
|
|
|
/* No content in this case */
|
|
|
|
if (!props->info)
|
2011-02-06 00:31:46 -08:00
|
|
|
return;
|
2011-01-30 02:36:39 -08:00
|
|
|
|
2011-02-01 04:17:52 -08:00
|
|
|
if (--(props->info->refcount) == 0)
|
|
|
|
{
|
|
|
|
if (props->info->glyph)
|
|
|
|
free(props->info->glyph);
|
2011-01-30 02:38:15 -08:00
|
|
|
#ifdef OT_SUPPORT
|
2011-02-01 04:17:52 -08:00
|
|
|
if (props->info->ot)
|
|
|
|
free(props->info->ot);
|
2011-01-30 02:38:15 -08:00
|
|
|
#endif
|
2011-02-01 04:17:52 -08:00
|
|
|
free(props->info);
|
|
|
|
props->info = NULL;
|
|
|
|
}
|
2011-01-30 02:38:15 -08:00
|
|
|
}
|
|
|
|
|
2011-02-06 00:31:46 -08:00
|
|
|
/* Won't work in the middle of ligatures, assumes cutoff < len */
|
2011-01-30 02:40:30 -08:00
|
|
|
EAPI void
|
|
|
|
evas_common_text_props_split(Evas_Text_Props *base,
|
2011-02-10 07:02:26 -08:00
|
|
|
Evas_Text_Props *ext, int _cutoff)
|
2011-01-30 02:40:30 -08:00
|
|
|
{
|
2011-02-10 07:02:26 -08:00
|
|
|
size_t cutoff;
|
|
|
|
|
|
|
|
/* Translate text cutoff pos to string object cutoff point */
|
|
|
|
#ifdef OT_SUPPORT
|
|
|
|
cutoff = 0;
|
|
|
|
|
|
|
|
{
|
|
|
|
Evas_Font_OT_Info *itr;
|
|
|
|
size_t i;
|
|
|
|
itr = base->info->ot + base->start;
|
|
|
|
_cutoff += base->text_offset;
|
|
|
|
/* FIXME: can I binary search? I don't think this is always sorted */
|
|
|
|
for (i = 0 ; i < base->len ; i++, itr++)
|
|
|
|
{
|
|
|
|
if (itr->source_cluster == (size_t) _cutoff)
|
|
|
|
{
|
|
|
|
if (base->bidi.dir == EVAS_BIDI_DIRECTION_RTL)
|
|
|
|
{
|
|
|
|
/* Walk to the last one of the same cluster */
|
|
|
|
for ( ; i < base->len ; i++, itr++)
|
|
|
|
{
|
|
|
|
if (itr->source_cluster != (size_t) _cutoff)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
cutoff = base->len - i;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cutoff = i;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we didn't find a reasonable cut location, return. */
|
|
|
|
if (cutoff == 0)
|
|
|
|
{
|
2011-02-13 05:07:37 -08:00
|
|
|
ERR("Couldn't find the cutoff position. Is it inside a cluster?");
|
2011-02-10 07:02:26 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
cutoff = (size_t) _cutoff;
|
|
|
|
#endif
|
|
|
|
|
2011-02-01 04:17:52 -08:00
|
|
|
evas_common_text_props_content_copy_and_ref(ext, base);
|
|
|
|
if (base->bidi.dir == EVAS_BIDI_DIRECTION_RTL)
|
|
|
|
{
|
|
|
|
ext->start = base->start;
|
|
|
|
ext->len = base->len - cutoff;
|
|
|
|
base->start = (base->start + base->len) - cutoff;
|
|
|
|
base->len = cutoff;
|
|
|
|
|
|
|
|
#ifdef OT_SUPPORT
|
|
|
|
ext->text_offset =
|
|
|
|
ext->info->ot[ext->start + ext->len - 1].source_cluster;
|
|
|
|
#else
|
|
|
|
ext->text_offset = base->text_offset + base->len;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ext->start = base->start + cutoff;
|
|
|
|
ext->len = base->len - cutoff;
|
|
|
|
base->len = cutoff;
|
|
|
|
|
2011-01-30 05:58:06 -08:00
|
|
|
#ifdef OT_SUPPORT
|
2011-02-01 04:17:52 -08:00
|
|
|
ext->text_offset = ext->info->ot[ext->start].source_cluster;
|
|
|
|
#else
|
|
|
|
ext->text_offset = base->text_offset + base->len;
|
2011-01-30 05:58:06 -08:00
|
|
|
#endif
|
2011-02-01 04:17:52 -08:00
|
|
|
}
|
2011-02-13 04:57:37 -08:00
|
|
|
ext->text_len = base->text_len - (ext->text_offset - base->text_offset);
|
|
|
|
base->text_len = (ext->text_offset - base->text_offset);
|
2011-01-30 02:40:30 -08:00
|
|
|
}
|
|
|
|
|
2011-01-30 02:41:42 -08:00
|
|
|
/* Won't work in the middle of ligatures */
|
|
|
|
EAPI void
|
|
|
|
evas_common_text_props_merge(Evas_Text_Props *item1,
|
|
|
|
const Evas_Text_Props *item2)
|
|
|
|
{
|
2011-02-01 04:17:52 -08:00
|
|
|
if (item1->info != item2->info)
|
|
|
|
{
|
|
|
|
ERR("tried merge back items that weren't together in the first place.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (item1->bidi.dir == EVAS_BIDI_DIRECTION_RTL)
|
|
|
|
{
|
|
|
|
item1->start = item2->start;
|
|
|
|
}
|
|
|
|
|
|
|
|
item1->len += item2->len;
|
2011-02-13 04:57:37 -08:00
|
|
|
item1->text_len += item2->text_len;
|
2011-02-01 04:17:52 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
EAPI Eina_Bool
|
|
|
|
evas_common_text_props_content_create(void *_fn, const Eina_Unicode *text,
|
|
|
|
Evas_Text_Props *text_props, int len)
|
|
|
|
{
|
|
|
|
RGBA_Font *fn = (RGBA_Font *) _fn;
|
|
|
|
RGBA_Font_Int *fi;
|
|
|
|
size_t char_index;
|
|
|
|
|
|
|
|
if (text_props->info)
|
|
|
|
{
|
|
|
|
evas_common_text_props_content_unref(text_props);
|
|
|
|
}
|
2011-02-10 07:29:13 -08:00
|
|
|
if (len == 0)
|
|
|
|
{
|
|
|
|
text_props->info = NULL;
|
|
|
|
text_props->start = text_props->len = text_props->text_offset = 0;
|
|
|
|
}
|
2011-02-01 04:17:52 -08:00
|
|
|
text_props->info = calloc(1, sizeof(Evas_Text_Props_Info));
|
|
|
|
|
|
|
|
fi = fn->fonts->data;
|
|
|
|
/* evas_common_font_size_use(fn); */
|
|
|
|
evas_common_font_int_reload(fi);
|
|
|
|
if (fi->src->current_size != fi->size)
|
|
|
|
{
|
|
|
|
FTLOCK();
|
|
|
|
FT_Activate_Size(fi->ft.size);
|
|
|
|
FTUNLOCK();
|
|
|
|
fi->src->current_size = fi->size;
|
|
|
|
}
|
|
|
|
|
2011-01-30 05:58:06 -08:00
|
|
|
#ifdef OT_SUPPORT
|
2011-02-16 08:36:16 -08:00
|
|
|
const Eina_Unicode *base_char;
|
2011-02-01 04:17:52 -08:00
|
|
|
evas_common_font_ot_populate_text_props(fn, text, text_props, len);
|
|
|
|
|
|
|
|
/* Load the glyph according to the first letter of the script, preety
|
|
|
|
* bad, but will have to do */
|
|
|
|
{
|
|
|
|
/* Skip common chars */
|
2011-02-16 08:36:16 -08:00
|
|
|
for (base_char = text ;
|
|
|
|
*base_char &&
|
|
|
|
evas_common_language_char_script_get(*base_char) ==
|
2011-02-01 04:17:52 -08:00
|
|
|
EVAS_SCRIPT_COMMON ;
|
2011-02-16 08:36:16 -08:00
|
|
|
base_char++)
|
2011-02-01 04:17:52 -08:00
|
|
|
;
|
2011-02-16 08:36:16 -08:00
|
|
|
if (!*base_char && (base_char > text)) base_char--;
|
|
|
|
evas_common_font_glyph_search(fn, &fi, *base_char);
|
2011-02-01 04:17:52 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
for (char_index = 0 ; char_index < text_props->len ; char_index++)
|
|
|
|
{
|
|
|
|
FT_UInt index;
|
|
|
|
RGBA_Font_Glyph *fg;
|
2011-02-16 08:36:16 -08:00
|
|
|
Eina_Bool is_replacement = EINA_FALSE;
|
|
|
|
/* If we got a malformed index, show the replacement char instead */
|
|
|
|
if (text_props->info->glyph[char_index].index == 0)
|
|
|
|
{
|
|
|
|
text_props->info->glyph[char_index].index =
|
|
|
|
evas_common_font_glyph_search(fn, &fi, REPLACEMENT_CHAR);
|
|
|
|
is_replacement = EINA_TRUE;
|
|
|
|
}
|
2011-02-01 04:17:52 -08:00
|
|
|
index = text_props->info->glyph[char_index].index;
|
|
|
|
LKL(fi->ft_mutex);
|
|
|
|
fg = evas_common_font_int_cache_glyph_get(fi, index);
|
|
|
|
if (!fg)
|
|
|
|
{
|
|
|
|
LKU(fi->ft_mutex);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
LKU(fi->ft_mutex);
|
2011-02-16 08:36:16 -08:00
|
|
|
if (is_replacement)
|
|
|
|
{
|
|
|
|
/* Update the advance accordingly */
|
|
|
|
text_props->info->glyph[char_index].advance =
|
|
|
|
fg->glyph->advance.x >> 10;
|
|
|
|
/* FIXME: reload fi, a bit slow, but I have no choice. */
|
|
|
|
evas_common_font_glyph_search(fn, &fi, *base_char);
|
|
|
|
}
|
2011-02-01 04:17:52 -08:00
|
|
|
text_props->info->glyph[char_index].x_bear =
|
|
|
|
fg->glyph_out->left;
|
|
|
|
text_props->info->glyph[char_index].width =
|
|
|
|
fg->glyph_out->bitmap.width;
|
|
|
|
/* text_props->info->glyph[char_index].advance =
|
|
|
|
* text_props->info->glyph[char_index].index =
|
|
|
|
* already done by the ot function */
|
|
|
|
if (EVAS_FONT_CHARACTER_IS_INVISIBLE(
|
|
|
|
text[text_props->info->ot[char_index].source_cluster]))
|
|
|
|
text_props->info->glyph[char_index].index = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
/* We are walking the string in visual ordering */
|
|
|
|
Eina_Bool use_kerning;
|
|
|
|
FT_UInt prev_index;
|
|
|
|
FT_Face pface = NULL;
|
|
|
|
int adv_d, i;
|
|
|
|
FTLOCK();
|
|
|
|
use_kerning = FT_HAS_KERNING(fi->src->ft.face);
|
|
|
|
FTUNLOCK();
|
|
|
|
prev_index = 0;
|
|
|
|
|
|
|
|
i = len;
|
|
|
|
text_props->info->glyph = calloc(len,
|
|
|
|
sizeof(Evas_Font_Glyph_Info));
|
|
|
|
|
|
|
|
if (text_props->bidi.dir == EVAS_BIDI_DIRECTION_RTL)
|
|
|
|
{
|
|
|
|
text += len - 1;
|
|
|
|
adv_d = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
adv_d = 1;
|
|
|
|
}
|
|
|
|
char_index = 0;
|
|
|
|
for ( ; i > 0 ; char_index++, text += adv_d, i--)
|
|
|
|
{
|
|
|
|
FT_UInt index;
|
|
|
|
RGBA_Font_Glyph *fg;
|
|
|
|
int _gl, kern;
|
|
|
|
_gl = *text;
|
|
|
|
if (_gl == 0) break;
|
|
|
|
|
|
|
|
index = evas_common_font_glyph_search(fn, &fi, _gl);
|
2011-02-16 08:36:16 -08:00
|
|
|
if (index == 0)
|
|
|
|
{
|
|
|
|
index = evas_common_font_glyph_search(fn, &fi, REPLACEMENT_CHAR);
|
|
|
|
}
|
2011-02-01 04:17:52 -08:00
|
|
|
LKL(fi->ft_mutex);
|
|
|
|
fg = evas_common_font_int_cache_glyph_get(fi, index);
|
|
|
|
if (!fg)
|
|
|
|
{
|
|
|
|
LKU(fi->ft_mutex);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
kern = 0;
|
|
|
|
|
|
|
|
if ((use_kerning) && (prev_index) && (index) &&
|
|
|
|
(pface == fi->src->ft.face))
|
|
|
|
{
|
|
|
|
# ifdef BIDI_SUPPORT
|
|
|
|
/* if it's rtl, the kerning matching should be reversed, */
|
|
|
|
/* i.e prev index is now the index and the other way */
|
|
|
|
/* around. There is a slight exception when there are */
|
|
|
|
/* compositing chars involved.*/
|
|
|
|
if (text_props &&
|
|
|
|
(text_props->bidi.dir != EVAS_BIDI_DIRECTION_RTL))
|
|
|
|
{
|
|
|
|
if (evas_common_font_query_kerning(fi, index, prev_index, &kern))
|
|
|
|
{
|
|
|
|
text_props->info->glyph[char_index - 1].advance +=
|
|
|
|
kern;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
# endif
|
|
|
|
{
|
|
|
|
if (evas_common_font_query_kerning(fi, prev_index, index, &kern))
|
|
|
|
{
|
|
|
|
text_props->info->glyph[char_index - 1].advance +=
|
|
|
|
kern;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pface = fi->src->ft.face;
|
|
|
|
LKU(fi->ft_mutex);
|
|
|
|
|
|
|
|
if (EVAS_FONT_CHARACTER_IS_INVISIBLE(_gl))
|
|
|
|
text_props->info->glyph[char_index].index = 0;
|
|
|
|
|
|
|
|
text_props->info->glyph[char_index].index = index;
|
|
|
|
text_props->info->glyph[char_index].x_bear =
|
|
|
|
fg->glyph_out->left;
|
|
|
|
text_props->info->glyph[char_index].advance =
|
|
|
|
fg->glyph->advance.x >> 10;
|
|
|
|
text_props->info->glyph[char_index].width =
|
|
|
|
fg->glyph_out->bitmap.width;
|
|
|
|
|
|
|
|
prev_index = index;
|
|
|
|
}
|
|
|
|
text_props->len = len;
|
2011-01-30 05:58:06 -08:00
|
|
|
#endif
|
2011-02-13 04:57:37 -08:00
|
|
|
text_props->text_len = len;
|
2011-02-01 04:17:52 -08:00
|
|
|
text_props->info->refcount = 1;
|
|
|
|
return EINA_TRUE;
|
2011-01-30 05:55:04 -08:00
|
|
|
}
|
2011-01-30 02:38:15 -08:00
|
|
|
|