herdsman pushed a commit to branch master.

http://git.enlightenment.org/core/efl.git/commit/?id=40dfc4a45dd076465ea36cfb9e7229da58a907f4

commit 40dfc4a45dd076465ea36cfb9e7229da58a907f4
Author: Daniel Hirt <daniel.h...@samsung.com>
Date:   Tue Oct 13 10:59:19 2015 +0300

    Evas textblock: add support for hyphenation wrap style
    
    We now support hyphenation in style. Use "wrap=hyphenation" to use this
    wrap option. It will hyphenate based on explicit SOFT HYPHEN (&shy;)
    placement in the text, and with the (optional) assistance of dictionaries
    compatible with Hunspell's "hyphen" library.
    
    This wrap mode favors breaking at hyphen positions in a word, over moving
    the whole word to the next line. It will put an additional "-" at the
    break position if it was hyphened.
    
    Enabling the hyphen dictionaries is done by adding these configure
    options:
      --enable-hyphen (requires Hunspell's "hyphen" library installed)
      --with-dictionaries-hyphen-dir=DIR (specifies the install location of
              the actual .dic dictionary files e.g. /usr/share/hyphen)
    
    Note that dictionary files are expected to be in the form of "en_US.dic"
    or anything that ends with it e.g. "hyph_en_US.dic" (this how they are
            named in Arch Linux).
    
    @feature
---
 configure.ac                                     |  34 +++
 src/Makefile_Evas.am                             |   1 +
 src/lib/evas/canvas/evas_object_textblock.c      | 318 ++++++++++++++++++++++-
 src/lib/evas/canvas/evas_textblock_hyphenation.x | 148 +++++++++++
 4 files changed, 492 insertions(+), 9 deletions(-)

diff --git a/configure.ac b/configure.ac
index 5c46f8f..d6933be 100644
--- a/configure.ac
+++ b/configure.ac
@@ -878,6 +878,9 @@ EINA_CONFIG([MAGIC_DEBUG], [test "x${have_magic_debug}" = 
"xyes"])
 AC_ARG_WITH([xattr-tests-path],
    [AS_HELP_STRING([--with-xattr-tests-path=DIR],[path of xattr enabled 
directory to create test 
files])],[XATTR_TEST_DIR=${withval}][AC_DEFINE_UNQUOTED([XATTR_TEST_DIR],["$withval"],
 [xattr enabled directory])])
 
+AC_ARG_WITH([dictionaries-hyphen-dir],
+   [AS_HELP_STRING([--with-dictionaries-hyphen-dir=DIR],[path of 
hunspell-compatible hyphen 
dictionaries])],[EVAS_DICTS_HYPHEN_DIR=${withval}][AC_DEFINE_UNQUOTED([EVAS_DICTS_HYPHEN_DIR],["$withval"],
 [Hunspell-compatible hyphen dictionaries install directory])])
+
 ### Checks for programs
 
 ### Checks for libraries
@@ -1609,6 +1612,18 @@ AC_ARG_ENABLE([harfbuzz],
    ],
    [want_harfbuzz="no"])
 
