*** empty log message ***
[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 label next NEXT LAST | |
61    | +--------------------------------------+ |
62    | +--- glyph_area (form) ----------------+ |
63    | | glyph[0]        ...        glyph[15] | |
64    | |   ...                        ...     | |
65    | | glyph[112]      ...        glyph[127]| |
66    | +--------------------------------------+ |
67    | +--- render_area (form) ---------------+ |
68    | | clear                                | |
69    | | +--- raw (box) --------------------+ | |
70    | | | raw_label raw_image              | | |
71    | | +--- seq (box) --------------------+ | |
72    | | | seq_label seq_image              | | |
73    | | +--- gsub (box) -------------------+ | |
74    | | | gsub_label gsub_image            | | |
75    | | +--- gpos (box) -------------------+ | |
76    | | | gpos_label gpos_image            | | |
77    | | +----------------------------------+ | |
78    | +--------------------------------------+ |
79    +------------------------------------------+ */
80 Widget shell, frame;
81 Widget command_area, quit, dump, *charmap;
82 Widget navi_area, FIRST, PREV, prev, label, next, NEXT, LAST;
83 Widget glyph_area, glyph[128];
84 Widget render_area, clear, raw, seq, gsub, gpos;
85 Widget raw_label, raw_image, seq_label, seq_image;
86 Widget gsub_label, gsub_image, gpos_label, gpos_image;
87
88 int glyph_char[128];
89
90 Display *display;
91 GC gc, gc_set, gc_or, gc_inv;
92
93 typedef struct {
94   Pixmap pixmap;
95   unsigned width, height;
96   int x, y;
97   int advance;
98 } BitmapRec;
99
100 BitmapRec bitmap[0x10000];
101
102 int render_width, render_height;
103 Pixmap raw_pixmap, seq_pixmap, gsub_pixmap, gpos_pixmap;
104 Pixmap none_pixmap;
105
106 FT_Face face;
107
108 struct {
109   int platform_id;
110   int encoding_id;
111   char name[20];
112 } charmap_rec[10];
113
114 int charmap_index;
115
116 unsigned glyph_width, glyph_height;
117 int glyph_x, glyph_y;
118 int glyph_index;
119
120 struct {
121   int n_glyphs;
122   int glyphs[64];
123   int codes[64];
124 } glyph_rec;
125
126 OTF *otf;
127 char *filename;
128
129 void
130 create_pixmap (int pixel_size, int index)
131 {
132   int err = FT_Load_Glyph (face, index, FT_LOAD_RENDER | FT_LOAD_MONOCHROME);
133   XImage ximage;
134   Pixmap pixmap;
135   int height = glyph_height + 1 + FONT_HEIGHT;
136   char index_buf[5];
137   
138   if (err)
139     {
140       bitmap[index].pixmap = (Pixmap) 0;
141       return;
142     }
143   ximage.height = face->glyph->bitmap.rows;
144   ximage.width = face->glyph->bitmap.width;
145   ximage.depth = 1;
146   ximage.bits_per_pixel = 1;
147   ximage.xoffset = 0;
148   ximage.format = XYPixmap;
149   ximage.data = (char *) face->glyph->bitmap.buffer;
150   ximage.byte_order = MSBFirst;
151   ximage.bitmap_unit = 8;
152   ximage.bitmap_bit_order = MSBFirst;
153   ximage.bitmap_pad = 8;
154   ximage.bytes_per_line = face->glyph->bitmap.pitch;
155   XInitImage (&ximage);
156   pixmap = XCreatePixmap (display, DefaultRootWindow (display),
157                           glyph_width, height, 1);
158   XFillRectangle (display, pixmap, gc, 0, 0, glyph_width, height);
159   XPutImage (display, pixmap, gc, &ximage, 0, 0,
160              glyph_x + face->glyph->bitmap_left,
161              glyph_y - face->glyph->bitmap_top,
162              ximage.width, ximage.height);
163   sprintf (index_buf, "%04X", index);
164   XDrawLine (display, pixmap, gc_inv,
165              0, glyph_height + 1, glyph_width, glyph_height + 1);
166   XDrawString (display, pixmap, gc_inv,
167                (glyph_width - XTextWidth (font, index_buf, 4)) / 2,
168                height - FONT_DESCENT, index_buf, 4);
169   bitmap[index].pixmap = pixmap;
170   bitmap[index].width = ximage.width;
171   bitmap[index].height = ximage.height;
172   bitmap[index].x = face->glyph->bitmap_left;
173   bitmap[index].y = - face->glyph->bitmap_top;
174   bitmap[index].advance = face->glyph->metrics.horiAdvance >> 6;
175 }
176
177 void
178 update_glyph_area ()
179 {
180   int i;
181   Arg arg[2];
182   char buf[16];
183
184   for (i = 0; i < 128; i++)
185     {
186       int index = glyph_index + i;
187
188       if (charmap_index >= 0)
189         index = FT_Get_Char_Index (face, (FT_ULong) index);
190       if (bitmap[index].pixmap)
191         XtSetArg (arg[0], XtNbitmap, bitmap[index].pixmap);
192       else
193         XtSetArg (arg[0], XtNbitmap, none_pixmap);
194       XtSetValues (glyph[i], arg, 1);
195     }
196
197   sprintf (buf, " %04X-%04X ", glyph_index, glyph_index + 0x7F);
198   XtSetArg (arg[0], XtNlabel, buf);
199   XtSetValues (label, arg, 1);
200 }
201
202 void
203 update_render_area ()
204 {
205   int i;
206   int x;
207   Arg arg[1];
208
209   XFillRectangle (display, raw_pixmap, gc, 0, 0, render_width, render_height);
210   XFillRectangle (display, seq_pixmap, gc, 0, 0, render_width, render_height);
211   for (i = 0, x = glyph_x; i < glyph_rec.n_glyphs; i++)
212     {
213       BitmapRec *bmp = bitmap + glyph_rec.glyphs[i];
214       char buf[5];
215
216       XCopyArea (display, bmp->pixmap, raw_pixmap, gc,
217                  0, 0, glyph_width, glyph_height,
218                  (glyph_width + 1) * i + 1, 1);
219       XDrawRectangle (display, raw_pixmap, gc_set,
220                       (glyph_width + 1) * i, 0,
221                       glyph_width + 1, glyph_height + 1);
222       XDrawLine (display, raw_pixmap, gc_set,
223                  (glyph_width + 1) * i + 1 + glyph_x, 1,
224                  (glyph_width + 1) * i + 1 + glyph_x, glyph_height + 1);
225       XDrawLine (display, raw_pixmap, gc_set,
226                  (glyph_width + 1) * i + 1 + glyph_x + bmp->advance, 1,
227                  (glyph_width + 1) * i + 1 + glyph_x + bmp->advance,
228                  glyph_height + 1);
229
230       sprintf (buf, "%04X", glyph_rec.codes[i]);
231       XDrawString (display, raw_pixmap, gc_inv, 
232                    (glyph_width + 1) * i + 1
233                    + (glyph_width - XTextWidth (font, buf, 4)) / 2,
234                    glyph_height + 2 + FONT_HEIGHT, buf, 4);
235       XCopyArea (display, bmp->pixmap, seq_pixmap, gc_or,
236                  glyph_x + bmp->x, glyph_y + bmp->y, bmp->width, bmp->height,
237                  x + bmp->x, glyph_y + bmp->y);
238       x += bmp->advance;
239     }
240   XtSetArg (arg[0], XtNbitmap, raw_pixmap);
241   XtSetValues (raw_image, arg, 1);
242   XtSetArg (arg[0], XtNbitmap, seq_pixmap);
243   XtSetValues (seq_image, arg, 1);
244   if (! otf)
245     return;
246   XFillRectangle (display, gsub_pixmap, gc, 0, 0, render_width, render_height);
247   XFillRectangle (display, gpos_pixmap, gc, 0, 0, render_width, render_height);
248   XtSetArg (arg[0], XtNbitmap, gsub_pixmap);
249   XtSetValues (gsub_image, arg, 1);
250   XtSetArg (arg[0], XtNbitmap, gpos_pixmap);
251   XtSetValues (gpos_image, arg, 1);
252 }
253
254 void
255 QuitProc (Widget w, XtPointer client_data, XtPointer call_data)
256 {
257   XtAppSetExitFlag (XtWidgetToApplicationContext (w));
258 }
259
260 void
261 DumpProc (Widget w, XtPointer client_data, XtPointer pixel_size)
262 {
263   int g_width, g_height, g_x, g_y, pix_width, pix_height;
264   int margin = 20 * 300 / 25.4;
265   int a4_width = 210 * 300 / 25.4 - margin * 2;
266   int a4_height = 297 * 300 / 25.4 - margin * 2;
267   int size = 100;
268   Pixmap pixmap;
269   XImage ximage, *image;
270   int i, x, y;
271   char *data;
272   int bytes_per_line;
273
274   FT_Set_Pixel_Sizes (face, 0, size);
275   g_width = ((face->bbox.xMax - face->bbox.xMin) * size / face->units_per_EM);
276   g_height = ((face->bbox.yMax - face->bbox.yMin) * size / face->units_per_EM);
277   pix_width = (g_width + 1) * 16 + margin + 1;
278   pix_height = (g_height + 1 + FONT_HEIGHT) * 16 + margin + 1;
279   while (pix_width > a4_width || pix_height > a4_height)
280     {
281       size--;
282       FT_Set_Pixel_Sizes (face, 0, size);
283       g_width = ((face->bbox.xMax - face->bbox.xMin)
284                  * size / face->units_per_EM);
285       g_height = ((face->bbox.yMax - face->bbox.yMin)
286                   * size / face->units_per_EM);
287       pix_width = (g_width + 1) * 16 + margin + 1;
288       pix_height = (g_height + 1 + FONT_HEIGHT) * 16 + margin + 1;
289     }
290
291   g_x = - (face->bbox.xMin * size / face->units_per_EM);
292   g_y = face->bbox.yMax * size / face->units_per_EM;
293   for (i = 0; i < 0xFF; i++)
294     {
295       int idx;
296
297       if (charmap_index >= 0)
298         idx = FT_Get_Char_Index (face, (FT_ULong) i);
299       else
300         idx = i;
301       if (FT_Load_Glyph (face, idx, FT_LOAD_RENDER | FT_LOAD_MONOCHROME) == 0)
302         {
303           if (g_x < - face->glyph->bitmap_left)
304             g_x = - face->glyph->bitmap_left;
305           if (g_y < face->glyph->bitmap_top)
306             g_y = face->glyph->bitmap_top;
307           if (g_width
308               < g_x + face->glyph->bitmap_left + face->glyph->bitmap.width)
309             g_width
310               = g_x + face->glyph->bitmap_left + face->glyph->bitmap.width;
311           if (g_height
312               < g_y - face->glyph->bitmap_top + face->glyph->bitmap.rows)
313             g_height
314               = g_y - face->glyph->bitmap_top + face->glyph->bitmap.rows;
315         }
316     }
317   pix_width = (g_width + 1) * 16 + margin + 1;
318   pix_height = (g_height + FONT_HEIGHT + 1) * 16 + margin + 1;
319   pixmap = XCreatePixmap (display,
320                           RootWindow (display, DefaultScreen (display)),
321                           pix_width, pix_height, 1);
322   XFillRectangle (display, pixmap, gc, 0, 0, pix_width, pix_height);
323
324   for (i = 0, x = margin; i <= 16; i++, x += g_width + 1)
325     XDrawLine (display, pixmap, gc_set, x, margin,
326                x, margin + (g_height + FONT_HEIGHT + 1) * 16);
327   for (i = 0, y = margin; i <= 16; i++, y += g_height + FONT_HEIGHT + 1)
328     XDrawLine (display, pixmap, gc_set, margin, y,
329                margin + (g_width + 1) * 16, y);
330   for (i = 0; i < 256; i++)
331     {
332       char str[5];
333       int idx;
334
335       if (charmap_index >= 0)
336         idx = FT_Get_Char_Index (face, (FT_ULong) i);
337       else
338         idx = i;
339       x = margin + (g_width + 1) * (i % 16);
340       y = margin + (g_height + FONT_HEIGHT + 1) * (i / 16);
341       if (FT_Load_Glyph (face, idx, FT_LOAD_RENDER | FT_LOAD_MONOCHROME) == 0)
342         {
343           ximage.height = face->glyph->bitmap.rows;
344           ximage.width = face->glyph->bitmap.width;
345           ximage.depth = 1;
346           ximage.bits_per_pixel = 1;
347           ximage.xoffset = 0;
348           ximage.format = XYPixmap;
349           ximage.data = (char *) face->glyph->bitmap.buffer;
350           ximage.byte_order = MSBFirst;
351           ximage.bitmap_unit = 8;
352           ximage.bitmap_bit_order = MSBFirst;
353           ximage.bitmap_pad = 8;
354           ximage.bytes_per_line = face->glyph->bitmap.pitch;
355           XInitImage (&ximage);
356           XPutImage (display, pixmap, gc, &ximage, 0, 0,
357                      x + g_x + face->glyph->bitmap_left,
358                      y + g_y - face->glyph->bitmap_top, 
359                      ximage.width, ximage.height);
360         }
361       sprintf (str, "0x%02X", i);
362       XDrawString (display, pixmap, gc_inv,
363                    x + (g_width - XTextWidth (font, str, 4))/ 2,
364                    y + g_height + FONT_ASCENT, str, 4);
365     }
366
367   image = XGetImage (display, pixmap, 0, 0, pix_width, pix_height,
368                      AllPlanes, XYPixmap);
369   XInitImage (image);
370   {
371     char *name = alloca (strlen (filename) + 5);
372     FILE *fp;
373
374     sprintf (name, "%s.pbm", filename);
375     printf ("Writing %s ...", name);
376     fp = fopen (name, "w");
377     fprintf (fp, "P4\n%d %d\n", image->width, image->height);
378     bytes_per_line = (image->width + 7) / 8;
379     data = image->data;
380     for (y = 0; y < image->height; y++, data += image->bytes_per_line)
381       fwrite (data, 1, bytes_per_line, fp);
382     fclose (fp);
383     printf ("done\n");
384   }
385   FT_Set_Pixel_Sizes (face, 0, (int) pixel_size);
386 }
387
388
389 void
390 GlyphProc (Widget w, XtPointer client_data, XtPointer call_data)
391 {
392   int old_glyph_index = glyph_index;
393
394   if ((int) client_data == -3 && glyph_index > 0)
395     glyph_index = 0;
396   else if ((int) client_data == -2 && glyph_index > 0)
397     glyph_index = (glyph_index - 1) & 0xF000;
398   else if ((int) client_data == -1 && glyph_index > 0)
399     glyph_index -= 0x80;
400   else if ((int) client_data == 1 && glyph_index < 0xFF80)
401     glyph_index += 0x80;
402   else if ((int) client_data == 2 && glyph_index < 0xF000)
403     glyph_index = (glyph_index + 0x1000) & 0xF000;
404   else if ((int) client_data == 3 && glyph_index < 0xF000)
405     glyph_index = 0xFF80;
406   if (glyph_index != old_glyph_index)
407     update_glyph_area ();
408 }
409
410 void
411 CharmapProc (Widget w, XtPointer client_data, XtPointer call_data)
412 {
413   if (charmap_index == (int) client_data)
414     return;
415   charmap_index = (int) client_data;
416   if (charmap_index >= 0)
417     FT_Set_Charmap (face, face->charmaps[charmap_index]);
418   update_glyph_area ();
419 }
420
421 void
422 RenderProc (Widget w, XtPointer client_data, XtPointer call_data)
423 {
424   if ((int) client_data < 0)
425     {
426       glyph_rec.n_glyphs = 0;
427       update_render_area ();
428     }
429   else if (glyph_rec.n_glyphs < 64)
430     {
431       int index = glyph_index + (int) client_data;
432
433       if (charmap_index >= 0)
434         index = FT_Get_Char_Index (face, (FT_ULong) index);
435       if (bitmap[index].pixmap)
436         {
437           glyph_rec.codes[glyph_rec.n_glyphs] = glyph_index + (int) client_data;
438           glyph_rec.glyphs[glyph_rec.n_glyphs++] = index;
439           update_render_area ();
440         }
441     }
442 }
443
444 void
445 create_widgets (int pixel_size)
446 {
447   String quit_action = "<KeyPress>q: set() notify() unset()";
448   String FIRST_action = "~Shift<KeyPress>f: set() notify() unset()";
449   String PREV_action = "Shift<KeyPress>p: set() notify() unset()";
450   String prev_action = "~Shift<KeyPress>p: set() notify() unset()";
451   String next_action = "~Shift<KeyPress>n: set() notify() unset()";
452   String NEXT_action = "Shift<KeyPress>n: set() notify() unset()";
453   String LAST_action = "~Shift<KeyPress>l: set() notify() unset()";
454   Arg arg[10];
455   int i, j;
456   int glyph_widget_height;
457
458   frame = XtCreateManagedWidget ("frame", formWidgetClass, shell, NULL, 0);
459   XtSetArg (arg[0], XtNleft, XawChainLeft);
460   XtSetArg (arg[1], XtNright, XawChainLeft);
461   XtSetArg (arg[2], XtNtop, XawChainTop);
462   XtSetArg (arg[3], XtNbottom, XawChainTop);
463   XtSetArg (arg[4], XtNborderWidth, 0);
464   XtSetArg (arg[5], XtNorientation, XtorientHorizontal);
465   command_area = XtCreateManagedWidget ("command-area", boxWidgetClass,
466                                         frame, arg, 6);
467   XtSetArg (arg[6], XtNfromVert, command_area);
468   navi_area = XtCreateManagedWidget ("navi-area", boxWidgetClass,
469                                      frame, arg, 7);
470   XtSetArg (arg[4], XtNborderWidth, 1);
471   XtSetArg (arg[5], XtNfromVert, navi_area);
472   XtSetArg (arg[6], XtNdefaultDistance, 0);
473   glyph_area = XtCreateManagedWidget ("glyph-area", formWidgetClass,
474                                       frame, arg, 7);
475   XtSetArg (arg[4], XtNborderWidth, 0);
476   XtSetArg (arg[5], XtNfromVert, glyph_area);
477   render_area = XtCreateManagedWidget ("render-area", formWidgetClass,
478                                        frame, arg, 6);
479
480   XtSetArg (arg[0], XtNaccelerators, XtParseAcceleratorTable (quit_action));
481   quit = XtCreateManagedWidget ("Quit", commandWidgetClass,
482                                 command_area, arg, 1);
483   XtAddCallback (quit, XtNcallback, QuitProc, NULL);
484
485   dump = XtCreateManagedWidget ("Dump Image", commandWidgetClass,
486                                 command_area, arg, 1);
487   XtAddCallback (dump, XtNcallback, DumpProc, (XtPointer) pixel_size);
488
489   charmap = alloca (sizeof (Widget) * (face->num_charmaps + 1));
490   XtSetArg (arg[0], XtNstate, True);
491   charmap[0] = XtCreateManagedWidget (charmap_rec[0].name, toggleWidgetClass,
492                                       command_area, arg, 1);
493   XtAddCallback (charmap[0], XtNcallback, CharmapProc, (XtPointer) -1);
494   XtSetArg (arg[0], XtNradioGroup, charmap[0]);
495   for (i = 0; i < face->num_charmaps; i++)
496     {
497       charmap[i + 1] = XtCreateManagedWidget (charmap_rec[i + 1].name,
498                                               toggleWidgetClass,
499                                               command_area, arg, 1);
500       XtAddCallback (charmap[i + 1], XtNcallback, CharmapProc, (XtPointer) i);
501     }
502
503   XtSetArg (arg[0], XtNlabel, " |< (f)");
504   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (FIRST_action));
505   FIRST = XtCreateManagedWidget ("FIRST", commandWidgetClass,
506                                  navi_area, arg, 2);
507   XtAddCallback (FIRST, XtNcallback, GlyphProc, (XtPointer) -3);
508   XtSetArg (arg[0], XtNlabel, "<< (P)");
509   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (PREV_action));
510   PREV = XtCreateManagedWidget ("PREV", commandWidgetClass,
511                                 navi_area, arg, 2);
512   XtAddCallback (PREV, XtNcallback, GlyphProc, (XtPointer) -2);
513   XtSetArg (arg[0], XtNlabel, "< (p)");
514   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (prev_action));
515   prev = XtCreateManagedWidget ("prev", commandWidgetClass,
516                                 navi_area, arg, 2);
517   XtAddCallback (prev, XtNcallback, GlyphProc, (XtPointer) -1);
518   XtSetArg (arg[0], XtNlabel, " 0000 ");
519   label = XtCreateManagedWidget ("label", labelWidgetClass,
520                                  navi_area, arg, 1);
521   XtSetArg (arg[0], XtNlabel, "> (n)");
522   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (next_action));
523   next = XtCreateManagedWidget ("next", commandWidgetClass,
524                                 navi_area, arg, 2);
525   XtAddCallback (next, XtNcallback, GlyphProc, (XtPointer) 1);
526   XtSetArg (arg[0], XtNlabel, ">> (N)");
527   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (NEXT_action));
528   NEXT = XtCreateManagedWidget ("NEXT", commandWidgetClass,
529                                 navi_area, arg, 2);
530   XtAddCallback (NEXT, XtNcallback, GlyphProc, (XtPointer) 2);
531   XtSetArg (arg[0], XtNlabel, ">| (l)");
532   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (LAST_action));
533   LAST = XtCreateManagedWidget ("LAST", commandWidgetClass,
534                                 navi_area, arg, 2);
535   XtAddCallback (LAST, XtNcallback, GlyphProc, (XtPointer) 3);
536
537   glyph_widget_height = glyph_height + 1 + FONT_HEIGHT;
538   XtSetArg (arg[0], XtNleft, XawChainLeft);
539   XtSetArg (arg[1], XtNright, XawChainLeft);
540   XtSetArg (arg[2], XtNtop, XawChainTop);
541   XtSetArg (arg[3], XtNbottom, XawChainTop);
542   for (i = 0; i < 8; i++)
543     for (j = 0; j < 16; j++)
544       {
545         int k = i * 16 + j;
546         int num_args = 4;
547
548         XtSetArg (arg[num_args], XtNwidth, glyph_width), num_args++;
549         XtSetArg (arg[num_args], XtNheight, glyph_widget_height), num_args++;
550         if (j > 0)
551           XtSetArg (arg[num_args], XtNfromHoriz, glyph[k - 1]), num_args++;
552         if (i > 0)
553           XtSetArg (arg[num_args], XtNfromVert, glyph[k - 16]), num_args++;
554         XtSetArg (arg[num_args], XtNinternalWidth, 0), num_args++;
555         glyph[k] = XtCreateManagedWidget ("glyph", commandWidgetClass,
556                                           glyph_area, arg, num_args);
557         XtAddCallback (glyph[k], XtNcallback, RenderProc, (XtPointer) k);
558       }
559
560   XtSetArg (arg[0], XtNleft, XawChainLeft);
561   XtSetArg (arg[1], XtNright, XawChainLeft);
562   XtSetArg (arg[2], XtNtop, XawChainTop);
563   XtSetArg (arg[3], XtNbottom, XawChainTop);
564   clear = XtCreateManagedWidget ("clear", commandWidgetClass,
565                                  render_area, arg, 4);
566   XtAddCallback (clear, XtNcallback, RenderProc, (XtPointer) -1);
567   XtSetArg (arg[4], XtNorientation, XtorientHorizontal);
568   XtSetArg (arg[5], XtNborderWidth, 0);
569   XtSetArg (arg[6], XtNfromVert, clear);
570   raw = XtCreateManagedWidget ("raw", boxWidgetClass,
571                                 render_area, arg, 7);
572   XtSetArg (arg[0], XtNborderWidth, 0);
573   XtSetArg (arg[1], XtNlabel, "raw: ");
574   raw_label = XtCreateManagedWidget ("raw-label", labelWidgetClass,
575                                       raw, arg, 2);
576   XtSetArg (arg[1], XtNbitmap, raw_pixmap);
577   raw_image = XtCreateManagedWidget ("raw-image", labelWidgetClass,
578                                       raw, arg, 2);
579   XtSetArg (arg[6], XtNfromVert, raw);
580   seq = XtCreateManagedWidget ("seq", boxWidgetClass,
581                                 render_area, arg, 7);
582   XtSetArg (arg[0], XtNborderWidth, 0);
583   XtSetArg (arg[1], XtNlabel, "seq: ");
584   seq_label = XtCreateManagedWidget ("seq-label", labelWidgetClass,
585                                       seq, arg, 2);
586   XtSetArg (arg[1], XtNbitmap, seq_pixmap);
587   seq_image = XtCreateManagedWidget ("seq-image", labelWidgetClass,
588                                       seq, arg, 2);
589   if (otf)
590     {
591       XtSetArg (arg[6], XtNfromVert, seq);
592       gsub = XtCreateManagedWidget ("gsub", boxWidgetClass,
593                                     render_area, arg, 7);
594       XtSetArg (arg[0], XtNborderWidth, 0);
595       XtSetArg (arg[1], XtNlabel, "gsub: ");
596       gsub_label = XtCreateManagedWidget ("gsub-label", labelWidgetClass,
597                                           gsub, arg, 2);
598       gsub_image = XtCreateManagedWidget ("gsub-image", labelWidgetClass,
599                                           gsub, arg, 1);
600       XtSetArg (arg[6], XtNfromVert, gsub);
601       gpos = XtCreateManagedWidget ("gpos", boxWidgetClass,
602                                     render_area, arg, 7);
603       XtSetArg (arg[0], XtNborderWidth, 0);
604       XtSetArg (arg[1], XtNlabel, "gpos: ");
605       gpos_label = XtCreateManagedWidget ("gpos-label", labelWidgetClass,
606                                           gpos, arg, 2);
607       gpos_image = XtCreateManagedWidget ("gpos-image", labelWidgetClass,
608                                           gpos, arg, 1);
609     }
610
611   XtInstallAllAccelerators (shell, shell);
612 }
613
614
615 /* Format MSG by FMT and print the result to the stderr, and exit.  */
616
617 #define FATAL_ERROR(fmt, arg)   \
618   do {                          \
619     fprintf (stderr, fmt, arg); \
620     exit (1);                   \
621   } while (0)
622
623 int
624 main (int argc, char **argv)
625 {
626   FT_Library library;
627
628   OTF_GlyphString gstring;
629   OTF_Glyph *g;
630
631   int err;
632   int i;
633   int pixel_size = DEFAULT_PIXEL_SIZE;
634   int fixed_pixel_size = 0;
635   int display_width;
636
637   {
638     char *str = getenv ("PIXEL_SIZE");
639
640     if (str && (i = atoi (str)) > 0)
641       {
642         pixel_size = i;
643         fixed_pixel_size = 1;
644       }
645   }
646
647   gstring.size = gstring.used = 256;
648   g = calloc (256, sizeof (OTF_Glyph));
649   gstring.glyphs = g;
650
651   shell = XtOpenApplication (&context, "OTFView", NULL, 0, &argc, argv, NULL,
652                              shellWidgetClass, NULL, 0);
653   display = XtDisplay (shell);
654   display_width = DisplayWidth (display,
655                                 XScreenNumberOfScreen (XtScreen (shell)));
656   font = XLoadQueryFont (display, DEFAULT_FONT_NAME);
657   if (! font)
658     font = XLoadQueryFont (display, "fixed");
659
660   if (argc != 2 || !strcmp (argv[1], "-h") || !strcmp (argv[1], "--help"))
661     {
662       fprintf (stderr, "Usage: %s [ X-OPTION ... ]  OTF-FILE\n",
663                basename (argv[0]));
664       exit (argc != 2);
665     }
666   filename = argv[1];
667   if (strstr (filename, ".ttf")
668       || strstr (filename, ".TTF")
669       || strstr (filename, ".otf")
670       || strstr (filename, ".OTF"))
671     {
672       otf = OTF_open (filename);
673       if (! otf
674           || OTF_get_table (otf, "head") < 0
675           || OTF_get_table (otf, "cmap") < 0
676           || (OTF_get_table (otf, "gsub") < 0
677               && OTF_get_table (otf, "gpos") < 0))
678         otf = NULL;
679     }
680
681   if ((err = FT_Init_FreeType (&library)))
682     FATAL_ERROR ("%s\n", "FT_Init_FreeType: error");
683   err = FT_New_Face (library, filename, 0, &face);
684   if (err == FT_Err_Unknown_File_Format)
685     FATAL_ERROR ("%s\n", "FT_New_Face: unknown file format");
686   else if (err)
687     FATAL_ERROR ("%s\n", "FT_New_Face: unknown error");
688   if ((err = FT_Set_Pixel_Sizes (face, 0, pixel_size)))
689     FATAL_ERROR ("%s\n", "FT_Set_Pixel_Sizes: error");
690
691   {
692     char title[256];
693     Arg arg[1];
694
695     filename = basename (filename);
696     sprintf (title, "%s family:%s style:%s",
697              filename, face->family_name, face->style_name);
698     XtSetArg (arg[0], XtNtitle, title);
699     XtSetValues (shell, arg, 1);
700   }
701
702   glyph_width = ((face->bbox.xMax - face->bbox.xMin)
703                  * pixel_size / face->units_per_EM);
704   if (! fixed_pixel_size && glyph_width * 16 > display_width * 0.8)
705     {
706       pixel_size = (pixel_size * display_width * 0.8 / 16 / glyph_width);
707       FT_Set_Pixel_Sizes (face, 0, pixel_size);
708       glyph_width = ((face->bbox.xMax - face->bbox.xMin)
709                      * pixel_size / face->units_per_EM);
710     }
711   if (glyph_width < FONT_WIDTH * 4)
712     glyph_width = FONT_WIDTH * 4;
713
714   glyph_height = ((face->bbox.yMax - face->bbox.yMin)
715                   *  pixel_size / face->units_per_EM);
716
717   glyph_x = - (face->bbox.xMin * pixel_size / face->units_per_EM);
718   glyph_y = face->bbox.yMax * pixel_size / face->units_per_EM;
719
720   for (i = 0; i < 0x10000; i++)
721     if (FT_Load_Glyph (face, i, FT_LOAD_RENDER | FT_LOAD_MONOCHROME) == 0)
722       {
723         if (glyph_x < - face->glyph->bitmap_left)
724           glyph_x = - face->glyph->bitmap_left;
725         if (glyph_y < face->glyph->bitmap_top)
726           glyph_y = face->glyph->bitmap_top;
727         if (glyph_width
728             < glyph_x + face->glyph->bitmap_left + face->glyph->bitmap.width)
729           glyph_width
730             = glyph_x + face->glyph->bitmap_left + face->glyph->bitmap.width;
731         if (glyph_height
732             < glyph_y - face->glyph->bitmap_top + face->glyph->bitmap.rows)
733           glyph_height
734             = glyph_y - face->glyph->bitmap_top + face->glyph->bitmap.rows;
735       }
736
737   none_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
738                                glyph_width, glyph_height + 1 + FONT_HEIGHT, 1);
739
740   {
741     unsigned long valuemask =  GCFunction | GCLineWidth;
742     XGCValues values;
743
744     gc = XCreateGC (display, none_pixmap, (unsigned long) 0, NULL);
745     values.function = GXset;
746     values.line_width = 1;
747     gc_set = XCreateGC (display, none_pixmap, valuemask, &values);
748     values.function = GXor;
749     gc_or = XCreateGC (display, none_pixmap, valuemask, &values);
750     values.function = GXcopyInverted;
751     gc_inv = XCreateGC (display, none_pixmap, valuemask, &values);
752   }
753
754   XFillRectangle (display, none_pixmap, gc, 0, 0,
755                   glyph_width, glyph_height + 1 + FONT_HEIGHT);
756   XDrawString (display, none_pixmap, gc_inv,
757                (glyph_width - XTextWidth (font, "none", 4)) / 2,
758                glyph_height / 2, "none", 4);
759
760   render_width = (glyph_width + 1) * 15 + 1;
761   render_height = glyph_height + FONT_HEIGHT + 2;
762
763   charmap_rec[0].platform_id = -1;
764   charmap_rec[0].encoding_id = -1;
765   strcpy (charmap_rec[0].name, "no charmap");
766
767   for (i = 0; i < face->num_charmaps; i++)
768     {
769       charmap_rec[i + 1].platform_id = face->charmaps[i]->platform_id;
770       charmap_rec[i + 1].encoding_id = face->charmaps[i]->encoding_id;
771       sprintf (charmap_rec[i + 1].name, "%d-%d",
772                charmap_rec[i + 1].platform_id, charmap_rec[i + 1].encoding_id);
773       if (face->charmaps[i]->platform_id == 0
774           || (face->charmaps[i]->platform_id == 3
775               && face->charmaps[i]->encoding_id == 1))
776         strcat (charmap_rec[i + 1].name, " (unicode)");
777       else if (face->charmaps[i]->platform_id == 1
778                && face->charmaps[i]->encoding_id == 0)
779         strcat (charmap_rec[i + 1].name, " (apple-roman)");
780     }
781
782   raw_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
783                               render_width, render_height, 1);
784   seq_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
785                               render_width, render_height, 1);
786   gsub_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
787                                render_width, render_height, 1);
788   gpos_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
789                                render_width, render_height, 1);
790
791   for (i = 0; i < 0x10000; i++)
792     create_pixmap (pixel_size, i);
793   create_widgets (pixel_size);
794   glyph_index = 0;
795   charmap_index = -1;
796   update_glyph_area ();
797   update_render_area ();
798
799   XtRealizeWidget (shell);
800   XtAppMainLoop (context);
801
802   exit (0);
803 }