(update_seq_area): Call OTF_drive_gdef only
[m17n/libotf.git] / example / otfview.c
index 2ec484e..57fcd7d 100644 (file)
@@ -28,6 +28,7 @@ write to the Free Software Foundation, Inc., 59 Temple Place, Suite
 #include <unistd.h>
 #include <libgen.h>
 
+#include <X11/Xatom.h>
 #include <X11/Intrinsic.h>
 #include <X11/StringDefs.h>
 #include <X11/Shell.h>
@@ -43,6 +44,8 @@ write to the Free Software Foundation, Inc., 59 Temple Place, Suite
 #include <otf.h>
 
 #define DEFAULT_PIXEL_SIZE 30
+int pixel_size;
+
 #define DEFAULT_FONT_NAME "6x13"
 XFontStruct *font;
 #define FONT_HEIGHT (font->ascent + font->descent)
@@ -66,7 +69,7 @@ XtAppContext context;
    | |         idxl[0]     ...    idxl[15]  | |
    | +--------------------------------------+ |
    | +--- render_area (form) ---------------+ |
-   | | clear del                            | |
+   | | clear del bidi                       | |
    | | +--- raw (box) --------------------+ | |
    | | | raw_label raw_image              | | |
    | | +----------------------------------+ | |
@@ -81,18 +84,27 @@ Widget shell, frame;
 Widget command_area, quit, dump, *charmap;
 Widget navi_area, FIRST, PREV, prev, range, next, NEXT, LAST;
 Widget glyph_area, glyph[128], index_label[8];
-Widget render_area, clear, del, raw, seq;
+Widget render_area, clear, del, bidi, raw, seq;
 Widget raw_label, raw_image, seq_label, seq_image;
 unsigned long foreground, background;
 
+#define MAX_FEATURE_COUNT 16
+
 typedef struct
 {
-  int idx;
-  int on;
-  Widget w;
+  char *label;
+  OTF_GSUB_GPOS *gsub_gpos;
+  OTF_LangSys *langsys;
+  struct {
+    OTF_Tag tag;
+    int on;
+    Widget w;
+  } features[MAX_FEATURE_COUNT];
 } FeatureRec;
 
-FeatureRec gsub[64], gpos[64];
+FeatureRec gsub, gpos;
+
+OTF_Tag script_tag;
 
 int glyph_char[128];
 
@@ -122,6 +134,7 @@ struct {
 
 int charmap_index;
 
+int reversed;
 unsigned glyph_width, glyph_height;
 int glyph_x, glyph_y;
 int glyph_index;
@@ -136,7 +149,7 @@ OTF *otf;
 char *filename;
 
 void
-create_pixmap (int pixel_size, int index)
+create_pixmap (int index)
 {
   int err = FT_Load_Glyph (face, index, FT_LOAD_RENDER | FT_LOAD_MONOCHROME);
   XImage ximage;
@@ -144,7 +157,7 @@ create_pixmap (int pixel_size, int index)
   
   if (err)
     {
-      bitmap[index].pixmap = (Pixmap) 0;
+      bitmap[index].pixmap = none_pixmap;
       return;
     }
   ximage.height = face->glyph->bitmap.rows;
@@ -189,10 +202,9 @@ update_glyph_area ()
 
       if (charmap_index >= 0)
        index = FT_Get_Char_Index (face, (FT_ULong) index);
-      if (bitmap[index].pixmap)
-       XtSetArg (arg[0], XtNbitmap, bitmap[index].pixmap);
-      else
-       XtSetArg (arg[0], XtNbitmap, none_pixmap);
+      if (! bitmap[index].pixmap)
+       create_pixmap (index);
+      XtSetArg (arg[0], XtNbitmap, bitmap[index].pixmap);
       XtSetValues (glyph[i], arg, 1);
     }
 
@@ -213,46 +225,76 @@ update_glyph_area ()
 }
 
 char *
-get_features (OTF_FeatureList *list, FeatureRec *features)
+get_features (OTF_FeatureList *list, FeatureRec *rec)
 {
-  int len = list->FeatureCount;
   int i, n;
   char *str, *p;
 
-  if (! features[0].on)
+  if (! rec->features[0].on)
     return NULL;
-  for (i = n = 0; i < len; i++)
+  for (i = n = 0; i < rec->langsys->FeatureCount; i++)
     {
-      if (features[i].on)
+      if (rec->features[i].on)
        n++;
       else
        break;
     }
-  if (i == len)
+  if (i == rec->langsys->FeatureCount)
     {
       str = malloc (2);
       strcpy (str, "*");
       return str;
     }
   str = malloc (n * 5);
-  for (i = 0, p = str; i < len ; i++, p += 5)
-    if (features[i].on)
-      {
-       OTF_tag_name (list->Feature[features[i].idx].FeatureTag, p);
-       p[4] = ',';
-      }
+  for (i = 0, p = str; i < n; i++, p += 5)
+    {
+      OTF_tag_name (rec->features[i].tag, p);
+      p[4] = ',';
+    }
   p[-1] = '\0';
   return str;
 }
 
 
