(create_otf_script_widgets): Create a box
[m17n/libotf.git] / example / otfview.c
index 0accf52..82c3cfe 100644 (file)
@@ -1,6 +1,6 @@
 /* otfview.c -- View glyphs of OpenType fonts.
 
-Copyright (C) 2003, 2004
+Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010
   National Institute of Advanced Industrial Science and Technology (AIST)
   Registration Number H15PRO167
 
@@ -28,6 +28,8 @@ write to the Free Software Foundation, Inc., 59 Temple Place, Suite
 #include <unistd.h>
 #include <libgen.h>
 
+#include "config.h"
+
 #ifdef HAVE_X11_XAW_COMMAND_H
 
 #include <X11/Xatom.h>
@@ -45,6 +47,19 @@ write to the Free Software Foundation, Inc., 59 Temple Place, Suite
 
 #include <otf.h>
 
+#define CAST_FROM_XTPOINTER(TYPE, DATA, VAR)   \
+  do {                                         \
+    long TYPE temp = (long TYPE) (DATA);       \
+    (VAR) = temp;                              \
+  } while (0)
+
+#define XtAddCallbackWithCast(TYPE, W, PROC, VAR)              \
+  do {                                                         \
+    long TYPE temp = (long TYPE) (VAR);                                \
+    XtAddCallback (W, XtNcallback, PROC, (XtPointer) temp);    \
+  } while (0)
+
+
 #define DEFAULT_PIXEL_SIZE 30
 int pixel_size;
 
@@ -70,8 +85,14 @@ XtAppContext context;
    | | idxh[7] glyph[112]  ...    glyph[127]| |
    | |         idxl[0]     ...    idxl[15]  | |
    | +--------------------------------------+ |
+   | +--- script_area (box) ----------------+ |
+   | | script(langsys) DFLT ...             | |
+   | +--------------------------------------+ |
+   | +---- uvs_area (box) (optional) -------+ |
+   | | uvs[?].w ...                         | |
+   | +--------------------------------------+ |
    | +--- render_area (form) ---------------+ |
-   | | clear del bidi                       | |
+   | | clear del bidi alt_subst             | |
    | | +--- raw (box) --------------------+ | |
    | | | raw_label raw_image              | | |
    | | +----------------------------------+ | |
@@ -80,14 +101,18 @@ XtAppContext context;
    | | +--- seq (box) --------------------+ | |
    | | | seq_label seq_image              | | |
    | | +----------------------------------+ | |
+   | | +--- code (box) -------------------+ | |
+   | | | code_label code_list ...         | | |   
+   | | +----------------------------------+ | |
    | +--------------------------------------+ |
    +------------------------------------------+ */
 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, bidi, raw, seq;
-Widget raw_label, raw_image, seq_label, seq_image;
+Widget uvs_area, uvs_label;
+Widget render_area, clear, del, bidi, alt_subst, raw, seq, code;
+Widget raw_label, raw_image, seq_label, seq_image, code_label, code_list;
 unsigned long foreground, background;
 
 typedef struct
@@ -109,7 +134,8 @@ typedef struct
 
 FeatureRec gsub, gpos;
 
-OTF_Tag script_tag;
+/* Currently selected script and langsys.  */
+OTF_Tag script_tag, langsys_tag;
 
 int glyph_char[128];
 
@@ -123,7 +149,7 @@ typedef struct {
   int advance;
 } BitmapRec;
 
-BitmapRec bitmap[0x10000];
+BitmapRec bitmap[0x110000];
 
 int render_width, render_height;
 Pixmap raw_pixmap, seq_pixmap, gsub_pixmap, gpos_pixmap;
@@ -140,6 +166,7 @@ struct {
 int charmap_index;
 
 int reversed;
+int do_alternate_subst;
 unsigned glyph_width, glyph_height;
 int glyph_x, glyph_y;
 int glyph_index;
@@ -150,8 +177,16 @@ struct {
   int codes[64];
 } glyph_rec;
 
+OTF_EncodingSubtable14 *sub14 = NULL;
+
+struct {
+  Widget w;
+  int c;
+} uvs[256];
+
 OTF *otf;
 char *filename;
+int fontindex;
 
 void
 create_pixmap (int index)
@@ -207,9 +242,14 @@ update_glyph_area ()
 
       if (charmap_index >= 0)
        index = FT_Get_Char_Index (face, (FT_ULong) index);
-      if (! bitmap[index].pixmap)
-       create_pixmap (index);
-      XtSetArg (arg[0], XtNbitmap, bitmap[index].pixmap);
+      if (charmap_index >= 0 && ! index)
+       XtSetArg (arg[0], XtNbitmap, none_pixmap);
+      else
+       {
+         if (! bitmap[index].pixmap)
+           create_pixmap (index);
+         XtSetArg (arg[0], XtNbitmap, bitmap[index].pixmap);
+       }
       XtSetValues (glyph[i], arg, 1);
     }
 
@@ -224,46 +264,65 @@ update_glyph_area ()
       XtSetValues (index_label[i], arg, 2);
     }
 
-  sprintf (buf, " %04X-%04X ", glyph_index, glyph_index + 0x7F);
+  if (glyph_index < 0x10000)
+    sprintf (buf, "  %04X-%04X  ", glyph_index, glyph_index + 0x7F);
+  else
+    sprintf (buf, "%06X-%06X", glyph_index, glyph_index + 0x7F);
   XtSetArg (arg[0], XtNlabel, buf);
   XtSetValues (range, arg, 1);
 }
 
+void
+update_uvs_area (int c)
+{
+  OTF_GlyphID code[256];
+  Arg arg[1];
+  int i;
+
+  OTF_get_variation_glyphs (otf, c, code);
+
+  for (i = 0; i < 256; i++)
+    if (uvs[i].w)
+      {
+       if (code[i])
+         XtSetArg (arg[0], XtNsensitive, True);
+       else
+         XtSetArg (arg[0], XtNsensitive, False);
+       XtSetValues (uvs[i].w, arg, 1);
+      }
+}
+
+
 char *
 get_features (OTF_FeatureList *list, FeatureRec *rec)
 {
   int i, n;
   char *str, *p;
 
-  if (! rec->features[0].on)
+  if (! rec->langsys || ! rec->features)
     return NULL;
   for (i = n = 0; i < rec->langsys->FeatureCount; i++)
-    {
-      if (rec->features[i].on)
-       n++;
-      else
-       break;
-    }
-  if (i == rec->langsys->FeatureCount)
-    {
-      str = malloc (2);
-      strcpy (str, "*");
-      return str;
-    }
+    if (rec->features[i].on)
+      n++;
+  if (n == 0)
+    return NULL;
   str = malloc (n * 5);
-  for (i = 0, p = str; i < n; i++, p += 5)
-    {
-      OTF_tag_name (rec->features[i].tag, p);
-      p[4] = ',';
-    }
+  for (i = 0, p = str; i < rec->langsys->FeatureCount; i++)
+    if (rec->features[i].on)
+      {
+       OTF_tag_name (rec->features[i].tag, p);
+       p[4] = ',';
+       p += 5;
+      }
   p[-1] = '\0';
   return str;
 }
 
 