+# Hyphenation
+AC_ARG_ENABLE([hyphen],
+   [AS_HELP_STRING([--enable-hyphen],[enable text hyphenation support. 
@<:@default=disabled@:>@])],
+   [
+    if test "x${enableval}" = "xyes" ; then
+       want_hyphen="yes"
+    else
+       want_hyphen="no"
+    fi
+   ],
+   [want_hyphen="no"])
+
 # Egl
 AC_ARG_ENABLE([egl],
    [AS_HELP_STRING([--enable-egl],[enable EGL rendering. 
@<:@default=disabled@:>@])],
@@ -2066,6 +2081,25 @@ EFL_EVAL_PKGS([EVAS])
 
 ### Checks for header files
 
+if test "x$want_hyphen" = "xyes" ; then
+
+   EFL_CHECK_LIB_CODE([EVAS], [-lhyphen], [have_fct], [[
+   #include <string.h>
+   #include <stdlib.h>
+   #include <ctype.h>
+   #include <stdio.h>
+   #include <hyphen.h>
+   ]], [[
+   HyphenDict *dict;
+   ]])
+
+   if test "${have_fct}" = "no"; then
+     AC_MSG_ERROR([Cannot find the hyphen library.])
+   else
+     AC_DEFINE([HAVE_HYPHEN], [1], [have hunspell hyphen support])
+   fi
+fi
+
 if test "x$have_harfbuzz" = "xyes" ; then
 
    CPPFLAGS_SAVE="$CPPFLAGS"
diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am
index e5abebc..9cc57a5 100644
--- a/src/Makefile_Evas.am
+++ b/src/Makefile_Evas.am
@@ -148,6 +148,7 @@ lib/evas/canvas/evas_object_box.c \
 lib/evas/canvas/evas_object_table.c \
 lib/evas/canvas/evas_object_text.c \
 lib/evas/canvas/evas_object_textblock.c \
+lib/evas/canvas/evas_textblock_hyphenation.x \
 lib/evas/canvas/evas_object_textgrid.c \
 lib/evas/canvas/evas_object_grid.c \
 lib/evas/canvas/evas_font_dir.c \
diff --git a/src/lib/evas/canvas/evas_object_textblock.c 
b/src/lib/evas/canvas/evas_object_textblock.c
index c559ed2..7a9aafc 100644
--- a/src/lib/evas/canvas/evas_object_textblock.c
+++ b/src/lib/evas/canvas/evas_object_textblock.c
@@ -453,6 +453,7 @@ struct _Evas_Object_Textblock_Format
    Eina_Bool            wrap_word : 1;  /**< EINA_TRUE if only wraps lines at 
word boundaries, else EINA_FALSE. */
    Eina_Bool            wrap_char : 1;  /**< EINA_TRUE if wraps at any 
character, else EINA_FALSE. */
    Eina_Bool            wrap_mixed : 1;  /**< EINA_TRUE if wrap at words if 
possible, else EINA_FALSE. */
+   Eina_Bool            wrap_hyphenation : 1;  /**< EINA_TRUE if wrap at mixed 
and hyphenate if possible, else EINA_FALSE. */
    Eina_Bool            underline : 1;  /**< EINA_TRUE if a single line under 
the text, else EINA_FALSE */
    Eina_Bool            underline2 : 1;  /**< EINA_TRUE if two lines under the 
text, else EINA_FALSE */
    Eina_Bool            underline_dash : 1;  /**< EINA_TRUE if a dashed line 
under the text, else EINA_FALSE */
@@ -498,6 +499,7 @@ struct _Evas_Object_Textblock
    Eina_List                          *anchors_a;
    Eina_List                          *anchors_item;
    Eina_List                          *obstacles;
+   Eina_List                          *hyphen_items; /* Hyphen items storage 
to free when clearing lines */
    int                                 last_w, last_h;
    struct {
       int                              l, r, t, b;
@@ -518,6 +520,7 @@ struct _Evas_Object_Textblock
    Eina_Bool                           content_changed : 1;
    Eina_Bool                           format_changed : 1;
    Eina_Bool                           have_ellipsis : 1;
+   Eina_Bool                           hyphenating : 1;
    Eina_Bool                           legacy_newline : 1;
    Eina_Bool                           inherit_paragraph_direction : 1;
    Eina_Bool                           changed_paragraph_direction : 1;
@@ -615,6 +618,12 @@ static void 
_evas_textblock_invalidate_all(Evas_Textblock_Data *o);
 static void _evas_textblock_cursors_update_offset(const Evas_Textblock_Cursor 
*cur, const Evas_Object_Textblock_Node_Text *n, size_t start, int offset);
 static void _evas_textblock_cursors_set_node(Evas_Textblock_Data *o, const 
Evas_Object_Textblock_Node_Text *n, Evas_Object_Textblock_Node_Text *new_node);
 
+
+#ifdef HAVE_HYPHEN
+/* Hyphenation */
+#include "evas_textblock_hyphenation.x"
+#endif
+
 /** selection iterator */
 /**
   * @internal
@@ -900,6 +909,7 @@ static const char escape_strings[] =
 "&ordf;\0"     "\xc2\xaa\0"
 "&laquo;\0"    "\xc2\xab\0"
 "&not;\0"      "\xc2\xac\0"
+"&shy;\0"      "\xc2\xad\0"
 "&reg;\0"      "\xc2\xae\0"
 "&macr;\0"     "\xc2\xaf\0"
 "&deg;\0"      "\xc2\xb0\0"
@@ -1838,6 +1848,7 @@ _format_command(Evas_Object *eo_obj, 
Evas_Object_Textblock_Format *fmt, const ch
          * @li "word" - Only wraps lines at word boundaries
          * @li "char" - Wraps at any character
          * @li "mixed" - Wrap at words if possible, if not at any character
+         * @li "hyphenation" - Hyphenate if possible, if not wrap at words if 
possible, if not at any character
          * @li "" - Don't wrap
          * @code
          * wrap=<value or preset>
@@ -1849,15 +1860,18 @@ _format_command(Evas_Object *eo_obj, 
Evas_Object_Textblock_Format *fmt, const ch
            Eina_Bool wrap_word;
            Eina_Bool wrap_char;
            Eina_Bool wrap_mixed;
+           Eina_Bool wrap_hyphenation;
         } wrap_named[] = {
-          { "word", 4, 1, 0, 0 },
-          { "char", 4, 0, 1, 0 },
-          { "mixed", 5, 0, 0, 1 },
-          { NULL, 0, 0, 0, 0 }
+          { "word",        4,  1, 0, 0, 0 },
+          { "char",        4,  0, 1, 0, 0 },
+          { "mixed",       5,  0, 0, 1, 0 },
+          { "hyphenation", 11, 0, 0, 0, 1 },
+          { NULL,          0,  0, 0, 0, 0 }
         };
         unsigned int i;
 
-        fmt->wrap_word = fmt->wrap_mixed = fmt->wrap_char = 0;
+        fmt->wrap_word = fmt->wrap_mixed = fmt->wrap_char =
+           fmt->wrap_hyphenation = 0;
         for (i = 0; wrap_named[i].param; i++)
           if (wrap_named[i].len == len &&
               !strcmp(wrap_named[i].param, param))
@@ -1865,8 +1879,19 @@ _format_command(Evas_Object *eo_obj, 
Evas_Object_Textblock_Format *fmt, const ch
                fmt->wrap_word = wrap_named[i].wrap_word;
                fmt->wrap_char = wrap_named[i].wrap_char;
                fmt->wrap_mixed = wrap_named[i].wrap_mixed;
+               fmt->wrap_hyphenation = wrap_named[i].wrap_hyphenation;
                break;
             }
+
+#ifdef HAVE_HYPHEN
+        /* Hyphenating textblocks are registered as "clients", so we 
load/unload
+         * the hyphenation dictionaries on-demand. */
+        if (fmt->wrap_hyphenation)
+          {
+             _dicts_hyphen_update(eo_obj);
+          }
+#endif
+
      }
    else if (cmd == left_marginstr)
      {
@@ -2562,6 +2587,7 @@ struct _Ctxt
    Evas_Object_Textblock_Paragraph *paragraphs;
    Evas_Object_Textblock_Paragraph *par;
    Evas_Object_Textblock_Line *ln;
+   Evas_Object_Textblock_Text_Item *hyphen_ti;
 
 
    Eina_List *format_stack;
@@ -2927,15 +2953,35 @@ _layout_update_bidi_props(const Evas_Textblock_Data *o,
  * Free the visual lines in the paragraph (logical items are kept)
  */
 static void
-_paragraph_clear(const Evas_Object *obj EINA_UNUSED,
+_paragraph_clear(const Evas_Object *obj,
       Evas_Object_Textblock_Paragraph *par)
 {
+   Evas_Textblock_Data *o = eo_data_scope_get(obj, MY_CLASS);
+
    while (par->lines)
      {
         Evas_Object_Textblock_Line *ln;
 
         ln = (Evas_Object_Textblock_Line *) par->lines;
         par->lines = (Evas_Object_Textblock_Line 
*)eina_inlist_remove(EINA_INLIST_GET(par->lines), EINA_INLIST_GET(par->lines));
+
+        /* Could be done better, but it's only when hyphenating and limited
+         * to number of hyphens created */
+        if (o->hyphenating)
+          {
+             Evas_Object_Textblock_Text_Item *ti;
+             Eina_List *i, *i_next;
+
+             EINA_LIST_FOREACH_SAFE(o->hyphen_items, i, i_next, ti)
+               {
+                  if (ti->parent.ln == ln)
+                    {
+                       o->hyphen_items = 
eina_list_remove_list(o->hyphen_items, i);
+                       _item_free(obj, NULL, _ITEM(ti));
+                    }
+               }
+          }
+
         _line_free(ln);
      }
 }
@@ -3594,6 +3640,16 @@ loop_advance:
 static void
 _layout_line_advance(Ctxt *c, Evas_Object_Textblock_Format *fmt)
 {
+   if (c->hyphen_ti)
+     {
+        c->ln->items = (Evas_Object_Textblock_Item *)
+           eina_inlist_append(EINA_INLIST_GET(c->ln->items),
+                 EINA_INLIST_GET(_ITEM(c->hyphen_ti)));
+        c->hyphen_ti->parent.ln = c->ln;
+        c->o->hyphen_items =
+           eina_list_append(c->o->hyphen_items, c->hyphen_ti);
+        c->hyphen_ti = NULL;
+     }
    _layout_line_finalize(c, fmt);
    _layout_line_new(c, fmt);
 }
@@ -3647,6 +3703,8 @@ _layout_text_cutoff_get(Ctxt *c, 
Evas_Object_Textblock_Format *fmt,
    return -1;
 }
 
+static Evas_Object_Textblock_Text_Item * _layout_hyphen_item_new(Ctxt *c, 
const Evas_Object_Textblock_Text_Item *cur_ti);
+
 /**
  * @internal
  * Split before cut, and strip if str[cut - 1] is a whitespace.
@@ -4364,6 +4422,175 @@ _layout_get_charwrap(Ctxt *c, 
Evas_Object_Textblock_Format *fmt,
 #define ALLOW_BREAK(i) \
    (breaks[i] <= LINEBREAK_ALLOWBREAK)
 
+/* Give a position in text, find the end of word by using Unicode word
+ * boundary rules */
+static inline size_t
+_layout_word_end(const char *breaks, size_t pos, size_t len)
+{
+   for ( ; (pos < len - 1) && (breaks[pos] != WORDBREAK_BREAK) ; pos++)
+      ;
+   return pos;
+}
+
+#define SHY_HYPHEN 0xad
+
+static int
+_layout_get_hyphenationwrap(Ctxt *c, Evas_Object_Textblock_Format *fmt,
+      const Evas_Object_Textblock_Item *it, size_t line_start,
+      const char *breaks, const char *wordbreaks)
+{
+   size_t wrap;
+   size_t orig_wrap;
+   const Eina_Unicode *str = eina_ustrbuf_string_get(
+         it->text_node->unicode);
+   int item_start = it->text_pos;
+   size_t len = eina_ustrbuf_length_get(it->text_node->unicode);
+   Eina_Bool try_hyphenate = EINA_FALSE;
+
+     {
+        int swrap = -1;
+        int hyphen_swrap = -1;
+
+        if (it->type == EVAS_TEXTBLOCK_ITEM_FORMAT)
+           swrap = 0;
+        else
+          {
+             Evas_Coord cw;
+
+             /* Get cutoff */
+             swrap = _layout_text_cutoff_get(c, fmt, _ITEM_TEXT(it));
+
+             /* Get cutoff considering an additional hyphen item */
+             cw = c->w;
+             c->hyphen_ti = _layout_hyphen_item_new(c, _ITEM_TEXT(it));
+             c->w -= c->hyphen_ti->parent.w;
+             hyphen_swrap = _layout_text_cutoff_get(c, fmt, _ITEM_TEXT(it));
+             c->w = cw;
+
+             /* Stronger condition than '< 0' for hyphenations */
+             if (hyphen_swrap >= 2)
+               {
+                  try_hyphenate = EINA_TRUE;
+               }
+             else
+               {
+                  _item_free(c->obj, NULL, _ITEM(c->hyphen_ti));
+                  c->hyphen_ti = NULL;
+               }
+          }
+
+        if (swrap < 0)
+           return -1;
+
+        orig_wrap = wrap = swrap + item_start;
+        if (try_hyphenate)
+          {
+             orig_wrap = wrap = hyphen_swrap + item_start;
+          }
+     }
+
+   if (wrap > line_start)
+     {
+        Eina_Bool found_hyphen = EINA_FALSE;
+        size_t word_end;
+
+        if (!_is_white(str[wrap]) || (wrap + 1 == len))
+           MOVE_PREV_UNTIL(line_start, wrap);
+
+        /* If there's a breakable point inside the text, scan backwards until
+         * we find it */
+        while (wrap > line_start)
+          {
+             /* When iterating back, 'wrap - 1' is the word delimiter,
+              * but isn't the word's start. The word's start is 'wrap'. */
+             if (try_hyphenate && ((wordbreaks[wrap - 1] == WORDBREAK_BREAK) ||
+                      (wrap - 1 == line_start)))
+               {
+                  size_t word_start, word_len;
+
+                  word_start = wrap; /* easier to understand if we tag this */
+                  word_end = _layout_word_end(wordbreaks, wrap, len);
+                  word_len = word_end - word_start + 1;
+
+                  if (word_len >= 4)
+                    {
+                       char *hyphens = NULL;
+                       size_t hyphen_off;
+                       size_t i = 0;
+                       size_t pos = 0;
+
+#ifdef HAVE_HYPHEN
+                       hyphens = _layout_wrap_hyphens_get(str, 
it->format->font.fdesc->lang, word_start, word_len);
+#endif
+
+                       /* This only happens one time, if the cutoff is in
+                        * the middle of this word */
+                       if (word_end > orig_wrap - 1)
+                         {
+                            word_end = orig_wrap - 1;
+                         }
+
+                       hyphen_off = word_end - word_start;
+
+                       /* We limit our search to the start of the line */
+                       if (word_start < line_start)
+                         {
+                            word_start = line_start;
+                         }
+
+                       for (i = hyphen_off, pos = word_end ; pos > word_start 
; i--, pos--)
+                         {
+                            if ((hyphens && (hyphens[i] & 1)) || str[pos] == 
SHY_HYPHEN)
+                              {
+                                 found_hyphen = EINA_TRUE;
+                                 break;
+                              }
+                         }
+
+                       if (hyphens)
+                         {
+                            free(hyphens);
+                            hyphens = NULL;
+                         }
+
+                       /* Rejecting sequences smaller than 2 characters.
+                        * This also works with 'i' initialized to 0 */
+                       if (found_hyphen)
+                         {
+                            wrap = pos;
+                            break;
+                         }
+                    }
+               }
+
+             /* SHY-HYPHEN is considered a wordbreak. We don't block it
+              * internally in ALLOW_BREAK, just here. */
+             if (ALLOW_BREAK(wrap) && (str[wrap] != SHY_HYPHEN))
+                break;
+             wrap--;
+          }
+
+        /* hyphen item cleanup */
+        if (!found_hyphen && c->hyphen_ti)
+          {
+             _item_free(c->obj, NULL, _ITEM(c->hyphen_ti));
+             c->hyphen_ti = NULL;
+          }
+
+        if ((wrap > line_start) ||
+              ((wrap == line_start) && (ALLOW_BREAK(wrap)) && (wrap < len)))
+          {
+             /* We found a suitable wrapping point, break here. */
+             MOVE_NEXT_UNTIL(len, wrap);
+             return wrap;
+          }
+     }
+
+   /* Hyphenation falls-back to char wrapping at start of line */
+   return _layout_get_charwrap(c, fmt, it,
+         line_start, breaks);
+}
+
 static int
 _layout_get_word_mixwrap_common(Ctxt *c, Evas_Object_Textblock_Format *fmt,
       const Evas_Object_Textblock_Item *it, Eina_Bool mixed_wrap,
@@ -4816,6 +5043,7 @@ _layout_par(Ctxt *c)
    int ret = 0;
    int wrap = -1;
    char *line_breaks = NULL;
+   char *word_breaks = NULL;
 
    /* Obstacles logic */
    Eina_Bool handle_obstacles = EINA_FALSE;
@@ -4983,7 +5211,7 @@ _layout_par(Ctxt *c)
                    ((2 * it->h + c->y >
                      c->h - c->o->style_pad.t - c->o->style_pad.b) ||
                     (!it->format->wrap_word && !it->format->wrap_char &&
-                     !it->format->wrap_mixed)))
+                     !it->format->wrap_mixed && 
!it->format->wrap_hyphenation)))
                {
                   _layout_handle_ellipsis(c, it, i);
                   ret = 1;
@@ -4992,7 +5220,7 @@ _layout_par(Ctxt *c)
              /* If we want to wrap and it's worth checking for wrapping
               * (i.e there's actually text). */
              else if (((wrap > 0) || it->format->wrap_word || 
it->format->wrap_char ||
-                it->format->wrap_mixed) && it->text_node)
+                it->format->wrap_mixed || it->format->wrap_hyphenation) && 
it->text_node)
                {
                   size_t line_start;
                   size_t it_len;
@@ -5006,7 +5234,8 @@ _layout_par(Ctxt *c)
                   if (!line_breaks)
                     {
                        /* Only relevant in those cases */
-                       if (it->format->wrap_word || it->format->wrap_mixed)
+                       if (it->format->wrap_word || it->format->wrap_mixed ||
+                           it->format->wrap_hyphenation)
                          {
                             const char *lang;
                             lang = (it->format->font.fdesc) ?
@@ -5021,6 +5250,22 @@ _layout_par(Ctxt *c)
                                   len, lang, line_breaks);
                          }
                     }