+#define DEVICE_DELTA(table, size)                              \
+  (((size) >= (table).StartSize && (size) <= (table).EndSize)  \
+   ? (table).DeltaValue[(size) >= (table).StartSize]           \
+   : 0)
+
+void
+adjust_anchor (OTF_Anchor *anchor, FT_Face ft_face,
+              OTF_Glyph *g, int *x, int *y)
+{
+  if (anchor->AnchorFormat == 2)
+    {
+      FT_Outline *outline;
+      int ap = anchor->f.f1.AnchorPoint;
+
+      FT_Load_Glyph (ft_face, (FT_UInt) g->glyph_id, FT_LOAD_MONOCHROME);
+      outline = &ft_face->glyph->outline;
+      if (ap < outline->n_points)
+       {
+         *x = outline->points[ap].x;
+         *y = outline->points[ap].y;
+       }
+    }
+  else if (anchor->AnchorFormat == 3)
+    {
+      *x += DEVICE_DELTA (anchor->f.f2.XDeviceTable, pixel_size);
+      *y += DEVICE_DELTA (anchor->f.f2.YDeviceTable, pixel_size);
+    }
+}
+
 void
 update_seq_area ()
 {
   int i, x;
   OTF_GlyphString gstring;
+  OTF_Glyph *g, *prev, *base, *mark;
+  int base_width;
   int len = glyph_rec.n_glyphs;
   Arg arg[1];
+  int unitsPerEm = face->units_per_EM;
 
   gstring.size = gstring.used = len;
   gstring.glyphs = alloca (sizeof (OTF_Glyph) * len);
@@ -261,13 +303,15 @@ update_seq_area ()
     gstring.glyphs[i].c = gstring.glyphs[i].glyph_id = glyph_rec.glyphs[i];
 
   XFillRectangle (display, seq_pixmap, gc, 0, 0, render_width, render_height);
+  XDrawLine (display, seq_pixmap, gc_set, 0, glyph_y, render_width, glyph_y);
   if (otf)
     {
       char *str;
 
+      OTF_drive_gdef (otf, &gstring);
       if (otf->gsub)
        {
-         str = get_features (&otf->gsub->FeatureList, gsub);
+         str = get_features (&otf->gsub->FeatureList, &gsub);
          if (str)
            {
              OTF_drive_gsub (otf, &gstring, NULL, NULL, str);
@@ -276,7 +320,7 @@ update_seq_area ()
        }
       if (otf->gpos)
        {
-         str = get_features (&otf->gpos->FeatureList, gpos);
+         str = get_features (&otf->gpos->FeatureList, &gpos);
          if (str)
            {
              OTF_drive_gpos (otf, &gstring, NULL, NULL, str);
@@ -285,14 +329,109 @@ update_seq_area ()
        }
     }
 
-  for (i = 0, x = glyph_x; i < gstring.used; i++)
+  prev = NULL;
+  if (reversed)
+    {
+      OTF_Glyph temp;
+
+      for (prev = gstring.glyphs, g = gstring.glyphs + gstring.used - 1;
+          prev < g; prev++, g--)
+       temp = *prev, *prev = *g, *g = temp;
+      for (g = gstring.glyphs; g < gstring.glyphs + gstring.used; g++)
+       if (g->GlyphClass == 3)
+         {
+           OTF_Glyph *g0;
+           prev = g++;
+           while (g < gstring.glyphs + gstring.used && g->GlyphClass == 3)
+             g++;
+           for (g0 = g; prev < g0; prev++, g0--)
+             temp = *prev, *prev = *g, *g = temp;
+         }
+    }
+
+
+  mark = base = NULL;
+  for (i = 0, x = glyph_x, prev = NULL, g = gstring.glyphs;
+       i < gstring.used; i++, prev = g++)
     {
       BitmapRec *bmp = bitmap + gstring.glyphs[i].glyph_id;
+      int xoff = 0, yoff = 0;
+      int prev_width;
+      int advance = bmp->advance;
 
+      if (! bmp->pixmap)
+       {
+         create_pixmap (gstring.glyphs[i].glyph_id);
+         if (! bmp->pixmap)
+           continue;
+       }
+      switch (g->positioning_type)
+       {
+       case 0:
+         break;
+
+       case 1: case 2:
+         {
+           int format = g->f.f1.format;
+
+           if (format & OTF_XPlacement)
+             xoff = g->f.f1.value->XPlacement * pixel_size / unitsPerEm;
+           if (format & OTF_XPlaDevice)
+             xoff += DEVICE_DELTA (g->f.f1.value->XPlaDevice, pixel_size);
+           if (format & OTF_YPlacement)
+             yoff = g->f.f1.value->YPlacement * pixel_size / unitsPerEm;
+           if (format & OTF_YPlaDevice)
+             yoff += DEVICE_DELTA (g->f.f1.value->YPlaDevice, pixel_size);
+           if (format & OTF_XAdvance)
+             advance += g->f.f1.value->XAdvance * pixel_size / unitsPerEm;
+           if (format & OTF_XAdvDevice)
+             advance += DEVICE_DELTA (g->f.f1.value->XAdvDevice, pixel_size);
+         }
+         break;
+
+       case 3:
+         /* Not yet supported.  */
+         break;
+       case 4: case 5:
+         if (! base)
+           break;
+         prev = base;
+         prev_width = base_width;
+         goto label_adjust_anchor;
+       default:                /* i.e. case 6 */
+         if (! mark)
+           break;
+         prev = mark;
+         prev_width = 0;
+       label_adjust_anchor:
+         {
+           int base_x, base_y, mark_x, mark_y;
+
+           base_x = g->f.f4.base_anchor->XCoordinate * pixel_size / unitsPerEm;
+           base_y = g->f.f4.base_anchor->YCoordinate * pixel_size / unitsPerEm;
+           mark_x = g->f.f4.mark_anchor->XCoordinate * pixel_size / unitsPerEm;
+           mark_y = g->f.f4.mark_anchor->YCoordinate * pixel_size / unitsPerEm;
+
+           if (g->f.f4.base_anchor->AnchorFormat != 1)
+             adjust_anchor (g->f.f4.base_anchor, face, prev, &base_x, &base_y);
+           if (g->f.f4.mark_anchor->AnchorFormat != 1)
+             adjust_anchor (g->f.f4.mark_anchor, face, g, &mark_x, &mark_y);
+           xoff = (base_x - prev_width) - mark_x;
+           yoff = base_y - mark_y;
+         }
+       }
+         
       XCopyArea (display, bmp->pixmap, seq_pixmap, gc_or,
                 glyph_x + bmp->x, glyph_y + bmp->y, bmp->width, bmp->height,
-                x + bmp->x, glyph_y + bmp->y);
-      x += bmp->advance;
+                x + bmp->x + xoff, glyph_y + bmp->y - yoff);
+      x += advance;
+
+      if (g->GlyphClass == OTF_GlyphClass0)
+       base = mark = g, base_width = advance;
+      else if (g->GlyphClass == OTF_GlyphClassMark)
+       mark = g;
+      else
+       base = g, base_width = advance;
     }
   XtSetArg (arg[0], XtNbitmap, seq_pixmap);
   XtSetValues (seq_image, arg, 1);
@@ -344,7 +483,7 @@ QuitProc (Widget w, XtPointer client_data, XtPointer call_data)
 }
 
 void
-DumpProc (Widget w, XtPointer client_data, XtPointer pixel_size)
+DumpProc (Widget w, XtPointer client_data, XtPointer call_data)
 {
   int g_width, g_height, g_x, g_y, pix_width, pix_height;
   int margin = 20 * 300 / 25.4;
@@ -534,109 +673,234 @@ RenderProc (Widget w, XtPointer client_data, XtPointer call_data)
 }
 
 void
-FeatureProc (OTF_FeatureList *list, FeatureRec *features, int idx)
+BidiProc (Widget w, XtPointer client_data, XtPointer call_data)
 {
-  int i, j;
-  Arg arg[3];
+  Arg arg[1];
+
+  reversed = ! reversed;
+  if (reversed)
+    XtSetArg (arg[0], XtNlabel, "L<-R");
+  else
+    XtSetArg (arg[0], XtNlabel, "L->R");
+  XtSetValues (w, arg, 1);
+  update_seq_area ();
+}
+
+void
+FeatureProc (Widget w, XtPointer client_data, XtPointer call_data)
+{
+  FeatureRec *rec = (FeatureRec *) client_data;
+  int idx, i, j;
+  Arg arg[4];
+  char *label;
 
+  if (! rec->langsys)
+    return;
+  XtSetArg (arg[0], XtNlabel, &label);
+  XtGetValues (w, arg, 1);
+  if (! strcmp (label, "all"))
+    idx = -2;
+  else if (! strcmp (label, "none"))
+    idx = -1;
+  else
+    {
+      for (idx = 0; idx < rec->langsys->FeatureCount; idx++)
+       if (rec->features[idx].w == w)
+         break;
+      if (idx == rec->langsys->FeatureCount)
+       idx = -1;
+    }
   if (idx < 0)
     {
       int on = idx == -2;
 
-      for (idx = 0; idx < list->FeatureCount; idx++)
+      for (i = j = 0; j < rec->langsys->FeatureCount; j++)
        {
-         features[idx].idx = idx;
-         features[idx].on = on;
+         int index  = rec->langsys->FeatureIndex[j];
+
+         rec->features[j].tag
+           = rec->gsub_gpos->FeatureList.Feature[index].FeatureTag;
+         rec->features[j].on = on;
        }
-      i = 0, j = list->FeatureCount - 1;
     }
   else
     {
-      int index = features[idx].idx;
-      i = idx;
+      OTF_Tag tag = rec->features[idx].tag;
 
-      if (features[i].on)
+      i = idx;
+      if (rec->features[i].on)
        {
-         for (j = i; j + 1 < list->FeatureCount && features[j + 1].on; j++)
-           features[j].idx = features[j + 1].idx;
-         features[j].idx = index;
-         features[j].on = 0;
+         for (j = i + 1;
+              j < rec->langsys->FeatureCount && rec->features[j].on; j++)
+           rec->features[j - 1].tag = rec->features[j].tag;
+         rec->features[j - 1].tag = tag;
+         rec->features[j - 1].on = 0;
        }
       else
        {
-         for (j = i; i > 0 && ! features[i - 1].on; i--)
-           features[i].idx = features[i - 1].idx;
-         features[i].idx = index;
-         features[i].on = 1;
+         for (j = i + 1; i > 0 && ! rec->features[i - 1].on; i--)
+           rec->features[i].tag = rec->features[i - 1].tag;
+         rec->features[i].tag = tag;
+         rec->features[i].on = 1;
        }
     }
 
-  for (; i <= j; i++)
+  for (; i < j; i++)
     {
       char str[5];
-      unsigned fore = foreground, back = background;
 
-      if (i == idx)
-       fore = background, back = foreground;
-      if (features[i].on)
+      if (rec->features[i].on)
        {
-         XtSetArg (arg[0], XtNforeground, back);
-         XtSetArg (arg[1], XtNbackground, fore);
+         XtSetArg (arg[0], XtNborderWidth, 3);
+         XtSetArg (arg[1], XtNinternalHeight, 2);
+         XtSetArg (arg[2], XtNinternalWidth, 2);
        }
       else
        {
-         XtSetArg (arg[0], XtNforeground, fore);
-         XtSetArg (arg[1], XtNbackground, back);
+         XtSetArg (arg[0], XtNborderWidth, 1);
+         XtSetArg (arg[1], XtNinternalHeight, 4);
+         XtSetArg (arg[2], XtNinternalWidth, 4);
        }
-      OTF_tag_name (list->Feature[features[i].idx].FeatureTag, str);
-      XtSetArg (arg[2], XtNlabel, str);
-      XtSetValues (features[i].w, arg, 3);
+      OTF_tag_name (rec->features[i].tag, str);
+      XtSetArg (arg[3], XtNlabel, str);
+      XtSetValues (rec->features[i].w, arg, 4);
     }
   update_seq_area ();
 }
 
 void
-GsubProc (Widget w, XtPointer client_data, XtPointer call_data)
+setup_feature_rec (FeatureRec *rec)
 {
-  FeatureProc (&otf->gsub->FeatureList, gsub, (int) client_data);
-}             
+  int i;
+  Arg arg[10];
+
+  rec->langsys = NULL;
+  for (i = 0; i < rec->gsub_gpos->ScriptList.ScriptCount; i++)
+    if (rec->gsub_gpos->ScriptList.Script[i].ScriptTag == script_tag)
+      {
+       rec->langsys = &rec->gsub_gpos->ScriptList.Script[i].DefaultLangSys;
+       break;
+      }
+
+  if (! rec->langsys)
+    i = 0;
+  else
+    {
+      XtSetArg (arg[0], XtNborderWidth, 1);
+      XtSetArg (arg[1], XtNinternalHeight, 4);
+      XtSetArg (arg[2], XtNinternalWidth, 4);
+      XtSetArg (arg[3], XtNborderColor, foreground);
+      XtSetArg (arg[4], XtNsensitive, True);
+      for (i = 0; i < rec->langsys->FeatureCount && i < MAX_FEATURE_COUNT; i++)
+       {
+         OTF_Feature *feature = rec->gsub_gpos->FeatureList.Feature;
+         int index = rec->langsys->FeatureIndex[i];
+         char label[5];
+
+         rec->features[i].tag = feature[index].FeatureTag;
+         rec->features[i].on = 0;
+         OTF_tag_name (rec->features[i].tag, label);
+         XtSetArg (arg[5], XtNlabel, label);
+         XtSetValues (rec->features[i].w, arg, 6);
+       }
+    }
+  XtSetArg (arg[0], XtNborderColor, background);
+  XtSetArg (arg[1], XtNsensitive, False);
+  XtSetArg (arg[2], XtNlabel, "    ");
+  for (; i < MAX_FEATURE_COUNT; i++)
+    XtSetValues (rec->features[i].w, arg, 3);
+}
 
 void