-#define DEVICE_DELTA(table, size)                              \
-  (((size) >= (table).StartSize && (size) <= (table).EndSize)  \
-   ? (table).DeltaValue[(size) >= (table).StartSize]           \
+#define DEVICE_DELTA(table, size)                                      \
+  (((table).DeltaValue                                                 \
+    && ((size) >= (table).StartSize && (size) <= (table).EndSize))     \
+   ? (table).DeltaValue[(size) >= (table).StartSize]                   \
    : 0)
 
 void
@@ -300,26 +359,62 @@ update_seq_area ()
   int len = glyph_rec.n_glyphs;
   Arg arg[1];
   int unitsPerEm = face->units_per_EM;
+  OTF_Tag *log = NULL;
+  int logsize;
 
   gstring.size = gstring.used = len;
   gstring.glyphs = malloc (sizeof (OTF_Glyph) * len);
   memset (gstring.glyphs, 0, sizeof (OTF_Glyph) * len);
   for (i = 0; i < len; i++)
-    gstring.glyphs[i].c = gstring.glyphs[i].glyph_id = glyph_rec.glyphs[i];
+    {
+      gstring.glyphs[i].c = glyph_rec.codes[i];
+      if (charmap_index < 0)
+       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 *script_name = NULL, *langsys_name = NULL, buf[10];
       char *str;
 
+      if (script_tag)
+       {
+         script_name = buf;
+         OTF_tag_name (script_tag, script_name);
+       }
+      if (langsys_tag)
+       {
+         langsys_name = buf + 5;
+         OTF_tag_name (langsys_tag, langsys_name);
+       }
+
+      OTF_drive_cmap (otf, &gstring);
       OTF_drive_gdef (otf, &gstring);
       if (otf->gsub)
        {
          str = get_features (&otf->gsub->FeatureList, &gsub);
          if (str)
            {
-             OTF_drive_gsub (otf, &gstring, NULL, NULL, str);
+             if (do_alternate_subst)
+               OTF_drive_gsub_alternate (otf, &gstring,
+                                         script_name, langsys_name, str);
+             else
+               {
+                 OTF_drive_gsub_with_log (otf, &gstring,
+                                          script_name, langsys_name, str);
+                 logsize = gstring.used * 2;
+                 log = alloca (sizeof (OTF_Tag) * logsize);
+                 for (i = 0; i < gstring.used; i++)
+                   {
+                     int idx = gstring.glyphs[i].positioning_type >> 4;
+                     if (idx)
+                       log[i] = otf->gsub->FeatureList.Feature[idx - 1].FeatureTag;
+                     else
+                       log[i] = 0;
+                   }
+               }
              free (str);
            }
        }
@@ -328,7 +423,32 @@ update_seq_area ()
          str = get_features (&otf->gpos->FeatureList, &gpos);
          if (str)
            {
-             OTF_drive_gpos (otf, &gstring, NULL, NULL, str);
+             OTF_drive_gpos_with_log (otf, &gstring,
+                                      script_name, langsys_name, str);
+             if (log)
+               {
+                 if (logsize < gstring.used)
+                   {
+                     OTF_Tag *log2 = alloca (sizeof (OTF_Tag) * gstring.used);
+                     memset (log2, 0, sizeof (OTF_Tag) * gstring.used);
+                     memcpy (log2, log, sizeof (OTF_Tag) * logsize);
+                     logsize = gstring.used;
+                     log = log2;
+                   }
+               }
+             else
+               {
+                 logsize = gstring.used;
+                 log = alloca (sizeof (OTF_Tag) * logsize);
+                 memset (log, 0, sizeof (OTF_Tag) * logsize);
+               }
+             for (i = 0; i < gstring.used; i++)
+               {
+                 int idx = gstring.glyphs[i].positioning_type >> 4;
+                 if (idx)
+                   log[i] = otf->gpos->FeatureList.Feature[idx - 1].FeatureTag;
+
+               }
              free (str);
            }
        }
@@ -363,70 +483,79 @@ update_seq_area ()
       int xoff = 0, yoff = 0;
       int prev_width;
       int advance = bmp->advance;
-      int j;
 
-      if (! bmp->pixmap)
+      if (gstring.glyphs[i].glyph_id && ! bmp->pixmap)
        {
          create_pixmap (gstring.glyphs[i].glyph_id);
          if (! bmp->pixmap)
            continue;
+         advance = bmp->advance;
        }
-      switch (g->positioning_type)
+      if (g->positioning_type & 0xF)
        {
-       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;
-         }
+         while (1)
+           {
+             switch (g->positioning_type & 0xF)
+               {
+               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;
+                 }
+               }
+             if (i + 1 == gstring.used
+                 || gstring.glyphs[i + 1].glyph_id
+                 || ! (gstring.glyphs[i + 1].positioning_type & 0xF))
+               break;
+             i++, g++;
+           }
        }
-         
+
       XCopyArea (display, bmp->pixmap, seq_pixmap, gc_or,
                 glyph_x + bmp->x, glyph_y + bmp->y, bmp->width, bmp->height,
                 x + bmp->x + xoff, glyph_y + bmp->y - yoff);
@@ -438,26 +567,44 @@ update_seq_area ()
        mark = g;
       else
        base = g, base_width = advance;
-
-      if (g->alternate_set)
-       for (j = 0; j < g->alternate_set->GlyphCount; j++)
-         {
-           bmp = bitmap + g->alternate_set->Alternate[j];
-           if (! bmp->pixmap)
-             create_pixmap (g->alternate_set->Alternate[j]);
-           if (! bmp->pixmap)
-             continue;
-           XCopyArea (display, bmp->pixmap, seq_pixmap, gc_or,
-                      glyph_x + bmp->x, glyph_y + bmp->y, 
-                      bmp->width, bmp->height,
-                      x + bmp->x + xoff, glyph_y + bmp->y - yoff);
-           x += bmp->advance;
-       }
     }
-  free (gstring.glyphs);
 
   XtSetArg (arg[0], XtNbitmap, seq_pixmap);
   XtSetValues (seq_image, arg, 1);
+
+  if (gstring.used > 0)
+    {
+      int size = render_width / FONT_WIDTH;
+      char *buf = alloca (size + 1);
+      char name[5];
+
+      sprintf (buf, "%04X", gstring.glyphs[0].glyph_id);
+      if (log && log[0])
+       {
+         OTF_tag_name (log[0], name);
+         sprintf (buf + 4, " (%s)", name);
+         x = 11;
+       }
+      else
+       x = 4;
+      for (i = 1; i < gstring.used && x + 5 < size; i++, x += 5)
+       {
+         sprintf (buf + x, " %04X", gstring.glyphs[i].glyph_id);
+         if (log && log[i] && x + 11 < size)
+           {
+             OTF_tag_name (log[i], name);
+             sprintf (buf + x + 5, "(%s)", name);
+             x += 6;
+           }
+       }
+      while (x < size)
+       buf[x++] = ' ';
+      buf[x] = '\0';
+      XtSetArg (arg[0], XtNlabel, buf);
+      XtSetValues (code_list, arg, 1);
+    }
+
+  free (gstring.glyphs);
 }
 
 
