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