(update_seq_area): Accumulate adjustments by
[m17n/libotf.git] / example / otfview.c
1 /* otfview.c -- View glyphs of OpenType fonts.
2
3 Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009
4   National Institute of Advanced Industrial Science and Technology (AIST)
5   Registration Number H15PRO167
6
7 This file is part of libotf.
8
9 Libotf is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published
11 by the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
13
14 Libotf is distributed in the hope that it will be useful, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
17 License for more details.
18
19 You should have received a copy of the GNU Lesser General Public
20 License along with this library, in a file named COPYING; if not,
21 write to the Free Software Foundation, Inc., 59 Temple Place, Suite
22 330, Boston, MA 02111-1307, USA.  */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <libgen.h>
30
31 #include "config.h"
32
33 #ifdef HAVE_X11_XAW_COMMAND_H
34
35 #include <X11/Xatom.h>
36 #include <X11/Intrinsic.h>
37 #include <X11/StringDefs.h>
38 #include <X11/Shell.h>
39 #include <X11/Xaw/Command.h>
40 #include <X11/Xaw/Toggle.h>
41 #include <X11/Xaw/Box.h>
42 #include <X11/Xaw/Form.h>
43 #include <X11/Xaw/Viewport.h>
44
45 #include <ft2build.h>
46 #include FT_FREETYPE_H
47
48 #include <otf.h>
49
50 #define CAST_FROM_XTPOINTER(TYPE, DATA, VAR)    \
51   do {                                          \
52     long TYPE temp = (long TYPE) (DATA);        \
53     (VAR) = temp;                               \
54   } while (0)
55
56 #define XtAddCallbackWithCast(TYPE, W, PROC, VAR)               \
57   do {                                                          \
58     long TYPE temp = (long TYPE) (VAR);                         \
59     XtAddCallback (W, XtNcallback, PROC, (XtPointer) temp);     \
60   } while (0)
61
62
63 #define DEFAULT_PIXEL_SIZE 30
64 int pixel_size;
65
66 #define DEFAULT_FONT_NAME "6x13"
67 XFontStruct *font;
68 #define FONT_HEIGHT (font->ascent + font->descent)
69 #define FONT_ASCENT (font->ascent)
70 #define FONT_DESCENT (font->descent)
71 #define FONT_WIDTH (font->max_bounds.width)
72
73 XtAppContext context;
74 /* Widget structure.
75    +--- frame (form) -------------------------+
76    | +--- command_area (box) ---------------+ |
77    | | quit dump charmap ...                | |
78    | +--------------------------------------+ |
79    | +---- navi_area (box) -----------------+ |
80    | | FIRST PREV prev range next NEXT LAST | |
81    | +--------------------------------------+ |
82    | +--- glyph_area (form) ----------------+ |
83    | | idxh[0] glyph[0]    ...    glyph[15] | |
84    | |   ...                        ...     | |
85    | | idxh[7] glyph[112]  ...    glyph[127]| |
86    | |         idxl[0]     ...    idxl[15]  | |
87    | +--------------------------------------+ |
88    | +---- uvs_area (box) (optional) -------+ |
89    | | uvs[?].w ...                         | |
90    | +--------------------------------------+ |
91    | +--- render_area (form) ---------------+ |
92    | | clear del bidi alt_subst             | |
93    | | +--- raw (box) --------------------+ | |
94    | | | raw_label raw_image              | | |
95    | | +----------------------------------+ | |
96    | | GSUB all none features...            | |
97    | | GPOS all none features...            | |
98    | | +--- seq (box) --------------------+ | |
99    | | | seq_label seq_image              | | |
100    | | +----------------------------------+ | |
101    | +--------------------------------------+ |
102    +------------------------------------------+ */
103 Widget shell, frame;
104 Widget command_area, quit, dump, *charmap;
105 Widget navi_area, FIRST, PREV, prev, range, next, NEXT, LAST;
106 Widget glyph_area, glyph[128], index_label[8];
107 Widget uvs_area, uvs_label;
108 Widget render_area, clear, del, bidi, alt_subst, raw, seq;
109 Widget raw_label, raw_image, seq_label, seq_image;
110 unsigned long foreground, background;
111
112 typedef struct
113 {
114   OTF_Tag tag;
115   int on;
116   Widget w;
117 } FeatureElement;
118
119 typedef struct
120 {
121   char *label;
122   OTF_GSUB_GPOS *gsub_gpos;
123   OTF_LangSys *langsys;
124   int num_features;
125   FeatureElement *features;
126   Widget parent;
127 } FeatureRec;
128
129 FeatureRec gsub, gpos;
130
131 /* Currently selected script and langsys.  */
132 OTF_Tag script_tag, langsys_tag;
133
134 int glyph_char[128];
135
136 Display *display;
137 GC gc, gc_set, gc_or, gc_inv;
138
139 typedef struct {
140   Pixmap pixmap;
141   unsigned width, height;
142   int x, y;
143   int advance;
144 } BitmapRec;
145
146 BitmapRec bitmap[0x110000];
147
148 int render_width, render_height;
149 Pixmap raw_pixmap, seq_pixmap, gsub_pixmap, gpos_pixmap;
150 Pixmap none_pixmap;
151
152 FT_Face face;
153
154 struct {
155   int platform_id;
156   int encoding_id;
157   char name[20];
158 } charmap_rec[10];
159
160 int charmap_index;
161
162 int reversed;
163 int do_alternate_subst;
164 unsigned glyph_width, glyph_height;
165 int glyph_x, glyph_y;
166 int glyph_index;
167
168 struct {
169   int n_glyphs;
170   int glyphs[64];
171   int codes[64];
172 } glyph_rec;
173
174 OTF_EncodingSubtable14 *sub14 = NULL;
175
176 struct {
177   Widget w;
178   int c;
179 } uvs[256];
180
181 OTF *otf;
182 char *filename;
183 int fontindex;
184
185 void
186 create_pixmap (int index)
187 {
188   int err = FT_Load_Glyph (face, index, FT_LOAD_RENDER | FT_LOAD_MONOCHROME);
189   XImage ximage;
190   Pixmap pixmap;
191   
192   if (err)
193     {
194       bitmap[index].pixmap = none_pixmap;
195       return;
196     }
197   ximage.height = face->glyph->bitmap.rows;
198   ximage.width = face->glyph->bitmap.width;
199   ximage.depth = 1;
200   ximage.bits_per_pixel = 1;
201   ximage.xoffset = 0;
202   ximage.format = XYPixmap;
203   ximage.data = (char *) face->glyph->bitmap.buffer;
204   ximage.byte_order = MSBFirst;
205   ximage.bitmap_unit = 8;
206   ximage.bitmap_bit_order = MSBFirst;
207   ximage.bitmap_pad = 8;
208   ximage.bytes_per_line = face->glyph->bitmap.pitch;
209   XInitImage (&ximage);
210   pixmap = XCreatePixmap (display, DefaultRootWindow (display),
211                           glyph_width, glyph_height, 1);
212   XFillRectangle (display, pixmap, gc, 0, 0, glyph_width, glyph_height);
213   XPutImage (display, pixmap, gc, &ximage, 0, 0,
214              glyph_x + face->glyph->bitmap_left,
215              glyph_y - face->glyph->bitmap_top,
216              ximage.width, ximage.height);
217   bitmap[index].pixmap = pixmap;
218   bitmap[index].width = ximage.width;
219   bitmap[index].height = ximage.height;
220   bitmap[index].x = face->glyph->bitmap_left;
221   bitmap[index].y = - face->glyph->bitmap_top;
222   bitmap[index].advance = face->glyph->metrics.horiAdvance >> 6;
223 }
224
225 void
226 update_glyph_area ()
227 {
228   int i;
229   Arg arg[2];
230   char buf[16];
231   int msb;
232
233   for (i = 0; i < 128; i++)
234     {
235       int index = glyph_index + i;
236
237       if (charmap_index >= 0)
238         index = FT_Get_Char_Index (face, (FT_ULong) index);
239       if (charmap_index >= 0 && ! index)
240         XtSetArg (arg[0], XtNbitmap, none_pixmap);
241       else
242         {
243           if (! bitmap[index].pixmap)
244             create_pixmap (index);
245           XtSetArg (arg[0], XtNbitmap, bitmap[index].pixmap);
246         }
247       XtSetValues (glyph[i], arg, 1);
248     }
249
250   msb = (glyph_index >> 7) % 2 ? 8 : 0;
251   for (i = 0; i < 8; i++)
252     {
253       char str[3];
254
255       sprintf (str, "%XX", i | msb );
256       XtSetArg (arg[0], XtNheight, glyph_height + 5);
257       XtSetArg (arg[1], XtNlabel, str);
258       XtSetValues (index_label[i], arg, 2);
259     }
260
261   if (glyph_index < 0x10000)
262     sprintf (buf, "  %04X-%04X  ", glyph_index, glyph_index + 0x7F);
263   else
264     sprintf (buf, "%06X-%06X", glyph_index, glyph_index + 0x7F);
265   XtSetArg (arg[0], XtNlabel, buf);
266   XtSetValues (range, arg, 1);
267 }
268
269 void
270 update_uvs_area (int c)
271 {
272   OTF_GlyphID code[256];
273   Arg arg[1];
274   int i;
275
276   OTF_get_variation_glyphs (otf, c, code);
277
278   for (i = 0; i < 256; i++)
279     if (uvs[i].w)
280       {
281         if (code[i])
282           XtSetArg (arg[0], XtNsensitive, True);
283         else
284           XtSetArg (arg[0], XtNsensitive, False);
285         XtSetValues (uvs[i].w, arg, 1);
286       }
287 }
288
289
290 char *
291 get_features (OTF_FeatureList *list, FeatureRec *rec)
292 {
293   int i, n;
294   char *str, *p;
295
296   if (! rec->langsys || ! rec->features || ! rec->features[0].on)
297     return NULL;
298   for (i = n = 0; i < rec->langsys->FeatureCount; i++)
299     {
300       if (rec->features[i].on)
301         n++;
302       else
303         break;
304     }
305   str = malloc (n * 5);
306   for (i = 0, p = str; i < n; i++, p += 5)
307     {
308       OTF_tag_name (rec->features[i].tag, p);
309       p[4] = ',';
310     }
311   p[-1] = '\0';
312   return str;
313 }
314
315
316 #define DEVICE_DELTA(table, size)                                       \
317   (((table).DeltaValue                                                  \
318     && ((size) >= (table).StartSize && (size) <= (table).EndSize))      \
319    ? (table).DeltaValue[(size) >= (table).StartSize]                    \
320    : 0)
321
322 void
323 adjust_anchor (OTF_Anchor *anchor, FT_Face ft_face,
324                OTF_Glyph *g, int *x, int *y)
325 {
326   if (anchor->AnchorFormat == 2)
327     {
328       FT_Outline *outline;
329       int ap = anchor->f.f1.AnchorPoint;
330
331       FT_Load_Glyph (ft_face, (FT_UInt) g->glyph_id, FT_LOAD_MONOCHROME);
332       outline = &ft_face->glyph->outline;
333       if (ap < outline->n_points)
334         {
335           *x = outline->points[ap].x;
336           *y = outline->points[ap].y;
337         }
338     }
339   else if (anchor->AnchorFormat == 3)
340     {
341       *x += DEVICE_DELTA (anchor->f.f2.XDeviceTable, pixel_size);
342       *y += DEVICE_DELTA (anchor->f.f2.YDeviceTable, pixel_size);
343     }
344 }
345
346 void
347 update_seq_area ()
348 {
349   int i, x;
350   OTF_GlyphString gstring;
351   OTF_Glyph *g, *prev, *base, *mark;
352   int base_width;
353   int len = glyph_rec.n_glyphs;
354   Arg arg[1];
355   int unitsPerEm = face->units_per_EM;
356
357   gstring.size = gstring.used = len;
358   gstring.glyphs = malloc (sizeof (OTF_Glyph) * len);
359   memset (gstring.glyphs, 0, sizeof (OTF_Glyph) * len);
360   for (i = 0; i < len; i++)
361     {
362       gstring.glyphs[i].c = glyph_rec.codes[i];
363       if (charmap_index < 0)
364         gstring.glyphs[i].glyph_id = glyph_rec.glyphs[i];
365     }
366
367   XFillRectangle (display, seq_pixmap, gc, 0, 0, render_width, render_height);
368   XDrawLine (display, seq_pixmap, gc_set, 0, glyph_y, render_width, glyph_y);
369   if (otf)
370     {
371       char *script_name = NULL, *langsys_name = NULL, buf[10];
372       char *str;
373
374       if (script_tag)
375         {
376           script_name = buf;
377           OTF_tag_name (script_tag, script_name);
378         }
379       if (langsys_tag)
380         {
381           langsys_name = buf + 5;
382           OTF_tag_name (langsys_tag, langsys_name);
383         }
384
385       OTF_drive_cmap (otf, &gstring);
386       OTF_drive_gdef (otf, &gstring);
387       if (otf->gsub)
388         {
389           str = get_features (&otf->gsub->FeatureList, &gsub);
390           if (str)
391             {
392               if (do_alternate_subst)
393                 OTF_drive_gsub_alternate (otf, &gstring,
394                                           script_name, langsys_name, str);
395               else
396                 OTF_drive_gsub (otf, &gstring, script_name, langsys_name, str);
397               free (str);
398             }
399         }
400       if (otf->gpos)
401         {
402           str = get_features (&otf->gpos->FeatureList, &gpos);
403           if (str)
404             {
405               OTF_drive_gpos2 (otf, &gstring, script_name, langsys_name, str);
406               free (str);
407             }
408         }
409     }
410
411   prev = NULL;
412   if (reversed)
413     {
414       OTF_Glyph temp;
415
416       for (prev = gstring.glyphs, g = gstring.glyphs + gstring.used - 1;
417            prev < g; prev++, g--)
418         temp = *prev, *prev = *g, *g = temp;
419       for (g = gstring.glyphs; g < gstring.glyphs + gstring.used; g++)
420         if (g->GlyphClass == 3)
421           {
422             OTF_Glyph *g0;
423             prev = g++;
424             while (g < gstring.glyphs + gstring.used && g->GlyphClass == 3)
425               g++;
426             for (g0 = g; prev < g0; prev++, g0--)
427               temp = *prev, *prev = *g, *g = temp;
428           }
429     }
430
431
432   mark = base = NULL;
433   for (i = 0, x = glyph_x, prev = NULL, g = gstring.glyphs;
434        i < gstring.used; i++, prev = g++)
435     {
436       BitmapRec *bmp = bitmap + gstring.glyphs[i].glyph_id;
437       int xoff = 0, yoff = 0;
438       int prev_width;
439       int advance = bmp->advance;
440
441       if (gstring.glyphs[i].glyph_id && ! bmp->pixmap)
442         {
443           create_pixmap (gstring.glyphs[i].glyph_id);
444           if (! bmp->pixmap)
445             continue;
446           advance = bmp->advance;
447         }
448       if (g->positioning_type)
449         {
450           while (1)
451             {
452               switch (g->positioning_type)
453                 {
454                 case 1: case 2:
455                   {
456                     int format = g->f.f1.format;
457
458                     if (format & OTF_XPlacement)
459                       xoff += g->f.f1.value->XPlacement * pixel_size / unitsPerEm;
460                     if (format & OTF_XPlaDevice)
461                       xoff += DEVICE_DELTA (g->f.f1.value->XPlaDevice, pixel_size);
462                     if (format & OTF_YPlacement)
463                       yoff += g->f.f1.value->YPlacement * pixel_size / unitsPerEm;
464                     if (format & OTF_YPlaDevice)
465                       yoff += DEVICE_DELTA (g->f.f1.value->YPlaDevice, pixel_size);
466                     if (format & OTF_XAdvance)
467                       advance += g->f.f1.value->XAdvance * pixel_size / unitsPerEm;
468                     if (format & OTF_XAdvDevice)
469                       advance += DEVICE_DELTA (g->f.f1.value->XAdvDevice,
470                                                pixel_size);
471                   }
472                   break;
473
474                 case 3:
475                   /* Not yet supported.  */
476                   break;
477                 case 4: case 5:
478                   if (! base)
479                     break;
480                   prev = base;
481                   prev_width = base_width;
482                   goto label_adjust_anchor;
483                 default:                /* i.e. case 6 */
484                   if (! mark)
485                     break;
486                   prev = mark;
487                   prev_width = 0;
488                 label_adjust_anchor:
489                   {
490                     int base_x, base_y, mark_x, mark_y;
491
492                     base_x = g->f.f4.base_anchor->XCoordinate * pixel_size / unitsPerEm;
493                     base_y = g->f.f4.base_anchor->YCoordinate * pixel_size / unitsPerEm;
494                     mark_x = g->f.f4.mark_anchor->XCoordinate * pixel_size / unitsPerEm;
495                     mark_y = g->f.f4.mark_anchor->YCoordinate * pixel_size / unitsPerEm;
496
497                     if (g->f.f4.base_anchor->AnchorFormat != 1)
498                       adjust_anchor (g->f.f4.base_anchor, face, prev, &base_x, &base_y);
499                     if (g->f.f4.mark_anchor->AnchorFormat != 1)
500                       adjust_anchor (g->f.f4.mark_anchor, face, g, &mark_x, &mark_y);
501                     xoff = (base_x - prev_width) - mark_x;
502                     yoff = base_y - mark_y;
503                   }
504                 }
505               if (i + 1 == gstring.used
506                   || gstring.glyphs[i + 1].glyph_id
507                   || ! gstring.glyphs[i + 1].positioning_type)
508                 break;
509               i++, g++;
510             }
511         }
512
513       XCopyArea (display, bmp->pixmap, seq_pixmap, gc_or,
514                  glyph_x + bmp->x, glyph_y + bmp->y, bmp->width, bmp->height,
515                  x + bmp->x + xoff, glyph_y + bmp->y - yoff);
516       x += advance;
517
518       if (g->GlyphClass == OTF_GlyphClass0)
519         base = mark = g, base_width = advance;
520       else if (g->GlyphClass == OTF_GlyphClassMark)
521         mark = g;
522       else
523         base = g, base_width = advance;
524     }
525   free (gstring.glyphs);
526
527   XtSetArg (arg[0], XtNbitmap, seq_pixmap);
528   XtSetValues (seq_image, arg, 1);
529 }
530
531
532 void
533 update_render_area ()
534 {
535   int i;
536   int x;
537   Arg arg[1];
538
539   XFillRectangle (display, raw_pixmap, gc, 0, 0, render_width, render_height);
540   for (i = 0, x = glyph_x; i < glyph_rec.n_glyphs; i++)
541     {
542       if (glyph_rec.glyphs[i] >= 0)
543         {
544           BitmapRec *bmp = bitmap + glyph_rec.glyphs[i];
545           char buf[5];
546
547           XCopyArea (display, bmp->pixmap, raw_pixmap, gc,
548                      0, 0, glyph_width, glyph_height,
549                      (glyph_width + 4) * i + 1, 1);
550           XDrawRectangle (display, raw_pixmap, gc_set,
551                           (glyph_width + 4) * i, 0,
552                           glyph_width + 1, glyph_height + 1);
553           XDrawLine (display, raw_pixmap, gc_set,
554                      (glyph_width + 4) * i + 1 + glyph_x, 1,
555                      (glyph_width + 4) * i + 1 + glyph_x, glyph_height + 1);
556           XDrawLine (display, raw_pixmap, gc_set,
557                      (glyph_width + 4) * i + 1 + glyph_x + bmp->advance, 1,
558                      (glyph_width + 4) * i + 1 + glyph_x + bmp->advance,
559                      glyph_height + 1);
560
561           sprintf (buf, "%04X", glyph_rec.codes[i]);
562           XDrawString (display, raw_pixmap, gc_inv, 
563                        (glyph_width + 4) * i + 1
564                        + (glyph_width - XTextWidth (font, buf, 4)) / 2,
565                        glyph_height + 2 + FONT_HEIGHT, buf, 4);
566         }
567       else
568         {
569           /* Variation Selector */
570           int idx = - glyph_rec.glyphs[i];
571           char buf[4];
572
573           sprintf (buf, "%03d", idx);
574           XDrawRectangle (display, raw_pixmap, gc_set,
575                           (glyph_width + 4) * i, 0,
576                           glyph_width + 1, glyph_height + 1);
577           XDrawString (display, raw_pixmap, gc_set,
578                        (glyph_width + 4) * i + 1
579                        + (glyph_width - XTextWidth (font, "VS", 2)) / 2,
580                        1 + glyph_height / 2, "VS", 2);
581           XDrawString (display, raw_pixmap, gc_set,
582                        (glyph_width + 4) * i + 1
583                        + (glyph_width - XTextWidth (font, buf, 3)) / 2,
584                        1 + glyph_height / 2 + FONT_ASCENT, buf, 3);
585         }
586     }
587   XtSetArg (arg[0], XtNbitmap, raw_pixmap);
588   XtSetValues (raw_image, arg, 1);
589   update_seq_area ();
590 }
591
592 void
593 QuitProc (Widget w, XtPointer client_data, XtPointer call_data)
594 {
595   XtAppSetExitFlag (XtWidgetToApplicationContext (w));
596 }
597
598 void
599 DumpProc (Widget w, XtPointer client_data, XtPointer call_data)
600 {
601   int g_width, g_height;
602   float g_x, g_y;
603   /* unit in points (1/72 inch); to fit in both US-letter and A4 */
604   static int xoff = 30, yoff = 30;
605   static int unit = 30;
606   static int margin = 2;
607   static int title_height = 20, label_height = 10;
608   int total_width = (unit + margin * 2) * 16;
609   int total_height = (unit + margin * 2 + label_height) * 16 + title_height;
610   /* pixel size (dots) */
611   int size = 128;
612   int i, j, k, l;
613   char *name = alloca (strlen (filename) + 10);
614   FILE *fp;
615   int index = (glyph_index / 0x100) * 0x100;
616   float scale;
617
618   g_width = face->bbox.xMax - face->bbox.xMin;
619   g_height = face->bbox.yMax - face->bbox.yMin;
620   if (g_width > g_height)
621     {
622       scale = g_width * size;
623       g_x = face->bbox.xMin * unit / g_width;
624       g_y = face->bbox.yMin * unit / g_width;
625     }
626   else
627     {
628       scale = g_height * size;
629       g_x = face->bbox.xMin * unit / g_height;
630       g_y = face->bbox.yMin * unit / g_height;
631     }
632   scale /= face->units_per_EM;
633
634   FT_Set_Pixel_Sizes (face, 0, size);
635
636   sprintf (name, "%s-%04X.ps", face->family_name, index);
637   printf ("Writing %s ... ", name);
638   fflush (stdout);
639   fp = fopen (name, "w");
640
641   fprintf (fp, "%s\n", "%!PS-Adobe-2.0 EPSF-2.0");
642   fprintf (fp, "%s\n", "%%Creater: otfview");
643   fprintf (fp, "%s %s(%s)-%04X\n", "%%Title:",
644            face->family_name, face->style_name, index);
645   fprintf (fp, "%s\n", "%%Pages: 1");
646   fprintf (fp, "%s %d %d %d %d\n", "%%BoundingBox:",
647            xoff, yoff, xoff + total_width, yoff + total_height);
648   fprintf (fp, "%s\n", "%%EndComments");
649   fprintf (fp, "%s\n", "%%BeginProlog");
650   fprintf (fp, "/W %d def\n", unit + margin * 2);
651   fprintf (fp, "/H %d def\n", unit + margin * 2 + label_height);
652   fprintf (fp, "/STR 10 string def\n");
653   fprintf (fp, "/DrawIndex {\n");
654   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");
655   fprintf (fp, "} def\n");
656   fprintf (fp, "/DrawTitle {\n");
657   fprintf (fp, "  /Courier findfont 20 scalefont setfont\n");
658   fprintf (fp, "  %d %d 4 add moveto\n", xoff + total_width / 2,
659            yoff + total_height - title_height + 2);
660   fprintf (fp, "  (%s(%s)-%04X) dup stringwidth pop 2 div neg 0 rmoveto show\n",
661            face->family_name, face->style_name, index);
662   fprintf (fp, "} def\n");
663   fprintf (fp, "/DrawFrame { gsave %d %d translate 0 setlinewidth\n",
664            xoff, yoff);
665   fprintf (fp, "  /Courier findfont 10 scalefont setfont\n");
666   fprintf (fp, "  /I %d def\n", index);
667   fprintf (fp, "  0 1 16 { W mul 0 moveto 0 H 16 mul rlineto stroke } for\n");
668   fprintf (fp, "  0 1 16 { H mul 0 exch moveto W 16 mul 0 rlineto stroke } for\n");
669   fprintf (fp, "  0 1 15 { H mul %d add 0 exch moveto W 16 mul 0 rlineto stroke } for\n", label_height);
670   fprintf (fp, "  15 -1 0 { gsave H mul 0 exch translate 0 0 moveto\n");
671   fprintf (fp, "    0 1 15 { gsave W mul 0 moveto\n");
672   fprintf (fp, "      4 2 rmoveto DrawIndex /I I 1 add def grestore} for\n");
673   fprintf (fp, "    grestore } for grestore } def\n");
674   fprintf (fp, "%s\n", "%%EndProlog");
675   fprintf (fp, "DrawTitle DrawFrame\n");
676
677   for (i = 0; i < 16; i++)
678     for (j = 0; j < 16; j++, index++)
679       {
680         int idx;
681
682         if (charmap_index >= 0) 
683           idx = FT_Get_Char_Index (face, (FT_ULong) index);
684         else
685           idx = index;
686         if (idx > 0
687             && FT_Load_Glyph (face, idx, FT_LOAD_RENDER | FT_LOAD_MONOCHROME) == 0
688             && face->glyph->bitmap.rows > 0
689             && face->glyph->bitmap.width > 0)
690           {
691             unsigned char *p = face->glyph->bitmap.buffer;
692             int width = (face->glyph->bitmap.width - 1) / 8 + 1;
693
694             fprintf (fp, "gsave %f %f translate %d %d scale 0 0 moveto\n",
695                      xoff + (unit + margin * 2) * j + margin - g_x,
696                      yoff + (unit + label_height + margin * 2) * (15 - i) + label_height + margin - g_y,
697                      unit, unit);
698             fprintf (fp, "%d %d true [%f 0 0 %f %d %d]\n",
699                      width * 8, face->glyph->bitmap.rows,
700                      scale, -scale, -face->glyph->bitmap_left,
701                      face->glyph->bitmap_top);
702             fprintf (fp, "{< ");
703             for (k = 0; k < face->glyph->bitmap.rows;
704                  k++, p += face->glyph->bitmap.pitch)
705               {
706                 for (l = 0; l < width; l++)
707                   fprintf (fp, "%02X", p[l]);
708                 fprintf (fp, "\n");
709               }
710             fprintf (fp, ">} imagemask grestore\n");
711           }
712         else
713           {
714             int boxsize = unit + margin * 2;
715
716             fprintf (fp, "gsave 0 setlinewidth %d %d translate\n",
717                      xoff + boxsize * j,
718                      yoff + (boxsize + label_height) * (15 - i) + label_height);
719             fprintf (fp, "0 0 moveto %d %d lineto stroke\n",
720                      boxsize, boxsize);
721             fprintf (fp, "0 %d moveto %d 0 lineto stroke grestore\n",
722                      boxsize, boxsize);
723           }
724       }
725   fprintf (fp, "showpage\n");
726   fclose (fp);
727   printf ("done\n");
728
729   FT_Set_Pixel_Sizes (face, 0, (int) pixel_size);
730 }
731
732
733 void
734 GlyphProc (Widget w, XtPointer client_data, XtPointer call_data)
735 {
736   int old_glyph_index = glyph_index;
737   int data;
738
739   CAST_FROM_XTPOINTER (int, client_data, data);
740
741   if (data == -3 && glyph_index > 0)
742     glyph_index = 0;
743   else if (data == -2 && glyph_index > 0)
744     glyph_index = (glyph_index - 1) & 0x1FF000;
745   else if (data == -1 && glyph_index > 0)
746     glyph_index -= 0x80;
747   else if (data == 1 && glyph_index < 0x10FF80)
748     glyph_index += 0x80;
749   else if (data == 2 && glyph_index < 0x10F000)
750     glyph_index = (glyph_index + 0x1000) & 0x1FF000;
751   else if (data == 3 && glyph_index < 0x10F000)
752     glyph_index = 0x10FF80;
753   if (glyph_index != old_glyph_index)
754     update_glyph_area ();
755 }
756
757 void
758 CharmapProc (Widget w, XtPointer client_data, XtPointer call_data)
759 {
760   int data;
761
762   CAST_FROM_XTPOINTER (int, client_data, data);
763
764   if (charmap_index == data)
765     return;
766   charmap_index = data;
767   if (charmap_index >= 0)
768     FT_Set_Charmap (face, face->charmaps[charmap_index]);
769   update_glyph_area ();
770 }
771
772 void
773 UVSProc (Widget w, XtPointer client_data, XtPointer call_data)
774 {
775   unsigned idx;
776   int selector;
777   OTF_VariationSelectorRecord *record;
778   int i;
779
780   CAST_FROM_XTPOINTER (unsigned, client_data, idx);
781   selector = uvs[idx].c;
782
783   if (glyph_rec.n_glyphs >= 64)
784     return;
785   for (i = 0; i < sub14->nRecords; i++)
786     {
787       record = sub14->Records + i;
788       if (record->varSelector == selector)
789         break;
790     }
791   if (i < sub14->nRecords)
792     {
793       if (glyph_rec.n_glyphs > 0
794           && glyph_rec.glyphs[glyph_rec.n_glyphs - 1] < 0)
795         glyph_rec.n_glyphs--;
796       glyph_rec.codes[glyph_rec.n_glyphs] = selector;
797       glyph_rec.glyphs[glyph_rec.n_glyphs++] = - idx - 1;
798       update_render_area ();
799     }
800 }
801
802 void
803 RenderProc (Widget w, XtPointer client_data, XtPointer call_data)
804 {
805   int data;
806
807   CAST_FROM_XTPOINTER (int, client_data, data);
808
809   if (data < 0)
810     {
811       if (glyph_rec.n_glyphs > 0)
812         {
813           if (data == -2)
814             glyph_rec.n_glyphs--;
815           else
816             glyph_rec.n_glyphs = 0;
817           update_render_area ();
818         }
819     }
820   else if (glyph_rec.n_glyphs < 64)
821     {
822       int index = glyph_index + data;
823
824       if (charmap_index >= 0)
825         index = FT_Get_Char_Index (face, (FT_ULong) index);
826       if (bitmap[index].pixmap)
827         {
828           glyph_rec.codes[glyph_rec.n_glyphs] = glyph_index + data;
829           glyph_rec.glyphs[glyph_rec.n_glyphs++] = index;
830           if (otf)
831             update_uvs_area (glyph_index + data);
832           update_render_area ();
833         }
834     }
835 }
836
837 void
838 BidiProc (Widget w, XtPointer client_data, XtPointer call_data)
839 {
840   Arg arg[1];
841
842   reversed = ! reversed;
843   if (reversed)
844     XtSetArg (arg[0], XtNlabel, "L<-R");
845   else
846     XtSetArg (arg[0], XtNlabel, "L->R");
847   XtSetValues (w, arg, 1);
848   update_seq_area ();
849 }
850
851 void
852 AltSubstProc (Widget w, XtPointer client_data, XtPointer call_data)
853 {
854   do_alternate_subst = ! do_alternate_subst;
855   update_seq_area ();
856 }
857
858 void
859 FeatureProc (Widget w, XtPointer client_data, XtPointer call_data)
860 {
861   FeatureRec *rec = (FeatureRec *) client_data;
862   int idx, i, j;
863   Arg arg[4];
864   char *label;
865
866   if (! rec->langsys)
867     return;
868   XtSetArg (arg[0], XtNlabel, &label);
869   XtGetValues (w, arg, 1);
870   if (! strcmp (label, "all"))
871     idx = -2;
872   else if (! strcmp (label, "none"))
873     idx = -1;
874   else
875     {
876       for (idx = 0; idx < rec->langsys->FeatureCount; idx++)
877         if (rec->features[idx].w == w)
878           break;
879       if (idx == rec->langsys->FeatureCount)
880         idx = -1;
881     }
882   if (idx < 0)
883     {
884       int on = idx == -2;
885
886       for (i = j = 0; j < rec->langsys->FeatureCount; j++)
887         {
888           int index  = rec->langsys->FeatureIndex[j];
889
890           rec->features[j].tag
891             = rec->gsub_gpos->FeatureList.Feature[index].FeatureTag;
892           rec->features[j].on = on;
893         }
894     }
895   else
896     {
897       OTF_Tag tag = rec->features[idx].tag;
898
899       i = idx;
900       if (rec->features[i].on)
901         {
902           for (j = i + 1;
903                j < rec->langsys->FeatureCount && rec->features[j].on; j++)
904             rec->features[j - 1].tag = rec->features[j].tag;
905           rec->features[j - 1].tag = tag;
906           rec->features[j - 1].on = 0;
907         }
908       else
909         {
910           for (j = i + 1; i > 0 && ! rec->features[i - 1].on; i--)
911             rec->features[i].tag = rec->features[i - 1].tag;
912           rec->features[i].tag = tag;
913           rec->features[i].on = 1;
914         }
915     }
916
917   for (; i < j; i++)
918     {
919       char str[5];
920
921       if (rec->features[i].on)
922         {
923           XtSetArg (arg[0], XtNborderWidth, 3);
924           XtSetArg (arg[1], XtNinternalHeight, 2);
925           XtSetArg (arg[2], XtNinternalWidth, 2);
926         }
927       else
928         {
929           XtSetArg (arg[0], XtNborderWidth, 1);
930           XtSetArg (arg[1], XtNinternalHeight, 4);
931           XtSetArg (arg[2], XtNinternalWidth, 4);
932         }
933       OTF_tag_name (rec->features[i].tag, str);
934       XtSetArg (arg[3], XtNlabel, str);
935       XtSetValues (rec->features[i].w, arg, 4);
936     }
937   update_seq_area ();
938 }
939
940 void
941 setup_feature_rec (FeatureRec *rec)
942 {
943   int i, j;
944   Arg arg[10];
945
946   rec->langsys = NULL;
947   if (! rec->gsub_gpos)
948     return;
949   for (i = 0; i < rec->gsub_gpos->ScriptList.ScriptCount; i++)
950     if (rec->gsub_gpos->ScriptList.Script[i].ScriptTag == script_tag)
951       {
952         OTF_Script *script = rec->gsub_gpos->ScriptList.Script + i;
953
954         if (langsys_tag)
955           {
956             for (j = 0; j < script->LangSysCount; j++)
957               if (script->LangSysRecord[j].LangSysTag == langsys_tag)
958                 {
959                   rec->langsys = script->LangSys + j;
960                   break;
961                 }
962           }
963         if (! rec->langsys)
964           rec->langsys = &rec->gsub_gpos->ScriptList.Script[i].DefaultLangSys;
965         break;
966       }
967
968   if (! rec->langsys)
969     i = 0;
970   else
971     {
972       XtSetArg (arg[0], XtNborderWidth, 1);
973       XtSetArg (arg[1], XtNinternalHeight, 4);
974       XtSetArg (arg[2], XtNinternalWidth, 4);
975       XtSetArg (arg[3], XtNborderColor, foreground);
976       XtSetArg (arg[4], XtNsensitive, True);
977       for (i = 0; i < rec->langsys->FeatureCount; i++)
978         {
979           OTF_Feature *feature = rec->gsub_gpos->FeatureList.Feature;
980           int index = rec->langsys->FeatureIndex[i];
981           char label[5];
982
983           if (! rec->features[i].w)
984             {
985               Widget w = XtCreateManagedWidget ("", commandWidgetClass,
986                                                 rec->parent, arg, 0);
987               XtAddCallback (w, XtNcallback, FeatureProc, (XtPointer) rec);
988               rec->features[i].w = w;
989             }
990
991           rec->features[i].tag = feature[index].FeatureTag;
992           rec->features[i].on = 0;
993           OTF_tag_name (rec->features[i].tag, label);
994           XtSetArg (arg[5], XtNlabel, label);
995           XtSetValues (rec->features[i].w, arg, 6);
996         }
997     }
998   XtSetArg (arg[0], XtNborderColor, background);
999   XtSetArg (arg[1], XtNsensitive, False);
1000   XtSetArg (arg[2], XtNlabel, "    ");
1001   for (; i < rec->num_features; i++)
1002     {
1003       if (! rec->features[i].w)
1004         {
1005           Widget w = XtCreateManagedWidget ("", commandWidgetClass,
1006                                             rec->parent, arg, 0);
1007           XtAddCallback (w, XtNcallback, FeatureProc, (XtPointer) rec);
1008           rec->features[i].w = w;
1009         }
1010       XtSetValues (rec->features[i].w, arg, 3);
1011     }
1012 }
1013
1014 void
1015 compose_script_langsys (OTF_Tag script, OTF_Tag langsys, char *name)
1016 {
1017   OTF_tag_name (script, name);
1018   if (langsys)
1019     {
1020       name[4] = '(';
1021       OTF_tag_name (langsys, name + 5);
1022       name[9] = ')';
1023       name[10] = '\0';
1024     }
1025 }
1026
1027 void
1028 decompose_script_langsys (OTF_Tag *script, OTF_Tag *langsys, char *name)
1029 {
1030   *script = OTF_tag (name);
1031   if (name[4])
1032     *langsys = OTF_tag (name + 5);
1033   else
1034     *langsys = 0;
1035 }
1036
1037 void
1038 ScriptProc (Widget w, XtPointer client_data, XtPointer call_data)
1039 {
1040   char *name;
1041   OTF_Tag script, langsys;
1042   Arg arg[1];
1043
1044   XtSetArg (arg[0], XtNlabel, &name);
1045   XtGetValues (w, arg, 1);
1046   decompose_script_langsys (&script, &langsys, name);
1047   if (script_tag == script && langsys_tag == langsys)
1048     return;
1049   script_tag = script;
1050   langsys_tag = langsys;
1051   setup_feature_rec (&gsub);
1052   setup_feature_rec (&gpos);
1053   update_seq_area ();
1054 }
1055
1056 Widget
1057 create_otf_script_widgets (Widget prev)
1058 {
1059   Widget w;
1060   Arg arg[10];
1061   int n, prev_n, i, j;
1062   struct {
1063     OTF_Tag script;
1064     OTF_Tag langsys;
1065   } *script_langsys;
1066   char name[11];
1067   int nfeatures;
1068
1069   XtSetArg (arg[0], XtNborderWidth, 0);
1070   XtSetArg (arg[1], XtNleft, XawChainLeft);
1071   XtSetArg (arg[2], XtNright, XawChainLeft);
1072   XtSetArg (arg[3], XtNtop, XawChainTop);
1073   XtSetArg (arg[4], XtNbottom, XawChainTop);
1074   XtSetArg (arg[5], XtNfromVert, prev);
1075   XtSetArg (arg[6], XtNorientation, XtorientHorizontal);
1076   prev = XtCreateManagedWidget ("Script", boxWidgetClass, render_area, arg, 7);
1077   XtCreateManagedWidget ("script(langsys)", labelWidgetClass, prev, arg, 1);
1078
1079   n = 0;
1080   if (otf->gsub)
1081     for (i = 0; i < otf->gsub->ScriptList.ScriptCount; i++)
1082       n += otf->gsub->ScriptList.Script[i].LangSysCount + 1;
1083   if (otf->gpos)
1084     for (i = 0; i < otf->gpos->ScriptList.ScriptCount; i++)
1085       n += otf->gpos->ScriptList.Script[i].LangSysCount + 1;
1086   script_langsys = alloca ((sizeof script_langsys[0]) * n);
1087   n = 0;
1088   nfeatures = 0;
1089   if (otf->gsub)
1090     for (i = 0; i < otf->gsub->ScriptList.ScriptCount; i++)
1091       {
1092         OTF_Tag tag = otf->gsub->ScriptList.Script[i].ScriptTag;
1093
1094         script_langsys[n].script = tag;
1095         script_langsys[n++].langsys = 0;
1096         if (nfeatures
1097             < otf->gsub->ScriptList.Script[i].DefaultLangSys.FeatureCount)
1098           nfeatures
1099             = otf->gsub->ScriptList.Script[i].DefaultLangSys.FeatureCount;
1100         for (j = 0; j < otf->gsub->ScriptList.Script[i].LangSysCount; j++)
1101           {
1102             script_langsys[n].script = tag;
1103             script_langsys[n++].langsys
1104               = otf->gsub->ScriptList.Script[i].LangSysRecord[j].LangSysTag;
1105             if (nfeatures
1106                 < otf->gsub->ScriptList.Script[i].LangSys[j].FeatureCount)
1107               nfeatures
1108                 = otf->gsub->ScriptList.Script[i].LangSys[j].FeatureCount;
1109           }
1110       }
1111   gsub.num_features = nfeatures;
1112   if (nfeatures > 0)
1113     {
1114       gsub.num_features = nfeatures;
1115       gsub.features = malloc ((sizeof (FeatureElement)) * nfeatures);
1116       memset (gsub.features, 0, (sizeof (FeatureElement)) * nfeatures);
1117     }
1118   prev_n = n;
1119   nfeatures = 0;
1120   if (otf->gpos)
1121     for (i = 0; i < otf->gpos->ScriptList.ScriptCount; i++)
1122       {
1123         OTF_Tag tag = otf->gpos->ScriptList.Script[i].ScriptTag;
1124         int k;
1125
1126         if (nfeatures
1127             < otf->gpos->ScriptList.Script[i].DefaultLangSys.FeatureCount)
1128           nfeatures
1129             = otf->gpos->ScriptList.Script[i].DefaultLangSys.FeatureCount;
1130         for (k = 0; k < prev_n; k++)
1131           if (tag == script_langsys[k].script)
1132             break;
1133         if (k == prev_n)
1134           {
1135             script_langsys[n].script = tag;
1136             script_langsys[n++].langsys = 0;
1137           }
1138         for (j = 0; j < otf->gpos->ScriptList.Script[i].LangSysCount; j++)
1139           {
1140             int l;
1141
1142             if (k < prev_n)
1143               {
1144                 OTF_Script *script = otf->gpos->ScriptList.Script + i;
1145
1146                 for (l = k; l < prev_n && tag == script_langsys[l].script; l++)
1147                   if (script->LangSysRecord[j].LangSysTag
1148                       == script_langsys[l].langsys)
1149                     break;
1150               }
1151             else
1152               l = prev_n;
1153             if (l == prev_n)
1154               {
1155                 script_langsys[n].script = tag;
1156                 script_langsys[n++].langsys = 0;
1157               }
1158             if (nfeatures
1159                 < otf->gpos->ScriptList.Script[i].LangSys[j].FeatureCount)
1160               nfeatures
1161                 = otf->gpos->ScriptList.Script[i].LangSys[j].FeatureCount;
1162           }
1163       }
1164
1165   if (nfeatures > 0)
1166     {
1167       gpos.num_features = nfeatures;
1168       gpos.features = malloc ((sizeof (FeatureElement)) * nfeatures);
1169       memset (gpos.features, 0, (sizeof (FeatureElement)) * nfeatures);
1170     }
1171
1172   if (n == 0)
1173     return prev;
1174
1175   script_tag = script_langsys[0].script;
1176   langsys_tag = script_langsys[0].langsys;
1177   compose_script_langsys (script_tag, langsys_tag, name);
1178
1179   if (n == 1)
1180     {
1181       XtSetArg (arg[0], XtNforeground, background);
1182       XtSetArg (arg[1], XtNbackground, foreground);
1183       XtCreateManagedWidget (name, labelWidgetClass, prev, arg, 2);
1184     }
1185   else
1186     {
1187       XtSetArg (arg[0], XtNstate, True);
1188       w = XtCreateManagedWidget (name, toggleWidgetClass, prev, arg, 1);
1189       XtAddCallback (w, XtNcallback, ScriptProc, NULL);
1190       XtSetArg (arg[0], XtNradioGroup, w);
1191       for (i = 1; i < n; i++)
1192         {
1193           compose_script_langsys (script_langsys[i].script,
1194                                   script_langsys[i].langsys, name);
1195           w = XtCreateManagedWidget (name, toggleWidgetClass, prev, arg, 1);
1196           XtAddCallback (w, XtNcallback, ScriptProc, NULL);
1197         }         
1198     }
1199   return prev;
1200 }
1201
1202
1203 Widget
1204 create_otf_widgets (Widget prev, FeatureRec *rec)
1205 {
1206   Arg arg[10];
1207   Widget w;
1208
1209   XtSetArg (arg[0], XtNborderWidth, 0);
1210   XtSetArg (arg[1], XtNleft, XawChainLeft);
1211   XtSetArg (arg[2], XtNright, XawChainLeft);
1212   XtSetArg (arg[3], XtNtop, XawChainTop);
1213   XtSetArg (arg[4], XtNbottom, XawChainTop);
1214   XtSetArg (arg[5], XtNfromVert, prev);
1215   XtSetArg (arg[6], XtNorientation, XtorientHorizontal);
1216   prev = XtCreateManagedWidget (rec->label, boxWidgetClass, render_area,
1217                                 arg, 7);
1218   XtCreateManagedWidget (rec->label, labelWidgetClass, prev, arg, 1);
1219   XtSetArg (arg[0], XtNborderWidth, 1);
1220   XtSetArg (arg[1], XtNinternalHeight, 4);
1221   XtSetArg (arg[2], XtNinternalWidth, 4);
1222   w = XtCreateManagedWidget ("all", commandWidgetClass, prev, arg, 3);
1223   XtAddCallback (w, XtNcallback, FeatureProc, (XtPointer) rec);
1224   w = XtCreateManagedWidget ("none", commandWidgetClass, prev, arg, 3);
1225   XtAddCallback (w, XtNcallback, FeatureProc, (XtPointer) rec);
1226
1227   rec->parent = prev;
1228   setup_feature_rec (rec);
1229   return prev;
1230 }
1231
1232 void
1233 create_widgets ()
1234 {
1235   String quit_action = "<KeyPress>q: set() notify() unset()";
1236   String FIRST_action = "<KeyPress>f: set() notify() unset()\n\
1237                          <KeyPress>Home: set() notify() unset()";
1238   String PREV_action = "Shift<KeyPress>p: set() notify() unset()\n\
1239                          <KeyPress>Up: set() notify() unset()";
1240   String prev_action = "~Shift<KeyPress>p: set() notify() unset()\n\
1241                          <KeyPress>Left: set() notify() unset()";
1242   String next_action = "~Shift<KeyPress>n: set() notify() unset()\n\
1243                          <KeyPress>Right: set() notify() unset()";
1244   String NEXT_action = "Shift<KeyPress>n: set() notify() unset()\n\
1245                          <KeyPress>Down: set() notify() unset()";
1246   String LAST_action = "<KeyPress>l: set() notify() unset()\n\
1247                          <KeyPress>End: set() notify() unset()";
1248   Arg arg[10];
1249   int i, j;
1250   Widget prev, w;
1251   String trans = "<Expose>: Expose()";
1252
1253   XtSetArg (arg[0], XtNtranslations, XtParseTranslationTable (trans));
1254   frame = XtCreateManagedWidget ("frame", formWidgetClass, shell, arg, 1);
1255
1256   XtSetArg (arg[0], XtNleft, XawChainLeft);
1257   XtSetArg (arg[1], XtNright, XawChainLeft);
1258   XtSetArg (arg[2], XtNtop, XawChainTop);
1259   XtSetArg (arg[3], XtNbottom, XawChainTop);
1260   XtSetArg (arg[4], XtNborderWidth, 0);
1261   XtSetArg (arg[5], XtNorientation, XtorientHorizontal);
1262   command_area = XtCreateManagedWidget ("command-area", boxWidgetClass,
1263                                         frame, arg, 6);
1264   XtSetArg (arg[6], XtNfromVert, command_area);
1265   navi_area = XtCreateManagedWidget ("navi-area", boxWidgetClass,
1266                                      frame, arg, 7);
1267   XtSetArg (arg[4], XtNborderWidth, 0);
1268   XtSetArg (arg[5], XtNfromVert, navi_area);
1269   XtSetArg (arg[6], XtNdefaultDistance, 0);
1270   glyph_area = XtCreateManagedWidget ("glyph-area", formWidgetClass,
1271                                       frame, arg, 7);
1272
1273   XtSetArg (arg[5], XtNfromVert, glyph_area);
1274   if (sub14)
1275     {
1276       Arg arg2[3];
1277
1278       XtSetArg (arg[6], XtNorientation, XtorientHorizontal);
1279       uvs_area = XtCreateManagedWidget ("uvs-area", boxWidgetClass,
1280                                         frame, arg, 7);
1281       XtSetArg (arg2[0], XtNborderWidth, 0);
1282       XtSetArg (arg2[1], XtNlabel, "Variation Selector: ");
1283       uvs_label = XtCreateManagedWidget ("uvs-label", labelWidgetClass,
1284                                          uvs_area, arg2, 2);
1285       XtSetArg (arg2[0], XtNborderWidth, 1);
1286       for (i = 0; i < sub14->nRecords; i++)
1287         {
1288           OTF_VariationSelectorRecord *record = sub14->Records + i;
1289           unsigned selector = record->varSelector;
1290           unsigned idx;
1291           char lbl[4];
1292           
1293           idx = (selector <= 0xFE0F ? selector - 0xFE00
1294                  : selector - 0xE0100 + 16);
1295           if (uvs[idx].c)
1296             continue;
1297           uvs[idx].c = selector;
1298           sprintf (lbl, "%03d", idx + 1);
1299           XtSetArg (arg2[1], XtNlabel, lbl);
1300           XtSetArg (arg2[2], XtNsensitive, False);
1301           uvs[idx].w = XtCreateManagedWidget ("lbl", commandWidgetClass,
1302                                                    uvs_area, arg2, 3);
1303           XtAddCallbackWithCast (unsigned, uvs[idx].w, UVSProc, idx);
1304         }
1305       XtSetArg (arg[5], XtNfromVert, uvs_area);
1306     }
1307   render_area = XtCreateManagedWidget ("render-area", formWidgetClass,
1308                                        frame, arg, 6);
1309
1310   XtSetArg (arg[0], XtNaccelerators, XtParseAcceleratorTable (quit_action));
1311   quit = XtCreateManagedWidget ("Quit", commandWidgetClass,
1312                                 command_area, arg, 1);
1313   XtAddCallback (quit, XtNcallback, QuitProc, NULL);
1314
1315   dump = XtCreateManagedWidget ("DumpImage", commandWidgetClass,
1316                                 command_area, arg, 1);
1317   XtAddCallback (dump, XtNcallback, DumpProc, NULL);
1318
1319   XtSetArg (arg[0], XtNborderWidth, 0);
1320   XtSetArg (arg[1], XtNwidth, 10);
1321   XtCreateManagedWidget ("spacer", boxWidgetClass, command_area, arg, 2);
1322
1323   charmap = alloca (sizeof (Widget) * (face->num_charmaps + 1));
1324   XtSetArg (arg[0], XtNstate, True);
1325   charmap[0] = XtCreateManagedWidget (charmap_rec[0].name, toggleWidgetClass,
1326                                       command_area, arg, 1);
1327   XtAddCallback (charmap[0], XtNcallback, CharmapProc, (XtPointer) -1);
1328   XtSetArg (arg[0], XtNradioGroup, charmap[0]);
1329   for (i = 0; i < face->num_charmaps; i++)
1330     {
1331       charmap[i + 1] = XtCreateManagedWidget (charmap_rec[i + 1].name,
1332                                               toggleWidgetClass,
1333                                               command_area, arg, 1);
1334       XtAddCallbackWithCast (int, charmap[i + 1], CharmapProc, i);
1335     }
1336
1337   XtSetArg (arg[0], XtNlabel, " |< (f)");
1338   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (FIRST_action));
1339   FIRST = XtCreateManagedWidget ("FIRST", commandWidgetClass,
1340                                  navi_area, arg, 2);
1341   XtAddCallback (FIRST, XtNcallback, GlyphProc, (XtPointer) -3);
1342   XtSetArg (arg[0], XtNlabel, "<< (P)");
1343   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (PREV_action));
1344   PREV = XtCreateManagedWidget ("PREV", commandWidgetClass,
1345                                 navi_area, arg, 2);
1346   XtAddCallback (PREV, XtNcallback, GlyphProc, (XtPointer) -2);
1347   XtSetArg (arg[0], XtNlabel, "< (p)");
1348   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (prev_action));
1349   prev = XtCreateManagedWidget ("prev", commandWidgetClass,
1350                                 navi_area, arg, 2);
1351   XtAddCallback (prev, XtNcallback, GlyphProc, (XtPointer) -1);
1352   XtSetArg (arg[0], XtNlabel, " 0000 ");
1353   range = XtCreateManagedWidget ("range", labelWidgetClass,
1354                                  navi_area, arg, 1);
1355   XtSetArg (arg[0], XtNforeground, &foreground);
1356   XtSetArg (arg[1], XtNbackground, &background);
1357   XtGetValues (range, arg, 2);
1358
1359   XtSetArg (arg[0], XtNlabel, "> (n)");
1360   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (next_action));
1361   next = XtCreateManagedWidget ("next", commandWidgetClass,
1362                                 navi_area, arg, 2);
1363   XtAddCallback (next, XtNcallback, GlyphProc, (XtPointer) 1);
1364   XtSetArg (arg[0], XtNlabel, ">> (N)");
1365   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (NEXT_action));
1366   NEXT = XtCreateManagedWidget ("NEXT", commandWidgetClass,
1367                                 navi_area, arg, 2);
1368   XtAddCallback (NEXT, XtNcallback, GlyphProc, (XtPointer) 2);
1369   XtSetArg (arg[0], XtNlabel, ">| (l)");
1370   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (LAST_action));
1371   LAST = XtCreateManagedWidget ("LAST", commandWidgetClass,
1372                                 navi_area, arg, 2);
1373   XtAddCallback (LAST, XtNcallback, GlyphProc, (XtPointer) 3);
1374
1375   XtSetArg (arg[0], XtNleft, XawChainLeft);
1376   XtSetArg (arg[1], XtNright, XawChainLeft);
1377   XtSetArg (arg[2], XtNtop, XawChainTop);
1378   XtSetArg (arg[3], XtNbottom, XawChainTop);
1379
1380   for (i = 0; i < 8; i++)
1381     {
1382       char str[3];
1383       int n = 4;
1384       Widget head;
1385
1386       sprintf (str, "%XX", i);
1387       XtSetArg (arg[n], XtNheight, glyph_height + 5), n++;
1388       XtSetArg (arg[n], XtNlabel, str), n++;
1389       XtSetArg (arg[n], XtNborderWidth, 0), n++;
1390       if (i > 0)
1391         XtSetArg (arg[n], XtNfromVert, w), n++;
1392       head = XtCreateManagedWidget (str, labelWidgetClass, glyph_area, arg, n);
1393       index_label[i] = head;
1394       for (j = 0; j < 16; j++)
1395         {
1396           int k = i * 16 + j;
1397
1398           n = 4;
1399           if (i > 0)
1400             XtSetArg (arg[n], XtNfromVert, w), n++;
1401           if (j == 0)
1402             XtSetArg (arg[n], XtNfromHoriz, head), n++;
1403           else
1404             XtSetArg (arg[n], XtNfromHoriz, glyph[k - 1]), n++;
1405           XtSetArg (arg[n], XtNbitmap, none_pixmap), n++;
1406           glyph[k] = XtCreateManagedWidget ("glyph", commandWidgetClass,
1407                                             glyph_area, arg, n);
1408           XtAddCallbackWithCast (int, glyph[k], RenderProc, k);
1409         }
1410       w = head;
1411     }
1412   /* 10 = (1 (border_width) + 4 (inner_width)) * 2 */
1413   XtSetArg(arg[4], XtNwidth, glyph_width + 10);
1414   XtSetArg (arg[5], XtNfromVert, glyph[112]);
1415   XtSetArg (arg[6], XtNfromHoriz, w);
1416   XtSetArg (arg[7], XtNborderWidth, 0);
1417
1418   for (j = 0; j < 16; j++)
1419     {
1420       char str[3];
1421
1422       sprintf (str, "X%X", j);
1423       XtSetArg (arg[8], XtNlabel, str);
1424       w = XtCreateManagedWidget ("idx", labelWidgetClass, glyph_area, arg, 9);
1425       XtSetArg (arg[6], XtNfromHoriz, w);
1426     }
1427
1428   XtSetArg (arg[0], XtNleft, XawChainLeft);
1429   XtSetArg (arg[1], XtNright, XawChainLeft);
1430   XtSetArg (arg[2], XtNtop, XawChainTop);
1431   XtSetArg (arg[3], XtNbottom, XawChainTop);
1432   XtSetArg (arg[4], XtNorientation, XtorientHorizontal);
1433   XtSetArg (arg[5], XtNborderWidth, 0);
1434   w = XtCreateManagedWidget ("clear-box", boxWidgetClass, render_area, arg, 6);
1435   clear = XtCreateManagedWidget ("clear", commandWidgetClass, w, arg, 0);
1436   XtAddCallback (clear, XtNcallback, RenderProc, (XtPointer) -1);
1437   del = XtCreateManagedWidget ("delete", commandWidgetClass, w, arg, 0);
1438   XtAddCallback (del, XtNcallback, RenderProc, (XtPointer) -2);
1439   bidi = XtCreateManagedWidget ("L->R", toggleWidgetClass, w, arg, 0);
1440   XtAddCallback (bidi, XtNcallback, BidiProc, NULL);
1441   alt_subst = XtCreateManagedWidget ("AltSubst", toggleWidgetClass, w, arg, 0);
1442   XtAddCallback (alt_subst, XtNcallback, AltSubstProc, NULL);
1443
1444   XtSetArg (arg[4], XtNorientation, XtorientHorizontal);
1445   XtSetArg (arg[5], XtNborderWidth, 0);
1446   XtSetArg (arg[6], XtNfromVert, w);
1447   raw = XtCreateManagedWidget ("raw", boxWidgetClass, render_area, arg, 7);
1448
1449   XtSetArg (arg[0], XtNborderWidth, 0);
1450   XtSetArg (arg[1], XtNlabel, "raw: ");
1451   raw_label = XtCreateManagedWidget ("raw-label", labelWidgetClass,
1452                                       raw, arg, 2);
1453   XtSetArg (arg[1], XtNbitmap, raw_pixmap);
1454   raw_image = XtCreateManagedWidget ("raw-image", labelWidgetClass,
1455                                       raw, arg, 2);
1456   w = raw;
1457   if (otf)
1458     {
1459       OTF_get_table (otf, "GSUB");
1460       OTF_get_table (otf, "GPOS");
1461       w = create_otf_script_widgets (w);
1462       if (otf->gsub)
1463         {
1464           gsub.label = "GSUB";
1465           gsub.gsub_gpos = otf->gsub;
1466           w = create_otf_widgets (w, &gsub);
1467         }
1468       if (otf->gpos)
1469         {
1470           gpos.label = "GPOS";
1471           gpos.gsub_gpos = otf->gpos;
1472           w = create_otf_widgets (w, &gpos);
1473         }
1474     }
1475
1476   XtSetArg (arg[6], XtNfromVert, w);
1477   seq = XtCreateManagedWidget ("seq", boxWidgetClass, render_area, arg, 7);
1478   XtSetArg (arg[0], XtNborderWidth, 0);
1479   XtSetArg (arg[1], XtNlabel, "seq: ");
1480   seq_label = XtCreateManagedWidget ("seq-label", labelWidgetClass,
1481                                      seq, arg, 2);
1482   XtSetArg (arg[1], XtNbitmap, seq_pixmap);
1483   seq_image = XtCreateManagedWidget ("seq-image", labelWidgetClass,
1484                                      seq, arg, 2);
1485   XtInstallAllAccelerators (shell, shell);
1486 }
1487
1488 static void
1489 ExposeProc (Widget w, XEvent *event, String *str, Cardinal *num)
1490 {
1491   XTextProperty text_prop;
1492   char *pname = "otfview";
1493   char *fname = basename (filename);
1494   char *name = alloca (strlen (fname) + 3 + strlen (pname) + 1);
1495
1496   sprintf (name, "%s - %s", pname, fname);
1497   text_prop.value = (unsigned char *) name;
1498   text_prop.encoding = XA_STRING;
1499   text_prop.format = 8;
1500   text_prop.nitems = strlen (name);
1501   XSetWMName (display, XtWindow (shell), &text_prop);
1502 }
1503
1504 /* Format MSG by FMT and print the result to the stderr, and exit.  */
1505
1506 #define FATAL_ERROR(fmt, arg)   \
1507   do {                          \
1508     fprintf (stderr, fmt, arg); \
1509     exit (1);                   \
1510   } while (0)
1511
1512 static int
1513 x_error_handler (Display *display, XErrorEvent *error)
1514 {
1515   return 0;
1516 }
1517
1518 void
1519 help (char **argv, int err)
1520 {
1521   FILE *fp = err ? stderr : stdout;
1522
1523   fprintf (fp, "Usage: %s [ X-OPTION ... ]  OTF-FILE [INDEX]\n",
1524            basename (argv[0]));
1525   fprintf (fp, "  Environment variable PIXEL_SIZE specifies the pixel size.\n");
1526   fprintf (fp, "  The default pixel size is %d, but is reduced\n",
1527            DEFAULT_PIXEL_SIZE);
1528   fprintf (fp, "  if your screen is not that wide.\n");
1529   exit (err);
1530 }
1531
1532 int
1533 main (int argc, char **argv)
1534 {
1535   XtActionsRec actions[] = { {"Expose", ExposeProc} };
1536   Arg arg[10];
1537
1538   FT_Library library;
1539   OTF_GlyphString gstring;
1540   OTF_Glyph *g;
1541
1542   int err;
1543   int i;
1544   int fixed_pixel_size = 0;
1545   int display_width;
1546
1547   pixel_size = DEFAULT_PIXEL_SIZE;
1548   {
1549     char *str = getenv ("PIXEL_SIZE");
1550
1551     if (str && (i = atoi (str)) > 0)
1552       {
1553         pixel_size = i;
1554         fixed_pixel_size = 1;
1555       }
1556   }
1557
1558   gstring.size = gstring.used = 256;
1559   g = calloc (256, sizeof (OTF_Glyph));
1560   gstring.glyphs = g;
1561
1562   shell = XtOpenApplication (&context, "OTFView", NULL, 0, &argc, argv, NULL,
1563                              shellWidgetClass, arg, 0);
1564   display = XtDisplay (shell);
1565   /*XSynchronize (display, True);*/
1566   XSetErrorHandler (x_error_handler);
1567   display_width = DisplayWidth (display,
1568                                 XScreenNumberOfScreen (XtScreen (shell)));
1569   font = XLoadQueryFont (display, DEFAULT_FONT_NAME);
1570   if (! font)
1571     font = XLoadQueryFont (display, "fixed");
1572
1573   if (argc < 2)
1574     help (argv, 1);
1575   if (!strcmp (argv[1], "-h") || !strcmp (argv[1], "--help"))
1576     help (argv, 0);
1577   filename = argv[1];
1578   if (argc > 2)
1579     {
1580       fontindex = atoi (argv[2]);
1581       if (fontindex < 0)
1582         FATAL_ERROR ("Invalid font index: %d\n", fontindex);
1583     }
1584
1585   if ((err = FT_Init_FreeType (&library)))
1586     FATAL_ERROR ("%s\n", "FT_Init_FreeType: error");
1587   err = FT_New_Face (library, filename, fontindex, &face);
1588   if (err == FT_Err_Unknown_File_Format)
1589     FATAL_ERROR ("%s\n", "FT_New_Face: unknown file format");
1590   else if (err)
1591     FATAL_ERROR ("%s\n", "FT_New_Face: unknown error (invalid face index?)");
1592   if ((err = FT_Set_Pixel_Sizes (face, 0, pixel_size)))
1593     FATAL_ERROR ("%s\n", "FT_Set_Pixel_Sizes: error");
1594
1595   if (strstr (filename, ".ttf")
1596       || strstr (filename, ".TTF")
1597       || strstr (filename, ".otf")
1598       || strstr (filename, ".OTF"))
1599     {
1600       otf = OTF_open_ft_face (face);
1601       if (otf)
1602         {
1603           if (OTF_get_table (otf, "head") < 0
1604               || OTF_get_table (otf, "cmap") < 0
1605               || (OTF_check_table (otf, "GSUB") < 0
1606                   && OTF_check_table (otf, "GPOS") < 0))
1607             {
1608               OTF_close (otf);
1609               otf = NULL;
1610             }
1611         }
1612       if (otf)
1613         for (i = 0; i < otf->cmap->numTables; i++)
1614           if (otf->cmap->EncodingRecord[i].subtable.format == 14)
1615             {
1616               sub14 = otf->cmap->EncodingRecord[i].subtable.f.f14;
1617               break;
1618             }
1619     }
1620
1621   {
1622     char title[256];
1623     Arg arg[1];
1624
1625     filename = basename (filename);
1626     sprintf (title, "%s family:%s style:%s",
1627              filename, face->family_name, face->style_name);
1628     XtSetArg (arg[0], XtNtitle, title);
1629     XtSetValues (shell, arg, 1);
1630   }
1631
1632   glyph_width = ((face->bbox.xMax - face->bbox.xMin)
1633                  * pixel_size / face->units_per_EM);
1634   if (! fixed_pixel_size && glyph_width * 16 > display_width * 0.8)
1635     {
1636       pixel_size = (pixel_size * display_width * 0.8 / 16 / glyph_width);
1637       FT_Set_Pixel_Sizes (face, 0, pixel_size);
1638       glyph_width = ((face->bbox.xMax - face->bbox.xMin)
1639                      * pixel_size / face->units_per_EM);
1640     }
1641   if (glyph_width < FONT_WIDTH * 4)
1642     glyph_width = FONT_WIDTH * 4;
1643
1644   glyph_height = ((face->bbox.yMax - face->bbox.yMin)
1645                   *  pixel_size / face->units_per_EM);
1646
1647   glyph_x = - (face->bbox.xMin * pixel_size / face->units_per_EM);
1648   glyph_y = face->bbox.yMax * pixel_size / face->units_per_EM;
1649   none_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
1650                                glyph_width, glyph_height, 1);
1651
1652   {
1653     unsigned long valuemask =  GCFunction | GCLineWidth;
1654     XGCValues values;
1655
1656     gc = XCreateGC (display, none_pixmap, (unsigned long) 0, NULL);
1657     XSetFont (display, gc, font->fid);
1658     values.function = GXset;
1659     values.line_width = 1;
1660     gc_set = XCreateGC (display, none_pixmap, valuemask, &values);
1661     XSetFont (display, gc_set, font->fid);
1662     values.function = GXor;
1663     gc_or = XCreateGC (display, none_pixmap, valuemask, &values);
1664     values.function = GXcopyInverted;
1665     gc_inv = XCreateGC (display, none_pixmap, valuemask, &values);
1666   }
1667
1668   XFillRectangle (display, none_pixmap, gc, 0, 0,
1669                   glyph_width, glyph_height);
1670   XDrawString (display, none_pixmap, gc_inv,
1671                (glyph_width - XTextWidth (font, "none", 4)) / 2,
1672                glyph_height / 2, "none", 4);
1673
1674   render_width = (glyph_width + 4) * 15 + 1;
1675   render_height = glyph_height + 2;
1676
1677   charmap_rec[0].platform_id = -1;
1678   charmap_rec[0].encoding_id = -1;
1679   strcpy (charmap_rec[0].name, "no charmap");
1680
1681   for (i = 0; i < face->num_charmaps; i++)
1682     {
1683       charmap_rec[i + 1].platform_id = face->charmaps[i]->platform_id;
1684       charmap_rec[i + 1].encoding_id = face->charmaps[i]->encoding_id;
1685       sprintf (charmap_rec[i + 1].name, "%d-%d",
1686                charmap_rec[i + 1].platform_id, charmap_rec[i + 1].encoding_id);
1687       if (face->charmaps[i]->platform_id == 0
1688           || (face->charmaps[i]->platform_id == 3
1689               && face->charmaps[i]->encoding_id == 1))
1690         strcat (charmap_rec[i + 1].name, " (unicode)");
1691       else if (face->charmaps[i]->platform_id == 1
1692                && face->charmaps[i]->encoding_id == 0)
1693         strcat (charmap_rec[i + 1].name, " (apple-roman)");
1694     }
1695
1696   raw_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
1697                               render_width, render_height, 1);
1698   seq_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
1699                               render_width, render_height, 1);
1700
1701   memset (bitmap, 0, sizeof (bitmap));
1702   create_widgets ();
1703   glyph_index = 0;
1704   charmap_index = -1;
1705   update_glyph_area ();
1706   update_render_area ();
1707
1708   XtAppAddActions (context, actions, XtNumber (actions));
1709   XtRealizeWidget (shell);
1710   XtAppMainLoop (context);
1711
1712   exit (0);
1713 }
1714
1715 #else /* not HAVE_X11_XAW_COMMAND_H */
1716
1717 int
1718 main (int argc, char **argv)
1719 {
1720   fprintf (stderr, 
1721            "Building of this program failed (lack of some header files)\n");
1722   exit (1);
1723 }
1724
1725 #endif