@@ -471,28 +618,50 @@ update_render_area ()
   XFillRectangle (display, raw_pixmap, gc, 0, 0, render_width, render_height);
   for (i = 0, x = glyph_x; i < glyph_rec.n_glyphs; i++)
     {
-      BitmapRec *bmp = bitmap + glyph_rec.glyphs[i];
-      char buf[5];
-
-      XCopyArea (display, bmp->pixmap, raw_pixmap, gc,
-                0, 0, glyph_width, glyph_height,
-                (glyph_width + 4) * i + 1, 1);
-      XDrawRectangle (display, raw_pixmap, gc_set,
-                     (glyph_width + 4) * i, 0,
-                     glyph_width + 1, glyph_height + 1);
-      XDrawLine (display, raw_pixmap, gc_set,
-                (glyph_width + 4) * i + 1 + glyph_x, 1,
-                (glyph_width + 4) * i + 1 + glyph_x, glyph_height + 1);
-      XDrawLine (display, raw_pixmap, gc_set,
-                (glyph_width + 4) * i + 1 + glyph_x + bmp->advance, 1,
-                (glyph_width + 4) * i + 1 + glyph_x + bmp->advance,
-                glyph_height + 1);
-
-      sprintf (buf, "%04X", glyph_rec.codes[i]);
-      XDrawString (display, raw_pixmap, gc_inv, 
-                  (glyph_width + 1) * i + 1
-                  + (glyph_width - XTextWidth (font, buf, 4)) / 2,
-                  glyph_height + 2 + FONT_HEIGHT, buf, 4);
+      if (glyph_rec.glyphs[i] >= 0)
+       {
+         BitmapRec *bmp = bitmap + glyph_rec.glyphs[i];
+         char buf[5];
+
+         XCopyArea (display, bmp->pixmap, raw_pixmap, gc,
+                    0, 0, glyph_width, glyph_height,
+                    (glyph_width + 4) * i + 1, 1);
+         XDrawRectangle (display, raw_pixmap, gc_set,
+                         (glyph_width + 4) * i, 0,
+                         glyph_width + 1, glyph_height + 1);
+         XDrawLine (display, raw_pixmap, gc_set,
+                    (glyph_width + 4) * i + 1 + glyph_x, 1,
+                    (glyph_width + 4) * i + 1 + glyph_x, glyph_height + 1);
+         XDrawLine (display, raw_pixmap, gc_set,
+                    (glyph_width + 4) * i + 1 + glyph_x + bmp->advance, 1,
+                    (glyph_width + 4) * i + 1 + glyph_x + bmp->advance,
+                    glyph_height + 1);
+
+         sprintf (buf, "%04X", glyph_rec.codes[i]);
+         XDrawString (display, raw_pixmap, gc_inv, 
+                      (glyph_width + 4) * i + 1
+                      + (glyph_width - XTextWidth (font, buf, 4)) / 2,
+                      glyph_height + 2 + FONT_HEIGHT, buf, 4);
+       }
+      else
+       {
+         /* Variation Selector */
+         int idx = - glyph_rec.glyphs[i];
+         char buf[4];
+
+         sprintf (buf, "%03d", idx);
+         XDrawRectangle (display, raw_pixmap, gc_set,
+                         (glyph_width + 4) * i, 0,
+                         glyph_width + 1, glyph_height + 1);
+         XDrawString (display, raw_pixmap, gc_set,
+                      (glyph_width + 4) * i + 1
+                      + (glyph_width - XTextWidth (font, "VS", 2)) / 2,
+                      1 + glyph_height / 2, "VS", 2);
+         XDrawString (display, raw_pixmap, gc_set,
+                      (glyph_width + 4) * i + 1
+                      + (glyph_width - XTextWidth (font, buf, 3)) / 2,
+                      1 + glyph_height / 2 + FONT_ASCENT, buf, 3);
+       }
     }
   XtSetArg (arg[0], XtNbitmap, raw_pixmap);
   XtSetValues (raw_image, arg, 1);
@@ -508,128 +677,134 @@ QuitProc (Widget w, XtPointer client_data, XtPointer call_data)
 void
 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;
-  int a4_width = 210 * 300 / 25.4 - margin * 2;
-  int a4_height = 297 * 300 / 25.4 - margin * 2;
-  int size = 100;
-  Pixmap pixmap;
-  XImage ximage, *image;
-  int i, x, y;
-  char *data;
-  int bytes_per_line;
-
-  FT_Set_Pixel_Sizes (face, 0, size);
-  g_width = ((face->bbox.xMax - face->bbox.xMin) * size / face->units_per_EM);
-  g_height = ((face->bbox.yMax - face->bbox.yMin) * size / face->units_per_EM);
-  pix_width = (g_width + 1) * 16 + margin + 1;
-  pix_height = (g_height + 1 + FONT_HEIGHT) * 16 + margin + 1;
-  while (pix_width > a4_width || pix_height > a4_height)
+  int g_width, g_height;
+  float g_x, g_y;
+  /* unit in points (1/72 inch); to fit in both US-letter and A4 */
+  static int xoff = 30, yoff = 30;
+  static int unit = 30;
+  static int margin = 2;
+  static int title_height = 20, label_height = 10;
+  int total_width = (unit + margin * 2) * 16;
+  int total_height = (unit + margin * 2 + label_height) * 16 + title_height;
+  /* pixel size (dots) */
+  int size = 128;
+  int i, j, k, l;
+  char *name = alloca (strlen (filename) + 10);
+  FILE *fp;
+  int index = (glyph_index / 0x100) * 0x100;
+  float scale;
+
+  g_width = face->bbox.xMax - face->bbox.xMin;
+  g_height = face->bbox.yMax - face->bbox.yMin;
+  if (g_width > g_height)
     {
-      size--;
-      FT_Set_Pixel_Sizes (face, 0, size);
-      g_width = ((face->bbox.xMax - face->bbox.xMin)
-                * size / face->units_per_EM);
-      g_height = ((face->bbox.yMax - face->bbox.yMin)
-                 * size / face->units_per_EM);
-      pix_width = (g_width + 1) * 16 + margin + 1;
-      pix_height = (g_height + 1 + FONT_HEIGHT) * 16 + margin + 1;
+      scale = g_width * size;
+      g_x = face->bbox.xMin * unit / g_width;
+      g_y = face->bbox.yMin * unit / g_width;
     }
-
-  g_x = - (face->bbox.xMin * size / face->units_per_EM);
-  g_y = face->bbox.yMax * size / face->units_per_EM;
-  for (i = 0; i < 0xFF; i++)
+  else
     {
-      int idx;
-
-      if (charmap_index >= 0)
-       idx = FT_Get_Char_Index (face, (FT_ULong) i);
-      else
-       idx = i;
-      if (FT_Load_Glyph (face, idx, FT_LOAD_RENDER | FT_LOAD_MONOCHROME) == 0)
-       {
-         if (g_x < - face->glyph->bitmap_left)
-           g_x = - face->glyph->bitmap_left;
-         if (g_y < face->glyph->bitmap_top)
-           g_y = face->glyph->bitmap_top;
-         if (g_width
-             < g_x + face->glyph->bitmap_left + face->glyph->bitmap.width)
-           g_width
-             = g_x + face->glyph->bitmap_left + face->glyph->bitmap.width;
-         if (g_height
-             < g_y - face->glyph->bitmap_top + face->glyph->bitmap.rows)
-           g_height
-             = g_y - face->glyph->bitmap_top + face->glyph->bitmap.rows;
-       }
+      scale = g_height * size;
+      g_x = face->bbox.xMin * unit / g_height;
+      g_y = face->bbox.yMin * unit / g_height;
     }
