*** empty log message ***
[m17n/libotf.git] / example / otfview.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/stat.h>
5 #include <unistd.h>
6 #include <libgen.h>
7
8 #include <X11/Intrinsic.h>
9 #include <X11/StringDefs.h>
10 #include <X11/Shell.h>
11 #include <X11/Xaw/Command.h>
12 #include <X11/Xaw/Toggle.h>
13 #include <X11/Xaw/Box.h>
14 #include <X11/Xaw/Form.h>
15 #include <X11/Xaw/Viewport.h>
16
17 #include <ft2build.h>
18 #include FT_FREETYPE_H
19
20 #include <otf.h>
21
22 #define DEFAULT_PIXEL_SIZE 30
23 #define DEFAULT_FONT_NAME "6x13"
24 XFontStruct *font;
25 #define FONT_HEIGHT (font->ascent + font->descent)
26
27 XtAppContext context;
28 /* Widget structure.
29    +--- frame (form) ------------------+
30    | +--- command_area (box) --------+ |
31    | | quit charmap ...              | |
32    | +-------------------------------+ |
33    | +---- navi_area (box) ----------+ |
34    | | PREV prev label next NEXT     | |
35    | +-------------------------------+ |
36    | +--- glyph_area (form) ---------+ |
37    | | glyph[0]     ...    glyph[15] | |
38    | |   ...                 ...     | |
39    | | glyph[112]   ...    glyph[127]| |
40    | +-------------------------------+ |
41    | +--- render_area (form) --------+ |
42    | | clear                         | |
43    | | +--- raw (box) -------------+ | |
44    | | | raw_label raw_image       | | |
45    | | +--- seq (box) -------------+ | |
46    | | | seq_label seq_image       | | |
47    | | +--- gsub (box) ------------+ | |
48    | | | gsub_label gsub_image     | | |
49    | | +--- gpos (box) ------------+ | |
50    | | | gpos_label gpos_image     | | |
51    | | +---------------------------+ | |
52    | +-------------------------------+ |
53    +-----------------------------------+ */
54 Widget shell, frame;
55 Widget command_area, quit, *charmap;
56 Widget navi_area, PREV, prev, label, next, NEXT;
57 Widget glyph_area, glyph[128];
58 Widget render_area, clear, raw, seq, gsub, gpos;
59 Widget raw_label, raw_image, seq_label, seq_image;
60 Widget gsub_label, gsub_image, gpos_label, gpos_image;
61
62 int glyph_char[128];
63
64 Display *display;
65 GC gc, gc_set, gc_or;
66
67 typedef struct {
68   Pixmap pixmap;
69   unsigned width, height;
70   int x, y;
71   int advance;
72 } BitmapRec;
73
74 BitmapRec bitmap[0x10000];
75
76 int render_width, render_height;
77 Pixmap raw_pixmap, seq_pixmap, gsub_pixmap, gpos_pixmap;
78
79 FT_Face face;
80
81 struct {
82   int platform_id;
83   int encoding_id;
84   char name[20];
85 } charmap_rec[10];
86
87 int charmap_index;
88
89 unsigned glyph_width, glyph_height;
90 int glyph_x, glyph_y;
91 int glyph_index;
92
93 struct {
94   int n_glyphs;
95   int glyphs[64];
96 } glyph_rec;  
97
98 OTF *otf;
99
100 void
101 create_pixmap (int pixel_size, int index)
102 {
103   int err = FT_Load_Glyph (face, index, FT_LOAD_RENDER | FT_LOAD_MONOCHROME);
104   XImage ximage;
105   Pixmap pixmap;
106   
107   if (err)
108     {
109       bitmap[index].pixmap = (Pixmap) 0;
110       return;
111     }
112   ximage.height = face->glyph->bitmap.rows;
113   ximage.width = face->glyph->bitmap.width;
114   ximage.depth = 1;
115   ximage.bits_per_pixel = 1;
116   ximage.xoffset = 0;
117   ximage.format = XYPixmap;
118   ximage.data = (char *) face->glyph->bitmap.buffer;
119   ximage.byte_order = MSBFirst;
120   ximage.bitmap_unit = 8;
121   ximage.bitmap_bit_order = MSBFirst;
122   ximage.bitmap_pad = 8;
123   ximage.bytes_per_line = face->glyph->bitmap.pitch;
124   XInitImage (&ximage);
125   pixmap = XCreatePixmap (display, DefaultRootWindow (display),
126                           glyph_width, glyph_height, 1);
127   XFillRectangle (display, pixmap, gc, 0, 0, glyph_width, glyph_height);
128   XPutImage (display, pixmap, gc, &ximage, 0, 0,
129              glyph_x + face->glyph->bitmap_left,
130              glyph_y - face->glyph->bitmap_top,
131              ximage.width, ximage.height);
132   bitmap[index].pixmap = pixmap;
133   bitmap[index].width = ximage.width;
134   bitmap[index].height = ximage.height;
135   bitmap[index].x = face->glyph->bitmap_left;
136   bitmap[index].y = - face->glyph->bitmap_top;
137   bitmap[index].advance = face->glyph->metrics.horiAdvance >> 6;
138 }
139
140 void
141 update_glyph_area ()
142 {
143   int i;
144   Arg arg[2];
145   char buf[16];
146
147   for (i = 0; i < 128; i++)
148     {
149       int index = glyph_index + i;
150       int num_args = 0;
151
152       if (charmap_index >= 0)
153         index = FT_Get_Char_Index (face, (FT_ULong) index);
154       XtSetArg (arg[num_args], XtNbitmap, bitmap[index].pixmap), num_args++;
155       if (! bitmap[index].pixmap)
156         XtSetArg (arg[num_args], XtNlabel, "none"), num_args++;
157       XtSetValues (glyph[i], arg, num_args);
158     }
159
160   sprintf (buf, " %04X-%04X ", glyph_index, glyph_index + 0x7F);
161   XtSetArg (arg[0], XtNlabel, buf);
162   XtSetValues (label, arg, 1);
163 }
164
165 void
166 update_render_area ()
167 {
168   int i;
169   int x;
170   Arg arg[1];
171
172   XFillRectangle (display, raw_pixmap, gc, 0, 0, render_width, render_height);
173   XFillRectangle (display, seq_pixmap, gc, 0, 0, render_width, render_height);
174   for (i = 0, x = glyph_x; i < glyph_rec.n_glyphs; i++)
175     {
176       BitmapRec *bmp = bitmap + glyph_rec.glyphs[i];
177
178       XCopyArea (display, bmp->pixmap, raw_pixmap, gc,
179                  0, 0, glyph_width, glyph_height,
180                  (glyph_width + 1) * i + 1, 1);
181       XDrawRectangle (display, raw_pixmap, gc_set,
182                       (glyph_width + 1) * i, 0,
183                       glyph_width + 1, glyph_height + 1);
184       XCopyArea (display, bmp->pixmap, seq_pixmap, gc_or,
185                  glyph_x + bmp->x, glyph_y + bmp->y, bmp->width, bmp->height,
186                  x + bmp->x, glyph_y + bmp->y);
187       x += bmp->advance;
188     }
189   XtSetArg (arg[0], XtNbitmap, raw_pixmap);
190   XtSetValues (raw_image, arg, 1);
191   XtSetArg (arg[0], XtNbitmap, seq_pixmap);
192   XtSetValues (seq_image, arg, 1);
193   if (! otf)
194     return;
195   XFillRectangle (display, gsub_pixmap, gc, 0, 0, render_width, render_height);
196   XFillRectangle (display, gpos_pixmap, gc, 0, 0, render_width, render_height);
197   XtSetArg (arg[0], XtNbitmap, gsub_pixmap);
198   XtSetValues (gsub_image, arg, 1);
199   XtSetArg (arg[0], XtNbitmap, gpos_pixmap);
200   XtSetValues (gpos_image, arg, 1);
201 }
202
203 void
204 QuitProc (Widget w, XtPointer client_data, XtPointer call_data)
205 {
206   XtAppSetExitFlag (XtWidgetToApplicationContext (w));
207 }
208
209 void
210 GlyphProc (Widget w, XtPointer client_data, XtPointer call_data)
211 {
212   int old_glyph_index = glyph_index;
213
214   if ((int) client_data == -2 && glyph_index > 0)
215     glyph_index = (glyph_index - 1) & 0xF000;
216   else if ((int) client_data == -1 && glyph_index > 0)
217     glyph_index -= 0x80;
218   else if ((int) client_data == 1 && glyph_index < 0xFF80)
219     glyph_index += 0x80;
220   else if ((int) client_data == 2 && glyph_index < 0xF000)
221     glyph_index = (glyph_index + 0x1000) & 0xF000;
222   if (glyph_index != old_glyph_index)
223     update_glyph_area ();
224 }
225
226 void
227 CharmapProc (Widget w, XtPointer client_data, XtPointer call_data)
228 {
229   if (charmap_index == (int) client_data)
230     return;
231   charmap_index = (int) client_data;
232   if (charmap_index >= 0)
233     FT_Set_Charmap (face, face->charmaps[charmap_index]);
234   update_glyph_area ();
235 }
236
237 void
238 RenderProc (Widget w, XtPointer client_data, XtPointer call_data)
239 {
240   if ((int) client_data < 0)
241     {
242       glyph_rec.n_glyphs = 0;
243       update_render_area ();
244     }
245   else if (glyph_rec.n_glyphs < 64)
246     {
247       int index = glyph_index + (int) client_data;
248
249       if (bitmap[index].pixmap)
250         {
251           if (charmap_index >= 0)
252             index = FT_Get_Char_Index (face, (FT_ULong) index);
253           glyph_rec.glyphs[glyph_rec.n_glyphs++] = index;
254           update_render_area ();
255         }
256     }
257 }
258
259 void
260 create_widgets ()
261 {
262   String quit_action = "<KeyPress>q: set() notify() unset()";
263   String PREV_action = "Shift<KeyPress>p: set() notify() unset()";
264   String prev_action = "~Shift<KeyPress>p: set() notify() unset()";
265   String next_action = "~Shift<KeyPress>n: set() notify() unset()";
266   String NEXT_action = "Shift<KeyPress>n: set() notify() unset()";
267   Arg arg[10];
268   int i, j;
269
270   frame = XtCreateManagedWidget ("frame", formWidgetClass, shell, NULL, 0);
271   XtSetArg (arg[0], XtNleft, XawChainLeft);
272   XtSetArg (arg[1], XtNright, XawChainLeft);
273   XtSetArg (arg[2], XtNtop, XawChainTop);
274   XtSetArg (arg[3], XtNbottom, XawChainTop);
275   XtSetArg (arg[4], XtNborderWidth, 0);
276   XtSetArg (arg[5], XtNorientation, XtorientHorizontal);
277   command_area = XtCreateManagedWidget ("command-area", boxWidgetClass,
278                                         frame, arg, 6);
279   XtSetArg (arg[6], XtNfromVert, command_area);
280   navi_area = XtCreateManagedWidget ("navi-area", boxWidgetClass,
281                                      frame, arg, 7);
282   XtSetArg (arg[4], XtNborderWidth, 1);
283   XtSetArg (arg[5], XtNfromVert, navi_area);
284   XtSetArg (arg[6], XtNdefaultDistance, 0);
285   glyph_area = XtCreateManagedWidget ("glyph-area", formWidgetClass,
286                                       frame, arg, 7);
287   XtSetArg (arg[4], XtNborderWidth, 0);
288   XtSetArg (arg[5], XtNfromVert, glyph_area);
289   render_area = XtCreateManagedWidget ("render-area", formWidgetClass,
290                                        frame, arg, 6);
291
292   XtSetArg (arg[0], XtNaccelerators, XtParseAcceleratorTable (quit_action));
293   quit = XtCreateManagedWidget ("quit", commandWidgetClass,
294                                 command_area, arg, 1);
295   XtAddCallback (quit, XtNcallback, QuitProc, NULL);
296
297   charmap = alloca (sizeof (Widget) * (face->num_charmaps + 1));
298   XtSetArg (arg[0], XtNstate, True);
299   charmap[0] = XtCreateManagedWidget (charmap_rec[0].name, toggleWidgetClass,
300                                       command_area, arg, 1);
301   XtAddCallback (charmap[0], XtNcallback, CharmapProc, (XtPointer) -1);
302   XtSetArg (arg[0], XtNradioGroup, charmap[0]);
303   for (i = 0; i < face->num_charmaps; i++)
304     {
305       charmap[i + 1] = XtCreateManagedWidget (charmap_rec[i + 1].name,
306                                               toggleWidgetClass,
307                                               command_area, arg, 1);
308       XtAddCallback (charmap[i + 1], XtNcallback, CharmapProc, (XtPointer) i);
309     }
310
311   XtSetArg (arg[0], XtNlabel, "-0x1000 (P)");
312   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (PREV_action));
313   PREV = XtCreateManagedWidget ("PREV", commandWidgetClass,
314                                 navi_area, arg, 2);
315   XtAddCallback (PREV, XtNcallback, GlyphProc, (XtPointer) -2);
316   XtSetArg (arg[0], XtNlabel, "-0x80 (p)");
317   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (prev_action));
318   prev = XtCreateManagedWidget ("prev", commandWidgetClass,
319                                 navi_area, arg, 2);
320   XtAddCallback (prev, XtNcallback, GlyphProc, (XtPointer) -1);
321   XtSetArg (arg[0], XtNlabel, " 0000 ");
322   label = XtCreateManagedWidget ("label", labelWidgetClass,
323                                  navi_area, arg, 1);
324   XtSetArg (arg[0], XtNlabel, "+0x80 (n)");
325   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (next_action));
326   next = XtCreateManagedWidget ("next", commandWidgetClass,
327                                 navi_area, arg, 2);
328   XtAddCallback (next, XtNcallback, GlyphProc, (XtPointer) 1);
329   XtSetArg (arg[0], XtNlabel, "+0x1000 (N)");
330   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (NEXT_action));
331   NEXT = XtCreateManagedWidget ("NEXT", commandWidgetClass,
332                                 navi_area, arg, 2);
333   XtAddCallback (NEXT, XtNcallback, GlyphProc, (XtPointer) 2);
334
335   XtSetArg (arg[0], XtNleft, XawChainLeft);
336   XtSetArg (arg[1], XtNright, XawChainLeft);
337   XtSetArg (arg[2], XtNtop, XawChainTop);
338   XtSetArg (arg[3], XtNbottom, XawChainTop);
339   for (i = 0; i < 8; i++)
340     for (j = 0; j < 16; j++)
341       {
342         int k = i * 16 + j;
343         int num_args = 4;
344
345         XtSetArg (arg[num_args], XtNwidth, glyph_width), num_args++;
346         XtSetArg (arg[num_args], XtNheight, glyph_height), num_args++;
347         if (j > 0)
348           XtSetArg (arg[num_args], XtNfromHoriz, glyph[k - 1]), num_args++;
349         if (i > 0)
350           XtSetArg (arg[num_args], XtNfromVert, glyph[k - 16]), num_args++;
351         glyph[k] = XtCreateManagedWidget ("glyph", commandWidgetClass,
352                                           glyph_area, arg, num_args);
353         XtAddCallback (glyph[k], XtNcallback, RenderProc, (XtPointer) k);
354       }
355
356   XtSetArg (arg[0], XtNleft, XawChainLeft);
357   XtSetArg (arg[1], XtNright, XawChainLeft);
358   XtSetArg (arg[2], XtNtop, XawChainTop);
359   XtSetArg (arg[3], XtNbottom, XawChainTop);
360   clear = XtCreateManagedWidget ("clear", commandWidgetClass,
361                                  render_area, arg, 4);
362   XtAddCallback (clear, XtNcallback, RenderProc, (XtPointer) -1);
363   XtSetArg (arg[4], XtNorientation, XtorientHorizontal);
364   XtSetArg (arg[5], XtNborderWidth, 0);
365   XtSetArg (arg[6], XtNfromVert, clear);
366   raw = XtCreateManagedWidget ("raw", boxWidgetClass,
367                                 render_area, arg, 7);
368   XtSetArg (arg[0], XtNborderWidth, 0);
369   XtSetArg (arg[1], XtNlabel, "raw: ");
370   raw_label = XtCreateManagedWidget ("raw-label", labelWidgetClass,
371                                       raw, arg, 2);
372   XtSetArg (arg[1], XtNbitmap, raw_pixmap);
373   raw_image = XtCreateManagedWidget ("raw-image", labelWidgetClass,
374                                       raw, arg, 2);
375   XtSetArg (arg[6], XtNfromVert, raw);
376   seq = XtCreateManagedWidget ("seq", boxWidgetClass,
377                                 render_area, arg, 7);
378   XtSetArg (arg[0], XtNborderWidth, 0);
379   XtSetArg (arg[1], XtNlabel, "seq: ");
380   seq_label = XtCreateManagedWidget ("seq-label", labelWidgetClass,
381                                       seq, arg, 2);
382   XtSetArg (arg[1], XtNbitmap, seq_pixmap);
383   seq_image = XtCreateManagedWidget ("seq-image", labelWidgetClass,
384                                       seq, arg, 2);
385   if (otf)
386     {
387       XtSetArg (arg[6], XtNfromVert, seq);
388       gsub = XtCreateManagedWidget ("gsub", boxWidgetClass,
389                                     render_area, arg, 7);
390       XtSetArg (arg[0], XtNborderWidth, 0);
391       XtSetArg (arg[1], XtNlabel, "gsub: ");
392       gsub_label = XtCreateManagedWidget ("gsub-label", labelWidgetClass,
393                                           gsub, arg, 2);
394       gsub_image = XtCreateManagedWidget ("gsub-image", labelWidgetClass,
395                                           gsub, arg, 1);
396       XtSetArg (arg[6], XtNfromVert, gsub);
397       gpos = XtCreateManagedWidget ("gpos", boxWidgetClass,
398                                     render_area, arg, 7);
399       XtSetArg (arg[0], XtNborderWidth, 0);
400       XtSetArg (arg[1], XtNlabel, "gpos: ");
401       gpos_label = XtCreateManagedWidget ("gpos-label", labelWidgetClass,
402                                           gpos, arg, 2);
403       gpos_image = XtCreateManagedWidget ("gpos-image", labelWidgetClass,
404                                           gpos, arg, 1);
405     }
406
407   XtInstallAllAccelerators (shell, shell);
408 }
409
410
411 /* Format MSG by FMT and print the result to the stderr, and exit.  */
412
413 #define FATAL_ERROR(fmt, arg)   \
414   do {                          \
415     fprintf (stderr, fmt, arg); \
416     exit (1);                   \
417   } while (0)
418
419 int
420 main (int argc, char **argv)
421 {
422   FT_Library library;
423
424   OTF_GlyphString gstring;
425   OTF_Glyph *g;
426
427   int err;
428   int i;
429   int pixel_size = DEFAULT_PIXEL_SIZE;
430   int display_width;
431
432   {
433     char *str = getenv ("PIXEL_SIZE");
434
435     if (str && (i = atoi (str)) > 0)
436       pixel_size = i;
437   }
438
439   gstring.size = gstring.used = 256;
440   g = calloc (256, sizeof (OTF_Glyph));
441   gstring.glyphs = g;
442
443   shell = XtOpenApplication (&context, "OTFView", NULL, 0, &argc, argv, NULL,
444                              shellWidgetClass, NULL, 0);
445   display = XtDisplay (shell);
446   display_width = DisplayWidth (display,
447                                 XScreenNumberOfScreen (XtScreen (shell)));
448
449   if (argc != 2)
450     FATAL_ERROR ("%s\n", "Usage: otfview [ X-OPTION ... ]  OTF-FILE");
451   
452   if (strstr (argv[1], ".ttf")
453       || strstr (argv[1], ".TTF")
454       || strstr (argv[1], ".otf")
455       || strstr (argv[1], ".OTF"))
456     {
457       otf = OTF_open (argv[1]);
458       if (! otf
459           || OTF_get_table (otf, "head") < 0
460           || OTF_get_table (otf, "cmap") < 0
461           || (OTF_get_table (otf, "gsub") < 0
462               && OTF_get_table (otf, "gpos") < 0))
463         otf = NULL;
464     }
465
466   if ((err = FT_Init_FreeType (&library)))
467     FATAL_ERROR ("%s\n", "FT_Init_FreeType: error");
468   err = FT_New_Face (library, argv[1], 0, &face);
469   if (err == FT_Err_Unknown_File_Format)
470     FATAL_ERROR ("%s\n", "FT_New_Face: unknown file format");
471   else if (err)
472     FATAL_ERROR ("%s\n", "FT_New_Face: unknown error");
473   if ((err = FT_Set_Pixel_Sizes (face, 0, pixel_size)))
474     FATAL_ERROR ("%s\n", "FT_Set_Pixel_Sizes: error");
475
476   {
477     char title[256];
478     Arg arg[1];
479
480     sprintf (title, "%s family:%s style:%s",
481              basename (argv[1]), face->family_name, face->style_name);
482     XtSetArg (arg[0], XtNtitle, title);
483     XtSetValues (shell, arg, 1);
484   }
485
486   glyph_width = ((face->bbox.xMax - face->bbox.xMin)
487                  * pixel_size / face->units_per_EM);
488   if (glyph_width * 16 > display_width * 0.8)
489     {
490       pixel_size = (pixel_size * display_width * 0.8 / 16 / glyph_width);
491       FT_Set_Pixel_Sizes (face, 0, pixel_size);
492       glyph_width = ((face->bbox.xMax - face->bbox.xMin)
493                      * pixel_size / face->units_per_EM);
494     }
495   glyph_height = ((face->bbox.yMax - face->bbox.yMin)
496                   *  pixel_size / face->units_per_EM);
497
498   glyph_x = - (face->bbox.xMin * pixel_size / face->units_per_EM);
499   glyph_y = face->bbox.yMax * pixel_size / face->units_per_EM;
500
501   render_width = (glyph_width + 1) * 15 + 1;
502   render_height = glyph_height + 2;
503
504   charmap_rec[0].platform_id = -1;
505   charmap_rec[0].encoding_id = -1;
506   strcpy (charmap_rec[0].name, "no charmap");
507
508   for (i = 0; i < face->num_charmaps; i++)
509     {
510       charmap_rec[i + 1].platform_id = face->charmaps[i]->platform_id;
511       charmap_rec[i + 1].encoding_id = face->charmaps[i]->encoding_id;
512       sprintf (charmap_rec[i + 1].name, "%d-%d",
513                charmap_rec[i + 1].platform_id, charmap_rec[i + 1].encoding_id);
514       if (face->charmaps[i]->platform_id == 0
515           || (face->charmaps[i]->platform_id == 3
516               && face->charmaps[i]->encoding_id == 1))
517         strcat (charmap_rec[i + 1].name, " (unicode)");
518       else if (face->charmaps[i]->platform_id == 1
519                && face->charmaps[i]->encoding_id == 0)
520         strcat (charmap_rec[i + 1].name, " (apple-roman)");
521     }
522
523   raw_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
524                               render_width, render_height, 1);
525   seq_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
526                               render_width, render_height, 1);
527   gsub_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
528                                render_width, render_height, 1);
529   gpos_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
530                                render_width, render_height, 1);
531   {
532     unsigned long valuemask =  GCFunction | GCLineWidth;
533     XGCValues values;
534
535     gc = XCreateGC (display, raw_pixmap, (unsigned long) 0, NULL);
536     values.function = GXset;
537     values.line_width = 1;
538     gc_set = XCreateGC (display, raw_pixmap, valuemask, &values);
539     values.function = GXor;
540     gc_or = XCreateGC (display, raw_pixmap, valuemask, &values);
541   }
542
543   for (i = 0; i < 0x10000; i++)
544     create_pixmap (pixel_size, i);
545   create_widgets ();
546   glyph_index = 0;
547   charmap_index = -1;
548   update_glyph_area ();
549   update_render_area ();
550
551   XtRealizeWidget (shell);
552   XtAppMainLoop (context);
553
554   exit (0);
555 }