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