-  pix_width = (g_width + 1) * 16 + margin + 1;
-  pix_height = (g_height + FONT_HEIGHT + 1) * 16 + margin + 1;
-  pixmap = XCreatePixmap (display,
-                         RootWindow (display, DefaultScreen (display)),
-                         pix_width, pix_height, 1);
-  XFillRectangle (display, pixmap, gc, 0, 0, pix_width, pix_height);
-
-  for (i = 0, x = margin; i <= 16; i++, x += g_width + 1)
-    XDrawLine (display, pixmap, gc_set, x, margin,
-              x, margin + (g_height + FONT_HEIGHT + 1) * 16);
-  for (i = 0, y = margin; i <= 16; i++, y += g_height + FONT_HEIGHT + 1)
-    XDrawLine (display, pixmap, gc_set, margin, y,
-              margin + (g_width + 1) * 16, y);
-  for (i = 0; i < 256; i++)
-    {
-      char str[5];
-      int idx;
+  scale /= face->units_per_EM;
 
-      if (charmap_index >= 0)
-       idx = FT_Get_Char_Index (face, (FT_ULong) i);
-      else
-       idx = i;
-      x = margin + (g_width + 1) * (i % 16);
-      y = margin + (g_height + FONT_HEIGHT + 1) * (i / 16);
-      if (FT_Load_Glyph (face, idx, FT_LOAD_RENDER | FT_LOAD_MONOCHROME) == 0)
-       {
-         ximage.height = face->glyph->bitmap.rows;
-         ximage.width = face->glyph->bitmap.width;
-         ximage.depth = 1;
-         ximage.bits_per_pixel = 1;
-         ximage.xoffset = 0;
-         ximage.format = XYPixmap;
-         ximage.data = (char *) face->glyph->bitmap.buffer;
-         ximage.byte_order = MSBFirst;
-         ximage.bitmap_unit = 8;
-         ximage.bitmap_bit_order = MSBFirst;
-         ximage.bitmap_pad = 8;
-         ximage.bytes_per_line = face->glyph->bitmap.pitch;
-         XInitImage (&ximage);
-         XPutImage (display, pixmap, gc, &ximage, 0, 0,
-                    x + g_x + face->glyph->bitmap_left,
-                    y + g_y - face->glyph->bitmap_top, 
-                    ximage.width, ximage.height);
-       }
-      sprintf (str, "0x%02X", i);
-      XDrawString (display, pixmap, gc_inv,
-                  x + (g_width - XTextWidth (font, str, 4))/ 2,
-                  y + g_height + FONT_ASCENT, str, 4);
-    }
+  FT_Set_Pixel_Sizes (face, 0, size);
+
+  sprintf (name, "%s-%04X.ps", face->family_name, index);
+  printf ("Writing %s ... ", name);
+  fflush (stdout);
+  fp = fopen (name, "w");
+
+  fprintf (fp, "%s\n", "%!PS-Adobe-2.0 EPSF-2.0");
+  fprintf (fp, "%s\n", "%%Creater: otfview");
+  fprintf (fp, "%s %s(%s)-%04X\n", "%%Title:",
+          face->family_name, face->style_name, index);
+  fprintf (fp, "%s\n", "%%Pages: 1");
+  fprintf (fp, "%s %d %d %d %d\n", "%%BoundingBox:",
+          xoff, yoff, xoff + total_width, yoff + total_height);
+  fprintf (fp, "%s\n", "%%EndComments");
+  fprintf (fp, "%s\n", "%%BeginProlog");
+  fprintf (fp, "/W %d def\n", unit + margin * 2);
+  fprintf (fp, "/H %d def\n", unit + margin * 2 + label_height);
+  fprintf (fp, "/STR 10 string def\n");
+  fprintf (fp, "/DrawIndex {\n");
+  fprintf (fp, "  I 16 lt { (000) show } { I 256 lt { (00) show } { I 4096 lt { (0) show} if } ifelse } ifelse I 16 STR cvrs show\n");
+  fprintf (fp, "} def\n");
+  fprintf (fp, "/DrawTitle {\n");
+  fprintf (fp, "  /Courier findfont 20 scalefont setfont\n");
+  fprintf (fp, "  %d %d 4 add moveto\n", xoff + total_width / 2,
+          yoff + total_height - title_height + 2);
+  fprintf (fp, "  (%s(%s)-%04X) dup stringwidth pop 2 div neg 0 rmoveto show\n",
+          face->family_name, face->style_name, index);
+  fprintf (fp, "} def\n");
+  fprintf (fp, "/DrawFrame { gsave %d %d translate 0 setlinewidth\n",
+          xoff, yoff);
+  fprintf (fp, "  /Courier findfont 10 scalefont setfont\n");
+  fprintf (fp, "  /I %d def\n", index);
+  fprintf (fp, "  0 1 16 { W mul 0 moveto 0 H 16 mul rlineto stroke } for\n");
+  fprintf (fp, "  0 1 16 { H mul 0 exch moveto W 16 mul 0 rlineto stroke } for\n");
+  fprintf (fp, "  0 1 15 { H mul %d add 0 exch moveto W 16 mul 0 rlineto stroke } for\n", label_height);
+  fprintf (fp, "  15 -1 0 { gsave H mul 0 exch translate 0 0 moveto\n");
+  fprintf (fp, "    0 1 15 { gsave W mul 0 moveto\n");
+  fprintf (fp, "      4 2 rmoveto DrawIndex /I I 1 add def grestore} for\n");
+  fprintf (fp, "    grestore } for grestore } def\n");
+  fprintf (fp, "%s\n", "%%EndProlog");
+  fprintf (fp, "DrawTitle DrawFrame\n");
+
+  for (i = 0; i < 16; i++)
+    for (j = 0; j < 16; j++, index++)
+      {
+       int idx;
+
+       if (charmap_index >= 0) 
+         idx = FT_Get_Char_Index (face, (FT_ULong) index);
+       else
+         idx = index;
+       if (idx > 0
+           && FT_Load_Glyph (face, idx, FT_LOAD_RENDER | FT_LOAD_MONOCHROME) == 0
+           && face->glyph->bitmap.rows > 0
+           && face->glyph->bitmap.width > 0)
+         {
+           unsigned char *p = face->glyph->bitmap.buffer;
+           int width = (face->glyph->bitmap.width - 1) / 8 + 1;
+
+           fprintf (fp, "gsave %f %f translate %d %d scale 0 0 moveto\n",
+                    xoff + (unit + margin * 2) * j + margin - g_x,
+                    yoff + (unit + label_height + margin * 2) * (15 - i) + label_height + margin - g_y,
+                    unit, unit);
+           fprintf (fp, "%d %d true [%f 0 0 %f %d %d]\n",
+                    width * 8, face->glyph->bitmap.rows,
+                    scale, -scale, -face->glyph->bitmap_left,
+                    face->glyph->bitmap_top);
+           fprintf (fp, "{< ");
+           for (k = 0; k < face->glyph->bitmap.rows;
+                k++, p += face->glyph->bitmap.pitch)
+             {
+               for (l = 0; l < width; l++)
+                 fprintf (fp, "%02X", p[l]);
+               fprintf (fp, "\n");
+             }
+           fprintf (fp, ">} imagemask grestore\n");
+         }
+       else
+         {
+           int boxsize = unit + margin * 2;
+
+           fprintf (fp, "gsave 0 setlinewidth %d %d translate\n",
+                    xoff + boxsize * j,
+                    yoff + (boxsize + label_height) * (15 - i) + label_height);
+           fprintf (fp, "0 0 moveto %d %d lineto stroke\n",
+                    boxsize, boxsize);
+           fprintf (fp, "0 %d moveto %d 0 lineto stroke grestore\n",
+                    boxsize, boxsize);
+         }
+      }
+  fprintf (fp, "showpage\n");
+  fclose (fp);
+  printf ("done\n");
 
