2ec484eb304290ead257f775e3873a9945fdeed7
[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       int index = features[idx].idx;
556       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 = index;
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 = index;
570           features[i].on = 1;
571         }
572     }
573
574   for (; i <= j; i++)
575     {
576       char str[5];
577       unsigned fore = foreground, back = background;
578
579       if (i == idx)
580         fore = background, back = foreground;
581       if (features[i].on)
582         {
583           XtSetArg (arg[0], XtNforeground, back);
584           XtSetArg (arg[1], XtNbackground, fore);
585         }
586       else
587         {
588           XtSetArg (arg[0], XtNforeground, fore);
589           XtSetArg (arg[1], XtNbackground, back);
590         }
591       OTF_tag_name (list->Feature[features[i].idx].FeatureTag, str);
592       XtSetArg (arg[2], XtNlabel, str);
593       XtSetValues (features[i].w, arg, 3);
594     }
595   update_seq_area ();
596 }
597
598 void
599 GsubProc (Widget w, XtPointer client_data, XtPointer call_data)
600 {
601   FeatureProc (&otf->gsub->FeatureList, gsub, (int) client_data);
602 }              
603
604 void
605 GposProc (Widget w, XtPointer client_data, XtPointer call_data)
606 {
607   FeatureProc (&otf->gpos->FeatureList, gpos, (int) client_data);
608 }
609
610 Widget
611 create_otf_widgets (int flag, Widget prev)
612 {
613   char *label;
614   OTF_FeatureList *list;
615   XtCallbackProc proc;
616   FeatureRec *features;
617   Arg arg[10];
618   Widget w;
619   char *box_label, feature_label[5];
620   int i;
621
622   if (flag)
623     {
624       label = "GSUB";
625       list = &otf->gsub->FeatureList;
626       proc = GsubProc;
627       features = gsub;
628     }
629   else
630     {
631       label = "GPOS";
632       list = &otf->gpos->FeatureList;
633       proc = GposProc;
634       features = gpos;
635     }
636   box_label = alloca (strlen (label) + 4);
637
638   strcpy (box_label, label);
639   strcat (box_label, "box");
640
641   XtSetArg (arg[0], XtNborderWidth, 0);
642   XtSetArg (arg[1], XtNleft, XawChainLeft);
643   XtSetArg (arg[2], XtNright, XawChainLeft);
644   XtSetArg (arg[3], XtNtop, XawChainTop);
645   XtSetArg (arg[4], XtNbottom, XawChainTop);
646   XtSetArg (arg[5], XtNfromVert, prev);
647   XtSetArg (arg[6], XtNorientation, XtorientHorizontal);
648   prev = XtCreateManagedWidget (box_label, boxWidgetClass, render_area, arg, 7);
649   XtCreateManagedWidget (label, labelWidgetClass, prev, arg, 1);
650   w = XtCreateManagedWidget ("all", commandWidgetClass, prev, NULL, 0);
651   XtAddCallback (w, XtNcallback, proc, (XtPointer) -2);
652   w = XtCreateManagedWidget ("none", commandWidgetClass, prev, NULL, 0);
653   XtAddCallback (w, XtNcallback, proc, (XtPointer) -1);
654   for (i = 0; i < list->FeatureCount; i++)
655     {
656       OTF_tag_name (list->Feature[i].FeatureTag, feature_label);
657       features[i].idx = i;
658       w = XtCreateManagedWidget (feature_label, commandWidgetClass, prev,
659                                  NULL, 0);
660       XtAddCallback (w, XtNcallback, proc, (XtPointer) i);
661       features[i].w = w;
662     }
663
664   return prev;
665 }
666
667 void
668 create_widgets (int pixel_size)
669 {
670   String quit_action = "<KeyPress>q: set() notify() unset()";
671   String FIRST_action = "~Shift<KeyPress>f: set() notify() unset()";
672   String PREV_action = "Shift<KeyPress>p: set() notify() unset()";
673   String prev_action = "~Shift<KeyPress>p: set() notify() unset()";
674   String next_action = "~Shift<KeyPress>n: set() notify() unset()";
675   String NEXT_action = "Shift<KeyPress>n: set() notify() unset()";
676   String LAST_action = "~Shift<KeyPress>l: set() notify() unset()";
677   Arg arg[10];
678   int i, j;
679   Widget prev, w;
680
681   frame = XtCreateManagedWidget ("frame", formWidgetClass, shell, NULL, 0);
682   XtSetArg (arg[0], XtNleft, XawChainLeft);
683   XtSetArg (arg[1], XtNright, XawChainLeft);
684   XtSetArg (arg[2], XtNtop, XawChainTop);
685   XtSetArg (arg[3], XtNbottom, XawChainTop);
686   XtSetArg (arg[4], XtNborderWidth, 0);
687   XtSetArg (arg[5], XtNorientation, XtorientHorizontal);
688   command_area = XtCreateManagedWidget ("command-area", boxWidgetClass,
689                                         frame, arg, 6);
690   XtSetArg (arg[6], XtNfromVert, command_area);
691   navi_area = XtCreateManagedWidget ("navi-area", boxWidgetClass,
692                                      frame, arg, 7);
693   XtSetArg (arg[4], XtNborderWidth, 0);
694   XtSetArg (arg[5], XtNfromVert, navi_area);
695   XtSetArg (arg[6], XtNdefaultDistance, 0);
696   glyph_area = XtCreateManagedWidget ("glyph-area", formWidgetClass,
697                                       frame, arg, 7);
698   XtSetArg (arg[5], XtNfromVert, glyph_area);
699   render_area = XtCreateManagedWidget ("render-area", formWidgetClass,
700                                        frame, arg, 6);
701
702   XtSetArg (arg[0], XtNaccelerators, XtParseAcceleratorTable (quit_action));
703   quit = XtCreateManagedWidget ("Quit", commandWidgetClass,
704                                 command_area, arg, 1);
705   XtAddCallback (quit, XtNcallback, QuitProc, NULL);
706
707   dump = XtCreateManagedWidget ("Dump Image", commandWidgetClass,
708                                 command_area, arg, 1);
709   XtAddCallback (dump, XtNcallback, DumpProc, (XtPointer) pixel_size);
710
711   charmap = alloca (sizeof (Widget) * (face->num_charmaps + 1));
712   XtSetArg (arg[0], XtNstate, True);
713   charmap[0] = XtCreateManagedWidget (charmap_rec[0].name, toggleWidgetClass,
714                                       command_area, arg, 1);
715   XtAddCallback (charmap[0], XtNcallback, CharmapProc, (XtPointer) -1);
716   XtSetArg (arg[0], XtNradioGroup, charmap[0]);
717   for (i = 0; i < face->num_charmaps; i++)
718     {
719       charmap[i + 1] = XtCreateManagedWidget (charmap_rec[i + 1].name,
720                                               toggleWidgetClass,
721                                               command_area, arg, 1);
722       XtAddCallback (charmap[i + 1], XtNcallback, CharmapProc, (XtPointer) i);
723     }
724
725   XtSetArg (arg[0], XtNlabel, " |< (f)");
726   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (FIRST_action));
727   FIRST = XtCreateManagedWidget ("FIRST", commandWidgetClass,
728                                  navi_area, arg, 2);
729   XtAddCallback (FIRST, XtNcallback, GlyphProc, (XtPointer) -3);
730   XtSetArg (arg[0], XtNlabel, "<< (P)");
731   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (PREV_action));
732   PREV = XtCreateManagedWidget ("PREV", commandWidgetClass,
733                                 navi_area, arg, 2);
734   XtAddCallback (PREV, XtNcallback, GlyphProc, (XtPointer) -2);
735   XtSetArg (arg[0], XtNlabel, "< (p)");
736   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (prev_action));
737   prev = XtCreateManagedWidget ("prev", commandWidgetClass,
738                                 navi_area, arg, 2);
739   XtAddCallback (prev, XtNcallback, GlyphProc, (XtPointer) -1);
740   XtSetArg (arg[0], XtNlabel, " 0000 ");
741   range = XtCreateManagedWidget ("range", labelWidgetClass,
742                                  navi_area, arg, 1);
743   XtSetArg (arg[0], XtNforeground, &foreground);
744   XtSetArg (arg[1], XtNbackground, &background);
745   XtGetValues (range, arg, 2);
746
747   XtSetArg (arg[0], XtNlabel, "> (n)");
748   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (next_action));
749   next = XtCreateManagedWidget ("next", commandWidgetClass,
750                                 navi_area, arg, 2);
751   XtAddCallback (next, XtNcallback, GlyphProc, (XtPointer) 1);
752   XtSetArg (arg[0], XtNlabel, ">> (N)");
753   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (NEXT_action));
754   NEXT = XtCreateManagedWidget ("NEXT", commandWidgetClass,
755                                 navi_area, arg, 2);
756   XtAddCallback (NEXT, XtNcallback, GlyphProc, (XtPointer) 2);
757   XtSetArg (arg[0], XtNlabel, ">| (l)");
758   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (LAST_action));
759   LAST = XtCreateManagedWidget ("LAST", commandWidgetClass,
760                                 navi_area, arg, 2);
761   XtAddCallback (LAST, XtNcallback, GlyphProc, (XtPointer) 3);
762
763   XtSetArg (arg[0], XtNleft, XawChainLeft);
764   XtSetArg (arg[1], XtNright, XawChainLeft);
765   XtSetArg (arg[2], XtNtop, XawChainTop);
766   XtSetArg (arg[3], XtNbottom, XawChainTop);
767
768   for (i = 0; i < 8; i++)
769     {
770       char str[3];
771       int n = 4;
772       Widget head;
773
774       sprintf (str, "%XX", i);
775       XtSetArg (arg[n], XtNheight, glyph_height + 5), n++;
776       XtSetArg (arg[n], XtNlabel, str), n++;
777       XtSetArg (arg[n], XtNborderWidth, 0), n++;
778       if (i > 0)
779         XtSetArg (arg[n], XtNfromVert, w), n++;
780       head = XtCreateManagedWidget (str, labelWidgetClass, glyph_area, arg, n);
781       index_label[i] = head;
782       for (j = 0; j < 16; j++)
783         {
784           int k = i * 16 + j;
785
786           n = 4;
787           XtSetArg (arg[n], XtNheight, glyph_height), n++;
788           XtSetArg (arg[n], XtNwidth, glyph_width), n++;
789           if (i > 0)
790             XtSetArg (arg[n], XtNfromVert, w), n++;
791           if (j == 0)
792             XtSetArg (arg[n], XtNfromHoriz, head), n++;
793           else
794             XtSetArg (arg[n], XtNfromHoriz, glyph[k - 1]), n++;
795           XtSetArg (arg[n], XtNinternalWidth, 0), n++;
796           glyph[k] = XtCreateManagedWidget ("glyph", commandWidgetClass,
797                                             glyph_area, arg, n);
798           XtAddCallback (glyph[k], XtNcallback, RenderProc, (XtPointer) k);
799         }
800       w = head;
801     }
802   XtSetArg(arg[4], XtNwidth, glyph_width + 2);
803   XtSetArg (arg[5], XtNfromVert, glyph[112]);
804   XtSetArg (arg[6], XtNfromHoriz, w);
805   XtSetArg (arg[7], XtNborderWidth, 0);
806
807   for (j = 0; j < 16; j++)
808     {
809       char str[3];
810
811       sprintf (str, "X%X", j);
812       XtSetArg (arg[8], XtNlabel, str);
813       w = XtCreateManagedWidget ("idx", labelWidgetClass, glyph_area, arg, 9);
814       XtSetArg (arg[6], XtNfromHoriz, w);
815     }
816
817   XtSetArg (arg[0], XtNleft, XawChainLeft);
818   XtSetArg (arg[1], XtNright, XawChainLeft);
819   XtSetArg (arg[2], XtNtop, XawChainTop);
820   XtSetArg (arg[3], XtNbottom, XawChainTop);
821   XtSetArg (arg[4], XtNorientation, XtorientHorizontal);
822   XtSetArg (arg[5], XtNborderWidth, 0);
823   w = XtCreateManagedWidget ("clear-box", boxWidgetClass, render_area, arg, 6);
824   clear = XtCreateManagedWidget ("clear", commandWidgetClass, w, arg, 0);
825   XtAddCallback (clear, XtNcallback, RenderProc, (XtPointer) -1);
826   del = XtCreateManagedWidget ("delete", commandWidgetClass, w, arg, 0);
827   XtAddCallback (del, XtNcallback, RenderProc, (XtPointer) -2);
828
829   XtSetArg (arg[4], XtNorientation, XtorientHorizontal);
830   XtSetArg (arg[5], XtNborderWidth, 0);
831   XtSetArg (arg[6], XtNfromVert, w);
832   raw = XtCreateManagedWidget ("raw", boxWidgetClass, render_area, arg, 7);
833
834   XtSetArg (arg[0], XtNborderWidth, 0);
835   XtSetArg (arg[1], XtNlabel, "raw: ");
836   raw_label = XtCreateManagedWidget ("raw-label", labelWidgetClass,
837                                       raw, arg, 2);
838   XtSetArg (arg[1], XtNbitmap, raw_pixmap);
839   raw_image = XtCreateManagedWidget ("raw-image", labelWidgetClass,
840                                       raw, arg, 2);
841   w = raw;
842   if (otf)
843     {
844       if (OTF_get_table (otf, "GSUB") >= 0)
845         w = create_otf_widgets (1, w);
846       if (OTF_get_table (otf, "GPOS") >= 0)
847         w = create_otf_widgets (0, w);
848     }
849
850   XtSetArg (arg[6], XtNfromVert, w);
851   seq = XtCreateManagedWidget ("seq", boxWidgetClass, render_area, arg, 7);
852   XtSetArg (arg[0], XtNborderWidth, 0);
853   XtSetArg (arg[1], XtNlabel, "seq: ");
854   seq_label = XtCreateManagedWidget ("seq-label", labelWidgetClass,
855                                       seq, arg, 2);
856   XtSetArg (arg[1], XtNbitmap, seq_pixmap);
857   seq_image = XtCreateManagedWidget ("seq-image", labelWidgetClass,
858                                       seq, arg, 2);
859   XtInstallAllAccelerators (shell, shell);
860 }
861
862
863 /* Format MSG by FMT and print the result to the stderr, and exit.  */
864
865 #define FATAL_ERROR(fmt, arg)   \
866   do {                          \
867     fprintf (stderr, fmt, arg); \
868     exit (1);                   \
869   } while (0)
870
871 static int
872 x_error_handler (Display *display, XErrorEvent *error)
873 {
874   return 0;
875 }
876
877
878 int
879 main (int argc, char **argv)
880 {
881   FT_Library library;
882
883   OTF_GlyphString gstring;
884   OTF_Glyph *g;
885
886   int err;
887   int i;
888   int pixel_size = DEFAULT_PIXEL_SIZE;
889   int fixed_pixel_size = 0;
890   int display_width;
891
892   {
893     char *str = getenv ("PIXEL_SIZE");
894
895     if (str && (i = atoi (str)) > 0)
896       {
897         pixel_size = i;
898         fixed_pixel_size = 1;
899       }
900   }
901
902   gstring.size = gstring.used = 256;
903   g = calloc (256, sizeof (OTF_Glyph));
904   gstring.glyphs = g;
905
906   shell = XtOpenApplication (&context, "OTFView", NULL, 0, &argc, argv, NULL,
907                              shellWidgetClass, NULL, 0);
908   display = XtDisplay (shell);
909   /*XSynchronize (display, True);*/
910   XSetErrorHandler (x_error_handler);
911   display_width = DisplayWidth (display,
912                                 XScreenNumberOfScreen (XtScreen (shell)));
913   font = XLoadQueryFont (display, DEFAULT_FONT_NAME);
914   if (! font)
915     font = XLoadQueryFont (display, "fixed");
916
917   if (argc != 2 || !strcmp (argv[1], "-h") || !strcmp (argv[1], "--help"))
918     {
919       fprintf (stderr, "Usage: %s [ X-OPTION ... ]  OTF-FILE\n",
920                basename (argv[0]));
921       exit (argc != 2);
922     }
923   filename = argv[1];
924   if (strstr (filename, ".ttf")
925       || strstr (filename, ".TTF")
926       || strstr (filename, ".otf")
927       || strstr (filename, ".OTF"))
928     {
929       otf = OTF_open (filename);
930       if (! otf
931           || OTF_get_table (otf, "head") < 0
932           || OTF_get_table (otf, "cmap") < 0
933           || (OTF_check_table (otf, "GSUB") < 0
934               && OTF_check_table (otf, "GPOS") < 0))
935         otf = NULL;
936     }
937
938   if (otf)
939     {
940       memset (gsub, 0, sizeof gsub);
941       memset (gpos, 0, sizeof gpos);
942     }
943   if ((err = FT_Init_FreeType (&library)))
944     FATAL_ERROR ("%s\n", "FT_Init_FreeType: error");
945   err = FT_New_Face (library, filename, 0, &face);
946   if (err == FT_Err_Unknown_File_Format)
947     FATAL_ERROR ("%s\n", "FT_New_Face: unknown file format");
948   else if (err)
949     FATAL_ERROR ("%s\n", "FT_New_Face: unknown error");
950   if ((err = FT_Set_Pixel_Sizes (face, 0, pixel_size)))
951     FATAL_ERROR ("%s\n", "FT_Set_Pixel_Sizes: error");
952
953   {
954     char title[256];
955     Arg arg[1];
956
957     filename = basename (filename);
958     sprintf (title, "%s family:%s style:%s",
959              filename, face->family_name, face->style_name);
960     XtSetArg (arg[0], XtNtitle, title);
961     XtSetValues (shell, arg, 1);
962   }
963
964   glyph_width = ((face->bbox.xMax - face->bbox.xMin)
965                  * pixel_size / face->units_per_EM);
966   if (! fixed_pixel_size && glyph_width * 16 > display_width * 0.8)
967     {
968       pixel_size = (pixel_size * display_width * 0.8 / 16 / glyph_width);
969       FT_Set_Pixel_Sizes (face, 0, pixel_size);
970       glyph_width = ((face->bbox.xMax - face->bbox.xMin)
971                      * pixel_size / face->units_per_EM);
972     }
973   if (glyph_width < FONT_WIDTH * 4)
974     glyph_width = FONT_WIDTH * 4;
975
976   glyph_height = ((face->bbox.yMax - face->bbox.yMin)
977                   *  pixel_size / face->units_per_EM);
978
979   glyph_x = - (face->bbox.xMin * pixel_size / face->units_per_EM);
980   glyph_y = face->bbox.yMax * pixel_size / face->units_per_EM;
981
982   for (i = 0; i < 0x10000; i++)
983     if (FT_Load_Glyph (face, i, FT_LOAD_RENDER | FT_LOAD_MONOCHROME) == 0)
984       {
985         if (glyph_x < - face->glyph->bitmap_left)
986           glyph_x = - face->glyph->bitmap_left;
987         if (glyph_y < face->glyph->bitmap_top)
988           glyph_y = face->glyph->bitmap_top;
989         if (glyph_width
990             < glyph_x + face->glyph->bitmap_left + face->glyph->bitmap.width)
991           glyph_width
992             = glyph_x + face->glyph->bitmap_left + face->glyph->bitmap.width;
993         if (glyph_height
994             < glyph_y - face->glyph->bitmap_top + face->glyph->bitmap.rows)
995           glyph_height
996             = glyph_y - face->glyph->bitmap_top + face->glyph->bitmap.rows;
997       }
998
999   none_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
1000                                glyph_width, glyph_height + 1 + FONT_HEIGHT, 1);
1001
1002   {
1003     unsigned long valuemask =  GCFunction | GCLineWidth;
1004     XGCValues values;
1005
1006     gc = XCreateGC (display, none_pixmap, (unsigned long) 0, NULL);
1007     values.function = GXset;
1008     values.line_width = 1;
1009     gc_set = XCreateGC (display, none_pixmap, valuemask, &values);
1010     values.function = GXor;
1011     gc_or = XCreateGC (display, none_pixmap, valuemask, &values);
1012     values.function = GXcopyInverted;
1013     gc_inv = XCreateGC (display, none_pixmap, valuemask, &values);
1014   }
1015
1016   XFillRectangle (display, none_pixmap, gc, 0, 0,
1017                   glyph_width, glyph_height + 1 + FONT_HEIGHT);
1018   XDrawString (display, none_pixmap, gc_inv,
1019                (glyph_width - XTextWidth (font, "none", 4)) / 2,
1020                glyph_height / 2, "none", 4);
1021
1022   render_width = (glyph_width + 4) * 15 + 1;
1023   render_height = glyph_height + 2;
1024
1025   charmap_rec[0].platform_id = -1;
1026   charmap_rec[0].encoding_id = -1;
1027   strcpy (charmap_rec[0].name, "no charmap");
1028
1029   for (i = 0; i < face->num_charmaps; i++)
1030     {
1031       charmap_rec[i + 1].platform_id = face->charmaps[i]->platform_id;
1032       charmap_rec[i + 1].encoding_id = face->charmaps[i]->encoding_id;
1033       sprintf (charmap_rec[i + 1].name, "%d-%d",
1034                charmap_rec[i + 1].platform_id, charmap_rec[i + 1].encoding_id);
1035       if (face->charmaps[i]->platform_id == 0
1036           || (face->charmaps[i]->platform_id == 3
1037               && face->charmaps[i]->encoding_id == 1))
1038         strcat (charmap_rec[i + 1].name, " (unicode)");
1039       else if (face->charmaps[i]->platform_id == 1
1040                && face->charmaps[i]->encoding_id == 0)
1041         strcat (charmap_rec[i + 1].name, " (apple-roman)");
1042     }
1043
1044   raw_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
1045                               render_width, render_height, 1);
1046   seq_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
1047                               render_width, render_height, 1);
1048
1049   for (i = 0; i < 0x10000; i++)
1050     create_pixmap (pixel_size, i);
1051   create_widgets (pixel_size);
1052   glyph_index = 0;
1053   charmap_index = -1;
1054   update_glyph_area ();
1055   update_render_area ();
1056
1057   XtRealizeWidget (shell);
1058   XtAppMainLoop (context);
1059
1060   exit (0);
1061 }