Index: modules/hangul/hangul-fc.c
===================================================================
RCS file: /cvs/gnome/pango/modules/hangul/hangul-fc.c,v
retrieving revision 1.12
diff -u -7 -p -r1.12 hangul-fc.c
--- modules/hangul/hangul-fc.c	3 Aug 2003 21:57:28 -0000	1.12
+++ modules/hangul/hangul-fc.c	22 Sep 2003 01:37:59 -0000
@@ -21,16 +21,18 @@
  */
 
 #include <string.h>
 
 #include "pango-engine.h"
 #include "pango-utils.h"
 #include "pangofc-font.h"
+#include "pango-ot.h"
 
 #include "hangul-defs.h"
+#include "hangul-utils.h"
 #include "tables-jamos.i"
 
 /* No extra fields needed */
 typedef PangoEngineShape      HangulEngineFc;
 typedef PangoEngineShapeClass HangulEngineFcClass ;
 
 #define SCRIPT_ENGINE_NAME "HangulScriptEngineFc"
@@ -48,14 +50,24 @@ static PangoEngineInfo script_engines[] 
     SCRIPT_ENGINE_NAME,
     PANGO_ENGINE_TYPE_SHAPE,
     RENDER_TYPE,
     hangul_ranges, G_N_ELEMENTS(hangul_ranges)
   }
 };
 
+typedef struct _HangulFontInfo 
+{
+  PangoFont *font;
+  PangoOTRuleset *ruleset;
+} HangulFontInfo; 
+
+typedef void (* RenderSyllableFunc) (HangulFontInfo *font, gunichar *text, 
+                                     int length, PangoGlyphString *glyphs,
+                                     int *n_glyphs, int cluster_offset);
+
 static void
 set_glyph (PangoFont *font, PangoGlyphString *glyphs, int i, int offset, PangoGlyph glyph)
 {
   PangoRectangle logical_rect;
 
   glyphs->glyphs[i].glyph = glyph;
   glyphs->glyphs[i].geometry.x_offset = 0;
@@ -73,15 +85,15 @@ set_glyph (PangoFont *font, PangoGlyphSt
  *   to attach it to.
  *   2. Adjust x_offset so that it is drawn to the left of a cluster.
  *   3. Set the logical width to zero. 
  */
 
 static void
 set_glyph_tone (PangoFont *font, PangoGlyphString *glyphs, int i,
-		            int offset, PangoGlyph glyph)
+		int offset, PangoGlyph glyph)
 {
   PangoRectangle logical_rect, ink_rect;
   PangoRectangle logical_rect_cluster;
 
   glyphs->glyphs[i].glyph = glyph;
   glyphs->glyphs[i].geometry.y_offset = 0;
   glyphs->log_clusters[i] = offset;
@@ -150,15 +162,15 @@ render_tone (PangoFont *font, gunichar t
       index = find_char (font, tone == HTONE1 ? 0x00b7 : 0x003a);
       if (index)
         {
           set_glyph_tone (font, glyphs, *n_glyphs, cluster_offset, index);
         }
       else 
         set_glyph (font, glyphs, *n_glyphs, cluster_offset,
-		   get_unknown_glyph (font, tone));
+                  get_unknown_glyph (font, tone));
     }
   (*n_glyphs)++;
 }
 
 /* This is a fallback for when we get a tone mark not preceded
  * by a syllable.
  */
@@ -183,62 +195,103 @@ render_isolated_tone (PangoFont *font, g
   (*n_glyphs)++;
 
   /* And the tone mrak
    */
   render_tone(font, tone, glyphs, n_glyphs, cluster_offset);
 }
 