+
+                  if (!word_breaks && it->format->wrap_hyphenation)
+                    {
+                       const char *lang;
+                       lang = (it->format->font.fdesc) ?
+                          it->format->font.fdesc->lang : "";
+                       size_t len =
+                          eina_ustrbuf_length_get(
+                                it->text_node->unicode);
+                       word_breaks = malloc(len);
+                       set_wordbreaks_utf32((const utf32_t *)
+                             eina_ustrbuf_string_get(
+                                it->text_node->unicode),
+                             len, lang, word_breaks);
+                    }
+
                   if (c->ln->items)
                      line_start = c->ln->items->text_pos;
                   else
@@ -5059,6 +5304,9 @@ _layout_par(Ctxt *c)
                        else if (it->format->wrap_mixed)
                           wrap = _layout_get_mixedwrap(c, it->format, it,
                                 line_start, line_breaks, allow_scan_fwd);
+                       else if (it->format->wrap_hyphenation)
+                          wrap = _layout_get_hyphenationwrap(c, it->format, it,
+                                line_start, line_breaks, word_breaks);
                        else
                           wrap = -1;
                        c->w = save_cw;
@@ -5233,6 +5481,8 @@ _layout_par(Ctxt *c)
 end:
    if (line_breaks)
       free(line_breaks);
