cf469001d1ee15dc11df379e28441eafb0945860
[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       if (charmap_index >= 0)
249         index = FT_Get_Char_Index (face, (FT_ULong) index);
250       glyph_rec.glyphs[glyph_rec.n_glyphs++] = index;
251       update_render_area ();
252     }
253 }
254
255 void
256 create_widgets ()
257 {
258   String quit_action = "<KeyPress>q: set() notify() unset()";
259   String PREV_action = "Shift<KeyPress>p: set() notify() unset()";
260   String prev_action = "~Shift<KeyPress>p: set() notify() unset()";
261   String next_action = "~Shift<KeyPress>n: set() notify() unset()";
262   String NEXT_action = "Shift<KeyPress>n: set() notify() unset()";
263   Arg arg[10];
264   int i, j;
265
266   frame = XtCreateManagedWidget ("frame", formWidgetClass, shell, NULL, 0);
267   XtSetArg (arg[0], XtNleft, XawChainLeft);
268   XtSetArg (arg[1], XtNright, XawChainLeft);
269   XtSetArg (arg[2], XtNtop, XawChainTop);
270   XtSetArg (arg[3], XtNbottom, XawChainTop);
271   XtSetArg (arg[4], XtNborderWidth, 0);
272   XtSetArg (arg[5], XtNorientation, XtorientHorizontal);
273   command_area = XtCreateManagedWidget ("command-area", boxWidgetClass,
274                                         frame, arg, 6);
275   XtSetArg (arg[6], XtNfromVert, command_area);
276   navi_area = XtCreateManagedWidget ("navi-area", boxWidgetClass,
277                                      frame, arg, 7);
278   XtSetArg (arg[4], XtNborderWidth, 1);
279   XtSetArg (arg[5], XtNfromVert, navi_area);
280   XtSetArg (arg[6], XtNdefaultDistance, 0);
281   glyph_area = XtCreateManagedWidget ("glyph-area", formWidgetClass,
282                                       frame, arg, 7);
283   XtSetArg (arg[4], XtNborderWidth, 0);
284   XtSetArg (arg[5], XtNfromVert, glyph_area);
285   render_area = XtCreateManagedWidget ("render-area", formWidgetClass,
286                                        frame, arg, 6);
287
288   XtSetArg (arg[0], XtNaccelerators, XtParseAcceleratorTable (quit_action));
289   quit = XtCreateManagedWidget ("quit", commandWidgetClass,
290                                 command_area, arg, 1);
291   XtAddCallback (quit, XtNcallback, QuitProc, NULL);
292
293   charmap = alloca (sizeof (Widget) * (face->num_charmaps + 1));
294   XtSetArg (arg[0], XtNstate, True);
295   charmap[0] = XtCreateManagedWidget (charmap_rec[0].name, toggleWidgetClass,
296                                       command_area, arg, 1);
297   XtAddCallback (charmap[0], XtNcallback, CharmapProc, (XtPointer) -1);
298   XtSetArg (arg[0], XtNradioGroup, charmap[0]);
299   for (i = 0; i < face->num_charmaps; i++)
300     {
301       charmap[i + 1] = XtCreateManagedWidget (charmap_rec[i + 1].name,
302                                               toggleWidgetClass,
303                                               command_area, arg, 1);
304       XtAddCallback (charmap[i + 1], XtNcallback, CharmapProc, (XtPointer) i);
305     }
306
307   XtSetArg (arg[0], XtNlabel, "<< (P)");
308   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (PREV_action));
309   PREV = XtCreateManagedWidget ("PREV", commandWidgetClass,
310                                 navi_area, arg, 2);
311   XtAddCallback (PREV, XtNcallback, GlyphProc, (XtPointer) -2);
312   XtSetArg (arg[0], XtNlabel, "< (p)");
313   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (prev_action));
314   prev = XtCreateManagedWidget ("prev", commandWidgetClass,
315                                 navi_area, arg, 2);
316   XtAddCallback (prev, XtNcallback, GlyphProc, (XtPointer) -1);
317   XtSetArg (arg[0], XtNlabel, " 0000 ");
318   label = XtCreateManagedWidget ("label", labelWidgetClass,
319                                  navi_area, arg, 1);
320   XtSetArg (arg[0], XtNlabel, "(n) >");
321   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (next_action));
322   next = XtCreateManagedWidget ("next", commandWidgetClass,
323                                 navi_area, arg, 2);
324   XtAddCallback (next, XtNcallback, GlyphProc, (XtPointer) 1);
325   XtSetArg (arg[0], XtNlabel, "(N) >>");
326   XtSetArg (arg[1], XtNaccelerators, XtParseAcceleratorTable (NEXT_action));
327   NEXT = XtCreateManagedWidget ("NEXT", commandWidgetClass,
328                                 navi_area, arg, 2);
329   XtAddCallback (NEXT, XtNcallback, GlyphProc, (XtPointer) 2);
330
331   XtSetArg (arg[0], XtNleft, XawChainLeft);
332   XtSetArg (arg[1], XtNright, XawChainLeft);
333   XtSetArg (arg[2], XtNtop, XawChainTop);
334   XtSetArg (arg[3], XtNbottom, XawChainTop);
335   for (i = 0; i < 8; i++)
336     for (j = 0; j < 16; j++)
337       {
338         int k = i * 16 + j;
339         int num_args = 4;
340
341         XtSetArg (arg[num_args], XtNwidth, glyph_width), num_args++;
342         XtSetArg (arg[num_args], XtNheight, glyph_height), num_args++;
343         if (j > 0)
344           XtSetArg (arg[num_args], XtNfromHoriz, glyph[k - 1]), num_args++;
345         if (i > 0)
346           XtSetArg (arg[num_args], XtNfromVert, glyph[k - 16]), num_args++;
347         glyph[k] = XtCreateManagedWidget ("glyph", commandWidgetClass,
348                                           glyph_area, arg, num_args);
349         XtAddCallback (glyph[k], XtNcallback, RenderProc, (XtPointer) k);
350       }
351
352   XtSetArg (arg[0], XtNleft, XawChainLeft);
353   XtSetArg (arg[1], XtNright, XawChainLeft);
354   XtSetArg (arg[2], XtNtop, XawChainTop);
355   XtSetArg (arg[3], XtNbottom, XawChainTop);
356   clear = XtCreateManagedWidget ("clear", commandWidgetClass,
357                                  render_area, arg, 4);
358   XtAddCallback (clear, XtNcallback, RenderProc, (XtPointer) -1);
359   XtSetArg (arg[4], XtNorientation, XtorientHorizontal);
360   XtSetArg (arg[5], XtNborderWidth, 0);
361   XtSetArg (arg[6], XtNfromVert, clear);
362   raw = XtCreateManagedWidget ("raw", boxWidgetClass,
363                                 render_area, arg, 7);
364   XtSetArg (arg[0], XtNborderWidth, 0);
365   XtSetArg (arg[1], XtNlabel, "raw: ");
366   raw_label = XtCreateManagedWidget ("raw-label", labelWidgetClass,
367                                       raw, arg, 2);
368   XtSetArg (arg[1], XtNbitmap, raw_pixmap);
369   raw_image = XtCreateManagedWidget ("raw-image", labelWidgetClass,
370                                       raw, arg, 2);
371   XtSetArg (arg[6], XtNfromVert, raw);
372   seq = XtCreateManagedWidget ("seq", boxWidgetClass,
373                                 render_area, arg, 7);
374   XtSetArg (arg[0], XtNborderWidth, 0);
375   XtSetArg (arg[1], XtNlabel, "seq: ");
376   seq_label = XtCreateManagedWidget ("seq-label", labelWidgetClass,
377                                       seq, arg, 2);
378   XtSetArg (arg[1], XtNbitmap, seq_pixmap);
379   seq_image = XtCreateManagedWidget ("seq-image", labelWidgetClass,
380                                       seq, arg, 2);
381   if (otf)
382     {
383       XtSetArg (arg[6], XtNfromVert, seq);
384       gsub = XtCreateManagedWidget ("gsub", boxWidgetClass,
385                                     render_area, arg, 7);
386       XtSetArg (arg[0], XtNborderWidth, 0);
387       XtSetArg (arg[1], XtNlabel, "gsub: ");
388       gsub_label = XtCreateManagedWidget ("gsub-label", labelWidgetClass,
389                                           gsub, arg, 2);
390       gsub_image = XtCreateManagedWidget ("gsub-image", labelWidgetClass,
391                                           gsub, arg, 1);
392       XtSetArg (arg[6], XtNfromVert, gsub);
393       gpos = XtCreateManagedWidget ("gpos", boxWidgetClass,
394                                     render_area, arg, 7);
395       XtSetArg (arg[0], XtNborderWidth, 0);
396       XtSetArg (arg[1], XtNlabel, "gpos: ");
397       gpos_label = XtCreateManagedWidget ("gpos-label", labelWidgetClass,
398                                           gpos, arg, 2);
399       gpos_image = XtCreateManagedWidget ("gpos-image", labelWidgetClass,
400                                           gpos, arg, 1);
401     }
402
403   XtInstallAllAccelerators (shell, shell);
404 }
405
406
407 /* Format MSG by FMT and print the result to the stderr, and exit.  */
408
409 #define FATAL_ERROR(fmt, arg)   \
410   do {                          \
411     fprintf (stderr, fmt, arg); \
412     exit (1);                   \
413   } while (0)
414
415 int
416 main (int argc, char **argv)
417 {
418   FT_Library library;
419
420   OTF_GlyphString gstring;
421   OTF_Glyph *g;
422
423   int err;
424   int i;
425   int pixel_size = DEFAULT_PIXEL_SIZE;
426
427   gstring.size = gstring.used = 256;
428   g = calloc (256, sizeof (OTF_Glyph));
429   gstring.glyphs = g;
430
431   shell = XtOpenApplication (&context, "OTFView", NULL, 0, &argc, argv, NULL,
432                              shellWidgetClass, NULL, 0);
433   display = XtDisplay (shell);
434
435   if (argc != 2)
436     FATAL_ERROR ("%s\n", "Usage: otfview [ X-OPTION ... ]  OTF-FILE");
437   
438   if (strstr (argv[1], ".ttf")
439       || strstr (argv[1], ".TTF")
440       || strstr (argv[1], ".otf")
441       || strstr (argv[1], ".OTF"))
442     {
443       otf = OTF_open (argv[1]);
444       if (! otf
445           || OTF_get_table (otf, "head") < 0
446           || OTF_get_table (otf, "cmap") < 0
447           || (OTF_get_table (otf, "gsub") < 0
448               && OTF_get_table (otf, "gpos") < 0))
449         otf = NULL;
450     }
451
452   if ((err = FT_Init_FreeType (&library)))
453     FATAL_ERROR ("%s\n", "FT_Init_FreeType: error");
454   err = FT_New_Face (library, argv[1], 0, &face);
455   if (err == FT_Err_Unknown_File_Format)
456     FATAL_ERROR ("%s\n", "FT_New_Face: unknown file format");
457   else if (err)
458     FATAL_ERROR ("%s\n", "FT_New_Face: unknown error");
459   if ((err = FT_Set_Pixel_Sizes (face, 0, pixel_size)))
460     FATAL_ERROR ("%s\n", "FT_Set_Char_Size: error");
461
462   {
463     char title[256];
464     Arg arg[1];
465
466     sprintf (title, "%s family:%s style:%s",
467              basename (argv[1]), face->family_name, face->style_name);
468     XtSetArg (arg[0], XtNtitle, title);
469     XtSetValues (shell, arg, 1);
470   }
471
472
473   glyph_width = ((face->bbox.xMax - face->bbox.xMin)
474                  * pixel_size / face->units_per_EM);
475   glyph_height = ((face->bbox.yMax - face->bbox.yMin)
476                   *  pixel_size / face->units_per_EM);
477   glyph_x = - (face->bbox.xMin * pixel_size / face->units_per_EM);
478   glyph_y = face->bbox.yMax * pixel_size / face->units_per_EM;
479
480   charmap_rec[0].platform_id = -1;
481   charmap_rec[0].encoding_id = -1;
482   strcpy (charmap_rec[0].name, "no charmap");
483
484   for (i = 0; i < face->num_charmaps; i++)
485     {
486       charmap_rec[i + 1].platform_id = face->charmaps[i]->platform_id;
487       charmap_rec[i + 1].encoding_id = face->charmaps[i]->encoding_id;
488       sprintf (charmap_rec[i + 1].name, "%d-%d",
489                charmap_rec[i + 1].platform_id, charmap_rec[i + 1].encoding_id);
490       if (face->charmaps[i]->platform_id == 0
491           || (face->charmaps[i]->platform_id == 3
492               && face->charmaps[i]->encoding_id == 1))
493         strcat (charmap_rec[i + 1].name, " (unicode)");
494       else if (face->charmaps[i]->platform_id == 1
495                && face->charmaps[i]->encoding_id == 0)
496         strcat (charmap_rec[i + 1].name, " (apple-roman)");
497     }
498
499   render_width = (glyph_width + 1) * 15 + 1;
500   render_height = glyph_height + 2;
501   raw_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
502                               render_width, render_height, 1);
503   seq_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
504                               render_width, render_height, 1);
505   gsub_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
506                                render_width, render_height, 1);
507   gpos_pixmap = XCreatePixmap (display, DefaultRootWindow (display),
508                                render_width, render_height, 1);
509   {
510     unsigned long valuemask =  GCFunction | GCLineWidth;
511     XGCValues values;
512
513     gc = XCreateGC (display, raw_pixmap, (unsigned long) 0, NULL);
514     values.function = GXset;
515     values.line_width = 1;
516     gc_set = XCreateGC (display, raw_pixmap, valuemask, &values);
517     values.function = GXor;
518     gc_or = XCreateGC (display, raw_pixmap, valuemask, &values);
519   }
520
521   for (i = 0; i < 0x10000; i++)
522     create_pixmap (pixel_size, i);
523   create_widgets ();
524   glyph_index = 0;
525   charmap_index = -1;
526   update_glyph_area ();
527   update_render_area ();
528
529   XtRealizeWidget (shell);
530   XtAppMainLoop (context);
531
532   exit (0);
533 }