glib/libglib/src/gunidecomp.c
changeset 0 e4d67989cc36
equal deleted inserted replaced
-1:000000000000 0:e4d67989cc36
       
     1 /* decomp.c - Character decomposition.
       
     2  *
       
     3  *  Copyright (C) 1999, 2000 Tom Tromey
       
     4  *  Copyright 2000 Red Hat, Inc.
       
     5  * Portions copyright (c) 2006 Nokia Corporation.  All rights reserved.
       
     6  *
       
     7  * The Gnome Library is free software; you can redistribute it and/or
       
     8  * modify it under the terms of the GNU Lesser General Public License as
       
     9  * published by the Free Software Foundation; either version 2 of the
       
    10  * License, or (at your option) any later version.
       
    11  *
       
    12  * The Gnome Library is distributed in the hope that it will be useful,
       
    13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    15  * Lesser General Public License for more details.
       
    16  *
       
    17  * You should have received a copy of the GNU Lesser General Public
       
    18  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
       
    19  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    20  *   Boston, MA 02111-1307, USA.
       
    21  */
       
    22 
       
    23 #include "config.h"
       
    24 
       
    25 #include <stdlib.h>
       
    26 
       
    27 #include "glib.h"
       
    28 #include "gunidecomp.h"
       
    29 #include "gunicomp.h"
       
    30 #include "gunicodeprivate.h"
       
    31 #include "galias.h"
       
    32 
       
    33 
       
    34 #define CC_PART1(Page, Char) \
       
    35   ((combining_class_table_part1[Page] >= G_UNICODE_MAX_TABLE_INDEX) \
       
    36    ? (combining_class_table_part1[Page] - G_UNICODE_MAX_TABLE_INDEX) \
       
    37    : (cclass_data[combining_class_table_part1[Page]][Char]))
       
    38 
       
    39 #define CC_PART2(Page, Char) \
       
    40   ((combining_class_table_part2[Page] >= G_UNICODE_MAX_TABLE_INDEX) \
       
    41    ? (combining_class_table_part2[Page] - G_UNICODE_MAX_TABLE_INDEX) \
       
    42    : (cclass_data[combining_class_table_part2[Page]][Char]))
       
    43 
       
    44 #define COMBINING_CLASS(Char) \
       
    45   (((Char) <= G_UNICODE_LAST_CHAR_PART1) \
       
    46    ? CC_PART1 ((Char) >> 8, (Char) & 0xff) \
       
    47    : (((Char) >= 0xe0000 && (Char) <= G_UNICODE_LAST_CHAR) \
       
    48       ? CC_PART2 (((Char) - 0xe0000) >> 8, (Char) & 0xff) \
       
    49       : 0))
       
    50 
       
    51 gint
       
    52 _g_unichar_combining_class (gunichar uc)
       
    53 {
       
    54   return COMBINING_CLASS (uc);
       
    55 }
       
    56 
       
    57 /* constants for hangul syllable [de]composition */
       
    58 #define SBase 0xAC00 
       
    59 #define LBase 0x1100 
       
    60 #define VBase 0x1161 
       
    61 #define TBase 0x11A7
       
    62 #define LCount 19 
       
    63 #define VCount 21
       
    64 #define TCount 28
       
    65 #define NCount (VCount * TCount)
       
    66 #define SCount (LCount * NCount)
       
    67 
       
    68 /**
       
    69  * g_unicode_canonical_ordering:
       
    70  * @string: a UCS-4 encoded string.
       
    71  * @len: the maximum length of @string to use.
       
    72  *
       
    73  * Computes the canonical ordering of a string in-place.  
       
    74  * This rearranges decomposed characters in the string 
       
    75  * according to their combining classes.  See the Unicode 
       
    76  * manual for more information. 
       
    77  **/
       
    78 EXPORT_C void
       
    79 g_unicode_canonical_ordering (gunichar *string,
       
    80 			      gsize     len)
       
    81 {
       
    82   gsize i;
       
    83   int swap = 1;
       
    84 
       
    85   while (swap)
       
    86     {
       
    87       int last;
       
    88       swap = 0;
       
    89       last = COMBINING_CLASS (string[0]);
       
    90       for (i = 0; i < len - 1; ++i)
       
    91 	{
       
    92 	  int next = COMBINING_CLASS (string[i + 1]);
       
    93 	  if (next != 0 && last > next)
       
    94 	    {
       
    95 	      gsize j;
       
    96 	      /* Percolate item leftward through string.  */
       
    97 	      for (j = i + 1; j > 0; --j)
       
    98 		{
       
    99 		  gunichar t;
       
   100 		  if (COMBINING_CLASS (string[j - 1]) <= next)
       
   101 		    break;
       
   102 		  t = string[j];
       
   103 		  string[j] = string[j - 1];
       
   104 		  string[j - 1] = t;
       
   105 		  swap = 1;
       
   106 		}
       
   107 	      /* We're re-entering the loop looking at the old
       
   108 		 character again.  */
       
   109 	      next = last;
       
   110 	    }
       
   111 	  last = next;
       
   112 	}
       
   113     }
       
   114 }
       
   115 
       
   116 /* http://www.unicode.org/unicode/reports/tr15/#Hangul
       
   117  * r should be null or have sufficient space. Calling with r == NULL will
       
   118  * only calculate the result_len; however, a buffer with space for three
       
   119  * characters will always be big enough. */
       
   120 static void
       
   121 decompose_hangul (gunichar s, 
       
   122                   gunichar *r,
       
   123                   gsize *result_len)
       
   124 {
       
   125   gint SIndex = s - SBase;
       
   126 
       
   127   /* not a hangul syllable */
       
   128   if (SIndex < 0 || SIndex >= SCount)
       
   129     {
       
   130       if (r)
       
   131         r[0] = s;
       
   132       *result_len = 1;
       
   133     }
       
   134   else
       
   135     {
       
   136       gunichar L = LBase + SIndex / NCount;
       
   137       gunichar V = VBase + (SIndex % NCount) / TCount;
       
   138       gunichar T = TBase + SIndex % TCount;
       
   139 
       
   140       if (r)
       
   141         {
       
   142           r[0] = L;
       
   143           r[1] = V;
       
   144         }
       
   145 
       
   146       if (T != TBase) 
       
   147         {
       
   148           if (r)
       
   149             r[2] = T;
       
   150           *result_len = 3;
       
   151         }
       
   152       else
       
   153         *result_len = 2;
       
   154     }
       
   155 }
       
   156 
       
   157 /* returns a pointer to a null-terminated UTF-8 string */
       
   158 static const gchar *
       
   159 find_decomposition (gunichar ch,
       
   160 		    gboolean compat)
       
   161 {
       
   162   int start = 0;
       
   163   int end = G_N_ELEMENTS (decomp_table);
       
   164   
       
   165   if (ch >= decomp_table[start].ch &&
       
   166       ch <= decomp_table[end - 1].ch)
       
   167     {
       
   168       while (TRUE)
       
   169 	{
       
   170 	  int half = (start + end) / 2;
       
   171 	  if (ch == decomp_table[half].ch)
       
   172 	    {
       
   173 	      int offset;
       
   174 
       
   175 	      if (compat)
       
   176 		{
       
   177 		  offset = decomp_table[half].compat_offset;
       
   178 		  if (offset == G_UNICODE_NOT_PRESENT_OFFSET)
       
   179 		    offset = decomp_table[half].canon_offset;
       
   180 		}
       
   181 	      else
       
   182 		{
       
   183 		  offset = decomp_table[half].canon_offset;
       
   184 		  if (offset == G_UNICODE_NOT_PRESENT_OFFSET)
       
   185 		    return NULL;
       
   186 		}
       
   187 	      
       
   188 	      return &(decomp_expansion_string[offset]);
       
   189 	    }
       
   190 	  else if (half == start)
       
   191 	    break;
       
   192 	  else if (ch > decomp_table[half].ch)
       
   193 	    start = half;
       
   194 	  else
       
   195 	    end = half;
       
   196 	}
       
   197     }
       
   198 
       
   199   return NULL;
       
   200 }
       
   201 
       
   202 /**
       
   203  * g_unicode_canonical_decomposition:
       
   204  * @ch: a Unicode character.
       
   205  * @result_len: location to store the length of the return value.
       
   206  *
       
   207  * Computes the canonical decomposition of a Unicode character.  
       
   208  * 
       
   209  * Return value: a newly allocated string of Unicode characters.
       
   210  *   @result_len is set to the resulting length of the string.
       
   211  **/
       
   212 EXPORT_C gunichar *
       
   213 g_unicode_canonical_decomposition (gunichar ch,
       
   214 				   gsize   *result_len)
       
   215 {
       
   216   const gchar *decomp;
       
   217   const gchar *p;
       
   218   gunichar *r;
       
   219 
       
   220   /* Hangul syllable */
       
   221   if (ch >= 0xac00 && ch <= 0xd7a3)
       
   222     {
       
   223       decompose_hangul (ch, NULL, result_len);
       
   224       r = g_malloc (*result_len * sizeof (gunichar));
       
   225       decompose_hangul (ch, r, result_len);
       
   226     }
       
   227   else if ((decomp = find_decomposition (ch, FALSE)) != NULL)
       
   228     {
       
   229       /* Found it.  */
       
   230       int i;
       
   231       
       
   232       *result_len = g_utf8_strlen (decomp, -1);
       
   233       r = g_malloc (*result_len * sizeof (gunichar));
       
   234       
       
   235       for (p = decomp, i = 0; *p != '\0'; p = g_utf8_next_char (p), i++)
       
   236         r[i] = g_utf8_get_char (p);
       
   237     }
       
   238   else
       
   239     {
       
   240       /* Not in our table.  */
       
   241       r = g_malloc (sizeof (gunichar));
       
   242       *r = ch;
       
   243       *result_len = 1;
       
   244     }
       
   245 
       
   246   /* Supposedly following the Unicode 2.1.9 table means that the
       
   247      decompositions come out in canonical order.  I haven't tested
       
   248      this, but we rely on it here.  */
       
   249   return r;
       
   250 }
       
   251 
       
   252 /* L,V => LV and LV,T => LVT  */
       
   253 static gboolean
       
   254 combine_hangul (gunichar a,
       
   255                 gunichar b,
       
   256                 gunichar *result)
       
   257 {
       
   258   gint LIndex = a - LBase;
       
   259   gint SIndex = a - SBase;
       
   260 
       
   261   gint VIndex = b - VBase;
       
   262   gint TIndex = b - TBase;
       
   263 
       
   264   if (0 <= LIndex && LIndex < LCount
       
   265       && 0 <= VIndex && VIndex < VCount)
       
   266     {
       
   267       *result = SBase + (LIndex * VCount + VIndex) * TCount;
       
   268       return TRUE;
       
   269     }
       
   270   else if (0 <= SIndex && SIndex < SCount && (SIndex % TCount) == 0
       
   271            && 0 < TIndex && TIndex < TCount)
       
   272     {
       
   273       *result = a + TIndex;
       
   274       return TRUE;
       
   275     }
       
   276 
       
   277   return FALSE;
       
   278 }
       
   279 
       
   280 #define CI(Page, Char) \
       
   281   ((compose_table[Page] >= G_UNICODE_MAX_TABLE_INDEX) \
       
   282    ? (compose_table[Page] - G_UNICODE_MAX_TABLE_INDEX) \
       
   283    : (compose_data[compose_table[Page]][Char]))
       
   284 
       
   285 #define COMPOSE_INDEX(Char) \
       
   286      (((Char >> 8) > (COMPOSE_TABLE_LAST)) ? 0 : CI((Char) >> 8, (Char) & 0xff))
       
   287 
       
   288 static gboolean
       
   289 combine (gunichar  a,
       
   290 	 gunichar  b,
       
   291 	 gunichar *result)
       
   292 {
       
   293   gushort index_a, index_b;
       
   294 
       
   295   if (combine_hangul (a, b, result))
       
   296     return TRUE;
       
   297 
       
   298   index_a = COMPOSE_INDEX(a);
       
   299 
       
   300   if (index_a >= COMPOSE_FIRST_SINGLE_START && index_a < COMPOSE_SECOND_START)
       
   301     {
       
   302       if (b == compose_first_single[index_a - COMPOSE_FIRST_SINGLE_START][0])
       
   303 	{
       
   304 	  *result = compose_first_single[index_a - COMPOSE_FIRST_SINGLE_START][1];
       
   305 	  return TRUE;
       
   306 	}
       
   307       else
       
   308         return FALSE;
       
   309     }
       
   310   
       
   311   index_b = COMPOSE_INDEX(b);
       
   312 
       
   313   if (index_b >= COMPOSE_SECOND_SINGLE_START)
       
   314     {
       
   315       if (a == compose_second_single[index_b - COMPOSE_SECOND_SINGLE_START][0])
       
   316 	{
       
   317 	  *result = compose_second_single[index_b - COMPOSE_SECOND_SINGLE_START][1];
       
   318 	  return TRUE;
       
   319 	}
       
   320       else
       
   321         return FALSE;
       
   322     }
       
   323 
       
   324   if (index_a >= COMPOSE_FIRST_START && index_a < COMPOSE_FIRST_SINGLE_START &&
       
   325       index_b >= COMPOSE_SECOND_START && index_b < COMPOSE_SECOND_SINGLE_START)
       
   326     {
       
   327       gunichar res = compose_array[index_a - COMPOSE_FIRST_START][index_b - COMPOSE_SECOND_START];
       
   328 
       
   329       if (res)
       
   330 	{
       
   331 	  *result = res;
       
   332 	  return TRUE;
       
   333 	}
       
   334     }
       
   335 
       
   336   return FALSE;
       
   337 }
       
   338 
       
   339 gunichar *
       
   340 _g_utf8_normalize_wc (const gchar    *str,
       
   341 		      gssize          max_len,
       
   342 		      GNormalizeMode  mode)
       
   343 {
       
   344   gsize n_wc;
       
   345   gunichar *wc_buffer;
       
   346   const char *p;
       
   347   gsize last_start;
       
   348   gboolean do_compat = (mode == G_NORMALIZE_NFKC ||
       
   349 			mode == G_NORMALIZE_NFKD);
       
   350   gboolean do_compose = (mode == G_NORMALIZE_NFC ||
       
   351 			 mode == G_NORMALIZE_NFKC);
       
   352 
       
   353   n_wc = 0;
       
   354   p = str;
       
   355   while ((max_len < 0 || p < str + max_len) && *p)
       
   356     {
       
   357       const gchar *decomp;
       
   358       gunichar wc = g_utf8_get_char (p);
       
   359 
       
   360       if (wc >= 0xac00 && wc <= 0xd7a3)
       
   361         {
       
   362           gsize result_len;
       
   363           decompose_hangul (wc, NULL, &result_len);
       
   364           n_wc += result_len;
       
   365         }
       
   366       else 
       
   367         {
       
   368           decomp = find_decomposition (wc, do_compat);
       
   369 
       
   370           if (decomp)
       
   371             n_wc += g_utf8_strlen (decomp, -1);
       
   372           else
       
   373             n_wc++;
       
   374         }
       
   375 
       
   376       p = g_utf8_next_char (p);
       
   377     }
       
   378   wc_buffer = g_new (gunichar, n_wc + 1);
       
   379 
       
   380   last_start = 0;
       
   381   n_wc = 0;
       
   382   p = str;
       
   383   while ((max_len < 0 || p < str + max_len) && *p)
       
   384     {
       
   385       gunichar wc = g_utf8_get_char (p);
       
   386       const gchar *decomp;
       
   387       int cc;
       
   388       gsize old_n_wc = n_wc;
       
   389 	  
       
   390       if (wc >= 0xac00 && wc <= 0xd7a3)
       
   391         {
       
   392           gsize result_len;
       
   393           decompose_hangul (wc, wc_buffer + n_wc, &result_len);
       
   394           n_wc += result_len;
       
   395         }
       
   396       else
       
   397         {
       
   398           decomp = find_decomposition (wc, do_compat);
       
   399           
       
   400           if (decomp)
       
   401             {
       
   402               const char *pd;
       
   403               for (pd = decomp; *pd != '\0'; pd = g_utf8_next_char (pd))
       
   404                 wc_buffer[n_wc++] = g_utf8_get_char (pd);
       
   405             }
       
   406           else
       
   407             wc_buffer[n_wc++] = wc;
       
   408         }
       
   409 
       
   410       if (n_wc > 0)
       
   411 	{
       
   412 	  cc = COMBINING_CLASS (wc_buffer[old_n_wc]);
       
   413 
       
   414 	  if (cc == 0)
       
   415 	    {
       
   416 	      g_unicode_canonical_ordering (wc_buffer + last_start, n_wc - last_start);
       
   417 	      last_start = old_n_wc;
       
   418 	    }
       
   419 	}
       
   420       
       
   421       p = g_utf8_next_char (p);
       
   422     }
       
   423 
       
   424   if (n_wc > 0)
       
   425     {
       
   426       g_unicode_canonical_ordering (wc_buffer + last_start, n_wc - last_start);
       
   427       last_start = n_wc;
       
   428     }
       
   429 	  
       
   430   wc_buffer[n_wc] = 0;
       
   431 
       
   432   /* All decomposed and reordered */ 
       
   433 
       
   434   if (do_compose && n_wc > 0)
       
   435     {
       
   436       gsize i, j;
       
   437       int last_cc = 0;
       
   438       last_start = 0;
       
   439       
       
   440       for (i = 0; i < n_wc; i++)
       
   441 	{
       
   442 	  int cc = COMBINING_CLASS (wc_buffer[i]);
       
   443 
       
   444 	  if (i > 0 &&
       
   445 	      (last_cc == 0 || last_cc != cc) &&
       
   446 	      combine (wc_buffer[last_start], wc_buffer[i],
       
   447 		       &wc_buffer[last_start]))
       
   448 	    {
       
   449 	      for (j = i + 1; j < n_wc; j++)
       
   450 		wc_buffer[j-1] = wc_buffer[j];
       
   451 	      n_wc--;
       
   452 	      i--;
       
   453 	      
       
   454 	      if (i == last_start)
       
   455 		last_cc = 0;
       
   456 	      else
       
   457 		last_cc = COMBINING_CLASS (wc_buffer[i-1]);
       
   458 	      
       
   459 	      continue;
       
   460 	    }
       
   461 
       
   462 	  if (cc == 0)
       
   463 	    last_start = i;
       
   464 
       
   465 	  last_cc = cc;
       
   466 	}
       
   467     }
       
   468 
       
   469   wc_buffer[n_wc] = 0;
       
   470 
       
   471   return wc_buffer;
       
   472 }
       
   473 
       
   474 /**
       
   475  * g_utf8_normalize:
       
   476  * @str: a UTF-8 encoded string.
       
   477  * @len: length of @str, in bytes, or -1 if @str is nul-terminated.
       
   478  * @mode: the type of normalization to perform.
       
   479  * 
       
   480  * Converts a string into canonical form, standardizing
       
   481  * such issues as whether a character with an accent
       
   482  * is represented as a base character and combining
       
   483  * accent or as a single precomposed character. You
       
   484  * should generally call g_utf8_normalize() before
       
   485  * comparing two Unicode strings.
       
   486  *
       
   487  * The normalization mode %G_NORMALIZE_DEFAULT only
       
   488  * standardizes differences that do not affect the
       
   489  * text content, such as the above-mentioned accent
       
   490  * representation. %G_NORMALIZE_ALL also standardizes
       
   491  * the "compatibility" characters in Unicode, such
       
   492  * as SUPERSCRIPT THREE to the standard forms
       
   493  * (in this case DIGIT THREE). Formatting information
       
   494  * may be lost but for most text operations such
       
   495  * characters should be considered the same.
       
   496  * For example, g_utf8_collate() normalizes
       
   497  * with %G_NORMALIZE_ALL as its first step.
       
   498  *
       
   499  * %G_NORMALIZE_DEFAULT_COMPOSE and %G_NORMALIZE_ALL_COMPOSE
       
   500  * are like %G_NORMALIZE_DEFAULT and %G_NORMALIZE_ALL,
       
   501  * but returned a result with composed forms rather
       
   502  * than a maximally decomposed form. This is often
       
   503  * useful if you intend to convert the string to
       
   504  * a legacy encoding or pass it to a system with
       
   505  * less capable Unicode handling.
       
   506  * 
       
   507  * Return value: a newly allocated string, that is the 
       
   508  *   normalized form of @str.
       
   509  **/
       
   510 EXPORT_C gchar *
       
   511 g_utf8_normalize (const gchar    *str,
       
   512 		  gssize          len,
       
   513 		  GNormalizeMode  mode)
       
   514 {
       
   515   gunichar *result_wc = _g_utf8_normalize_wc (str, len, mode);
       
   516   gchar *result;
       
   517 
       
   518   result = g_ucs4_to_utf8 (result_wc, -1, NULL, NULL, NULL);
       
   519   g_free (result_wc);
       
   520 
       
   521   return result;
       
   522 }
       
   523 
       
   524 #define __G_UNIDECOMP_C__
       
   525 #include "galiasdef.c"