-GposProc (Widget w, XtPointer client_data, XtPointer call_data)
+ScriptProc (Widget w, XtPointer client_data, XtPointer call_data)
 {
-  FeatureProc (&otf->gpos->FeatureList, gpos, (int) client_data);
+  if (script_tag == (OTF_Tag) client_data)
+    return;
+  script_tag = (OTF_Tag) client_data;
+  setup_feature_rec (&gsub);
+  setup_feature_rec (&gpos);
+  update_seq_area ();
 }
 
 Widget
-create_otf_widgets (int flag, Widget prev)
+create_otf_script_widgets (Widget prev)
 {
-  char *label;
-  OTF_FeatureList *list;
-  XtCallbackProc proc;
-  FeatureRec *features;
-  Arg arg[10];
   Widget w;
-  char *box_label, feature_label[5];
-  int i;
+  Arg arg[10];
+  int n, i;
+  OTF_Tag *scripts;
+  char script_name[5];
+
+  XtSetArg (arg[0], XtNborderWidth, 0);
+  XtSetArg (arg[1], XtNleft, XawChainLeft);
+  XtSetArg (arg[2], XtNright, XawChainLeft);
+  XtSetArg (arg[3], XtNtop, XawChainTop);
+  XtSetArg (arg[4], XtNbottom, XawChainTop);
+  XtSetArg (arg[5], XtNfromVert, prev);
+  XtSetArg (arg[6], XtNorientation, XtorientHorizontal);
+  prev = XtCreateManagedWidget ("Script", boxWidgetClass, render_area, arg, 7);
+  XtCreateManagedWidget ("script", labelWidgetClass, prev, arg, 1);
+
+  n = 0;
+  if (otf->gsub)
+    n = otf->gsub->ScriptList.ScriptCount;
+  if (otf->gpos)
+    n += otf->gpos->ScriptList.ScriptCount;
+  scripts = alloca (sizeof (OTF_Tag) * n);
+  i = 0;
+  if (otf->gsub)
+    for (; i < otf->gsub->ScriptList.ScriptCount; i++)
+      scripts[i] = otf->gsub->ScriptList.Script[i].ScriptTag;
+  n = i;
+  if (otf->gpos)
+    for (; i < otf->gpos->ScriptList.ScriptCount; i++)
+      {
+       OTF_Tag tag = otf->gpos->ScriptList.Script[i].ScriptTag;
+       int j;
+
+       for (j = 0; j < i; j++)
+         if (tag == scripts[j])
+           break;
+       if (j == i)
+         scripts[n++] = tag;
+      }
 
-  if (flag)
+  if (n == 0)
+    return prev;
+
+  script_tag = scripts[0];
+  OTF_tag_name (scripts[0], script_name);
+  if (n == 1)
     {
-      label = "GSUB";
-      list = &otf->gsub->FeatureList;
-      proc = GsubProc;
-      features = gsub;
+      XtSetArg (arg[0], XtNforeground, background);
+      XtSetArg (arg[1], XtNbackground, foreground);
+      XtCreateManagedWidget (script_name, labelWidgetClass, prev, arg, 2);
     }
   else
     {
-      label = "GPOS";
-      list = &otf->gpos->FeatureList;
-      proc = GposProc;
-      features = gpos;
+      char name[5];
+
+      XtSetArg (arg[0], XtNstate, True);
+      w = XtCreateManagedWidget (script_name, toggleWidgetClass, prev, arg, 1);
+      XtAddCallback (w, XtNcallback, ScriptProc, (XtPointer) scripts[0]);
+      XtSetArg (arg[0], XtNradioGroup, w);
+      for (i = 1; i < n; i++)
+       {
+         OTF_tag_name (scripts[i], name);
+         w = XtCreateManagedWidget (name, toggleWidgetClass, prev, arg, 1);
+         XtAddCallback (w, XtNcallback, ScriptProc, (XtPointer) scripts[i]);
+       }         
     }
-  box_label = alloca (strlen (label) + 4);
+  return prev;
+}
 
