Big change to implement driving OpenType
[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/Intrinsic.h>
32 #include <X11/StringDefs.h>
33 #include <X11/Shell.h>
34 #include <X11/Xaw/Command.h>
35 #include <X11/Xaw/Toggle.h>
36 #include <X11/Xaw/Box.h>
37 #include <X11/Xaw/Form.h>
38 #include <X11/Xaw/Viewport.h>
39
40 #include <ft2build.h>
41 #include FT_FREETYPE_H
42
43 #include <otf.h>
44
45 #define DEFAULT_PIXEL_SIZE 30
46 #define DEFAULT_FONT_NAME "6x13"
47 XFontStruct *font;
48 #define FONT_HEIGHT (font->ascent + font->descent)
49 #define FONT_ASCENT (font->ascent)
50 #define FONT_DESCENT (font->descent)
51 #define FONT_WIDTH (font->max_bounds.width)
52
53 XtAppContext context;
54 /* Widget structure.
55    +--- frame (form) -------------------------+
56    | +--- command_area (box) ---------------+ |
57    | | quit dump charmap ...                | |
58    | +--------------------------------------+ |
59    | +---- navi_area (box) -----------------+ |
60    | | FIRST PREV prev range next NEXT LAST | |
61    | +--------------------------------------+ |
62    | +--- glyph_area (form) ----------------+ |
63    | | idxh[0] glyph[0]    ...    glyph[15] | |
64    | |   ...                        ...     | |
65    | | idxh[7] glyph[112]  ...    glyph[127]| |
66    | |         idxl[0]     ...    idxl[15]  | |
67    | +--------------------------------------+ |
68    | +--- render_area (form) ---------------+ |
69    | | clear del                            | |
70    | | +--- raw (box) --------------------+ | |
71    | | | raw_label raw_image              | | |
72    | | +----------------------------------+ | |
73    | | GSUB all none features...            | |
74    | | GPOS all none features...            | |
75    | | +--- seq (box) --------------------+ | |
76    | | | seq_label seq_image              | | |
77    | | +----------------------------------+ | |
78    | +--------------------------------------+ |
79    +------------------------------------------+ */
80 Widget shell, frame;
81 Widget command_area, quit, dump, *charmap;
82 Widget navi_area, FIRST, PREV, prev, range, next, NEXT, LAST;
83 Widget glyph_area, glyph[128], index_label[8];
84 Widget render_area, clear, del, raw, seq;
85 Widget raw_label, raw_image, seq_label, seq_image;
86 unsigned long foreground, background;
87
88 typedef struct
89 {
90   int idx;
91   int on;
92   Widget w;
93 } FeatureRec;
94
95 FeatureRec gsub[64], gpos[64];
96
97 int glyph_char[128];
98
99 Display *display;
100 GC gc, gc_set, gc_or, gc_inv;
101
102 typedef struct {
103   Pixmap pixmap;
104   unsigned width, height;
105   int x, y;
106   int advance;
107 } BitmapRec;
108
109 BitmapRec bitmap[0x10000];
110
111 int render_width, render_height;
112 Pixmap raw_pixmap, seq_pixmap, gsub_pixmap, gpos_pixmap;
113 Pixmap none_pixmap;
114
115 FT_Face face;
116
117 struct {
118   int platform_id;
119   int encoding_id;
120   char name[20];
121 } charmap_rec[10];
122
123 int charmap_index;
124
125 unsigned glyph_width, glyph_height;
126 int glyph_x, glyph_y;
127 int glyph_index;
128
129 struct {
130   int n_glyphs;
131   int glyphs[64];
132   int codes[64];
133 } glyph_rec;
134
135 OTF *otf;
136 char *filename;
137
138 void
139 create_pixmap (int pixel_size, int index)
140 {
141   int err = FT_Load_Glyph (face, index, FT_LOAD_RENDER | FT_LOAD_MONOCHROME);
142   XImage ximage;
143   Pixmap pixmap;
144   
145   if (err)
146     {
147       bitmap[index].pixmap = (Pixmap) 0;
148       return;
149     }
150   ximage.height = face->glyph->bitmap.rows;
151   ximage.width = face->glyph->bitmap.width;
152   ximage.depth = 1;
153   ximage.bits_per_pixel = 1;
154   ximage.xoffset = 0;
155   ximage.format = XYPixmap;
156   ximage.data = (char *) face->glyph->bitmap.buffer;
157   ximage.byte_order = MSBFirst;
158   ximage.bitmap_unit = 8;
159   ximage.bitmap_bit_order = MSBFirst;
160   ximage.bitmap_pad = 8;
161   ximage.bytes_per_line = face->glyph->bitmap.pitch;
162   XInitImage (&ximage);
163   pixmap = XCreatePixmap (display, DefaultRootWindow (display),
164                           glyph_width, glyph_height, 1);
165   XFillRectangle (display, pixmap, gc, 0, 0, glyph_width, glyph_height);
166   XPutImage (display, pixmap, gc, &ximage, 0, 0,
167              glyph_x + face->glyph->bitmap_left,
168              glyph_y - face->glyph->bitmap_top,
169              ximage.width, ximage.height);
170   bitmap[index].pixmap = pixmap;
171   bitmap[index].width = ximage.width;
172   bitmap[index].height = ximage.height;
173   bitmap[index].x = face->glyph->bitmap_left;
174   bitmap[index].y = - face->glyph->bitmap_top;
175   bitmap[index].advance = face->glyph->metrics.horiAdvance >> 6;
176 }
177
178 void
179 update_glyph_area ()
180 {
181   int i;
182   Arg arg[2];
183   char buf[16];
184   int msb;
185
186   for (i = 0; i < 128; i++)
187     {
188       int index = glyph_index + i;
189
190       if (charmap_index >= 0)
191         index = FT_Get_Char_Index (face, (FT_ULong) index);
192       if (bitmap[index].pixmap)
193         XtSetArg (arg[0], XtNbitmap, bitmap[index].pixmap);
194       else
195         XtSetArg (arg[0], XtNbitmap, none_pixmap);
196       XtSetValues (glyph[i], arg, 1);
197     }
198
199   msb = (glyph_index >> 7) % 2 ? 8 : 0;
200   for (i = 0; i < 8; i++)
201     {
202       char str[3];
203
204       sprintf (str, "%XX", i | msb );
205       XtSetArg (arg[0], XtNheight, glyph_height + 5);
206       XtSetArg (arg[1], XtNlabel, str);
207       XtSetValues (index_label[i], arg, 2);
208     }
209
210   sprintf (buf, " %04X-%04X ", glyph_index, glyph_index + 0x7F);
211   XtSetArg (arg[0], XtNlabel, buf);
212   XtSetValues (range, arg, 1);
213 }
214
215 char *
216 get_features (OTF_FeatureList *list, FeatureRec *features)
217 {
218   int len = list->FeatureCount;
219   int i, n;
220   char *str, *p;
221
222   if (! features[0].on)
223     return NULL;
224   for (i = n = 0; i < len; i++)
225     {
226       if (features[i].on)
227         n++;
228       else
229         break;
230     }
231   if (i == len)
232     {
233       str = malloc (2);
234       strcpy (str, "*");
235       return str;
236     }
237   str = malloc (n * 5);
238   for (i = 0, p = str; i < len ; i++, p += 5)
239     if (features[i].on)
240       {
241         OTF_tag_name (list->Feature[features[i].idx].FeatureTag, p);
242         p[4] = ',';
243       }
244   p[-1] = '\0';
245   return str;
246 }
247
248
249 void
250 update_seq_area ()
251 {
252   int i, x;
253   OTF_GlyphString gstring;
254   int len = glyph_rec.n_glyphs;
255   Arg arg[1];
256
257   gstring.size = gstring.used = len;
258   gstring.glyphs = alloca (sizeof (OTF_Glyph) * len);
259   memset (gstring.glyphs, 0, sizeof (OTF_Glyph) * len);
260   for (i = 0; i < len; i++)
261     gstring.glyphs[i].c = gstring.glyphs[i].glyph_id = glyph_rec.glyphs[i];
262
263   XFillRectangle (display, seq_pixmap, gc, 0, 0, render_width, render_height);
264   if (otf)
265     {
266       char *str;
267
268       if (otf->gsub)
269         {
270           str = get_features (&otf->gsub->FeatureList, gsub);
271           if (str)
272             {
273               OTF_drive_gsub (otf, &gstring, NULL, NULL, str);
274               free (str);
275             }
276         }
277       if (otf->gpos)
278         {
279           str = get_features (&otf->gpos->FeatureList, gpos);
280           if (str)
281             {
282               OTF_drive_gpos (otf, &gstring, NULL, NULL, str);
283               free (str);
284             }
285         }
286     }
287
288   for (i = 0, x = glyph_x; i < gstring.used; i++)
289     {
290       BitmapRec *bmp = bitmap + gstring.glyphs[i].glyph_id;
291
292       XCopyArea (display, bmp->pixmap, seq_pixmap, gc_or,
293                  glyph_x + bmp->x, glyph_y + bmp->y, bmp->width, bmp->height,
294                  x + bmp->x, glyph_y + bmp->y);
295       x += bmp->advance;
296     }
297   XtSetArg (arg[0], XtNbitmap, seq_pixmap);
298   XtSetValues (seq_image, arg, 1);
299 }
300
301
302 void
303 update_render_area ()
304 {
305   int i;
306   int x;
307   Arg arg[1];
308
309   XFillRectangle (display, raw_pixmap, gc, 0, 0, render_width, render_height);
310   for (i = 0, x = glyph_x; i < glyph_rec.n_glyphs; i++)
311     {
312       BitmapRec *bmp = bitmap + glyph_rec.glyphs[i];
313       char buf[5];
314
315       XCopyArea (display, bmp->pixmap, raw_pixmap, gc,
316                  0, 0, glyph_width, glyph_height,
317                  (glyph_width + 4) * i + 1, 1);
318       XDrawRectangle (display, raw_pixmap, gc_set,
319                       (glyph_width + 4) * i, 0,
320                       glyph_width + 1, glyph_height + 1);
321       XDrawLine (display, raw_pixmap, gc_set,
322                  (glyph_width + 4) * i + 1 + glyph_x, 1,
323                  (glyph_width + 4) * i + 1 + glyph_x, glyph_height + 1);
324       XDrawLine (display, raw_pixmap, gc_set,
325                  (glyph_width + 4) * i + 1 + glyph_x + bmp->advance, 1,
326                  (glyph_width + 4) * i + 1 + glyph_x + bmp->advance,
327                  glyph_height + 1);
328
329       sprintf (buf, "%04X", glyph_rec.codes[i]);
330       XDrawString (display, raw_pixmap, gc_inv, 
331                    (glyph_width + 1) * i + 1
332                    + (glyph_width - XTextWidth (font, buf, 4)) / 2,
333                    glyph_height + 2 + FONT_HEIGHT, buf, 4);
334     }
335   XtSetArg (arg[0], XtNbitmap, raw_pixmap);
336   XtSetValues (raw_image, arg, 1);
337   update_seq_area ();
338 }
339
340 void
341 QuitProc (Widget w, XtPointer client_data, XtPointer call_data)
342 {
343   XtAppSetExitFlag (XtWidgetToApplicationContext (w));
344 }
345
346 void
347 DumpProc (Widget w, XtPointer client_data, XtPointer pixel_size)
348 {
349   int g_width, g_height, g_x, g_y, pix_width, pix_height;
350   int margin = 20 * 300 / 25.4;
351   int a4_width = 210 * 300 / 25.4 - margin * 2;
352   int a4_height = 297 * 300 / 25.4 - margin * 2;
353   int size = 100;
354   Pixmap pixmap;
355   XImage ximage, *image;
356   int i, x, y;
357   char *data;
358   int bytes_per_line;
359
360   FT_Set_Pixel_Sizes (face, 0, size);
361   g_width = ((face->bbox.xMax - face->bbox.xMin) * size / face->units_per_EM);
362   g_height = ((face->bbox.yMax - face->bbox.yMin) * size / face->units_per_EM);
363   pix_width = (g_width + 1) * 16 + margin + 1;
364   pix_height = (g_height + 1 + FONT_HEIGHT) * 16 + margin + 1;
365   while (pix_width > a4_width || pix_height > a4_height)
366     {
367       size--;
368       FT_Set_Pixel_Sizes (face, 0, size);
369       g_width = ((face->bbox.xMax - face->bbox.xMin)
370                  * size / face->units_per_EM);
371       g_height = ((face->bbox.yMax - face->bbox.yMin)
372                   * size / face->units_per_EM);
373       pix_width = (g_width + 1) * 16 + margin + 1;
374       pix_height = (g_height + 1 + FONT_HEIGHT) * 16 + margin + 1;
375     }
376
377   g_x = - (face->bbox.xMin * size / face->units_per_EM);
378   g_y = face->bbox.yMax * size / face->units_per_EM;
379   for (i = 0; i < 0xFF; i++)
380     {
381       int idx;
382
383       if (charmap_index >= 0)
384         idx = FT_Get_Char_Index (face, (FT_ULong) i);
385       else
386         idx = i;
387       if (FT_Load_Glyph (face, idx, FT_LOAD_RENDER | FT_LOAD_MONOCHROME) == 0)
388         {
389           if (g_x < - face->glyph->bitmap_left)
390             g_x = - face->glyph->bitmap_left;
391           if (g_y < face->glyph->bitmap_top)
392             g_y = face->glyph->bitmap_top;
393           if (g_width
394               < g_x + face->glyph->bitmap_left + face->glyph->bitmap.width)
395             g_width
396               = g_x + face->glyph->bitmap_left + face->glyph->bitmap.width;
397           if (g_height
398               < g_y - face->glyph->bitmap_top + face->glyph->bitmap.rows)
399             g_height
400               = g_y - face->glyph->bitmap_top + face->glyph->bitmap.rows;
401         }
402     }
403   pix_width = (g_width + 1) * 16 + margin + 1;
404   pix_height = (g_height + FONT_HEIGHT + 1) * 16 + margin + 1;
405   pixmap = XCreatePixmap (display,
406                           RootWindow (display, DefaultScreen (display)),
407                           pix_width, pix_height, 1);
408   XFillRectangle (display, pixmap, gc, 0, 0, pix_width, pix_height);
409
410   for (i = 0, x = margin; i <= 16; i++, x += g_width + 1)
411     XDrawLine (display, pixmap, gc_set, x, margin,
412                x, margin + (g_height + FONT_HEIGHT + 1) * 16);
413   for (i = 0, y = margin; i <= 16; i++, y += g_height + FONT_HEIGHT + 1)
414     XDrawLine (display, pixmap, gc_set, margin, y,
415                margin + (g_width + 1) * 16, y);
416   for (i = 0; i < 256; i++)
417     {
418       char str[5];
419       int idx;
420
421       if (charmap_index >= 0)
422         idx = FT_Get_Char_Index (face, (FT_ULong) i);
423       else
424         idx = i;
425       x = margin + (g_width + 1) * (i % 16);
426       y = margin + (g_height + FONT_HEIGHT + 1) * (i / 16);
427       if (FT_Load_Glyph (face, idx, FT_LOAD_RENDER | FT_LOAD_MONOCHROME) == 0)
428         {
429           ximage.height = face->glyph->bitmap.rows;
430           ximage.width = face->glyph->bitmap.width;
431           ximage.depth = 1;
432           ximage.bits_per_pixel = 1;
433           ximage.xoffset = 0;
434           ximage.format = XYPixmap;
435           ximage.data = (char *) face->glyph->bitmap.buffer;
436           ximage.byte_order = MSBFirst;
437           ximage.bitmap_unit = 8;
438           ximage.bitmap_bit_order = MSBFirst;
439           ximage.bitmap_pad = 8;
440           ximage.bytes_per_line = face->glyph->bitmap.pitch;
441           XInitImage (&ximage);
442           XPutImage (display, pixmap, gc, &ximage, 0, 0,
443                      x + g_x + face->glyph->bitmap_left,
444                      y + g_y - face->glyph->bitmap_top, 
445                      ximage.width, ximage.height);
446         }
447       sprintf (str, "0x%02X", i);
448       XDrawString (display, pixmap, gc_inv,
449                    x + (g_width - XTextWidth (font, str, 4))/ 2,
450                    y + g_height + FONT_ASCENT, str, 4);
451     }
452
453   image = XGetImage (display, pixmap, 0, 0, pix_width, pix_height,
454                      AllPlanes, XYPixmap);
455   XInitImage (image);
456   {
457     char *name = alloca (strlen (filename) + 5);
458     FILE *fp;
459
460     sprintf (name, "%s.pbm", filename);
461     printf ("Writing %s ...", name);
462     fp = fopen (name, "w");
463     fprintf (fp, "P4\n%d %d\n", image->width, image->height);
464     bytes_per_line = (image->width + 7) / 8;
465     data = image->data;
466     for (y = 0; y < image->height; y++, data += image->bytes_per_line)
467       fwrite (data, 1, bytes_per_line, fp);
468     fclose (fp);
469     printf ("done\n");
470   }
471   FT_Set_Pixel_Sizes (face, 0, (int) pixel_size);
472 }
473
474
475 void
476 GlyphProc (Widget w, XtPointer client_data, XtPointer call_data)
477 {
478   int old_glyph_index = glyph_index;
479
480   if ((int) client_data == -3 && glyph_index > 0)
481     glyph_index = 0;
482   else if ((int) client_data == -2 && glyph_index > 0)
483     glyph_index = (glyph_index - 1) & 0xF000;
484   else if ((int) client_data == -1 && glyph_index > 0)
485     glyph_index -= 0x80;
486   else if ((int) client_data == 1 && glyph_index < 0xFF80)
487     glyph_index += 0x80;
488   else if ((int) client_data == 2 && glyph_index < 0xF000)
489     glyph_index = (glyph_index + 0x1000) & 0xF000;
490   else if ((int) client_data == 3 && glyph_index < 0xF000)
491     glyph_index = 0xFF80;
492   if (glyph_index != old_glyph_index)
493     update_glyph_area ();
494 }
495
496 void
497 CharmapProc (Widget w, XtPointer client_data, XtPointer call_data)
498 {
499   if (charmap_index == (int) client_data)
500     return;
501   charmap_index = (int) client_data;
502   if (charmap_index >= 0)
503     FT_Set_Charmap (face, face->charmaps[charmap_index]);
504   update_glyph_area ();
505 }
506
507 void
508 RenderProc (Widget w, XtPointer client_data, XtPointer call_data)
509 {
510   if ((int) client_data < 0)
511     {
512       if (glyph_rec.n_glyphs > 0)
513         {
514           if ((int) client_data == -2)
515             glyph_rec.n_glyphs--;
516           else
517             glyph_rec.n_glyphs = 0;
518           update_render_area ();
519         }
520     }
521   else if (glyph_rec.n_glyphs < 64)
522     {
523       int index = glyph_index + (int) client_data;
524
525       if (charmap_index >= 0)
526         index = FT_Get_Char_Index (face, (FT_ULong) index);
527       if (bitmap[index].pixmap)
528         {
529           glyph_rec.codes[glyph_rec.n_glyphs] = glyph_index + (int) client_data;
530           glyph_rec.glyphs[glyph_rec.n_glyphs++] = index;
531           update_render_area ();
532         }
533     }
534 }
535
536 void
537 FeatureProc (OTF_FeatureList *list, FeatureRec *features, int idx)
538 {
539   int i, j;
540   Arg arg[3];
541
542   if (idx < 0)
543     {
544       int on = idx == -2;
545
546       for (idx = 0; idx < list->FeatureCount; idx++)
547         {
548           features[idx].idx = idx;
549           features[idx].on = on;
550         }
551       i = 0, j = list->FeatureCount - 1;
552     }
553   else
554     {
555       i = idx;
556       idx = features[i].idx;
557
558       if (features[i].on)
559         {
560           for (j = i; j + 1 < list->FeatureCount && features[j + 1].on; j++)
561             features[j].idx = features[j + 1].idx;
562           features[j].idx = idx;
563           features[j].on = 0;
564         }
565       else
566         {
567           for (j = i; i > 0 && ! features[i - 1].on; i--)
568             features[i].idx = features[i - 1].idx;
569           features[i].idx = idx;
570           features[i].on = 1;
571         }
572     }
573
574   for (; i <= j; i++)
575     {
576       char str[5];
577
578       OTF_tag_name (list->Feature[features[i].idx].FeatureTag, str);
579       XtSetArg (arg[0], XtNlabel, str);
580       if (features[i].on)
581         {
582           XtSetArg (arg[1], XtNforeground, background);
583           XtSetArg (arg[2], XtNbackground, foreground);
584         }
585       else
586         {
587           XtSetArg (arg[1], XtNforeground, foreground);
588           XtSetArg (arg[2], XtNbackground, background);
589         }
590       XtSetValues (features[i].w, arg, 3);
591     }
592   update_seq_area ();
593 }
594
595 void
596 GsubProc (Widget w, XtPointer client_data, XtPointer call_data)
597 {
598   FeatureProc (&otf->gsub->FeatureList, gsub, (int) client_data);
599 }              
600
601 void
602 GposProc (Widget w, XtPointer client_data, XtPointer call_data)
603 {
604   FeatureProc (&otf->gpos->FeatureList, gpos, (int) client_data);
605 }
606
607 Widget
608 create_otf_widgets (int flag, Widget prev)
609 {
610   char *label;
611   OTF_FeatureList *list;
612   XtCallbackProc proc;
613   FeatureRec *features;
614   Arg arg[10];
615   Widget w;
616   char *box_label, feature_label[5];
617   int i;
618
619   if (flag)
620     {
621       label = "GSUB";
622       list = &otf->gsub->FeatureList;
623       proc = GsubProc;
624       features = gsub;
625     }
626   else
627     {
628       label = "GPOS";
629       list = &otf->gpos->FeatureList;
630       proc = GposProc;
631       features = gpos;
632     }
633   box_label = alloca (strlen (label) + 4);
634
635   strcpy (box_label, label);
636   strcat (box_label, "box");
637
638   XtSetArg (arg[0], XtNborderWidth, 0);
639   XtSetArg (arg[1], XtNleft, XawChainLeft);
640   XtSetArg (arg[2], XtNright, XawChainLeft);
641   XtSetArg (arg[3], XtNtop, XawChainTop);
642   XtSetArg (arg[4], XtNbottom, XawChainTop);
643   XtSetArg (arg[5], XtNfromVert, prev);
644   XtSetArg (arg[6], XtNorientation, XtorientHorizontal);
645   prev = XtCreateManagedWidget (box_label, boxWidgetClass, render_area, arg, 7);
646   XtCreateManagedWidget (label, labelWidgetClass, prev, arg, 1);
647   w = XtCreateManagedWidget ("all", commandWidgetClass, prev, NULL, 0);
648   XtAddCallback (w, XtNcallback, proc, (XtPointer) -2);
649   w = XtCreateManagedWidget ("none", commandWidgetClass, prev, NULL, 0);
650   XtAddCallback (w, XtNcallback, proc, (XtPointer) -1);
651   for (i = 0; i < list->FeatureCount; i++)
652     {
653       OTF_tag_name (list->Feature[i].FeatureTag, feature_label);
654       features[i].idx = i;
655       w = XtCreateManagedWidget (feature_label, commandWidgetClass, prev,
656                                  NULL, 0);
657       XtAddCallback (w, XtNcallback, proc, (XtPointer) i);
658       features[i].w = w;
659     }
660
661   return prev;
662 }
663
664 void
665 create_widgets (int pixel_size)
666 {
667   String quit_action = "<KeyPress>q: set() notify() unset()";
668   String FIRST_action = "~Shift<KeyPress>f: set() notify() unset()";
669   String PREV_action = "Shift<KeyPress>p: set() notify() unset()";
670   String prev_action = "~Shift<KeyPress>p: set() notify() unset()";
671   String next_action = "~Shift<KeyPress>n: set() notify() unset()";
672   String NEXT_action = "Shift<KeyPress>n: set() notify() unset()";
673   String LAST_action = "~Shift<KeyPress>l: set() notify() unset()";
674   Arg arg[10];
675   int i, j;
676   Widget prev, w;
677
678   frame = XtCreateManagedWidget ("frame", formWidgetClass, shell, NULL, 0);
679   XtSetArg (arg[0], XtNleft, XawChainLeft);
680   XtSetArg (arg[1], XtNright, XawChainLeft);
681   XtSetArg (arg[2], XtNtop, XawChainTop);
682   XtSetArg (arg[3], XtNbottom, XawChainTop);
683   XtSetArg (arg[4], XtNborderWidth, 0);
684   XtSetArg (arg[5], XtNorientation, XtorientHorizontal);
685   command_area = XtCreateManagedWidget ("command-area", boxWidgetClass,
686                                         frame, arg, 6);
687   XtSetArg (arg[6], XtNfromVert, command_area);
688   navi_area = XtCreateManagedWidget ("navi-area", boxWidgetClass,
689                                      frame, arg, 7);
690   XtSetArg (arg[4], XtNborderWidth, 0);
691   XtSetArg (arg[5], XtNfromVert, navi_area);
692   XtSetArg (arg[6], XtNdefaultDistance, 0);
693   glyph_area = XtCreateManagedWidget ("glyph-area", formWidgetClass,
694                                       frame, arg, 7);
695   XtSetArg (arg[5], XtNfromVert, glyph_area);
696   render_area = XtCreateManagedWidget ("render-area", formWidgetClass,
697                                        frame, arg, 6);
698
699   XtSetArg (arg[0], XtNaccelerators, XtParseAcceleratorTable (quit_action));
700   quit = XtCreateManagedWidget ("Quit", commandWidgetClass,
701                                 command_area, arg, 1);
702   XtAddCallback (quit, XtNcallback, QuitProc, NULL);
703
704   dump = XtCreateManagedWidget ("Dump Image", commandWidgetClass,
705                                 command_area, arg, 1);
706   XtAddCallback (dump, XtNcallback, DumpProc, (XtPointer) pixel_size);
707
708   charmap = alloca (sizeof (Widget) * (face->num_charmaps + 1));
709   XtSetArg (arg[0], XtNstate, True);
710   charmap[0] = XtCreateManagedWidget (charmap_rec[0].name, toggleWidgetClass,
711                                       command_area, arg, 1);
712   XtAddCallback (charmap[0], XtNcallback, CharmapProc, (XtPointer) -1);
713   XtSetArg (arg[0], XtNradioGroup, charmap[0]);
714   for (i = 0; i < face->num_charmaps; i++)
715     {
716       charmap[i + 1] = XtCreateManagedWidget (charmap_rec[i + 1].name,
717                                               toggleWidgetClass,
718                                               command_area, arg, 1);
719       XtAddCallback (charmap[i + 1], XtNcallback, CharmapProc, (XtPointer) i);
720     }
721
722   XtSetArg (arg[0], XtNlabel, " |< (f)");
723   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (FIRST_action));
724   FIRST = XtCreateManagedWidget ("FIRST", commandWidgetClass,
725                                  navi_area, arg, 2);
726   XtAddCallback (FIRST, XtNcallback, GlyphProc, (XtPointer) -3);
727   XtSetArg (arg[0], XtNlabel, "<< (P)");
728   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (PREV_action));
729   PREV = XtCreateManagedWidget ("PREV", commandWidgetClass,
730                                 navi_area, arg, 2);
731   XtAddCallback (PREV, XtNcallback, GlyphProc, (XtPointer) -2);
732   XtSetArg (arg[0], XtNlabel, "< (p)");
733   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (prev_action));
734   prev = XtCreateManagedWidget ("prev", commandWidgetClass,
735                                 navi_area, arg, 2);
736   XtAddCallback (prev, XtNcallback, GlyphProc, (XtPointer) -1);
737   XtSetArg (arg[0], XtNlabel, " 0000 ");
738   range = XtCreateManagedWidget ("range", labelWidgetClass,
739                                  navi_area, arg, 1);
740   XtSetArg (arg[0], XtNforeground, &foreground);
741   XtSetArg (arg[1], XtNbackground, &background);
742   XtGetValues (range, arg, 2);
743
744   XtSetArg (arg[0], XtNlabel, "> (n)");
745   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (next_action));
746   next = XtCreateManagedWidget ("next", commandWidgetClass,
747                                 navi_area, arg, 2);
748   XtAddCallback (next, XtNcallback, GlyphProc, (XtPointer) 1);
749   XtSetArg (arg[0], XtNlabel, ">> (N)");
750   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (NEXT_action));
751   NEXT = XtCreateManagedWidget ("NEXT", commandWidgetClass,
752                                 navi_area, arg, 2);
753   XtAddCallback (NEXT, XtNcallback, GlyphProc, (XtPointer) 2);
754   XtSetArg (arg[0], XtNlabel, ">| (l)");
755   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (LAST_action));
756   LAST = XtCreateManagedWidget ("LAST", commandWidgetClass,
757                                 navi_area, arg, 2);
758   XtAddCallback (LAST, XtNcallback, GlyphProc, (XtPointer) 3);
759
760   XtSetArg (arg[0], XtNleft, XawChainLeft);
761   XtSetArg (arg[1], XtNright, XawChainLeft);
762   XtSetArg (arg[2], XtNtop, XawChainTop);
763   XtSetArg (arg[3], XtNbottom, XawChainTop);
764
765   for (i = 0; i < 8; i++)
766     {
767       char str[3];
768       int n = 4;
769       Widget head;
770
771       sprintf (str, "%XX", i);
772       XtSetArg (arg[n], XtNheight, glyph_height + 5), n++;
773       XtSetArg (arg[n], XtNlabel, str), n++;
774       XtSetArg (arg[n], XtNborderWidth, 0), n++;
775       if (i > 0)
776         XtSetArg (arg[n], XtNfromVert, w), n++;
777       head = XtCreateManagedWidget (str, labelWidgetClass, glyph_area, arg, n);
778       index_label[i] = head;
779       for (j = 0; j < 16; j++)
780         {
781           int k = i * 16 + j;
782
783           n = 4;
784           XtSetArg (arg[n], XtNheight, glyph_height), n++;
785           XtSetArg (arg[n], XtNwidth, glyph_width), n++;
786           if (i > 0)
787             XtSetArg (arg[n], XtNfromVert, w), n++;
788           if (j == 0)
789             XtSetArg (arg[n], XtNfromHoriz, head), n++;
790           else
791             XtSetArg (arg[n], XtNfromHoriz, glyph[k - 1]), n++;
792           XtSetArg (arg[n], XtNinternalWidth, 0), n++;
793           glyph[k] = XtCreateManagedWidget ("glyph", commandWidgetClass,
794                                             glyph_area, arg, n);
795           XtAddCallback (glyph[k], XtNcallback, RenderProc, (XtPointer) k);
796         }
797       w = head;
798     }
799   XtSetArg(arg[4], XtNwidth, glyph_width + 2);
800   XtSetArg (arg[5], XtNfromVert, glyph[112]);
801   XtSetArg (arg[6], XtNfromHoriz, w);
802   XtSetArg (arg[7], XtNborderWidth, 0);
803
804   for (j = 0; j < 16; j++)
805     {
806       char str[3];
807
808       sprintf (str, "X%X", j);
809       XtSetArg (arg[8], XtNlabel, str);
810       w = XtCreateManagedWidget ("idx", labelWidgetClass, glyph_area, arg, 9);
811       XtSetArg (arg[6], XtNfromHoriz, w);
812     }
813
814   XtSetArg (arg[0], XtNleft, XawChainLeft);
815   XtSetArg (arg[1], XtNright, XawChainLeft);
816   XtSetArg (arg[2], XtNtop, XawChainTop);
817   XtSetArg (arg[3], XtNbottom, XawChainTop);
818   XtSetArg (arg[4], XtNorientation, XtorientHorizontal);
819   XtSetArg (arg[5], XtNborderWidth, 0);
820   w = XtCreateManagedWidget ("clear-box", boxWidgetClass, render_area, arg, 6);
821   clear = XtCreateManagedWidget ("clear", commandWidgetClass, w, arg, 0);
822   XtAddCallback (clear, XtNcallback, RenderProc, (XtPointer) -1);
823   del = XtCreateManagedWidget ("delete", commandWidgetClass, w, arg, 0);
824   XtAddCallback (del, XtNcallback, RenderProc, (XtPointer) -2);
825
826   XtSetArg (arg[4], XtNorientation, XtorientHorizontal);
827   XtSetArg (arg[5], XtNborderWidth, 0);
828   XtSetArg (arg[6], XtNfromVert, w);
829   raw = XtCreateManagedWidget ("raw", boxWidgetClass, render_area, arg, 7);
830
831   XtSetArg (arg[0], XtNborderWidth, 0);
832   XtSetArg (arg[1], XtNlabel, "raw: ");
833   raw_label = XtCreateManagedWidget ("raw-label", labelWidgetClass,
834                                       raw, arg, 2);
835   XtSetArg (arg[1], XtNbitmap, raw_pixmap);
836   raw_image = XtCreateManagedWidget ("raw-image", labelWidgetClass,
837                                       raw, arg, 2);
838   w = raw;
839   if (otf)
840     {
841       if (OTF_get_table (otf, "GSUB") >= 0)
842         w = create_otf_widgets (1, w);
843       if (OTF_get_table (otf, "GPOS") >= 0)
844         w = create_otf_widgets (0, w);
845     }
846
847   XtSetArg (arg[6], XtNfromVert, w);
848   seq = XtCreateManagedWidget ("seq", boxWidgetClass, render_area, arg, 7);
849   XtSetArg (arg[0], XtNborderWidth, 0);
850   XtSetArg (arg[1], XtNlabel, "seq: ");
851   seq_label = XtCreateManagedWidget ("seq-label", labelWidgetClass,
852                                       seq, arg, 2);
853   XtSetArg (arg[1], XtNbitmap, seq_pixmap);
854   seq_image = XtCreateManagedWidget ("seq-image", labelWidgetClass,
855                                       seq, arg, 2);
856   XtInstallAllAccelerators (shell, shell);
857 }
858
859
860 /* Format MSG by FMT and print the result to the stderr, and exit.  */
861
862 #define FATAL_ERROR(fmt, arg)   \
863   do {                          \
864     fprintf (stderr, fmt, arg); \
865     exit (1);                   \
866   } while (0)
867
868 static int
869 x_error_handler (Display *display, XErrorEvent *error)
870 {
871   return 0;
872 }
873
874
875 int
876 main (int argc, char **argv)
877 {
878   FT_Library library;
879
880   OTF_GlyphString gstring;
881   OTF_Glyph *g;
882
883   int err;
884   int i;
885   int pixel_size = DEFAULT_PIXEL_SIZE;
886   int fixed_pixel_size = 0;
887   int display_width;
888
889   {
890     char *str = getenv ("PIXEL_SIZE");
891
892     if (str && (i = atoi (str)) > 0)
893       {
894         pixel_size = i;
895         fixed_pixel_size = 1;
896       }
897   }
898
899   gstring.size = gstring.used = 256;
900   g = calloc (256, sizeof (OTF_Glyph));
901   gstring.glyphs = g;
902
903   shell = XtOpenApplication (&context, "OTFView", NULL, 0, &argc, argv, NULL,
904                              shellWidgetClass, NULL, 0);
905   display = XtDisplay (shell);
906   /*XSynchronize (display, True);*/
907   XSetErrorHandler (x_error_handler);
908   display_width = DisplayWidth (display,
909                                 XScreenNumberOfScreen (XtScreen (shell)));
910   font = XLoadQueryFont (display, DEFAULT_FONT_NAME);
911   if (! font)
912     font = XLoadQueryFont (display, "fixed");
913
914   if (argc != 2 || !strcmp (argv[1], "-h") || !strcmp (argv[1], "--help"))
915     {
916       fprintf (stderr, "Usage: %s [ X-OPTION ... ]  OTF-FILE\n",
917                basename (argv[0]));
918       exit (argc != 2);
919     }
920   filename = argv[1];
921   if (strstr (filename, ".ttf")
922       || strstr (filename, ".TTF")
923       || strstr (filename, ".otf")
924       || strstr (filename, ".OTF"))
925     {
926       otf = OTF_open (filename);
927       if (! otf
928           || OTF_get_table (otf, "head") < 0
929           || OTF_get_table (otf, "cmap") < 0
930           || (OTF_check_table (otf, "GSUB") < 0
931               && OTF_check_table (otf, "GPOS") < 0))
932         otf = NULL;
933     }
934
935   if (otf)
936     {
937       memset (gsub, 0, sizeof gsub);
938       memset (gpos, 0, sizeof gpos);
939     }
940   if ((err = FT_Init_FreeType (&library)))
941     FATAL_ERROR ("%s\n", "FT_Init_FreeType: error");
942   err = FT_New_Face (library, filename, 0, &face);
943   if (err == FT_Err_Unknown_File_Format)
944     FATAL_ERROR ("%s\n", "FT_New_Face: unknown file format");
945   else if (err)
946     FATAL_ERROR ("%s\n", "FT_New_Face: unknown error");
947   if ((err = FT_Set_Pixel_Sizes (face, 0, pixel_size)))
948     FATAL_ERROR ("%s\n", "FT_Set_Pixel_Sizes: error");
949
950   {
951     char title[256];
952     Arg arg[1];
953
954     filename = basename (filename);
955     sprintf (title, "%s family:%s style:%s",
956              filename, face->family_name, face->style_name);
957     XtSetArg (arg[0], XtNtitle, title);
958     XtSetValues (shell, arg, 1);
959   }
960
961   glyph_width = ((face->bbox.xMax - face->bbox.xMin)
962                  * pixel_size / face->units_per_EM);
963   if (! fixed_pixel_size && glyph_width * 16 > display_width * 0.8)
964     {
965       pixel_size = (pixel_size * display_width * 0.8 / 16 / glyph_width);
966       FT_Set_Pixel_Sizes (face, 0, pixel_size);
967       glyph_width = ((face->bbox.xMax - face->bbox.xMin)
968                      * pixel_size / face->units_per_EM);
969     }
970   if (glyph_width < FONT_WIDTH * 4)
971     glyph_width = FONT_WIDTH * 4;
972
973   glyph_height = ((face->bbox.yMax - face->bbox.yMin)
974                   *  pixel_size / face->units_per_EM);
975
976   glyph_x = - (face->bbox.xMin * pixel_size / face->units_per_EM);
977   glyph_y = face->bbox.yMax * pixel_size / face->units_per_EM;
978
979   for (i = 0; i < 0x10000; i++)
980     if (FT_Load_Glyph (face, i, FT_LOAD_RENDER | FT_LOAD_MONOCHROME) == 0)
981       {
982         if (glyph_x < - face->glyph->bitmap_left)
983           glyph_x = - face->glyph->bitmap_left;
984         if (glyph_y < face->glyph->bitmap_top)
985           glyph_y = face->glyph->bitmap_top;
986         if (glyph_width
987             < glyph_x + face->glyph->bitmap_left + face->glyph->bitmap.width)
988           glyph_width
989             = glyph_x + face->glyph->bitmap_left + face->glyph->bitmap.width;
990         if (glyph_height
991             < glyph_y - face->glyph->bitmap_top + face->glyph->bitmap.rows)
992           glyph_height
993             = glyph_y - face->glyph->bitmap_top + face->glyph->bitmap.rows;
994       }
995
996   none_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
997                                glyph_width, glyph_height + 1 + FONT_HEIGHT, 1);
998
999   {
1000     unsigned long valuemask =  GCFunction | GCLineWidth;
1001     XGCValues values;
1002
1003     gc = XCreateGC (display, none_pixmap, (unsigned long) 0, NULL);
1004     values.function = GXset;
1005     values.line_width = 1;
1006     gc_set = XCreateGC (display, none_pixmap, valuemask, &values);
1007     values.function = GXor;
1008     gc_or = XCreateGC (display, none_pixmap, valuemask, &values);
1009     values.function = GXcopyInverted;
1010     gc_inv = XCreateGC (display, none_pixmap, valuemask, &values);
1011   }
1012
1013   XFillRectangle (display, none_pixmap, gc, 0, 0,
1014                   glyph_width, glyph_height + 1 + FONT_HEIGHT);
1015   XDrawString (display, none_pixmap, gc_inv,
1016                (glyph_width - XTextWidth (font, "none", 4)) / 2,
1017                glyph_height / 2, "none", 4);
1018
1019   render_width = (glyph_width + 4) * 15 + 1;
1020   render_height = glyph_height + 2;
1021
1022   charmap_rec[0].platform_id = -1;
1023   charmap_rec[0].encoding_id = -1;
1024   strcpy (charmap_rec[0].name, "no charmap");
1025
1026   for (i = 0; i < face->num_charmaps; i++)
1027     {
1028       charmap_rec[i + 1].platform_id = face->charmaps[i]->platform_id;
1029       charmap_rec[i + 1].encoding_id = face->charmaps[i]->encoding_id;
1030       sprintf (charmap_rec[i + 1].name, "%d-%d",
1031                charmap_rec[i + 1].platform_id, charmap_rec[i + 1].encoding_id);
1032       if (face->charmaps[i]->platform_id == 0
1033           || (face->charmaps[i]->platform_id == 3
1034               && face->charmaps[i]->encoding_id == 1))
1035         strcat (charmap_rec[i + 1].name, " (unicode)");
1036       else if (face->charmaps[i]->platform_id == 1
1037                && face->charmaps[i]->encoding_id == 0)
1038         strcat (charmap_rec[i + 1].name, " (apple-roman)");
1039     }
1040
1041   raw_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
1042                               render_width, render_height, 1);
1043   seq_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
1044                               render_width, render_height, 1);
1045
1046   for (i = 0; i < 0x10000; i++)
1047     create_pixmap (pixel_size, i);
1048   create_widgets (pixel_size);
1049   glyph_index = 0;
1050   charmap_index = -1;
1051   update_glyph_area ();
1052   update_render_area ();
1053
1054   XtRealizeWidget (shell);
1055   XtAppMainLoop (context);
1056
1057   exit (0);
1058 }