-  image = XGetImage (display, pixmap, 0, 0, pix_width, pix_height,
-                    AllPlanes, XYPixmap);
-  XInitImage (image);
-  {
-    char *name = alloca (strlen (filename) + 5);
-    FILE *fp;
-
-    sprintf (name, "%s.pbm", filename);
-    printf ("Writing %s ...", name);
-    fp = fopen (name, "w");
-    fprintf (fp, "P4\n%d %d\n", image->width, image->height);
-    bytes_per_line = (image->width + 7) / 8;
-    data = image->data;
-    for (y = 0; y < image->height; y++, data += image->bytes_per_line)
-      fwrite (data, 1, bytes_per_line, fp);
-    fclose (fp);
-    printf ("done\n");
-  }
   FT_Set_Pixel_Sizes (face, 0, (int) pixel_size);
 }
 
@@ -638,19 +813,22 @@ void
 GlyphProc (Widget w, XtPointer client_data, XtPointer call_data)
 {
   int old_glyph_index = glyph_index;
+  int data;
 
-  if ((int) client_data == -3 && glyph_index > 0)
+  CAST_FROM_XTPOINTER (int, client_data, data);
+
+  if (data == -3 && glyph_index > 0)
     glyph_index = 0;
-  else if ((int) client_data == -2 && glyph_index > 0)
-    glyph_index = (glyph_index - 1) & 0xF000;
-  else if ((int) client_data == -1 && glyph_index > 0)
+  else if (data == -2 && glyph_index > 0)
+    glyph_index = (glyph_index - 1) & 0x1FF000;
+  else if (data == -1 && glyph_index > 0)
     glyph_index -= 0x80;
-  else if ((int) client_data == 1 && glyph_index < 0xFF80)
+  else if (data == 1 && glyph_index < 0x10FF80)
     glyph_index += 0x80;
-  else if ((int) client_data == 2 && glyph_index < 0xF000)
-    glyph_index = (glyph_index + 0x1000) & 0xF000;
-  else if ((int) client_data == 3 && glyph_index < 0xF000)
-    glyph_index = 0xFF80;
+  else if (data == 2 && glyph_index < 0x10F000)
+    glyph_index = (glyph_index + 0x1000) & 0x1FF000;
+  else if (data == 3 && glyph_index < 0x10F000)
+    glyph_index = 0x10FF80;
   if (glyph_index != old_glyph_index)
     update_glyph_area ();
 }
@@ -658,22 +836,60 @@ GlyphProc (Widget w, XtPointer client_data, XtPointer call_data)
 void
 CharmapProc (Widget w, XtPointer client_data, XtPointer call_data)
 {
-  if (charmap_index == (int) client_data)
+  int data;
+
+  CAST_FROM_XTPOINTER (int, client_data, data);
+
+  if (charmap_index == data)
     return;
-  charmap_index = (int) client_data;
+  charmap_index = data;
   if (charmap_index >= 0)
     FT_Set_Charmap (face, face->charmaps[charmap_index]);
   update_glyph_area ();
 }
 
 void