-  strcpy (box_label, label);
-  strcat (box_label, "box");
+
+Widget
+create_otf_widgets (Widget prev, FeatureRec *rec)
+{
+  Arg arg[10];
+  Widget w;
+  int i;
 
   XtSetArg (arg[0], XtNborderWidth, 0);
   XtSetArg (arg[1], XtNleft, XawChainLeft);
@@ -645,40 +909,52 @@ create_otf_widgets (int flag, Widget prev)
   XtSetArg (arg[4], XtNbottom, XawChainTop);
   XtSetArg (arg[5], XtNfromVert, prev);
   XtSetArg (arg[6], XtNorientation, XtorientHorizontal);
-  prev = XtCreateManagedWidget (box_label, boxWidgetClass, render_area, arg, 7);
-  XtCreateManagedWidget (label, labelWidgetClass, prev, arg, 1);
-  w = XtCreateManagedWidget ("all", commandWidgetClass, prev, NULL, 0);
-  XtAddCallback (w, XtNcallback, proc, (XtPointer) -2);
-  w = XtCreateManagedWidget ("none", commandWidgetClass, prev, NULL, 0);
-  XtAddCallback (w, XtNcallback, proc, (XtPointer) -1);
-  for (i = 0; i < list->FeatureCount; i++)
+  prev = XtCreateManagedWidget (rec->label, boxWidgetClass, render_area,
+                               arg, 7);
+  XtCreateManagedWidget (rec->label, labelWidgetClass, prev, arg, 1);
+  XtSetArg (arg[0], XtNborderWidth, 1);
+  XtSetArg (arg[1], XtNinternalHeight, 4);
+  XtSetArg (arg[2], XtNinternalWidth, 4);
+  w = XtCreateManagedWidget ("all", commandWidgetClass, prev, arg, 3);
+  XtAddCallback (w, XtNcallback, FeatureProc, (XtPointer) rec);
+  w = XtCreateManagedWidget ("none", commandWidgetClass, prev, arg, 3);
+  XtAddCallback (w, XtNcallback, FeatureProc, (XtPointer) rec);
+
+  for (i = 0; i < MAX_FEATURE_COUNT; i++)
     {
-      OTF_tag_name (list->Feature[i].FeatureTag, feature_label);
-      features[i].idx = i;
-      w = XtCreateManagedWidget (feature_label, commandWidgetClass, prev,
-                                NULL, 0);
-      XtAddCallback (w, XtNcallback, proc, (XtPointer) i);
-      features[i].w = w;
+      w = XtCreateManagedWidget ("", commandWidgetClass, prev, arg, 0);
+      XtAddCallback (w, XtNcallback, FeatureProc, (XtPointer) rec);
+      rec->features[i].w = w;
     }
 
+  setup_feature_rec (rec);
   return prev;
 }
 
 void