+   if (word_breaks)
+      free(word_breaks);
 
 #ifdef BIDI_SUPPORT
    if (c->par->bidi_props)
@@ -5641,6 +5891,7 @@ _layout(const Evas_Object *eo_obj, int w, int h, int 
*w_ret, int *h_ret)
    c->ln = NULL;
    c->width_changed = (obj->cur->geometry.w != o->last_w);
    c->obs_infos = NULL;
+   c->hyphen_ti = NULL;
 
    /* Start of logical layout creation */
    /* setup default base style */
@@ -7200,6 +7451,47 @@ _layout_item_obstacle_get(Ctxt *c, 
Evas_Object_Textblock_Item *it)
    return min_obs;
 }
 
+/* Hyphenation (since 1.17) */
+static Evas_Object_Textblock_Text_Item *
+_layout_hyphen_item_new(Ctxt *c, const Evas_Object_Textblock_Text_Item *cur_ti)
+{
+   /* U+2010 - Unicode HYPHEN */
+   const Eina_Unicode _hyphen_str[2] = { 0x2010, '\0' };
+   Evas_Object_Textblock_Text_Item *hyphen_ti;
+   Evas_Script_Type script;
+   Evas_Font_Instance *script_fi = NULL, *cur_fi;
+   size_t len = 1; /* The length of _hyphen_str */
+
+   if (c->hyphen_ti)
+     {
+        _item_free(c->obj, NULL, _ITEM(c->hyphen_ti));
+     }
+   c->hyphen_ti = hyphen_ti = _layout_text_item_new(c, cur_ti->parent.format);
+   hyphen_ti->parent.text_node = cur_ti->parent.text_node;
+   hyphen_ti->parent.text_pos = cur_ti->parent.text_pos + 
cur_ti->text_props.text_len - 1;
+   script = evas_common_language_script_type_get(_hyphen_str, len);
+
+   evas_common_text_props_bidi_set(&hyphen_ti->text_props,
+         c->par->bidi_props, hyphen_ti->parent.text_pos);
+   evas_common_text_props_script_set (&hyphen_ti->text_props, script);
+
+   if (hyphen_ti->parent.format->font.font)
+     {
+        Evas_Object_Protected_Data *obj = eo_data_scope_get(c->obj, 
EVAS_OBJECT_CLASS);
+        /* It's only 1 char anyway, we don't need the run end. */
+        (void) ENFN->font_run_end_get(ENDT,
+              hyphen_ti->parent.format->font.font, &script_fi, &cur_fi,
+              script, _hyphen_str, len);
+
+        ENFN->font_text_props_info_create(ENDT,
+              cur_fi, _hyphen_str, &hyphen_ti->text_props,
+              c->par->bidi_props, hyphen_ti->parent.text_pos, len, 
EVAS_TEXT_PROPS_MODE_SHAPE);
+     }
+
+   _text_item_update_sizes(c, hyphen_ti);
+   return hyphen_ti;
+}
+
 /* cursors */
 
 /**
@@ -11566,6 +11858,14 @@ evas_object_textblock_free(Evas_Object *eo_obj)
 
   /* remove obstacles */
   _obstacles_free(eo_obj, o);