+UVSProc (Widget w, XtPointer client_data, XtPointer call_data)
+{
+  unsigned idx;
+  int selector;
+  OTF_VariationSelectorRecord *record;
+  int i;
+
+  CAST_FROM_XTPOINTER (unsigned, client_data, idx);
+  selector = uvs[idx].c;
+
+  if (glyph_rec.n_glyphs >= 64)
+    return;
+  for (i = 0; i < sub14->nRecords; i++)
+    {
+      record = sub14->Records + i;
+      if (record->varSelector == selector)
+       break;
+    }
+  if (i < sub14->nRecords)
+    {
+      if (glyph_rec.n_glyphs > 0
+         && glyph_rec.glyphs[glyph_rec.n_glyphs - 1] < 0)
+       glyph_rec.n_glyphs--;
+      glyph_rec.codes[glyph_rec.n_glyphs] = selector;
+      glyph_rec.glyphs[glyph_rec.n_glyphs++] = - idx - 1;
+      update_render_area ();
+    }
+}
+
+void
 RenderProc (Widget w, XtPointer client_data, XtPointer call_data)
 {
-  if ((int) client_data < 0)
+  int data;
+
+  CAST_FROM_XTPOINTER (int, client_data, data);
+
+  if (data < 0)
     {
       if (glyph_rec.n_glyphs > 0)
        {
-         if ((int) client_data == -2)
+         if (data == -2)
            glyph_rec.n_glyphs--;
          else
            glyph_rec.n_glyphs = 0;
@@ -682,14 +898,16 @@ RenderProc (Widget w, XtPointer client_data, XtPointer call_data)
     }
   else if (glyph_rec.n_glyphs < 64)
     {
-      int index = glyph_index + (int) client_data;
+      int index = glyph_index + data;
 
       if (charmap_index >= 0)
        index = FT_Get_Char_Index (face, (FT_ULong) index);
       if (bitmap[index].pixmap)
        {
-         glyph_rec.codes[glyph_rec.n_glyphs] = glyph_index + (int) client_data;
+         glyph_rec.codes[glyph_rec.n_glyphs] = glyph_index + data;
          glyph_rec.glyphs[glyph_rec.n_glyphs++] = index;
+         if (otf)
+           update_uvs_area (glyph_index + data);
          update_render_area ();
        }
     }
@@ -710,6 +928,13 @@ BidiProc (Widget w, XtPointer client_data, XtPointer call_data)
 }
 
 void
+AltSubstProc (Widget w, XtPointer client_data, XtPointer call_data)
+{
+  do_alternate_subst = ! do_alternate_subst;
+  update_seq_area ();
+}
+
+void
 FeatureProc (Widget w, XtPointer client_data, XtPointer call_data)
 {
   FeatureRec *rec = (FeatureRec *) client_data;
@@ -736,43 +961,35 @@ FeatureProc (Widget w, XtPointer client_data, XtPointer call_data)
   if (idx < 0)
     {
       int on = idx == -2;
+      char str[5];
 
-      for (i = j = 0; j < rec->langsys->FeatureCount; j++)
-       {
-         int index  = rec->langsys->FeatureIndex[j];
-
-         rec->features[j].tag
-           = rec->gsub_gpos->FeatureList.Feature[index].FeatureTag;
-         rec->features[j].on = on;
-       }
+      for (i = 0; i < rec->langsys->FeatureCount; i++)
+       if (rec->features[i].on != on)
+         {
+           rec->features[i].on = on;
+           if (on)
+             {
+               XtSetArg (arg[0], XtNborderWidth, 3);
+               XtSetArg (arg[1], XtNinternalHeight, 2);
+               XtSetArg (arg[2], XtNinternalWidth, 2);
+             }
+           else
+             {
+               XtSetArg (arg[0], XtNborderWidth, 1);
+               XtSetArg (arg[1], XtNinternalHeight, 4);
+               XtSetArg (arg[2], XtNinternalWidth, 4);
+             }
+           OTF_tag_name (rec->features[i].tag, str);
+           XtSetArg (arg[3], XtNlabel, str);
+           XtSetValues (rec->features[i].w, arg, 4);
+         }
     }
   else
     {
-      OTF_Tag tag = rec->features[idx].tag;
-
-      i = idx;
-      if (rec->features[i].on)
-       {
-         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 + 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++)
-    {
       char str[5];
 
-      if (rec->features[i].on)
+      rec->features[idx].on = ! rec->features[idx].on;
+      if (rec->features[idx].on)
        {
          XtSetArg (arg[0], XtNborderWidth, 3);
          XtSetArg (arg[1], XtNinternalHeight, 2);
@@ -784,9 +1001,9 @@ FeatureProc (Widget w, XtPointer client_data, XtPointer call_data)
          XtSetArg (arg[1], XtNinternalHeight, 4);
          XtSetArg (arg[2], XtNinternalWidth, 4);
        }
-      OTF_tag_name (rec->features[i].tag, str);
+      OTF_tag_name (rec->features[idx].tag, str);
       XtSetArg (arg[3], XtNlabel, str);
-      XtSetValues (rec->features[i].w, arg, 4);
+      XtSetValues (rec->features[idx].w, arg, 4);
     }
   update_seq_area ();
 }
@@ -794,34 +1011,31 @@ FeatureProc (Widget w, XtPointer client_data, XtPointer call_data)
 void
 setup_feature_rec (FeatureRec *rec)
 {
-  int i;
+  int i, j;
   Arg arg[10];
 
   rec->langsys = NULL;
+  if (! rec->gsub_gpos)
+    return;
   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;
+       OTF_Script *script = rec->gsub_gpos->ScriptList.Script + i;
+
+       if (langsys_tag)
+         {
+           for (j = 0; j < script->LangSysCount; j++)
+             if (script->LangSysRecord[j].LangSysTag == langsys_tag)
+               {
+                 rec->langsys = script->LangSys + j;
+                 break;
+               }
+         }
+       if (! rec->langsys)
+         rec->langsys = &rec->gsub_gpos->ScriptList.Script[i].DefaultLangSys;
        break;
       }
 
-  if (! rec->features)
-    {
-      rec->num_features = rec->langsys->FeatureCount;
-      rec->features = malloc ((sizeof (FeatureElement)) * rec->num_features);
-      memset (rec->features, 0, (sizeof (FeatureElement)) * rec->num_features);
-    }
-  else if (rec->num_features < rec->langsys->FeatureCount)
-    {
-      rec->features = realloc (rec->features, 
-                              (sizeof (FeatureElement))
-                              * rec->langsys->FeatureCount);
-      memset (rec->features + rec->num_features, 0,
-             (sizeof (FeatureElement)) 
-             * (rec->langsys->FeatureCount - rec->num_features));
-      rec->num_features = rec->langsys->FeatureCount;
-    }
-
   if (! rec->langsys)
     i = 0;
   else
@@ -855,7 +1069,7 @@ setup_feature_rec (FeatureRec *rec)
   XtSetArg (arg[0], XtNborderColor, background);
   XtSetArg (arg[1], XtNsensitive, False);
   XtSetArg (arg[2], XtNlabel, "    ");
-  for (; i < rec->langsys->FeatureCount; i++)
+  for (; i < rec->num_features; i++)
     {
       if (! rec->features[i].w)
        {
@@ -869,11 +1083,42 @@ setup_feature_rec (FeatureRec *rec)
 }
 
 void
+compose_script_langsys (OTF_Tag script, OTF_Tag langsys, char *name)
+{
+  OTF_tag_name (script, name);
+  if (langsys)
+    {
+      name[4] = '(';
+      OTF_tag_name (langsys, name + 5);
+      name[9] = ')';
+      name[10] = '\0';
+    }
+}
+
+void
+decompose_script_langsys (OTF_Tag *script, OTF_Tag *langsys, char *name)
+{
+  *script = OTF_tag (name);
+  if (name[4])
+    *langsys = OTF_tag (name + 5);
+  else
+    *langsys = 0;
+}
+
+void
 ScriptProc (Widget w, XtPointer client_data, XtPointer call_data)
 {
-  if (script_tag == (OTF_Tag) client_data)
+  char *name;
+  OTF_Tag script, langsys;
+  Arg arg[1];
+
+  XtSetArg (arg[0], XtNlabel, &name);
+  XtGetValues (w, arg, 1);
+  decompose_script_langsys (&script, &langsys, name);
+  if (script_tag == script && langsys_tag == langsys)
     return;
-  script_tag = (OTF_Tag) client_data;
+  script_tag = script;
+  langsys_tag = langsys;
   setup_feature_rec (&gsub);
   setup_feature_rec (&gpos);
   update_seq_area ();
@@ -884,9 +1129,13 @@ create_otf_script_widgets (Widget prev)
 {
   Widget w;
   Arg arg[10];
-  int n, i;
-  OTF_Tag *scripts;
-  char script_name[5];
+  int n, prev_n, i, j;
+  struct {
+    OTF_Tag script;
+    OTF_Tag langsys;
+  } *script_langsys;
+  char name[11];
+  int nfeatures;
 
   XtSetArg (arg[0], XtNborderWidth, 0);
   XtSetArg (arg[1], XtNleft, XawChainLeft);
@@ -896,56 +1145,131 @@ create_otf_script_widgets (Widget prev)
   XtSetArg (arg[5], XtNfromVert, prev);
   XtSetArg (arg[6], XtNorientation, XtorientHorizontal);
   prev = XtCreateManagedWidget ("Script", boxWidgetClass, render_area, arg, 7);
-  XtCreateManagedWidget ("script", labelWidgetClass, prev, arg, 1);
+  XtCreateManagedWidget ("script(langsys)", labelWidgetClass, prev, arg, 1);
 
   n = 0;
   if (otf->gsub)
-    n = otf->gsub->ScriptList.ScriptCount;
+    for (i = 0; i < otf->gsub->ScriptList.ScriptCount; i++)
+      n += otf->gsub->ScriptList.Script[i].LangSysCount + 1;
   if (otf->gpos)
-    n += otf->gpos->ScriptList.ScriptCount;
-  scripts = alloca (sizeof (OTF_Tag) * n);
-  i = 0;
+    for (i = 0; i < otf->gpos->ScriptList.ScriptCount; i++)
+      n += otf->gpos->ScriptList.Script[i].LangSysCount + 1;
+  script_langsys = alloca ((sizeof script_langsys[0]) * n);
+  n = 0;
+  nfeatures = 0;
   if (otf->gsub)
-    for (; i < otf->gsub->ScriptList.ScriptCount; i++)
-      scripts[i] = otf->gsub->ScriptList.Script[i].ScriptTag;
-  n = i;
+    for (i = 0; i < otf->gsub->ScriptList.ScriptCount; i++)
+      {
+       OTF_Tag tag = otf->gsub->ScriptList.Script[i].ScriptTag;
+
+       script_langsys[n].script = tag;
+       script_langsys[n++].langsys = 0;
+       if (nfeatures
+           < otf->gsub->ScriptList.Script[i].DefaultLangSys.FeatureCount)
+         nfeatures
+           = otf->gsub->ScriptList.Script[i].DefaultLangSys.FeatureCount;
+       for (j = 0; j < otf->gsub->ScriptList.Script[i].LangSysCount; j++)
+         {
+           script_langsys[n].script = tag;
+           script_langsys[n++].langsys
+             = otf->gsub->ScriptList.Script[i].LangSysRecord[j].LangSysTag;
+           if (nfeatures
+               < otf->gsub->ScriptList.Script[i].LangSys[j].FeatureCount)
+             nfeatures
+               = otf->gsub->ScriptList.Script[i].LangSys[j].FeatureCount;
+         }
+      }
+  gsub.num_features = nfeatures;
+  if (nfeatures > 0)
+    {
+      gsub.num_features = nfeatures;
+      gsub.features = malloc ((sizeof (FeatureElement)) * nfeatures);
+      memset (gsub.features, 0, (sizeof (FeatureElement)) * nfeatures);
+    }
+  prev_n = n;
+  nfeatures = 0;
   if (otf->gpos)
-    for (; i < otf->gpos->ScriptList.ScriptCount; i++)
+    for (i = 0; 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])
+       int k;
+
+       if (nfeatures
+           < otf->gpos->ScriptList.Script[i].DefaultLangSys.FeatureCount)
+         nfeatures
+           = otf->gpos->ScriptList.Script[i].DefaultLangSys.FeatureCount;
+       for (k = 0; k < prev_n; k++)
+         if (tag == script_langsys[k].script)
            break;
-       if (j == i)
-         scripts[n++] = tag;
+       if (k == prev_n)
+         {
+           script_langsys[n].script = tag;
+           script_langsys[n++].langsys = 0;
+         }
+       for (j = 0; j < otf->gpos->ScriptList.Script[i].LangSysCount; j++)
+         {
+           int l;
+
+           if (k < prev_n)
+             {
+               OTF_Script *script = otf->gpos->ScriptList.Script + i;
+
+               for (l = k; l < prev_n && tag == script_langsys[l].script; l++)
+                 if (script->LangSysRecord[j].LangSysTag
+                     == script_langsys[l].langsys)
+                   break;
+             }
+           else
+             l = prev_n;
+           if (l == prev_n)
+             {
+               script_langsys[n].script = tag;
+               script_langsys[n++].langsys = 0;
+             }
+           if (nfeatures
+               < otf->gpos->ScriptList.Script[i].LangSys[j].FeatureCount)
+             nfeatures
+               = otf->gpos->ScriptList.Script[i].LangSys[j].FeatureCount;
+         }
       }
 
+  if (nfeatures > 0)
+    {
+      gpos.num_features = nfeatures;
+      gpos.features = malloc ((sizeof (FeatureElement)) * nfeatures);
+      memset (gpos.features, 0, (sizeof (FeatureElement)) * nfeatures);
+    }
+
   if (n == 0)
     return prev;
 
-  script_tag = scripts[0];
-  OTF_tag_name (scripts[0], script_name);
+  script_tag = script_langsys[0].script;
+  langsys_tag = script_langsys[0].langsys;
+  compose_script_langsys (script_tag, langsys_tag, name);
+
   if (n == 1)
     {
       XtSetArg (arg[0], XtNforeground, background);
       XtSetArg (arg[1], XtNbackground, foreground);
-      XtCreateManagedWidget (script_name, labelWidgetClass, prev, arg, 2);
+      XtCreateManagedWidget (name, labelWidgetClass, prev, arg, 2);
     }
   else
     {
-      char name[5];
-
+      Widget box;
+      XtSetArg (arg[0], XtNborderWidth, 0);
+      XtSetArg (arg[1], XtNwidth, render_width - (FONT_WIDTH * 15));
+      XtSetArg (arg[2], XtNorientation, XtorientHorizontal);
+      box = XtCreateManagedWidget ("scritp-list", boxWidgetClass, prev, arg, 2);
       XtSetArg (arg[0], XtNstate, True);
-      w = XtCreateManagedWidget (script_name, toggleWidgetClass, prev, arg, 1);
-      XtAddCallback (w, XtNcallback, ScriptProc, (XtPointer) scripts[0]);
+      w = XtCreateManagedWidget (name, toggleWidgetClass, box, arg, 1);
+      XtAddCallback (w, XtNcallback, ScriptProc, NULL);
       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]);
+         compose_script_langsys (script_langsys[i].script,
+                                 script_langsys[i].langsys, name);
+         w = XtCreateManagedWidget (name, toggleWidgetClass, box, arg, 1);
+         XtAddCallback (w, XtNcallback, ScriptProc, NULL);
        }         
     }
   return prev;