-create_widgets (int pixel_size)
+create_widgets ()
 {
   String quit_action = "<KeyPress>q: set() notify() unset()";
-  String FIRST_action = "~Shift<KeyPress>f: set() notify() unset()";
-  String PREV_action = "Shift<KeyPress>p: set() notify() unset()";
-  String prev_action = "~Shift<KeyPress>p: set() notify() unset()";
-  String next_action = "~Shift<KeyPress>n: set() notify() unset()";
-  String NEXT_action = "Shift<KeyPress>n: set() notify() unset()";
-  String LAST_action = "~Shift<KeyPress>l: set() notify() unset()";
+  String FIRST_action = "<KeyPress>f: set() notify() unset()\n\
+                        <KeyPress>Home: set() notify() unset()";
+  String PREV_action = "Shift<KeyPress>p: set() notify() unset()\n\
+                        <KeyPress>Up: set() notify() unset()";
+  String prev_action = "~Shift<KeyPress>p: set() notify() unset()\n\
+                        <KeyPress>Left: set() notify() unset()";
+  String next_action = "~Shift<KeyPress>n: set() notify() unset()\n\
+                        <KeyPress>Right: set() notify() unset()";
+  String NEXT_action = "Shift<KeyPress>n: set() notify() unset()\n\
+                        <KeyPress>Down: set() notify() unset()";
+  String LAST_action = "<KeyPress>l: set() notify() unset()\n\
+                        <KeyPress>End: set() notify() unset()";
   Arg arg[10];
   int i, j;
   Widget prev, w;
+  String trans = "<Expose>: Expose()";
+
+  XtSetArg (arg[0], XtNtranslations, XtParseTranslationTable (trans));
+  frame = XtCreateManagedWidget ("frame", formWidgetClass, shell, arg, 1);
 
-  frame = XtCreateManagedWidget ("frame", formWidgetClass, shell, NULL, 0);
   XtSetArg (arg[0], XtNleft, XawChainLeft);
   XtSetArg (arg[1], XtNright, XawChainLeft);
   XtSetArg (arg[2], XtNtop, XawChainTop);
@@ -706,7 +982,11 @@ create_widgets (int pixel_size)
 
   dump = XtCreateManagedWidget ("Dump Image", commandWidgetClass,
                                command_area, arg, 1);
-  XtAddCallback (dump, XtNcallback, DumpProc, (XtPointer) pixel_size);
+  XtAddCallback (dump, XtNcallback, DumpProc, NULL);
+
+  XtSetArg (arg[0], XtNborderWidth, 0);
+  XtSetArg (arg[1], XtNwidth, 10);
+  XtCreateManagedWidget ("spacer", boxWidgetClass, command_area, arg, 2);
 
   charmap = alloca (sizeof (Widget) * (face->num_charmaps + 1));
   XtSetArg (arg[0], XtNstate, True);
@@ -784,22 +1064,21 @@ create_widgets (int pixel_size)
          int k = i * 16 + j;
 
          n = 4;
-         XtSetArg (arg[n], XtNheight, glyph_height), n++;
-         XtSetArg (arg[n], XtNwidth, glyph_width), n++;
          if (i > 0)
            XtSetArg (arg[n], XtNfromVert, w), n++;
          if (j == 0)
            XtSetArg (arg[n], XtNfromHoriz, head), n++;
          else
            XtSetArg (arg[n], XtNfromHoriz, glyph[k - 1]), n++;
-         XtSetArg (arg[n], XtNinternalWidth, 0), n++;
+         XtSetArg (arg[n], XtNbitmap, none_pixmap), n++;
          glyph[k] = XtCreateManagedWidget ("glyph", commandWidgetClass,
                                            glyph_area, arg, n);
          XtAddCallback (glyph[k], XtNcallback, RenderProc, (XtPointer) k);
        }
       w = head;
     }
