(update_seq_area): Fix previous change.
[m17n/libotf.git] / example / otfview.c
1 /* otfview.c -- View glyphs of OpenType fonts.
2
3 Copyright (C) 2003, 2004
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 <X11/Xatom.h>
32 #include <X11/Intrinsic.h>
33 #include <X11/StringDefs.h>
34 #include <X11/Shell.h>
35 #include <X11/Xaw/Command.h>
36 #include <X11/Xaw/Toggle.h>
37 #include <X11/Xaw/Box.h>
38 #include <X11/Xaw/Form.h>
39 #include <X11/Xaw/Viewport.h>
40
41 #include <ft2build.h>
42 #include FT_FREETYPE_H
43
44 #include <otf.h>
45
46 #define DEFAULT_PIXEL_SIZE 30
47 int pixel_size;
48
49 #define DEFAULT_FONT_NAME "6x13"
50 XFontStruct *font;
51 #define FONT_HEIGHT (font->ascent + font->descent)
52 #define FONT_ASCENT (font->ascent)
53 #define FONT_DESCENT (font->descent)
54 #define FONT_WIDTH (font->max_bounds.width)
55
56 XtAppContext context;
57 /* Widget structure.
58    +--- frame (form) -------------------------+
59    | +--- command_area (box) ---------------+ |
60    | | quit dump charmap ...                | |
61    | +--------------------------------------+ |
62    | +---- navi_area (box) -----------------+ |
63    | | FIRST PREV prev range next NEXT LAST | |
64    | +--------------------------------------+ |
65    | +--- glyph_area (form) ----------------+ |
66    | | idxh[0] glyph[0]    ...    glyph[15] | |
67    | |   ...                        ...     | |
68    | | idxh[7] glyph[112]  ...    glyph[127]| |
69    | |         idxl[0]     ...    idxl[15]  | |
70    | +--------------------------------------+ |
71    | +--- render_area (form) ---------------+ |
72    | | clear del bidi                       | |
73    | | +--- raw (box) --------------------+ | |
74    | | | raw_label raw_image              | | |
75    | | +----------------------------------+ | |
76    | | GSUB all none features...            | |
77    | | GPOS all none features...            | |
78    | | +--- seq (box) --------------------+ | |
79    | | | seq_label seq_image              | | |
80    | | +----------------------------------+ | |
81    | +--------------------------------------+ |
82    +------------------------------------------+ */
83 Widget shell, frame;
84 Widget command_area, quit, dump, *charmap;
85 Widget navi_area, FIRST, PREV, prev, range, next, NEXT, LAST;
86 Widget glyph_area, glyph[128], index_label[8];
87 Widget render_area, clear, del, bidi, raw, seq;
88 Widget raw_label, raw_image, seq_label, seq_image;
89 unsigned long foreground, background;
90
91 #define MAX_FEATURE_COUNT 16
92
93 typedef struct
94 {
95   char *label;
96   OTF_GSUB_GPOS *gsub_gpos;
97   OTF_LangSys *langsys;
98   struct {
99     OTF_Tag tag;
100     int on;
101     Widget w;
102   } features[MAX_FEATURE_COUNT];
103 } FeatureRec;
104
105 FeatureRec gsub, gpos;
106
107 OTF_Tag script_tag;
108
109 int glyph_char[128];
110
111 Display *display;
112 GC gc, gc_set, gc_or, gc_inv;
113
114 typedef struct {
115   Pixmap pixmap;
116   unsigned width, height;
117   int x, y;
118   int advance;
119 } BitmapRec;
120
121 BitmapRec bitmap[0x10000];
122
123 int render_width, render_height;
124 Pixmap raw_pixmap, seq_pixmap, gsub_pixmap, gpos_pixmap;
125 Pixmap none_pixmap;
126
127 FT_Face face;
128
129 struct {
130   int platform_id;
131   int encoding_id;
132   char name[20];
133 } charmap_rec[10];
134
135 int charmap_index;
136
137 int reversed;
138 unsigned glyph_width, glyph_height;
139 int glyph_x, glyph_y;
140 int glyph_index;
141
142 struct {
143   int n_glyphs;
144   int glyphs[64];
145   int codes[64];
146 } glyph_rec;
147
148 OTF *otf;
149 char *filename;
150
151 void
152 create_pixmap (int index)
153 {
154   int err = FT_Load_Glyph (face, index, FT_LOAD_RENDER | FT_LOAD_MONOCHROME);
155   XImage ximage;
156   Pixmap pixmap;
157   
158   if (err)
159     {
160       bitmap[index].pixmap = none_pixmap;
161       return;
162     }
163   ximage.height = face->glyph->bitmap.rows;
164   ximage.width = face->glyph->bitmap.width;
165   ximage.depth = 1;
166   ximage.bits_per_pixel = 1;
167   ximage.xoffset = 0;
168   ximage.format = XYPixmap;
169   ximage.data = (char *) face->glyph->bitmap.buffer;
170   ximage.byte_order = MSBFirst;
171   ximage.bitmap_unit = 8;
172   ximage.bitmap_bit_order = MSBFirst;
173   ximage.bitmap_pad = 8;
174   ximage.bytes_per_line = face->glyph->bitmap.pitch;
175   XInitImage (&ximage);
176   pixmap = XCreatePixmap (display, DefaultRootWindow (display),
177                           glyph_width, glyph_height, 1);
178   XFillRectangle (display, pixmap, gc, 0, 0, glyph_width, glyph_height);
179   XPutImage (display, pixmap, gc, &ximage, 0, 0,
180              glyph_x + face->glyph->bitmap_left,
181              glyph_y - face->glyph->bitmap_top,
182              ximage.width, ximage.height);
183   bitmap[index].pixmap = pixmap;
184   bitmap[index].width = ximage.width;
185   bitmap[index].height = ximage.height;
186   bitmap[index].x = face->glyph->bitmap_left;
187   bitmap[index].y = - face->glyph->bitmap_top;
188   bitmap[index].advance = face->glyph->metrics.horiAdvance >> 6;
189 }
190
191 void
192 update_glyph_area ()
193 {
194   int i;
195   Arg arg[2];
196   char buf[16];
197   int msb;
198
199   for (i = 0; i < 128; i++)
200     {
201       int index = glyph_index + i;
202
203       if (charmap_index >= 0)
204         index = FT_Get_Char_Index (face, (FT_ULong) index);
205       if (! bitmap[index].pixmap)
206         create_pixmap (index);
207       XtSetArg (arg[0], XtNbitmap, bitmap[index].pixmap);
208       XtSetValues (glyph[i], arg, 1);
209     }
210
211   msb = (glyph_index >> 7) % 2 ? 8 : 0;
212   for (i = 0; i < 8; i++)
213     {
214       char str[3];
215
216       sprintf (str, "%XX", i | msb );
217       XtSetArg (arg[0], XtNheight, glyph_height + 5);
218       XtSetArg (arg[1], XtNlabel, str);
219       XtSetValues (index_label[i], arg, 2);
220     }
221
222   sprintf (buf, " %04X-%04X ", glyph_index, glyph_index + 0x7F);
223   XtSetArg (arg[0], XtNlabel, buf);
224   XtSetValues (range, arg, 1);
225 }
226
227 char *
228 get_features (OTF_FeatureList *list, FeatureRec *rec)
229 {
230   int i, n;
231   char *str, *p;
232
233   if (! rec->features[0].on)
234     return NULL;
235   for (i = n = 0; i < rec->langsys->FeatureCount; i++)
236     {
237       if (rec->features[i].on)
238         n++;
239       else
240         break;
241     }
242   if (i == rec->langsys->FeatureCount)
243     {
244       str = malloc (2);
245       strcpy (str, "*");
246       return str;
247     }
248   str = malloc (n * 5);
249   for (i = 0, p = str; i < n; i++, p += 5)
250     {
251       OTF_tag_name (rec->features[i].tag, p);
252       p[4] = ',';
253     }
254   p[-1] = '\0';
255   return str;
256 }
257
258
259 #define DEVICE_DELTA(table, size)                               \
260   (((size) >= (table).StartSize && (size) <= (table).EndSize)   \
261    ? (table).DeltaValue[(size) >= (table).StartSize]            \
262    : 0)
263
264 void
265 adjust_anchor (OTF_Anchor *anchor, FT_Face ft_face,
266                OTF_Glyph *g, int *x, int *y)
267 {
268   if (anchor->AnchorFormat == 2)
269     {
270       FT_Outline *outline;
271       int ap = anchor->f.f1.AnchorPoint;
272
273       FT_Load_Glyph (ft_face, (FT_UInt) g->glyph_id, FT_LOAD_MONOCHROME);
274       outline = &ft_face->glyph->outline;
275       if (ap < outline->n_points)
276         {
277           *x = outline->points[ap].x;
278           *y = outline->points[ap].y;
279         }
280     }
281   else if (anchor->AnchorFormat == 3)
282     {
283       *x += DEVICE_DELTA (anchor->f.f2.XDeviceTable, pixel_size);
284       *y += DEVICE_DELTA (anchor->f.f2.YDeviceTable, pixel_size);
285     }
286 }
287
288 void
289 update_seq_area ()
290 {
291   int i, x;
292   OTF_GlyphString gstring;
293   OTF_Glyph *g, *prev, *base, *mark;
294   int base_width;
295   int len = glyph_rec.n_glyphs;
296   Arg arg[1];
297   int unitsPerEm = face->units_per_EM;
298
299   gstring.size = gstring.used = len;
300   gstring.glyphs = alloca (sizeof (OTF_Glyph) * len);
301   memset (gstring.glyphs, 0, sizeof (OTF_Glyph) * len);
302   for (i = 0; i < len; i++)
303     gstring.glyphs[i].c = gstring.glyphs[i].glyph_id = glyph_rec.glyphs[i];
304   OTF_drive_gdef (otf, &gstring);
305
306   XFillRectangle (display, seq_pixmap, gc, 0, 0, render_width, render_height);
307   XDrawLine (display, seq_pixmap, gc_set, 0, glyph_y, render_width, glyph_y);
308   if (otf)
309     {
310       char *str;
311
312       if (otf->gsub)
313         {
314           str = get_features (&otf->gsub->FeatureList, &gsub);
315           if (str)
316             {
317               OTF_drive_gsub (otf, &gstring, NULL, NULL, str);
318               free (str);
319             }
320         }
321       if (otf->gpos)
322         {
323           str = get_features (&otf->gpos->FeatureList, &gpos);
324           if (str)
325             {
326               OTF_drive_gpos (otf, &gstring, NULL, NULL, str);
327               free (str);
328             }
329         }
330     }
331
332   prev = NULL;
333   if (reversed)
334     {
335       OTF_Glyph temp;
336
337       for (prev = gstring.glyphs, g = gstring.glyphs + gstring.used - 1;
338            prev < g; prev++, g--)
339         temp = *prev, *prev = *g, *g = temp;
340       for (g = gstring.glyphs; g < gstring.glyphs + gstring.used; g++)
341         if (g->GlyphClass == 3)
342           {
343             OTF_Glyph *g0;
344             prev = g++;
345             while (g < gstring.glyphs + gstring.used && g->GlyphClass == 3)
346               g++;
347             for (g0 = g; prev < g0; prev++, g0--)
348               temp = *prev, *prev = *g, *g = temp;
349           }
350     }
351
352
353   mark = base = NULL;
354   for (i = 0, x = glyph_x, prev = NULL, g = gstring.glyphs;
355        i < gstring.used; i++, prev = g++)
356     {
357       BitmapRec *bmp = bitmap + gstring.glyphs[i].glyph_id;
358       int xoff = 0, yoff = 0;
359       int prev_width;
360       int advance = bmp->advance;
361
362       if (! bmp->pixmap)
363         {
364           create_pixmap (gstring.glyphs[i].glyph_id);
365           if (! bmp->pixmap)
366             continue;
367         }
368       switch (g->positioning_type)
369         {
370         case 0:
371           break;
372
373         case 1: case 2:
374           {
375             int format = g->f.f1.format;
376
377             if (format & OTF_XPlacement)
378               xoff = g->f.f1.value->XPlacement * pixel_size / unitsPerEm;
379             if (format & OTF_XPlaDevice)
380               xoff += DEVICE_DELTA (g->f.f1.value->XPlaDevice, pixel_size);
381             if (format & OTF_YPlacement)
382               yoff = g->f.f1.value->YPlacement * pixel_size / unitsPerEm;
383             if (format & OTF_YPlaDevice)
384               yoff += DEVICE_DELTA (g->f.f1.value->YPlaDevice, pixel_size);
385             if (format & OTF_XAdvance)
386               advance += g->f.f1.value->XAdvance * pixel_size / unitsPerEm;
387             if (format & OTF_XAdvDevice)
388               advance += DEVICE_DELTA (g->f.f1.value->XAdvDevice, pixel_size);
389           }
390           break;
391
392         case 3:
393           /* Not yet supported.  */
394           break;
395         case 4: case 5:
396           if (! base)
397             break;
398           prev = base;
399           prev_width = base_width;
400           goto label_adjust_anchor;
401         default:                /* i.e. case 6 */
402           if (! mark)
403             break;
404           prev = mark;
405           prev_width = 0;
406         label_adjust_anchor:
407           {
408             int base_x, base_y, mark_x, mark_y;
409
410             base_x = g->f.f4.base_anchor->XCoordinate * pixel_size / unitsPerEm;
411             base_y = g->f.f4.base_anchor->YCoordinate * pixel_size / unitsPerEm;
412             mark_x = g->f.f4.mark_anchor->XCoordinate * pixel_size / unitsPerEm;
413             mark_y = g->f.f4.mark_anchor->YCoordinate * pixel_size / unitsPerEm;
414
415             if (g->f.f4.base_anchor->AnchorFormat != 1)
416               adjust_anchor (g->f.f4.base_anchor, face, prev, &base_x, &base_y);
417             if (g->f.f4.mark_anchor->AnchorFormat != 1)
418               adjust_anchor (g->f.f4.mark_anchor, face, g, &mark_x, &mark_y);
419             xoff = (base_x - prev_width) - mark_x;
420             yoff = base_y - mark_y;
421           }
422         }
423           
424       XCopyArea (display, bmp->pixmap, seq_pixmap, gc_or,
425                  glyph_x + bmp->x, glyph_y + bmp->y, bmp->width, bmp->height,
426                  x + bmp->x + xoff, glyph_y + bmp->y - yoff);
427       x += advance;
428
429       if (g->GlyphClass == OTF_GlyphClass0)
430         base = mark = g, base_width = advance;
431       else if (g->GlyphClass == OTF_GlyphClassMark)
432         mark = g;
433       else
434         base = g, base_width = advance;
435     }
436   XtSetArg (arg[0], XtNbitmap, seq_pixmap);
437   XtSetValues (seq_image, arg, 1);
438 }
439
440
441 void
442 update_render_area ()
443 {
444   int i;
445   int x;
446   Arg arg[1];
447
448   XFillRectangle (display, raw_pixmap, gc, 0, 0, render_width, render_height);
449   for (i = 0, x = glyph_x; i < glyph_rec.n_glyphs; i++)
450     {
451       BitmapRec *bmp = bitmap + glyph_rec.glyphs[i];
452       char buf[5];
453
454       XCopyArea (display, bmp->pixmap, raw_pixmap, gc,
455                  0, 0, glyph_width, glyph_height,
456                  (glyph_width + 4) * i + 1, 1);
457       XDrawRectangle (display, raw_pixmap, gc_set,
458                       (glyph_width + 4) * i, 0,
459                       glyph_width + 1, glyph_height + 1);
460       XDrawLine (display, raw_pixmap, gc_set,
461                  (glyph_width + 4) * i + 1 + glyph_x, 1,
462                  (glyph_width + 4) * i + 1 + glyph_x, glyph_height + 1);
463       XDrawLine (display, raw_pixmap, gc_set,
464                  (glyph_width + 4) * i + 1 + glyph_x + bmp->advance, 1,
465                  (glyph_width + 4) * i + 1 + glyph_x + bmp->advance,
466                  glyph_height + 1);
467
468       sprintf (buf, "%04X", glyph_rec.codes[i]);
469       XDrawString (display, raw_pixmap, gc_inv, 
470                    (glyph_width + 1) * i + 1
471                    + (glyph_width - XTextWidth (font, buf, 4)) / 2,
472                    glyph_height + 2 + FONT_HEIGHT, buf, 4);
473     }
474   XtSetArg (arg[0], XtNbitmap, raw_pixmap);
475   XtSetValues (raw_image, arg, 1);
476   update_seq_area ();
477 }
478
479 void
480 QuitProc (Widget w, XtPointer client_data, XtPointer call_data)
481 {
482   XtAppSetExitFlag (XtWidgetToApplicationContext (w));
483 }
484
485 void
486 DumpProc (Widget w, XtPointer client_data, XtPointer call_data)
487 {
488   int g_width, g_height, g_x, g_y, pix_width, pix_height;
489   int margin = 20 * 300 / 25.4;
490   int a4_width = 210 * 300 / 25.4 - margin * 2;
491   int a4_height = 297 * 300 / 25.4 - margin * 2;
492   int size = 100;
493   Pixmap pixmap;
494   XImage ximage, *image;
495   int i, x, y;
496   char *data;
497   int bytes_per_line;
498
499   FT_Set_Pixel_Sizes (face, 0, size);
500   g_width = ((face->bbox.xMax - face->bbox.xMin) * size / face->units_per_EM);
501   g_height = ((face->bbox.yMax - face->bbox.yMin) * size / face->units_per_EM);
502   pix_width = (g_width + 1) * 16 + margin + 1;
503   pix_height = (g_height + 1 + FONT_HEIGHT) * 16 + margin + 1;
504   while (pix_width > a4_width || pix_height > a4_height)
505     {
506       size--;
507       FT_Set_Pixel_Sizes (face, 0, size);
508       g_width = ((face->bbox.xMax - face->bbox.xMin)
509                  * size / face->units_per_EM);
510       g_height = ((face->bbox.yMax - face->bbox.yMin)
511                   * size / face->units_per_EM);
512       pix_width = (g_width + 1) * 16 + margin + 1;
513       pix_height = (g_height + 1 + FONT_HEIGHT) * 16 + margin + 1;
514     }
515
516   g_x = - (face->bbox.xMin * size / face->units_per_EM);
517   g_y = face->bbox.yMax * size / face->units_per_EM;
518   for (i = 0; i < 0xFF; i++)
519     {
520       int idx;
521
522       if (charmap_index >= 0)
523         idx = FT_Get_Char_Index (face, (FT_ULong) i);
524       else
525         idx = i;
526       if (FT_Load_Glyph (face, idx, FT_LOAD_RENDER | FT_LOAD_MONOCHROME) == 0)
527         {
528           if (g_x < - face->glyph->bitmap_left)
529             g_x = - face->glyph->bitmap_left;
530           if (g_y < face->glyph->bitmap_top)
531             g_y = face->glyph->bitmap_top;
532           if (g_width
533               < g_x + face->glyph->bitmap_left + face->glyph->bitmap.width)
534             g_width
535               = g_x + face->glyph->bitmap_left + face->glyph->bitmap.width;
536           if (g_height
537               < g_y - face->glyph->bitmap_top + face->glyph->bitmap.rows)
538             g_height
539               = g_y - face->glyph->bitmap_top + face->glyph->bitmap.rows;
540         }
541     }
542   pix_width = (g_width + 1) * 16 + margin + 1;
543   pix_height = (g_height + FONT_HEIGHT + 1) * 16 + margin + 1;
544   pixmap = XCreatePixmap (display,
545                           RootWindow (display, DefaultScreen (display)),
546                           pix_width, pix_height, 1);
547   XFillRectangle (display, pixmap, gc, 0, 0, pix_width, pix_height);
548
549   for (i = 0, x = margin; i <= 16; i++, x += g_width + 1)
550     XDrawLine (display, pixmap, gc_set, x, margin,
551                x, margin + (g_height + FONT_HEIGHT + 1) * 16);
552   for (i = 0, y = margin; i <= 16; i++, y += g_height + FONT_HEIGHT + 1)
553     XDrawLine (display, pixmap, gc_set, margin, y,
554                margin + (g_width + 1) * 16, y);
555   for (i = 0; i < 256; i++)
556     {
557       char str[5];
558       int idx;
559
560       if (charmap_index >= 0)
561         idx = FT_Get_Char_Index (face, (FT_ULong) i);
562       else
563         idx = i;
564       x = margin + (g_width + 1) * (i % 16);
565       y = margin + (g_height + FONT_HEIGHT + 1) * (i / 16);
566       if (FT_Load_Glyph (face, idx, FT_LOAD_RENDER | FT_LOAD_MONOCHROME) == 0)
567         {
568           ximage.height = face->glyph->bitmap.rows;
569           ximage.width = face->glyph->bitmap.width;
570           ximage.depth = 1;
571           ximage.bits_per_pixel = 1;
572           ximage.xoffset = 0;
573           ximage.format = XYPixmap;
574           ximage.data = (char *) face->glyph->bitmap.buffer;
575           ximage.byte_order = MSBFirst;
576           ximage.bitmap_unit = 8;
577           ximage.bitmap_bit_order = MSBFirst;
578           ximage.bitmap_pad = 8;
579           ximage.bytes_per_line = face->glyph->bitmap.pitch;
580           XInitImage (&ximage);
581           XPutImage (display, pixmap, gc, &ximage, 0, 0,
582                      x + g_x + face->glyph->bitmap_left,
583                      y + g_y - face->glyph->bitmap_top, 
584                      ximage.width, ximage.height);
585         }
586       sprintf (str, "0x%02X", i);
587       XDrawString (display, pixmap, gc_inv,
588                    x + (g_width - XTextWidth (font, str, 4))/ 2,
589                    y + g_height + FONT_ASCENT, str, 4);
590     }
591
592   image = XGetImage (display, pixmap, 0, 0, pix_width, pix_height,
593                      AllPlanes, XYPixmap);
594   XInitImage (image);
595   {
596     char *name = alloca (strlen (filename) + 5);
597     FILE *fp;
598
599     sprintf (name, "%s.pbm", filename);
600     printf ("Writing %s ...", name);
601     fp = fopen (name, "w");
602     fprintf (fp, "P4\n%d %d\n", image->width, image->height);
603     bytes_per_line = (image->width + 7) / 8;
604     data = image->data;
605     for (y = 0; y < image->height; y++, data += image->bytes_per_line)
606       fwrite (data, 1, bytes_per_line, fp);
607     fclose (fp);
608     printf ("done\n");
609   }
610   FT_Set_Pixel_Sizes (face, 0, (int) pixel_size);
611 }
612
613
614 void
615 GlyphProc (Widget w, XtPointer client_data, XtPointer call_data)
616 {
617   int old_glyph_index = glyph_index;
618
619   if ((int) client_data == -3 && glyph_index > 0)
620     glyph_index = 0;
621   else if ((int) client_data == -2 && glyph_index > 0)
622     glyph_index = (glyph_index - 1) & 0xF000;
623   else if ((int) client_data == -1 && glyph_index > 0)
624     glyph_index -= 0x80;
625   else if ((int) client_data == 1 && glyph_index < 0xFF80)
626     glyph_index += 0x80;
627   else if ((int) client_data == 2 && glyph_index < 0xF000)
628     glyph_index = (glyph_index + 0x1000) & 0xF000;
629   else if ((int) client_data == 3 && glyph_index < 0xF000)
630     glyph_index = 0xFF80;
631   if (glyph_index != old_glyph_index)
632     update_glyph_area ();
633 }
634
635 void
636 CharmapProc (Widget w, XtPointer client_data, XtPointer call_data)
637 {
638   if (charmap_index == (int) client_data)
639     return;
640   charmap_index = (int) client_data;
641   if (charmap_index >= 0)
642     FT_Set_Charmap (face, face->charmaps[charmap_index]);
643   update_glyph_area ();
644 }
645
646 void
647 RenderProc (Widget w, XtPointer client_data, XtPointer call_data)
648 {
649   if ((int) client_data < 0)
650     {
651       if (glyph_rec.n_glyphs > 0)
652         {
653           if ((int) client_data == -2)
654             glyph_rec.n_glyphs--;
655           else
656             glyph_rec.n_glyphs = 0;
657           update_render_area ();
658         }
659     }
660   else if (glyph_rec.n_glyphs < 64)
661     {
662       int index = glyph_index + (int) client_data;
663
664       if (charmap_index >= 0)
665         index = FT_Get_Char_Index (face, (FT_ULong) index);
666       if (bitmap[index].pixmap)
667         {
668           glyph_rec.codes[glyph_rec.n_glyphs] = glyph_index + (int) client_data;
669           glyph_rec.glyphs[glyph_rec.n_glyphs++] = index;
670           update_render_area ();
671         }
672     }
673 }
674
675 void
676 BidiProc (Widget w, XtPointer client_data, XtPointer call_data)
677 {
678   Arg arg[1];
679
680   reversed = ! reversed;
681   if (reversed)
682     XtSetArg (arg[0], XtNlabel, "L<-R");
683   else
684     XtSetArg (arg[0], XtNlabel, "L->R");
685   XtSetValues (w, arg, 1);
686   update_seq_area ();
687 }
688
689 void
690 FeatureProc (Widget w, XtPointer client_data, XtPointer call_data)
691 {
692   FeatureRec *rec = (FeatureRec *) client_data;
693   int idx, i, j;
694   Arg arg[3];
695   char *label;
696
697   if (! rec->langsys)
698     return;
699   XtSetArg (arg[0], XtNlabel, &label);
700   XtGetValues (w, arg, 1);
701   if (! strcmp (label, "all"))
702     idx = -2;
703   else if (! strcmp (label, "none"))
704     idx = -1;
705   else
706     {
707       for (idx = 0; idx < rec->langsys->FeatureCount; idx++)
708         if (rec->features[idx].w == w)
709           break;
710       if (idx == rec->langsys->FeatureCount)
711         idx = -1;
712     }
713   if (idx < 0)
714     {
715       int on = idx == -2;
716
717       for (i = j = 0; j < rec->langsys->FeatureCount; j++)
718         {
719           int index  = rec->langsys->FeatureIndex[j];
720
721           rec->features[j].tag
722             = rec->gsub_gpos->FeatureList.Feature[index].FeatureTag;
723           rec->features[j].on = on;
724         }
725     }
726   else
727     {
728       OTF_Tag tag = rec->features[idx].tag;
729
730       i = idx;
731       if (rec->features[i].on)
732         {
733           for (j = i + 1;
734                j < rec->langsys->FeatureCount && rec->features[j].on; j++)
735             rec->features[j - 1].tag = rec->features[j].tag;
736           rec->features[j - 1].tag = tag;
737           rec->features[j - 1].on = 0;
738         }
739       else
740         {
741           for (j = i + 1; i > 0 && ! rec->features[i - 1].on; i--)
742             rec->features[i].tag = rec->features[i - 1].tag;
743           rec->features[i].tag = tag;
744           rec->features[i].on = 1;
745         }
746     }
747
748   for (; i < j; i++)
749     {
750       char str[5];
751       unsigned fore = foreground, back = background;
752
753 #if 0
754       if (i == idx)
755         fore = background, back = foreground;
756 #endif
757       if (rec->features[i].on)
758         {
759           XtSetArg (arg[0], XtNforeground, back);
760           XtSetArg (arg[1], XtNbackground, fore);
761         }
762       else
763         {
764           XtSetArg (arg[0], XtNforeground, fore);
765           XtSetArg (arg[1], XtNbackground, back);
766         }
767       OTF_tag_name (rec->features[i].tag, str);
768       XtSetArg (arg[2], XtNlabel, str);
769       XtSetValues (rec->features[i].w, arg, 3);
770     }
771   update_seq_area ();
772 }
773
774 void
775 setup_feature_rec (FeatureRec *rec)
776 {
777   int i;
778   Arg arg[10];
779
780   rec->langsys = NULL;
781   for (i = 0; i < rec->gsub_gpos->ScriptList.ScriptCount; i++)
782     if (rec->gsub_gpos->ScriptList.Script[i].ScriptTag == script_tag)
783       {
784         rec->langsys = &rec->gsub_gpos->ScriptList.Script[i].DefaultLangSys;
785         break;
786       }
787
788   if (! rec->langsys)
789     i = 0;
790   else
791     {
792       XtSetArg (arg[0], XtNborderWidth, 1);
793       XtSetArg (arg[1], XtNborderColor, foreground);
794       XtSetArg (arg[2], XtNsensitive, True);
795       XtSetArg (arg[3], XtNforeground, foreground);
796       XtSetArg (arg[4], XtNbackground, background);
797       for (i = 0; i < rec->langsys->FeatureCount && i < MAX_FEATURE_COUNT; i++)
798         {
799           OTF_Feature *feature = rec->gsub_gpos->FeatureList.Feature;
800           int index = rec->langsys->FeatureIndex[i];
801           char label[5];
802
803           rec->features[i].tag = feature[index].FeatureTag;
804           rec->features[i].on = 0;
805           OTF_tag_name (rec->features[i].tag, label);
806           XtSetArg (arg[5], XtNlabel, label);
807           XtSetValues (rec->features[i].w, arg, 6);
808         }
809     }
810   XtSetArg (arg[0], XtNborderWidth, 1);
811   XtSetArg (arg[1], XtNborderColor, background);
812   XtSetArg (arg[2], XtNsensitive, False);
813   XtSetArg (arg[3], XtNforeground, foreground);
814   XtSetArg (arg[4], XtNbackground, background);
815   XtSetArg (arg[5], XtNlabel, "    ");
816   for (; i < MAX_FEATURE_COUNT; i++)
817     XtSetValues (rec->features[i].w, arg, 6);
818 }
819
820 void
821 ScriptProc (Widget w, XtPointer client_data, XtPointer call_data)
822 {
823   if (script_tag == (OTF_Tag) client_data)
824     return;
825   script_tag = (OTF_Tag) client_data;
826   setup_feature_rec (&gsub);
827   setup_feature_rec (&gpos);
828   update_seq_area ();
829 }
830
831 Widget
832 create_otf_script_widgets (Widget prev)
833 {
834   Widget w;
835   Arg arg[10];
836   int n, i;
837   OTF_Tag *scripts;
838   char script_name[5];
839
840   XtSetArg (arg[0], XtNborderWidth, 0);
841   XtSetArg (arg[1], XtNleft, XawChainLeft);
842   XtSetArg (arg[2], XtNright, XawChainLeft);
843   XtSetArg (arg[3], XtNtop, XawChainTop);
844   XtSetArg (arg[4], XtNbottom, XawChainTop);
845   XtSetArg (arg[5], XtNfromVert, prev);
846   XtSetArg (arg[6], XtNorientation, XtorientHorizontal);
847   prev = XtCreateManagedWidget ("Script", boxWidgetClass, render_area, arg, 7);
848   XtCreateManagedWidget ("script", labelWidgetClass, prev, arg, 1);
849
850   n = 0;
851   if (otf->gsub)
852     n = otf->gsub->ScriptList.ScriptCount;
853   if (otf->gpos)
854     n += otf->gpos->ScriptList.ScriptCount;
855   scripts = alloca (sizeof (OTF_Tag) * n);
856   i = 0;
857   if (otf->gsub)
858     for (; i < otf->gsub->ScriptList.ScriptCount; i++)
859       scripts[i] = otf->gsub->ScriptList.Script[i].ScriptTag;
860   n = i;
861   if (otf->gpos)
862     for (; i < otf->gpos->ScriptList.ScriptCount; i++)
863       {
864         OTF_Tag tag = otf->gpos->ScriptList.Script[i].ScriptTag;
865         int j;
866
867         for (j = 0; j < i; j++)
868           if (tag == scripts[j])
869             break;
870         if (j == i)
871           scripts[n++] = tag;
872       }
873
874   if (n == 0)
875     return prev;
876
877   script_tag = scripts[0];
878   OTF_tag_name (scripts[0], script_name);
879   if (n == 1)
880     {
881       XtSetArg (arg[0], XtNforeground, background);
882       XtSetArg (arg[1], XtNbackground, foreground);
883       XtCreateManagedWidget (script_name, labelWidgetClass, prev, arg, 2);
884     }
885   else
886     {
887       char name[5];
888
889       XtSetArg (arg[0], XtNstate, True);
890       w = XtCreateManagedWidget (script_name, toggleWidgetClass, prev, arg, 1);
891       XtAddCallback (w, XtNcallback, ScriptProc, (XtPointer) scripts[0]);
892       XtSetArg (arg[0], XtNradioGroup, w);
893       for (i = 1; i < n; i++)
894         {
895           OTF_tag_name (scripts[i], name);
896           w = XtCreateManagedWidget (name, toggleWidgetClass, prev, arg, 1);
897           XtAddCallback (w, XtNcallback, ScriptProc, (XtPointer) scripts[i]);
898         }         
899     }
900   return prev;
901 }
902
903
904 Widget
905 create_otf_widgets (Widget prev, FeatureRec *rec)
906 {
907   Arg arg[10];
908   Widget w;
909   int i;
910
911   XtSetArg (arg[0], XtNborderWidth, 0);
912   XtSetArg (arg[1], XtNleft, XawChainLeft);
913   XtSetArg (arg[2], XtNright, XawChainLeft);
914   XtSetArg (arg[3], XtNtop, XawChainTop);
915   XtSetArg (arg[4], XtNbottom, XawChainTop);
916   XtSetArg (arg[5], XtNfromVert, prev);
917   XtSetArg (arg[6], XtNorientation, XtorientHorizontal);
918   prev = XtCreateManagedWidget (rec->label, boxWidgetClass, render_area,
919                                 arg, 7);
920   XtCreateManagedWidget (rec->label, labelWidgetClass, prev, arg, 1);
921   w = XtCreateManagedWidget ("all", commandWidgetClass, prev, NULL, 0);
922   XtAddCallback (w, XtNcallback, FeatureProc, (XtPointer) rec);
923   w = XtCreateManagedWidget ("none", commandWidgetClass, prev, NULL, 0);
924   XtAddCallback (w, XtNcallback, FeatureProc, (XtPointer) rec);
925
926   for (i = 0; i < MAX_FEATURE_COUNT; i++)
927     {
928       w = XtCreateManagedWidget ("", commandWidgetClass, prev, arg, 1);
929       XtAddCallback (w, XtNcallback, FeatureProc, (XtPointer) rec);
930       rec->features[i].w = w;
931     }
932
933   setup_feature_rec (rec);
934   return prev;
935 }
936
937 void
938 create_widgets ()
939 {
940   String quit_action = "<KeyPress>q: set() notify() unset()";
941   String FIRST_action = "<KeyPress>f: set() notify() unset()\n\
942                          <KeyPress>Home: set() notify() unset()";
943   String PREV_action = "Shift<KeyPress>p: set() notify() unset()\n\
944                          <KeyPress>Up: set() notify() unset()";
945   String prev_action = "~Shift<KeyPress>p: set() notify() unset()\n\
946                          <KeyPress>Left: set() notify() unset()";
947   String next_action = "~Shift<KeyPress>n: set() notify() unset()\n\
948                          <KeyPress>Right: set() notify() unset()";
949   String NEXT_action = "Shift<KeyPress>n: set() notify() unset()\n\
950                          <KeyPress>Down: set() notify() unset()";
951   String LAST_action = "<KeyPress>l: set() notify() unset()\n\
952                          <KeyPress>End: set() notify() unset()";
953   Arg arg[10];
954   int i, j;
955   Widget prev, w;
956   String trans = "<Expose>: Expose()";
957
958   XtSetArg (arg[0], XtNtranslations, XtParseTranslationTable (trans));
959   frame = XtCreateManagedWidget ("frame", formWidgetClass, shell, arg, 1);
960
961   XtSetArg (arg[0], XtNleft, XawChainLeft);
962   XtSetArg (arg[1], XtNright, XawChainLeft);
963   XtSetArg (arg[2], XtNtop, XawChainTop);
964   XtSetArg (arg[3], XtNbottom, XawChainTop);
965   XtSetArg (arg[4], XtNborderWidth, 0);
966   XtSetArg (arg[5], XtNorientation, XtorientHorizontal);
967   command_area = XtCreateManagedWidget ("command-area", boxWidgetClass,
968                                         frame, arg, 6);
969   XtSetArg (arg[6], XtNfromVert, command_area);
970   navi_area = XtCreateManagedWidget ("navi-area", boxWidgetClass,
971                                      frame, arg, 7);
972   XtSetArg (arg[4], XtNborderWidth, 0);
973   XtSetArg (arg[5], XtNfromVert, navi_area);
974   XtSetArg (arg[6], XtNdefaultDistance, 0);
975   glyph_area = XtCreateManagedWidget ("glyph-area", formWidgetClass,
976                                       frame, arg, 7);
977   XtSetArg (arg[5], XtNfromVert, glyph_area);
978   render_area = XtCreateManagedWidget ("render-area", formWidgetClass,
979                                        frame, arg, 6);
980
981   XtSetArg (arg[0], XtNaccelerators, XtParseAcceleratorTable (quit_action));
982   quit = XtCreateManagedWidget ("Quit", commandWidgetClass,
983                                 command_area, arg, 1);
984   XtAddCallback (quit, XtNcallback, QuitProc, NULL);
985
986   dump = XtCreateManagedWidget ("Dump Image", commandWidgetClass,
987                                 command_area, arg, 1);
988   XtAddCallback (dump, XtNcallback, DumpProc, NULL);
989
990   XtSetArg (arg[0], XtNborderWidth, 0);
991   XtSetArg (arg[1], XtNwidth, 10);
992   XtCreateManagedWidget ("spacer", boxWidgetClass, command_area, arg, 2);
993
994   charmap = alloca (sizeof (Widget) * (face->num_charmaps + 1));
995   XtSetArg (arg[0], XtNstate, True);
996   charmap[0] = XtCreateManagedWidget (charmap_rec[0].name, toggleWidgetClass,
997                                       command_area, arg, 1);
998   XtAddCallback (charmap[0], XtNcallback, CharmapProc, (XtPointer) -1);
999   XtSetArg (arg[0], XtNradioGroup, charmap[0]);
1000   for (i = 0; i < face->num_charmaps; i++)
1001     {
1002       charmap[i + 1] = XtCreateManagedWidget (charmap_rec[i + 1].name,
1003                                               toggleWidgetClass,
1004                                               command_area, arg, 1);
1005       XtAddCallback (charmap[i + 1], XtNcallback, CharmapProc, (XtPointer) i);
1006     }
1007
1008   XtSetArg (arg[0], XtNlabel, " |< (f)");
1009   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (FIRST_action));
1010   FIRST = XtCreateManagedWidget ("FIRST", commandWidgetClass,
1011                                  navi_area, arg, 2);
1012   XtAddCallback (FIRST, XtNcallback, GlyphProc, (XtPointer) -3);
1013   XtSetArg (arg[0], XtNlabel, "<< (P)");
1014   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (PREV_action));
1015   PREV = XtCreateManagedWidget ("PREV", commandWidgetClass,
1016                                 navi_area, arg, 2);
1017   XtAddCallback (PREV, XtNcallback, GlyphProc, (XtPointer) -2);
1018   XtSetArg (arg[0], XtNlabel, "< (p)");
1019   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (prev_action));
1020   prev = XtCreateManagedWidget ("prev", commandWidgetClass,
1021                                 navi_area, arg, 2);
1022   XtAddCallback (prev, XtNcallback, GlyphProc, (XtPointer) -1);
1023   XtSetArg (arg[0], XtNlabel, " 0000 ");
1024   range = XtCreateManagedWidget ("range", labelWidgetClass,
1025                                  navi_area, arg, 1);
1026   XtSetArg (arg[0], XtNforeground, &foreground);
1027   XtSetArg (arg[1], XtNbackground, &background);
1028   XtGetValues (range, arg, 2);
1029
1030   XtSetArg (arg[0], XtNlabel, "> (n)");
1031   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (next_action));
1032   next = XtCreateManagedWidget ("next", commandWidgetClass,
1033                                 navi_area, arg, 2);
1034   XtAddCallback (next, XtNcallback, GlyphProc, (XtPointer) 1);
1035   XtSetArg (arg[0], XtNlabel, ">> (N)");
1036   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (NEXT_action));
1037   NEXT = XtCreateManagedWidget ("NEXT", commandWidgetClass,
1038                                 navi_area, arg, 2);
1039   XtAddCallback (NEXT, XtNcallback, GlyphProc, (XtPointer) 2);
1040   XtSetArg (arg[0], XtNlabel, ">| (l)");
1041   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (LAST_action));
1042   LAST = XtCreateManagedWidget ("LAST", commandWidgetClass,
1043                                 navi_area, arg, 2);
1044   XtAddCallback (LAST, XtNcallback, GlyphProc, (XtPointer) 3);
1045
1046   XtSetArg (arg[0], XtNleft, XawChainLeft);
1047   XtSetArg (arg[1], XtNright, XawChainLeft);
1048   XtSetArg (arg[2], XtNtop, XawChainTop);
1049   XtSetArg (arg[3], XtNbottom, XawChainTop);
1050
1051   for (i = 0; i < 8; i++)
1052     {
1053       char str[3];
1054       int n = 4;
1055       Widget head;
1056
1057       sprintf (str, "%XX", i);
1058       XtSetArg (arg[n], XtNheight, glyph_height + 5), n++;
1059       XtSetArg (arg[n], XtNlabel, str), n++;
1060       XtSetArg (arg[n], XtNborderWidth, 0), n++;
1061       if (i > 0)
1062         XtSetArg (arg[n], XtNfromVert, w), n++;
1063       head = XtCreateManagedWidget (str, labelWidgetClass, glyph_area, arg, n);
1064       index_label[i] = head;
1065       for (j = 0; j < 16; j++)
1066         {
1067           int k = i * 16 + j;
1068
1069           n = 4;
1070           if (i > 0)
1071             XtSetArg (arg[n], XtNfromVert, w), n++;
1072           if (j == 0)
1073             XtSetArg (arg[n], XtNfromHoriz, head), n++;
1074           else
1075             XtSetArg (arg[n], XtNfromHoriz, glyph[k - 1]), n++;
1076           XtSetArg (arg[n], XtNbitmap, none_pixmap), n++;
1077           glyph[k] = XtCreateManagedWidget ("glyph", commandWidgetClass,
1078                                             glyph_area, arg, n);
1079           XtAddCallback (glyph[k], XtNcallback, RenderProc, (XtPointer) k);
1080         }
1081       w = head;
1082     }
1083   /* 10 = (1 (border_width) + 4 (inner_width)) * 2 */
1084   XtSetArg(arg[4], XtNwidth, glyph_width + 10);
1085   XtSetArg (arg[5], XtNfromVert, glyph[112]);
1086   XtSetArg (arg[6], XtNfromHoriz, w);
1087   XtSetArg (arg[7], XtNborderWidth, 0);
1088
1089   for (j = 0; j < 16; j++)
1090     {
1091       char str[3];
1092
1093       sprintf (str, "X%X", j);
1094       XtSetArg (arg[8], XtNlabel, str);
1095       w = XtCreateManagedWidget ("idx", labelWidgetClass, glyph_area, arg, 9);
1096       XtSetArg (arg[6], XtNfromHoriz, w);
1097     }
1098
1099   XtSetArg (arg[0], XtNleft, XawChainLeft);
1100   XtSetArg (arg[1], XtNright, XawChainLeft);
1101   XtSetArg (arg[2], XtNtop, XawChainTop);
1102   XtSetArg (arg[3], XtNbottom, XawChainTop);
1103   XtSetArg (arg[4], XtNorientation, XtorientHorizontal);
1104   XtSetArg (arg[5], XtNborderWidth, 0);
1105   w = XtCreateManagedWidget ("clear-box", boxWidgetClass, render_area, arg, 6);
1106   clear = XtCreateManagedWidget ("clear", commandWidgetClass, w, arg, 0);
1107   XtAddCallback (clear, XtNcallback, RenderProc, (XtPointer) -1);
1108   del = XtCreateManagedWidget ("delete", commandWidgetClass, w, arg, 0);
1109   XtAddCallback (del, XtNcallback, RenderProc, (XtPointer) -2);
1110   bidi = XtCreateManagedWidget ("L->R", toggleWidgetClass, w, arg, 0);
1111   XtAddCallback (bidi, XtNcallback, BidiProc, NULL);
1112
1113   XtSetArg (arg[4], XtNorientation, XtorientHorizontal);
1114   XtSetArg (arg[5], XtNborderWidth, 0);
1115   XtSetArg (arg[6], XtNfromVert, w);
1116   raw = XtCreateManagedWidget ("raw", boxWidgetClass, render_area, arg, 7);
1117
1118   XtSetArg (arg[0], XtNborderWidth, 0);
1119   XtSetArg (arg[1], XtNlabel, "raw: ");
1120   raw_label = XtCreateManagedWidget ("raw-label", labelWidgetClass,
1121                                       raw, arg, 2);
1122   XtSetArg (arg[1], XtNbitmap, raw_pixmap);
1123   raw_image = XtCreateManagedWidget ("raw-image", labelWidgetClass,
1124                                       raw, arg, 2);
1125   w = raw;
1126   if (otf)
1127     {
1128       OTF_get_table (otf, "GSUB");
1129       OTF_get_table (otf, "GPOS");
1130       w = create_otf_script_widgets (w);
1131       if (otf->gsub)
1132         {
1133           gsub.label = "GSUB";
1134           gsub.gsub_gpos = otf->gsub;
1135           w = create_otf_widgets (w, &gsub);
1136         }
1137       if (otf->gpos)
1138         {
1139           gpos.label = "GPOS";
1140           gpos.gsub_gpos = otf->gpos;
1141           w = create_otf_widgets (w, &gpos);
1142         }
1143     }
1144
1145   XtSetArg (arg[6], XtNfromVert, w);
1146   seq = XtCreateManagedWidget ("seq", boxWidgetClass, render_area, arg, 7);
1147   XtSetArg (arg[0], XtNborderWidth, 0);
1148   XtSetArg (arg[1], XtNlabel, "seq: ");
1149   seq_label = XtCreateManagedWidget ("seq-label", labelWidgetClass,
1150                                      seq, arg, 2);
1151   XtSetArg (arg[1], XtNbitmap, seq_pixmap);
1152   seq_image = XtCreateManagedWidget ("seq-image", labelWidgetClass,
1153                                      seq, arg, 2);
1154   XtInstallAllAccelerators (shell, shell);
1155 }
1156
1157 static void
1158 ExposeProc (Widget w, XEvent *event, String *str, Cardinal *num)
1159 {
1160   XTextProperty text_prop;
1161   char *pname = "otfview";
1162   char *fname = basename (filename);
1163   char *name = alloca (strlen (fname) + 3 + strlen (pname) + 1);
1164
1165   sprintf (name, "%s - %s", pname, fname);
1166   text_prop.value = (unsigned char *) name;
1167   text_prop.encoding = XA_STRING;
1168   text_prop.format = 8;
1169   text_prop.nitems = strlen (name);
1170   XSetWMName (display, XtWindow (shell), &text_prop);
1171 }
1172
1173 /* Format MSG by FMT and print the result to the stderr, and exit.  */
1174
1175 #define FATAL_ERROR(fmt, arg)   \
1176   do {                          \
1177     fprintf (stderr, fmt, arg); \
1178     exit (1);                   \
1179   } while (0)
1180
1181 static int
1182 x_error_handler (Display *display, XErrorEvent *error)
1183 {
1184   return 0;
1185 }
1186
1187
1188 int
1189 main (int argc, char **argv)
1190 {
1191   XtActionsRec actions[] = { {"Expose", ExposeProc} };
1192   Arg arg[10];
1193
1194   FT_Library library;
1195   OTF_GlyphString gstring;
1196   OTF_Glyph *g;
1197
1198   int err;
1199   int i;
1200   int fixed_pixel_size = 0;
1201   int display_width;
1202
1203   pixel_size = DEFAULT_PIXEL_SIZE;
1204   {
1205     char *str = getenv ("PIXEL_SIZE");
1206
1207     if (str && (i = atoi (str)) > 0)
1208       {
1209         pixel_size = i;
1210         fixed_pixel_size = 1;
1211       }
1212   }
1213
1214   gstring.size = gstring.used = 256;
1215   g = calloc (256, sizeof (OTF_Glyph));
1216   gstring.glyphs = g;
1217
1218   shell = XtOpenApplication (&context, "OTFView", NULL, 0, &argc, argv, NULL,
1219                              shellWidgetClass, arg, 0);
1220   display = XtDisplay (shell);
1221   /*XSynchronize (display, True);*/
1222   XSetErrorHandler (x_error_handler);
1223   display_width = DisplayWidth (display,
1224                                 XScreenNumberOfScreen (XtScreen (shell)));
1225   font = XLoadQueryFont (display, DEFAULT_FONT_NAME);
1226   if (! font)
1227     font = XLoadQueryFont (display, "fixed");
1228
1229   if (argc != 2 || !strcmp (argv[1], "-h") || !strcmp (argv[1], "--help"))
1230     {
1231       fprintf (stderr, "Usage: %s [ X-OPTION ... ]  OTF-FILE\n",
1232                basename (argv[0]));
1233       exit (argc != 2);
1234     }
1235   filename = argv[1];
1236   if (strstr (filename, ".ttf")
1237       || strstr (filename, ".TTF")
1238       || strstr (filename, ".otf")
1239       || strstr (filename, ".OTF"))
1240     {
1241       otf = OTF_open (filename);
1242       if (! otf
1243           || OTF_get_table (otf, "head") < 0
1244           || OTF_get_table (otf, "cmap") < 0
1245           || (OTF_check_table (otf, "GSUB") < 0
1246               && OTF_check_table (otf, "GPOS") < 0))
1247         otf = NULL;
1248     }
1249
1250   if ((err = FT_Init_FreeType (&library)))
1251     FATAL_ERROR ("%s\n", "FT_Init_FreeType: error");
1252   err = FT_New_Face (library, filename, 0, &face);
1253   if (err == FT_Err_Unknown_File_Format)
1254     FATAL_ERROR ("%s\n", "FT_New_Face: unknown file format");
1255   else if (err)
1256     FATAL_ERROR ("%s\n", "FT_New_Face: unknown error");
1257   if ((err = FT_Set_Pixel_Sizes (face, 0, pixel_size)))
1258     FATAL_ERROR ("%s\n", "FT_Set_Pixel_Sizes: error");
1259
1260   {
1261     char title[256];
1262     Arg arg[1];
1263
1264     filename = basename (filename);
1265     sprintf (title, "%s family:%s style:%s",
1266              filename, face->family_name, face->style_name);
1267     XtSetArg (arg[0], XtNtitle, title);
1268     XtSetValues (shell, arg, 1);
1269   }
1270
1271   glyph_width = ((face->bbox.xMax - face->bbox.xMin)
1272                  * pixel_size / face->units_per_EM);
1273   if (! fixed_pixel_size && glyph_width * 16 > display_width * 0.8)
1274     {
1275       pixel_size = (pixel_size * display_width * 0.8 / 16 / glyph_width);
1276       FT_Set_Pixel_Sizes (face, 0, pixel_size);
1277       glyph_width = ((face->bbox.xMax - face->bbox.xMin)
1278                      * pixel_size / face->units_per_EM);
1279     }
1280   if (glyph_width < FONT_WIDTH * 4)
1281     glyph_width = FONT_WIDTH * 4;
1282
1283   glyph_height = ((face->bbox.yMax - face->bbox.yMin)
1284                   *  pixel_size / face->units_per_EM);
1285
1286   glyph_x = - (face->bbox.xMin * pixel_size / face->units_per_EM);
1287   glyph_y = face->bbox.yMax * pixel_size / face->units_per_EM;
1288   none_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
1289                                glyph_width, glyph_height, 1);
1290
1291   {
1292     unsigned long valuemask =  GCFunction | GCLineWidth;
1293     XGCValues values;
1294
1295     gc = XCreateGC (display, none_pixmap, (unsigned long) 0, NULL);
1296     values.function = GXset;
1297     values.line_width = 1;
1298     gc_set = XCreateGC (display, none_pixmap, valuemask, &values);
1299     values.function = GXor;
1300     gc_or = XCreateGC (display, none_pixmap, valuemask, &values);
1301     values.function = GXcopyInverted;
1302     gc_inv = XCreateGC (display, none_pixmap, valuemask, &values);
1303   }
1304
1305   XFillRectangle (display, none_pixmap, gc, 0, 0,
1306                   glyph_width, glyph_height);
1307   XDrawString (display, none_pixmap, gc_inv,
1308                (glyph_width - XTextWidth (font, "none", 4)) / 2,
1309                glyph_height / 2, "none", 4);
1310
1311   render_width = (glyph_width + 4) * 15 + 1;
1312   render_height = glyph_height + 2;
1313
1314   charmap_rec[0].platform_id = -1;
1315   charmap_rec[0].encoding_id = -1;
1316   strcpy (charmap_rec[0].name, "no charmap");
1317
1318   for (i = 0; i < face->num_charmaps; i++)
1319     {
1320       charmap_rec[i + 1].platform_id = face->charmaps[i]->platform_id;
1321       charmap_rec[i + 1].encoding_id = face->charmaps[i]->encoding_id;
1322       sprintf (charmap_rec[i + 1].name, "%d-%d",
1323                charmap_rec[i + 1].platform_id, charmap_rec[i + 1].encoding_id);
1324       if (face->charmaps[i]->platform_id == 0
1325           || (face->charmaps[i]->platform_id == 3
1326               && face->charmaps[i]->encoding_id == 1))
1327         strcat (charmap_rec[i + 1].name, " (unicode)");
1328       else if (face->charmaps[i]->platform_id == 1
1329                && face->charmaps[i]->encoding_id == 0)
1330         strcat (charmap_rec[i + 1].name, " (apple-roman)");
1331     }
1332
1333   raw_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
1334                               render_width, render_height, 1);
1335   seq_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
1336                               render_width, render_height, 1);
1337
1338   memset (bitmap, 0, sizeof (bitmap));
1339   create_widgets ();
1340   glyph_index = 0;
1341   charmap_index = -1;
1342   update_glyph_area ();
1343   update_render_area ();
1344
1345   XtAppAddActions (context, actions, XtNumber (actions));
1346   XtRealizeWidget (shell);
1347   XtAppMainLoop (context);
1348
1349   exit (0);
1350 }