@@ -957,7 +1281,6 @@ create_otf_widgets (Widget prev, FeatureRec *rec)
 {
   Arg arg[10];
   Widget w;
-  int i;
 
   XtSetArg (arg[0], XtNborderWidth, 0);
   XtSetArg (arg[1], XtNleft, XawChainLeft);
@@ -1022,7 +1345,41 @@ create_widgets ()
   XtSetArg (arg[6], XtNdefaultDistance, 0);
   glyph_area = XtCreateManagedWidget ("glyph-area", formWidgetClass,
                                      frame, arg, 7);
+
   XtSetArg (arg[5], XtNfromVert, glyph_area);
+  if (sub14)
+    {
+      Arg arg2[3];
+
+      XtSetArg (arg[6], XtNorientation, XtorientHorizontal);
+      uvs_area = XtCreateManagedWidget ("uvs-area", boxWidgetClass,
+                                       frame, arg, 7);
+      XtSetArg (arg2[0], XtNborderWidth, 0);
+      XtSetArg (arg2[1], XtNlabel, "Variation Selector: ");
+      uvs_label = XtCreateManagedWidget ("uvs-label", labelWidgetClass,
+                                        uvs_area, arg2, 2);
+      XtSetArg (arg2[0], XtNborderWidth, 1);
+      for (i = 0; i < sub14->nRecords; i++)
+       {
+         OTF_VariationSelectorRecord *record = sub14->Records + i;
+         unsigned selector = record->varSelector;
+         unsigned idx;
+         char lbl[4];
+         
+         idx = (selector <= 0xFE0F ? selector - 0xFE00
+                : selector - 0xE0100 + 16);
+         if (uvs[idx].c)
+           continue;
+         uvs[idx].c = selector;
+         sprintf (lbl, "%03d", idx + 1);
+         XtSetArg (arg2[1], XtNlabel, lbl);
+         XtSetArg (arg2[2], XtNsensitive, False);
+         uvs[idx].w = XtCreateManagedWidget ("lbl", commandWidgetClass,
+                                                  uvs_area, arg2, 3);
+         XtAddCallbackWithCast (unsigned, uvs[idx].w, UVSProc, idx);
+       }
+      XtSetArg (arg[5], XtNfromVert, uvs_area);
+    }
   render_area = XtCreateManagedWidget ("render-area", formWidgetClass,
                                       frame, arg, 6);
 
@@ -1031,7 +1388,7 @@ create_widgets ()
                                command_area, arg, 1);
   XtAddCallback (quit, XtNcallback, QuitProc, NULL);
 