-  XtSetArg(arg[4], XtNwidth, glyph_width + 2);
+  /* 10 = (1 (border_width) + 4 (inner_width)) * 2 */
+  XtSetArg(arg[4], XtNwidth, glyph_width + 10);
   XtSetArg (arg[5], XtNfromVert, glyph[112]);
   XtSetArg (arg[6], XtNfromHoriz, w);
   XtSetArg (arg[7], XtNborderWidth, 0);
@@ -825,6 +1104,8 @@ create_widgets (int pixel_size)
   XtAddCallback (clear, XtNcallback, RenderProc, (XtPointer) -1);
   del = XtCreateManagedWidget ("delete", commandWidgetClass, w, arg, 0);
   XtAddCallback (del, XtNcallback, RenderProc, (XtPointer) -2);
+  bidi = XtCreateManagedWidget ("L->R", toggleWidgetClass, w, arg, 0);
+  XtAddCallback (bidi, XtNcallback, BidiProc, NULL);
 
   XtSetArg (arg[4], XtNorientation, XtorientHorizontal);
   XtSetArg (arg[5], XtNborderWidth, 0);
@@ -841,10 +1122,21 @@ create_widgets (int pixel_size)
   w = raw;
   if (otf)
     {
-      if (OTF_get_table (otf, "GSUB") >= 0)
-       w = create_otf_widgets (1, w);
-      if (OTF_get_table (otf, "GPOS") >= 0)
-       w = create_otf_widgets (0, w);
+      OTF_get_table (otf, "GSUB");
+      OTF_get_table (otf, "GPOS");
+      w = create_otf_script_widgets (w);
+      if (otf->gsub)
+       {
+         gsub.label = "GSUB";
+         gsub.gsub_gpos = otf->gsub;
+         w = create_otf_widgets (w, &gsub);
+       }
+      if (otf->gpos)
+       {
+         gpos.label = "GPOS";
+         gpos.gsub_gpos = otf->gpos;
+         w = create_otf_widgets (w, &gpos);
+       }
     }
 
   XtSetArg (arg[6], XtNfromVert, w);