+
+#ifdef HAVE_HYPHEN
+  /* Hyphenation */
+  if (o->hyphenating)
+    {
+       _dicts_hyphen_detach();
+    }
+#endif
 }
 
 static void
diff --git a/src/lib/evas/canvas/evas_textblock_hyphenation.x 
b/src/lib/evas/canvas/evas_textblock_hyphenation.x
new file mode 100644
index 0000000..930b5af
--- /dev/null
+++ b/src/lib/evas/canvas/evas_textblock_hyphenation.x
@@ -0,0 +1,148 @@
+#ifndef EVAS_TEXTBLOCK_HYPHENATION_H
+#define EVAS_TEXTBLOCK_HYPHENATION_H
+#ifdef HAVE_HYPHEN
+#include <hyphen.h>
+
+typedef struct
+{
+   const char *lang;
+   HyphenDict *dict;
+} Dict_Hyphen;
+
+/* Hyphenation dictionaries */
+static Dict_Hyphen _dicts_hyphen[64];
+static Eina_Bool  _dicts_hyphen_init = EINA_FALSE;
+static size_t     _hyphens_num = 0;
+static size_t     _hyphen_clients = 0;
+
+static void
+_dicts_hyphen_update(Eo *eo_obj)
+{
+   Eina_Iterator *it;
+   Eina_File_Direct_Info *dir;
+
+   Evas_Textblock_Data *o = eo_data_scope_get(eo_obj, MY_CLASS);
+
+   if (!o->hyphenating)
+     {
+        _hyphen_clients++;
+        o->hyphenating = EINA_TRUE;
+     }
+
+   if (_dicts_hyphen_init) return;
+
+   it = eina_file_direct_ls(EVAS_DICTS_HYPHEN_DIR);
+   if (!it)
+     {
+        ERR("Couldn't list files in hyphens path: %s\n", 
EVAS_DICTS_HYPHEN_DIR);
+        return;
+     }
+
+   _dicts_hyphen_init = EINA_TRUE;
+
+   /* The following is based on how files are installed in arch linux:
+    * the files are in the pattern of "hyph_xx_XX.dic" (e.g. hyph_en_US.dic).
+    * We are actually trying a bit more in case these are installed in another
+    * name. We assume that they probably end in "xx_XX.dic" anyway. */
+   EINA_ITERATOR_FOREACH(it, dir)
+     {
+        const char *file = dir->path + dir->name_start;
+        char *prefix_off; /* 'hyph_' prefix (may be in some distros) */
+        char *dic_off; /* '.dic' file extension offset */
+        void *dict;
+
+        /* Check a few assumptions and reject if aren't met. */
+        prefix_off = strstr(file, "hyph_");
+        dic_off = strrchr(file, '.');
+        if (!dic_off || ((size_t) (dic_off - file) + 4 != dir->name_length) ||
+            (dic_off - file < 5)  ||
+            ((dic_off - file > 0) && !prefix_off) ||
+            strncmp(dic_off, ".dic", 4))
+          {
+             continue;
+          }
+
+        dict = hnj_hyphen_load(dir->path);
+        if (!dict)
+          {
+             ERR("Couldn't load hyphen dictionary: %s\n", dic_off - 5);
+             continue;
+          }
+        _dicts_hyphen[_hyphens_num].lang = strndup(dic_off - 5, 5);
+        _dicts_hyphen[_hyphens_num++].dict = dict;
+     }
+
+   if (it) eina_iterator_free(it);
+}
+
+static void
+_dicts_hyphen_free(void)
+{
+   if (_hyphens_num == 0) return;
+
+   for (size_t i = 0; i < _hyphens_num; i++)
+     {
+        hnj_hyphen_free(_dicts_hyphen[i].dict);
+     }
+
+   _hyphens_num = 0;
+   _dicts_hyphen_init = EINA_FALSE;
+}
+
+static inline void
+_dicts_hyphen_detach(void)
+{
+   _hyphen_clients--;
+   if (_hyphen_clients == 0) _dicts_hyphen_free();
+}
+
+/* Returns the hyphen dictionary that matches the given language
+ * string. The string should be in the format xx_XX e.g. en_US */
+static inline void *
+_hyphen_dict_get_from_lang(const char *lang)
+{
+   if (!lang || !(*lang))
+     {
+        if (!lang) lang = evas_common_language_from_locale_full_get();
+        if (!lang || !(*lang)) return NULL;
+     }
+
+   for (size_t i = 0; i < _hyphens_num; i++)
+     {
+        if (!strcmp(_dicts_hyphen[i].lang, lang))
+          {
+             return _dicts_hyphen[i].dict;
+          }
+     }
+   return NULL;
+}
+
+static char *
+_layout_wrap_hyphens_get(const Eina_Unicode *text, const char *lang,
+      int word_start, int word_len)
+{
+   char *utf8;
+   int utf8_len; /* length of word */
+   char *hyphens;
+   char **rep = NULL;
+   int *pos = NULL;
+   int *cut = NULL;
+   void *dict;
+
+   dict = _hyphen_dict_get_from_lang(lang);
+   if (!dict)
+     {
+        ERR("Couldn't find matching dictionary and couldn't fallback to locale 
%s\n", lang);
+        return NULL;
+     }
+
+   utf8 = eina_unicode_unicode_to_utf8_range(
+         text + word_start, word_len, &utf8_len);
+   hyphens = malloc(sizeof(char) * (word_len + 5));
+   hnj_hyphen_hyphenate2(dict, utf8, word_len, hyphens, NULL, &rep, &pos, 
&cut);
+   free(utf8);
+   return hyphens;
+}
+
+#endif //HAVE_HYPHEN
+#endif //EVAS_TEXTBLOCK_HYPHENATION_H_

-- 


Reply via email to