-  dump = XtCreateManagedWidget ("Dump Image", commandWidgetClass,
+  dump = XtCreateManagedWidget ("DumpImage", commandWidgetClass,
                                command_area, arg, 1);
   XtAddCallback (dump, XtNcallback, DumpProc, NULL);
 
@@ -1050,7 +1407,7 @@ create_widgets ()
       charmap[i + 1] = XtCreateManagedWidget (charmap_rec[i + 1].name,
                                              toggleWidgetClass,
                                              command_area, arg, 1);
-      XtAddCallback (charmap[i + 1], XtNcallback, CharmapProc, (XtPointer) i);
+      XtAddCallbackWithCast (int, charmap[i + 1], CharmapProc, i);
     }
 
   XtSetArg (arg[0], XtNlabel, " |< (f)");
@@ -1124,7 +1481,7 @@ create_widgets ()
          XtSetArg (arg[n], XtNbitmap, none_pixmap), n++;
          glyph[k] = XtCreateManagedWidget ("glyph", commandWidgetClass,
                                            glyph_area, arg, n);
-         XtAddCallback (glyph[k], XtNcallback, RenderProc, (XtPointer) k);
+         XtAddCallbackWithCast (int, glyph[k], RenderProc, k);
        }
       w = head;
     }
@@ -1157,6 +1514,8 @@ create_widgets ()
   XtAddCallback (del, XtNcallback, RenderProc, (XtPointer) -2);
   bidi = XtCreateManagedWidget ("L->R", toggleWidgetClass, w, arg, 0);
   XtAddCallback (bidi, XtNcallback, BidiProc, NULL);
+  alt_subst = XtCreateManagedWidget ("AltSubst", toggleWidgetClass, w, arg, 0);
+  XtAddCallback (alt_subst, XtNcallback, AltSubstProc, NULL);
 
   XtSetArg (arg[4], XtNorientation, XtorientHorizontal);
   XtSetArg (arg[5], XtNborderWidth, 0);
@@ -1199,6 +1558,16 @@ create_widgets ()
   XtSetArg (arg[1], XtNbitmap, seq_pixmap);
   seq_image = XtCreateManagedWidget ("seq-image", labelWidgetClass,
                                     seq, arg, 2);
+  XtSetArg (arg[6], XtNfromVert, seq);
+  code = XtCreateManagedWidget ("code", boxWidgetClass, render_area, arg, 7);
+  XtSetArg (arg[0], XtNborderWidth, 0);
+  XtSetArg (arg[1], XtNlabel, "code:");
+  code_label = XtCreateManagedWidget ("code-label", labelWidgetClass,
+                                    code, arg, 2);
+  XtSetArg (arg[1], XtNlabel, "");
+  XtSetArg (arg[2], XtNwidth, render_width);
+  code_list = XtCreateManagedWidget ("code-list", labelWidgetClass,
+                                    code, arg, 3);
   XtInstallAllAccelerators (shell, shell);
 }
 
@@ -1232,6 +1601,19 @@ x_error_handler (Display *display, XErrorEvent *error)
   return 0;
 }
 
+void
+help (char **argv, int err)
+{
+  FILE *fp = err ? stderr : stdout;
+
+  fprintf (fp, "Usage: %s [ X-OPTION ... ]  OTF-FILE [INDEX]\n",
+          basename (argv[0]));
+  fprintf (fp, "  Environment variable PIXEL_SIZE specifies the pixel size.\n");
+  fprintf (fp, "  The default pixel size is %d, but is reduced\n",
+          DEFAULT_PIXEL_SIZE);
+  fprintf (fp, "  if your screen is not that wide.\n");
+  exit (err);
+}
 
 int
 main (int argc, char **argv)
@@ -1274,39 +1656,54 @@ main (int argc, char **argv)
   if (! font)
     font = XLoadQueryFont (display, "fixed");
 
-  if (argc != 2 || !strcmp (argv[1], "-h") || !strcmp (argv[1], "--help"))
-    {
-      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);
-    }
+  if (argc < 2)
+    help (argv, 1);
+  if (!strcmp (argv[1], "-h") || !strcmp (argv[1], "--help"))
+    help (argv, 0);
   filename = argv[1];
-  if (strstr (filename, ".ttf")
-      || strstr (filename, ".TTF")
-      || strstr (filename, ".otf")
-      || strstr (filename, ".OTF"))
+  if (argc > 2)
     {
-      otf = OTF_open (filename);
-      if (! otf
-         || OTF_get_table (otf, "head") < 0
-         || OTF_get_table (otf, "cmap") < 0
-         || (OTF_check_table (otf, "GSUB") < 0
-             && OTF_check_table (otf, "GPOS") < 0))
-       otf = NULL;
+      fontindex = atoi (argv[2]);
+      if (fontindex < 0)
+       FATAL_ERROR ("Invalid font index: %d\n", fontindex);
     }
 
   if ((err = FT_Init_FreeType (&library)))
     FATAL_ERROR ("%s\n", "FT_Init_FreeType: error");
-  err = FT_New_Face (library, filename, 0, &face);
+  err = FT_New_Face (library, filename, fontindex, &face);
   if (err == FT_Err_Unknown_File_Format)
     FATAL_ERROR ("%s\n", "FT_New_Face: unknown file format");
   else if (err)
-    FATAL_ERROR ("%s\n", "FT_New_Face: unknown error");
+    FATAL_ERROR ("%s\n", "FT_New_Face: unknown error (invalid face index?)");
   if ((err = FT_Set_Pixel_Sizes (face, 0, pixel_size)))
     FATAL_ERROR ("%s\n", "FT_Set_Pixel_Sizes: error");
 
+  if (strstr (filename, ".ttf")
+      || strstr (filename, ".TTF")
+      || strstr (filename, ".otf")
+      || strstr (filename, ".OTF"))
+    {
+      otf = OTF_open_ft_face (face);
+      if (otf)
+       {
+         if (OTF_get_table (otf, "head") < 0
+             || OTF_get_table (otf, "cmap") < 0
+             || (OTF_check_table (otf, "GSUB") < 0
+                 && OTF_check_table (otf, "GPOS") < 0))
+           {
+             OTF_close (otf);
+             otf = NULL;
+           }
+       }
+      if (otf)
+       for (i = 0; i < otf->cmap->numTables; i++)
+         if (otf->cmap->EncodingRecord[i].subtable.format == 14)
+           {
+             sub14 = otf->cmap->EncodingRecord[i].subtable.f.f14;
+             break;
+           }
+    }
+
   {
     char title[256];
     Arg arg[1];
@@ -1343,9 +1740,11 @@ main (int argc, char **argv)
     XGCValues values;
 
     gc = XCreateGC (display, none_pixmap, (unsigned long) 0, NULL);
+    XSetFont (display, gc, font->fid);
     values.function = GXset;
     values.line_width = 1;
     gc_set = XCreateGC (display, none_pixmap, valuemask, &values);
+    XSetFont (display, gc_set, font->fid);
     values.function = GXor;
     gc_or = XCreateGC (display, none_pixmap, valuemask, &values);
     values.function = GXcopyInverted;
@@ -1405,8 +1804,7 @@ int
 main (int argc, char **argv)
 {
   fprintf (stderr, 
-          "Building of this program failed (lack of some header files)\n",
-          argv[0]);
+          "Building of this program failed (lack of some header files)\n");
   exit (1);
 }