@@ -852,13 +1144,28 @@ create_widgets (int pixel_size)
   XtSetArg (arg[0], XtNborderWidth, 0);
   XtSetArg (arg[1], XtNlabel, "seq: ");
   seq_label = XtCreateManagedWidget ("seq-label", labelWidgetClass,
-                                     seq, arg, 2);
+                                    seq, arg, 2);
   XtSetArg (arg[1], XtNbitmap, seq_pixmap);
   seq_image = XtCreateManagedWidget ("seq-image", labelWidgetClass,
-                                     seq, arg, 2);
+                                    seq, arg, 2);
   XtInstallAllAccelerators (shell, shell);
 }
 
+static void
+ExposeProc (Widget w, XEvent *event, String *str, Cardinal *num)
+{
+  XTextProperty text_prop;
+  char *pname = "otfview";
+  char *fname = basename (filename);
+  char *name = alloca (strlen (fname) + 3 + strlen (pname) + 1);
+
+  sprintf (name, "%s - %s", pname, fname);
+  text_prop.value = (unsigned char *) name;
+  text_prop.encoding = XA_STRING;
+  text_prop.format = 8;
+  text_prop.nitems = strlen (name);
+  XSetWMName (display, XtWindow (shell), &text_prop);
+}
 
 /* Format MSG by FMT and print the result to the stderr, and exit.  */
 