-static void
-render_syllable (PangoFont *font, gunichar *text, int length,
-		 PangoGlyphString *glyphs, int *n_glyphs, int cluster_offset)
+static int
+render_as_precomp_syllable (PangoFont 	     *font, 
+			    gunichar 	     *text, 
+			    int 	     *length,
+			    PangoGlyphString *glyphs,
+			    int 	     *n_glyphs, 
+			    int              cluster_offset,
+			    gboolean         spacing_jamo)
 {
-  int n_prev_glyphs = *n_glyphs;
-  int index;
-  gunichar wc, tone;
-  int i, j, composed;
-
-  if (IS_M (text[length - 1]))
-    {
-      tone = text[length - 1];
-      length--;
-    }
-  else
-    tone = 0;
+  int composed;
 
-  if (length >= 3 && IS_L_S(text[0]) && IS_V_S(text[1]) && IS_T_S(text[2]))
+  if ((*length == 3 || (spacing_jamo && *length > 3)) &&
+	IS_L_S(text[0]) && IS_V_S(text[1]) && IS_T_S(text[2]))
     composed = 3;
-  else if (length >= 2 && IS_L_S(text[0]) && IS_V_S(text[1]))
+  else if ((*length == 2 || (spacing_jamo && *length > 2)) &&
+	   IS_L_S(text[0])  && IS_V_S(text[1]))
     composed = 2;
   else
     composed = 0;
 
   if (composed)
     {
+      gunichar wc;
+      int index;
       if (composed == 3)
 	wc = S_FROM_LVT(text[0], text[1], text[2]);
       else
 	wc = S_FROM_LV(text[0], text[1]);
       index = find_char (font, wc);
       pango_glyph_string_set_size (glyphs, *n_glyphs + 1);
       if (!index)
 	set_glyph (font, glyphs, *n_glyphs, cluster_offset,
 		   get_unknown_glyph (font, wc));
       else
 	set_glyph (font, glyphs, *n_glyphs, cluster_offset, index);
       (*n_glyphs)++;
-      text += composed;
-      length -= composed;
+      *length -= composed;
+    }
+  return composed;
+
+}
+
+static void
+render_syllable_base (HangulFontInfo   *fontinfo, 
+		      gunichar	       *text, 
+		      int	       length,
+		      PangoGlyphString *glyphs, 
+		      int	       *n_glyphs, 
+		      int	       cluster_offset,
+		      gboolean         spacing_jamo)
+{
+  int index;
+  gunichar wc,*text0;
+  int i, j;
+  gunichar tone;
+  PangoFont *font = fontinfo->font; 
+
+  if (IS_M (text[length - 1]))
+    {
+      tone = text[length - 1];
+      length--;
     }
+  else
+    tone = 0;
+
+  /* XXX : need to check if we have an isolated precomp. syllable and
+   * expedite things to avoid decomposition/recomposition in that case.
+   * At the moment, we don't have to because hangul_engine_shape()
+   * decomposes a precomposed syllble into a jamo seq.
+   */
+
+  text = _normalize_jamo (text, &length);
+  text0 = text;  /* preserve the addrs. for g_free */
+
+  text += render_as_precomp_syllable (font, text, &length, glyphs, n_glyphs,
+          cluster_offset, spacing_jamo);
+
 
   /* Render the remaining text as uncomposed forms as a fallback.  */
   for (i = 0; i < length; i++)
     {
       int jindex;
 
-      if (text[i] == LFILL || text[i] == VFILL)
+      /* skip VFILL if  VFILL is not the only char. in a given syllable.
+       * There's a strange problem with pango-break(). For an unknown reason,
+       * (the code in break.c has no problem handling Jamo breaks.)
+       * sometimes L+Vf is broken into L and Vf and shaping engine gets them
+       * one by one.*/
+      if ( text[i] == VFILL && length > 1)
 	continue;
 
       index = find_char (font, text[i]);
       if (index)
 	{
 	  pango_glyph_string_set_size (glyphs, *n_glyphs + 1);
 	  set_glyph (font, glyphs, *n_glyphs, cluster_offset, index);
@@ -258,28 +311,242 @@ render_syllable (PangoFont *font, gunich
 	    set_glyph (font, glyphs, *n_glyphs, cluster_offset,
 		       get_unknown_glyph (font, index));
 	  else
 	    set_glyph (font, glyphs, *n_glyphs, cluster_offset, index);
 	  (*n_glyphs)++;
 	}
     }
-  if (n_prev_glyphs == *n_glyphs)
+
+  if (tone)
+    render_tone(font, tone, glyphs, n_glyphs, cluster_offset);
+
+  g_free (text0);
+}  
+
+static void
+render_syllable_with_spacing_jamo (HangulFontInfo     *fontinfo,
+			     	   gunichar	      *text, 
+			     	   int	      	      length,
+			     	   PangoGlyphString   *glyphs, 
+			     	   int	              *n_glyphs, 
+				   int	              cluster_offset)
+{
+  render_syllable_base (fontinfo, text, length, glyphs, n_glyphs, 
+			cluster_offset, TRUE);
+}
+
+static void
+render_syllable_with_combining_jamo (HangulFontInfo   *fontinfo,
+			     	     gunichar	      *text, 
+			     	     int	      length,
+			     	     PangoGlyphString *glyphs, 
+			     	     int	      *n_glyphs, 
+				     int	      cluster_offset)
+{
+  render_syllable_base (fontinfo, text, length, glyphs, n_glyphs, 
+			cluster_offset, FALSE);
+}
+
+static void
+render_syllable_with_otfont (HangulFontInfo   *fontinfo, 
+			     gunichar	      *text, 
+			     int	      length,
+			     PangoGlyphString *glyphs, 
+			     int	      *n_glyphs, 
+			     int	      cluster_offset)
+{
+  int index;
+  int i;
+  gunichar *text0 = NULL;
+  gunichar tone = 0;
+  PangoFont *font = fontinfo->font; 
+  gulong *properties; 
+
+  if (IS_M (text[length - 1]))
     {
-      index = find_char (font, 0x3164);
+      tone = text[length - 1];
+      length--;
+      index = find_char (font, tone);
+      pango_glyph_string_set_size (glyphs, *n_glyphs + 1);
+      if (!index)
+	set_glyph (font, glyphs, *n_glyphs, cluster_offset,
+		   get_unknown_glyph (font, index));
+      else
+	set_glyph (font, glyphs, *n_glyphs, cluster_offset, index);
+    }
+
+  /* XXX : need to check if we have an isolated precomp. syllable and
+   * expedite things to avoid decomposition/recomposition in that case.
+   * At the moment, we don't have to because hangul_engine_shape()
+   * decomposes a precomposed syllble into a jamo seq.
+   */
+
+  text = _normalize_jamo (text, &length);
+  text0 = text;
+
+  text += render_as_precomp_syllable (font, text, &length, glyphs, n_glyphs,
+          cluster_offset, FALSE);
+
+  if (! length)
+    {
+      if (tone)
+        render_tone(font, tone, glyphs, n_glyphs, cluster_offset);
+      g_free (text0);
+      return;
+    }
+
+  for (i = 0; i < length; ++i)
+    {
+      index = find_char (font, text[i]);
       pango_glyph_string_set_size (glyphs, *n_glyphs + 1);
       if (!index)
 	set_glyph (font, glyphs, *n_glyphs, cluster_offset,
 		   get_unknown_glyph (font, index));
       else
 	set_glyph (font, glyphs, *n_glyphs, cluster_offset, index);
-      glyphs->log_clusters[*n_glyphs] = cluster_offset;
       (*n_glyphs)++;
     }
-  if (tone)
-    render_tone(font, tone, glyphs, n_glyphs, cluster_offset);
+
+  g_free (text0);
+
+  properties = g_new0 (gulong, *n_glyphs); /* XXX is this right? */
+
+  g_assert(fontinfo->ruleset);
+  pango_ot_ruleset_shape (fontinfo->ruleset, glyphs, properties);
+
+  g_free(properties);
+
+  return; 
+}
+
+static void
+print_tag (FT_ULong tag)
+{
+    g_print ("%c%c%c%c", 
+             (unsigned char)(tag >> 24), (unsigned char)((tag & 0xff0000) >> 16),
+             (unsigned char)((tag & 0xff00) >> 8), (unsigned char)(tag & 0xff));
+}
+
+
+static gchar gsub_features[][5] =
+{
+  "ccmp",    /* XXX MS Hangul OT fonts may not have it */
+  "ljmo",
+  "vjmo",
+  "tjmo"
+};
+
+static PangoOTRuleset* 
+get_hangul_ot_ruleset (FT_Face face)
+{
+  PangoOTInfo *info = NULL;
+  PangoOTRuleset *ruleset = NULL;
+  PangoOTTag script_tag = FT_MAKE_TAG('h', 'a', 'n', 'g');
+  guint script_index; 
+  int i;
+
+  static GQuark ruleset_quark = 0; 
+
+  PangoOTTag feature_tags[G_N_ELEMENTS(gsub_features)];
+  guint feature_indices[G_N_ELEMENTS(gsub_features)];
+
+  info = pango_ot_info_get (face); /* will be gone when face is unlocked */
+  if (!info)
+    return NULL;
+  g_print("hangul-fc.c: found OT info\n");
+
+  if (!pango_ot_info_find_script (info, PANGO_OT_TABLE_GSUB, script_tag, 
+				  &script_index))
+    return NULL;
+  g_print("hangul-fc.c: found ");
+  print_tag (script_tag);
+  g_print("\n");
+
+  if (!ruleset_quark)
+    ruleset_quark = g_quark_from_static_string ("pango-hangul-ruleset");
+
+  ruleset = g_object_get_qdata(G_OBJECT (info), ruleset_quark);
+
+  if (ruleset)
+    return ruleset;
+
+  ruleset = pango_ot_ruleset_new (info);
+  g_print("hangul-fc.c: got ruleset\n");
+
+  for (i = 0; i < G_N_ELEMENTS (gsub_features); ++i)
+    {
+      feature_tags[i] = FT_MAKE_TAG (gsub_features[i][0],
+				     gsub_features[i][1],
+				     gsub_features[i][2],
+				     gsub_features[i][3]);
+
+      g_print("hangul-fc.c: looking for feature ");
+      print_tag (feature_tags[i]);
+      g_print("\n");
+
+      /* 0xffff : default language */
+      if (!pango_ot_info_find_feature(info, PANGO_OT_TABLE_GSUB,
+				      feature_tags[i], script_index, 0xffff, 
+				      &feature_indices[i]))
+	return NULL;
+      g_print("hangul-fc.c: found feature %s\n", gsub_features[i]);
+      print_tag (feature_tags[i]);
+      g_print("\n");
+    }
+
+  for (i = 0; i < G_N_ELEMENTS (gsub_features); ++i)
+    {
+      pango_ot_ruleset_add_feature (ruleset, PANGO_OT_TABLE_GSUB, 
+				    feature_indices[i], 0xffff);
+      g_print("hangul-fc.c: adding feature %s\n", gsub_features[i]);
+      print_tag (feature_tags[i]);
+      g_print("\n");
+    }
+
+  g_object_set_qdata_full (G_OBJECT (info), ruleset_quark, ruleset, 
+			   (GDestroyNotify) g_object_unref);
+
+  return ruleset;
+}
+
+static
+void set_render_func (HangulFontInfo     *fontinfo,
+		      RenderSyllableFunc *render_func)
+{
+  guint gindex;
+  PangoFcFont *fc_font;
+  PangoFont *font; 
+
+  g_assert (fontinfo != NULL && fontinfo->font != NULL && render_func !=NULL);
+
+  font = fontinfo->font;
+  fc_font = PANGO_FC_FONT (font);
+  FT_Face face = pango_fc_font_lock_face (fc_font);
+  g_assert (face != NULL);
+
+
+  if ( (fontinfo->ruleset = get_hangul_ot_ruleset(face)) ) {
+    *render_func = render_syllable_with_otfont;
+  }
+  /* if VBASE (U+1161 : the first vowel) is non-spacing/zero-width,
+   * assume that a font has combining Jamo glyphs.
+   */
+  else if ((gindex=find_char (font, VBASE)))
+    {
+      PangoRectangle l_rect;
+      pango_font_get_glyph_extents (font, (PangoGlyph) gindex, NULL, &l_rect);
+      if ( ! l_rect.width ) 
+	*render_func = render_syllable_with_combining_jamo;
+      else 
+	*render_func = render_syllable_with_spacing_jamo;
+    }
+  else
+    *render_func = render_syllable_with_spacing_jamo;
+
+  pango_fc_font_unlock_face (fc_font);
 }
 
 static void 
 hangul_engine_shape (PangoEngineShape *engine,
 		     PangoFont        *font,
 		     const char       *text,
 		     gint              length,
@@ -291,43 +558,48 @@ hangul_engine_shape (PangoEngineShape *e
   const char *p, *start;
 
   gunichar jamos_static[8];
   guint max_jamos = G_N_ELEMENTS (jamos_static);
   gunichar *jamos = jamos_static;
   int n_jamos;
 
+  RenderSyllableFunc render_func = NULL;
+
+  HangulFontInfo fontinfo; 
+
   n_chars = g_utf8_strlen (text, length);
   n_glyphs = 0;
   start = p = text;
   n_jamos = 0;
 
+  fontinfo.font = font;
+  set_render_func (&fontinfo, &render_func);
+
   for (i = 0; i < n_chars; i++)
     {
+      gunichar prev = jamos[n_jamos - 1];
       gunichar wc;
 
       wc = g_utf8_get_char (p);
 
       /* Check syllable boundaries. */
-      if (n_jamos)
+      if (n_jamos &&
+	  ((!IS_L (prev) && IS_S (wc)) ||
+	   (IS_T (prev) && IS_L (wc)) ||
+	   (IS_V (prev) && IS_L (wc)) ||
+	   (IS_T (prev) && IS_V (wc)) ||
+	   IS_M(prev)))
 	{
-	  gunichar prev = jamos[n_jamos - 1];
-	  if ((!IS_L (prev) && IS_S (wc)) ||
-	      (IS_T (prev) && IS_L (wc)) ||
-	      (IS_V (prev) && IS_L (wc)) ||
-	      (IS_T (prev) && IS_V (wc)) ||
-	      IS_M(prev))
-	    {
-	      /* Draw a syllable. */
-	      render_syllable (font, jamos, n_jamos, glyphs,
-			       &n_glyphs, start - text);
-	      n_jamos = 0;
-	      start = p;
-	    }
+	  /* Draw a syllable. */
+	  render_func (&fontinfo, jamos, n_jamos, glyphs, &n_glyphs,
+		       start - text);
+	  n_jamos = 0;
+	  start = p;
 	}
-	  
+
       if (n_jamos == max_jamos)
 	{
 	  max_jamos += 3;	/* at most 3 for each syllable code (L+V+T) */
 	  if (jamos == jamos_static)
 	    {
 	      jamos = g_new (gunichar, max_jamos);
 	      memcpy (jamos, jamos_static, n_jamos*sizeof(gunichar));
@@ -350,20 +622,19 @@ hangul_engine_shape (PangoEngineShape *e
 	  start = g_utf8_next_char (p);
 	}
       else
 	jamos[n_jamos++] = wc;
       p = g_utf8_next_char (p);
     }
 
-  if (n_jamos != 0)
-    render_syllable (font, jamos, n_jamos, glyphs, &n_glyphs,
-		     start - text);
+  if (n_jamos != 0) 
+    render_func (&fontinfo, jamos, n_jamos, glyphs, &n_glyphs, start - text);
 
   if (jamos != jamos_static)
-    g_free(jamos);
+    g_free (jamos);
 }
 
 static void
 hangul_engine_fc_class_init (PangoEngineShapeClass *class)
 {
   class->script_shape = hangul_engine_shape;
 }
@@ -380,15 +651,15 @@ PANGO_MODULE_ENTRY(init) (GTypeModule *m
 void 
 PANGO_MODULE_ENTRY(exit) (void)
 {
 }
 
 void 
 PANGO_MODULE_ENTRY(list) (PangoEngineInfo **engines,
-			  int              *n_engines)
+	  		  int              *n_engines)
 {
   *engines = script_engines;
   *n_engines = G_N_ELEMENTS (script_engines);
 }
 
 PangoEngine *
 PANGO_MODULE_ENTRY(create) (const char *id)