@@ -878,17 +1185,19 @@ x_error_handler (Display *display, XErrorEvent *error)
 int
 main (int argc, char **argv)
 {
-  FT_Library library;
+  XtActionsRec actions[] = { {"Expose", ExposeProc} };
+  Arg arg[10];
 
+  FT_Library library;
   OTF_GlyphString gstring;
   OTF_Glyph *g;
 
   int err;
   int i;
-  int pixel_size = DEFAULT_PIXEL_SIZE;
   int fixed_pixel_size = 0;
   int display_width;
 
+  pixel_size = DEFAULT_PIXEL_SIZE;
   {
     char *str = getenv ("PIXEL_SIZE");
 
@@ -904,7 +1213,7 @@ main (int argc, char **argv)
   gstring.glyphs = g;
 
   shell = XtOpenApplication (&context, "OTFView", NULL, 0, &argc, argv, NULL,
-                            shellWidgetClass, NULL, 0);
+                            shellWidgetClass, arg, 0);
   display = XtDisplay (shell);
   /*XSynchronize (display, True);*/
   XSetErrorHandler (x_error_handler);
@@ -918,6 +1227,8 @@ main (int argc, char **argv)
     {
       fprintf (stderr, "Usage: %s [ X-OPTION ... ]  OTF-FILE\n",
               basename (argv[0]));
+      fprintf (stderr,
+              "  Pixel size is decided by the environment variable PIXEL_SIZE ((default %d).\n", DEFAULT_PIXEL_SIZE);
       exit (argc != 2);
     }
   filename = argv[1];
@@ -935,11 +1246,6 @@ main (int argc, char **argv)
        otf = NULL;
     }
 
-  if (otf)
-    {
-      memset (gsub, 0, sizeof gsub);
-      memset (gpos, 0, sizeof gpos);
-    }
   if ((err = FT_Init_FreeType (&library)))
     FATAL_ERROR ("%s\n", "FT_Init_FreeType: error");
   err = FT_New_Face (library, filename, 0, &face);
@@ -978,26 +1284,8 @@ main (int argc, char **argv)
 
   glyph_x = - (face->bbox.xMin * pixel_size / face->units_per_EM);
   glyph_y = face->bbox.yMax * pixel_size / face->units_per_EM;
-
-  for (i = 0; i < 0x10000; i++)
-    if (FT_Load_Glyph (face, i, FT_LOAD_RENDER | FT_LOAD_MONOCHROME) == 0)
-      {
-       if (glyph_x < - face->glyph->bitmap_left)
-         glyph_x = - face->glyph->bitmap_left;
-       if (glyph_y < face->glyph->bitmap_top)
-         glyph_y = face->glyph->bitmap_top;
-       if (glyph_width
-           < glyph_x + face->glyph->bitmap_left + face->glyph->bitmap.width)
-         glyph_width
-           = glyph_x + face->glyph->bitmap_left + face->glyph->bitmap.width;
-       if (glyph_height
-           < glyph_y - face->glyph->bitmap_top + face->glyph->bitmap.rows)
-         glyph_height
-           = glyph_y - face->glyph->bitmap_top + face->glyph->bitmap.rows;
-      }
-
   none_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
-                              glyph_width, glyph_height + 1 + FONT_HEIGHT, 1);
+                              glyph_width, glyph_height, 1);
 
   {
     unsigned long valuemask =  GCFunction | GCLineWidth;
@@ -1014,7 +1302,7 @@ main (int argc, char **argv)
   }
 
   XFillRectangle (display, none_pixmap, gc, 0, 0,
-                 glyph_width, glyph_height + 1 + FONT_HEIGHT);
+                 glyph_width, glyph_height);
   XDrawString (display, none_pixmap, gc_inv,
               (glyph_width - XTextWidth (font, "none", 4)) / 2,
               glyph_height / 2, "none", 4);
@@ -1046,14 +1334,14 @@ main (int argc, char **argv)
   seq_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
                              render_width, render_height, 1);
 
-  for (i = 0; i < 0x10000; i++)
-    create_pixmap (pixel_size, i);
-  create_widgets (pixel_size);
+  memset (bitmap, 0, sizeof (bitmap));
+  create_widgets ();
   glyph_index = 0;
   charmap_index = -1;
   update_glyph_area ();
   update_render_area ();
 
+  XtAppAddActions (context, actions, XtNumber (actions));
   XtRealizeWidget (shell);
   XtAppMainLoop (context);