*** 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
7 #include <otf.h>
8
9 #include <ft2build.h>
10 #include FT_FREETYPE_H
11
12 #include <X11/Xlib.h>
13 #include <X11/keysym.h>
14 #include <X11/Xutil.h>
15
16 #define PIXEL_SIZE 40
17
18 #define FONT_NAME "6x13"
19 #define FONT_HEIGHT 14
20
21 int font_height, font_width;
22
23 FT_Library library;
24 FT_Face face;
25
26 Display *display;
27 int screen;
28 Window win;
29 XFontStruct *font;
30 GC gc_norm, gc_rev, gc_xor;
31 unsigned long valuemask;
32 unsigned long foreground, background;
33 XGCValues values;
34
35 typedef struct
36 {
37   int advance;
38   int left, top;
39   int rows;
40   int width;
41   int pitch;
42   int unicode;
43   unsigned char* buf;
44 } Bitmap;
45
46 Bitmap bitmap[0x10000];
47
48 /* Unicode to glyph index mapping table.  */
49 int utog[0x10000];
50
51 void
52 draw_bitmap (int index, int x, int y)
53 {
54   Bitmap *bmp = bitmap + index;
55   unsigned char *buf = bmp->buf;
56   int i, j;
57
58   if (buf)
59     {
60       x += bmp->left;
61       y -= bmp->top;
62       for (i = 0; i < bmp->rows; i++, buf += bmp->pitch)
63         for (j = 0; j < bmp->width; j++)
64           if (buf[j / 8] & (1 << (7 - (j % 8))))
65             XDrawPoint (display, win, gc_norm, x + j, y + i);
66     }
67 }
68
69 void
70 quit (char *msg)
71 {
72   fprintf (stderr, "Error by %s\n", msg);
73   exit (1);
74 }
75
76
77 int
78 read_unicode_seq (char *filename, int *code)
79 {
80   FILE *fp = fopen (filename, "r");
81   int i = 0;
82       
83   if (! fp)
84     {
85       fprintf (stderr, "File \"%s\" can't be opened.\n", filename);
86       exit (1);
87     }
88   while (i < 256
89          && fscanf (fp, "%x", code + i) == 1)
90     i++;
91   fclose (fp);
92   return i;
93 }
94
95
96 typedef struct
97 {
98   int platform_id;
99   int encoding_id;
100   int index;
101   char name[256];
102 } CharmapRec;
103
104 int
105 main (int argc, char **argv)
106 {
107   OTF *otf = NULL;
108   int err;
109   int i, j;
110   int first_idx;
111   int update_mask;
112 #define UPDATE_RENDERING 1
113 #define UPDATE_CHARMAP 2
114 #define UPDATE_BITMAP 3
115   OTF_GlyphString gstring;
116   OTF_Glyph *g;
117   int unicode_seq[256];
118   int n_codes = 0;
119   int pixel_size = PIXEL_SIZE;
120   CharmapRec *charmap_rec;
121   int charmap_index = -1;
122   char charmap_line[256];
123
124   /* Window structure.
125
126   +-------------------------+
127   | +--- rendering area --+ |
128   | |Unicode: ...         | |
129   | |   cmap: ...         | |
130   | |   GSUB: ...         | |
131   | |   GPOS: ...         | |
132   | +---------------------+ |
133   | +--- charmap area ----+ |
134   | |                     | |
135   | +---------------------+ |
136   | +--- bitmap area -----+ |
137   | |                     | |
138   | |                     | |
139   | |                     | |
140   | +---------------------+ |
141   +-------------------------+
142   */
143   int margin = 2;
144   int win_width, win_height;
145   int cols = 16, rows = 8;
146   int max_glyph_width, max_glyph_height;
147   int inner_width, rendering_area_height;
148   int charmap_area_height, bitmap_area_height;
149   int x, y, x0, y0, x1, y1;
150   char buf[1024];
151
152   gstring.size = gstring.used = 256;
153   g = calloc (256, sizeof (OTF_Glyph));
154   gstring.glyphs = g;
155
156   if (argc != 2 && argc != 3)
157     {
158       fprintf (stderr, "Usage, otfview OTF-FILE [CODE-FILE]\n");
159       exit (1);
160     }
161   
162   if (strstr (argv[1], ".ttf")
163       || strstr (argv[1], ".TTF")
164       || strstr (argv[1], ".otf")
165       || strstr (argv[1], ".OTF"))
166     {
167       otf = OTF_open (argv[1]);
168       if (! otf
169           || OTF_get_table (otf, "head") < 0
170           || OTF_get_table (otf, "cmap") < 0)
171         {
172           OTF_perror ("otfview");
173           otf = NULL;
174         }
175     }
176
177   {
178     char *p = getenv ("PIXEL_SIZE");
179     int n;
180
181     if (p && sscanf (p, "%d", &n) == 1)
182       pixel_size = n;
183   }
184
185   err = FT_Init_FreeType (&library);
186   if (err)
187     quit ("FT_Init_FreeType");
188   err = FT_New_Face (library, argv[1], 0, &face);
189   if (err == FT_Err_Unknown_File_Format)
190     quit ("FT_New_Face: unknown file format");
191   else if (err)
192     quit ("FT_New_Face: unknown error");
193   err = FT_Set_Pixel_Sizes (face, 0, pixel_size);
194   if (err)
195     quit ("FT_Set_Char_Size");
196   charmap_rec = alloca (sizeof (CharmapRec) * face->num_charmaps);
197   strcpy (charmap_line, "raw");
198   for (i = 0; i < face->num_charmaps; i++)
199     {
200       strcat (charmap_line, "  ");
201       charmap_rec[i].index = strlen (charmap_line);
202       charmap_rec[i].platform_id = face->charmaps[i]->platform_id;
203       charmap_rec[i].encoding_id = face->charmaps[i]->encoding_id;
204       sprintf (charmap_rec[i].name, "%d-%d",
205                charmap_rec[i].platform_id, charmap_rec[i].encoding_id);
206       if (face->charmaps[i]->platform_id == 0
207           || (face->charmaps[i]->platform_id == 3
208               && face->charmaps[i]->encoding_id == 1))
209         strcat (charmap_rec[i].name, " (unicode)");
210       else if (face->charmaps[i]->platform_id == 1
211                && face->charmaps[i]->encoding_id == 0)
212         strcat (charmap_rec[i].name, " (apple-roman)");
213       strcat (charmap_line, charmap_rec[i].name);
214     }
215   strcat (charmap_line, " ");
216
217   memset (utog, 0, sizeof (utog));
218   x0 = x1 = y0 = y1 = 0;
219   for (i = 0; i < 0x10000; i++)
220     {
221       err = FT_Load_Glyph (face, i, FT_LOAD_RENDER | FT_LOAD_MONOCHROME);
222       if (err)
223         bitmap[i].buf = NULL;
224       else
225         {
226           Bitmap *bmp = bitmap + i;
227           int bmpsize;
228
229           bmp->advance = face->glyph->metrics.horiAdvance >> 6;
230           bmp->left = face->glyph->bitmap_left;
231           bmp->top = face->glyph->bitmap_top;
232           bmp->rows = face->glyph->bitmap.rows;
233           bmp->width = face->glyph->bitmap.width;
234           bmp->pitch = face->glyph->bitmap.pitch;
235           bmpsize = bmp->rows * bmp->pitch;
236           bmp->buf = malloc (bmpsize);
237           memcpy (bmp->buf, face->glyph->bitmap.buffer, bmpsize);
238           if (x0 > bmp->left)
239             x0 = bmp->left;
240           if (y0 > - bmp->top)
241             y0 = - bmp->top;
242           if (x1 < bmp->left + bmp->width)
243             x1 = bmp->left + bmp->width;
244           if (y1 < bmp->rows - bmp->top)
245             y1 = bmp->rows - bmp->top;
246         }
247     }
248
249   max_glyph_height = y1 - y0;
250   max_glyph_width = x1 - x0;
251
252   for (i = 0; i < 0x10000; i++)
253     {
254       if (i >= 0xD800 && i < 0xE000)
255         continue;
256       gstring.glyphs[i & 0xFF].c = i;
257       if ((i & 0xFF) == 0xFF)
258         {
259           OTF_drive_cmap (otf, &gstring);
260           for (j = 0; j < 0x100; j++)
261             {
262               utog[(i & 0xFF00) + j] = gstring.glyphs[j].glyph_id;
263               if (gstring.glyphs[j].glyph_id > 0
264                   && gstring.glyphs[j].glyph_id < 0x10000)
265                 bitmap[gstring.glyphs[j].glyph_id].unicode = (i & 0xFF00) + j;
266             }
267         }
268     }
269
270   if (argc == 3)
271     n_codes = read_unicode_seq (argv[2], unicode_seq);
272
273   display = XOpenDisplay (NULL);
274   screen = DefaultScreen (display);
275   font = XLoadQueryFont (display, FONT_NAME);
276   if (! font)
277     font = XLoadQueryFont (display, "fixed");
278   font_height = font->ascent + font->descent;
279   font_width = font->max_bounds.width; 
280
281   inner_width = (max_glyph_width + 1) * cols + font_width * 4 + 2;
282   rendering_area_height= (max_glyph_height + 1) * 3 + font_height;
283   charmap_area_height = font_height + 2;
284   bitmap_area_height = (max_glyph_height + 1) * rows + font_height + 2;
285   win_width = inner_width + margin * 2;
286   win_height = (rendering_area_height
287                 + charmap_area_height
288                 + bitmap_area_height
289                 + margin * 4);
290   win = XCreateSimpleWindow (display, RootWindow (display, screen),
291                              0, 0, win_width, win_height, 1,
292                              BlackPixel (display, screen),
293                              WhitePixel (display, screen));
294
295   valuemask = GCForeground | GCBackground | GCFunction | GCFont;
296
297   values.foreground = BlackPixel (display, screen);
298   values.background = WhitePixel (display, screen);
299   values.function = GXcopy;
300   values.font = font->fid;
301   gc_norm = XCreateGC (display, win, valuemask, &values);
302
303   values.foreground = WhitePixel (display, screen);
304   values.background = BlackPixel (display, screen);
305   values.function = GXcopy;
306   gc_rev = XCreateGC (display, win, valuemask, &values);
307
308   values.foreground = BlackPixel (display, screen);
309   values.background = WhitePixel (display, screen);
310   values.function = values.foreground ? GXxor : GXequiv;
311   gc_xor = XCreateGC (display, win, valuemask, &values);
312
313   XMapWindow (display, win);
314   XSelectInput (display, win, ExposureMask | KeyPressMask | ButtonPressMask);
315
316   first_idx = 0;
317   update_mask = 0;
318   while (1)
319     {
320       XEvent event;
321
322       XNextEvent (display, &event);
323
324       switch (event.type)
325         {
326         case ButtonPress:
327           {
328             int x = event.xbutton.x;
329             int y = event.xbutton.y;
330
331             if (x < margin || x >= margin + inner_width)
332               break;
333             if (margin + rendering_area_height + 3 <= y
334                 && y < margin + rendering_area_height + 3 + font_height)
335               {
336                 charmap_index++;
337                 if (charmap_index >= face->num_charmaps)
338                   charmap_index = -1;
339                 update_mask = UPDATE_CHARMAP | UPDATE_BITMAP;
340                 goto redraw;
341               }
342             if (margin + 1 <= y && y < margin + 1 + font_height)
343               {
344                 n_codes = read_unicode_seq (argv[2], unicode_seq);
345                 
346                 goto redraw;
347               }
348           }
349           break;
350
351         case KeyPress:
352           {
353             char buf[512];
354             KeySym keysym;
355             int n;
356
357             n = XLookupString ((XKeyEvent *) &event, buf, 512, &keysym, NULL);
358             if (! n)
359               break;
360             if (buf[0] == 'q')
361               goto finish;
362             if (buf[0] == 'n' || buf[0] == ' ')
363               {
364                 if (first_idx + cols * rows < 0x10000)
365                   {
366                     first_idx += cols * rows;
367                     update_mask |= UPDATE_BITMAP;
368                     goto redraw;
369                   }
370               }
371             else if (buf[0] == 'p'
372                      || keysym == XK_BackSpace || keysym == XK_Delete)
373               {
374                 if (first_idx > 0)
375                   {
376                     first_idx -= cols * rows;
377                     update_mask |= UPDATE_BITMAP;
378                     goto redraw;
379                   }
380               }
381           }
382           break;
383
384         default:
385           update_mask = UPDATE_RENDERING | UPDATE_BITMAP;
386           goto redraw;
387         }
388       continue;
389
390     redraw:
391       if (update_mask == (UPDATE_RENDERING | UPDATE_CHARMAP | UPDATE_BITMAP))
392         {
393           XClearWindow (display, win);
394           x = margin;
395           y = margin + font->ascent;
396           XDrawImageString (display, win, gc_norm, x, y, "Unicode: ", 9);
397           y += font_height + (max_glyph_height - font_height) / 2;
398           XDrawImageString (display, win, gc_norm, x, y, "   cmap: ", 9);
399           y += max_glyph_height + 1;
400           XDrawImageString (display, win, gc_norm, x, y, "   GSUB: ", 9);
401           y += max_glyph_height + 1;
402           XDrawImageString (display, win, gc_norm, x, y, "   GPOS: ", 9);
403
404           y = margin * 3 + rendering_area_height + charmap_area_height;
405           XDrawLine (display, win, gc_norm, x, y, x + inner_width - 1, y);
406           y += font_height + 1;
407           for (i = 0; i <= rows; i++, y += max_glyph_height + 1)
408             XDrawLine (display, win, gc_norm, x, y, x + inner_width - 1, y);
409           y = margin * 3 + rendering_area_height + charmap_area_height;
410           XDrawLine (display, win, gc_norm, x, y,
411                      x, y + bitmap_area_height - 1);
412           x += font_width * 4 + 1;
413           for (i = 0; i <= cols; i++, x += max_glyph_width + 1)
414             XDrawLine (display, win, gc_norm, x, y,
415                        x, y + bitmap_area_height - 1);
416           y += font->ascent + 1;
417           x = (margin + font_width * 4 + 2
418                + (max_glyph_width - font_width * 4) / 2);
419           for (i = 0; i < cols; i++, x += max_glyph_width + 1)
420             {
421               sprintf (buf, "xxx%X", i);
422               XDrawImageString (display, win, gc_norm, x, y, buf, 4);
423             }
424         }
425
426       if (otf && update_mask & UPDATE_RENDERING)      
427         {
428           x = margin + font_width * 9;
429           y = margin + font->ascent;
430           for (i = 0; i < n_codes; i++)
431             {
432               sprintf (buf + i * 5, "%04X ", unicode_seq[i]);
433               gstring.glyphs[i].c = unicode_seq[i];
434             }
435           gstring.used = n_codes;
436           XDrawImageString (display, win, gc_norm, x, y, buf, n_codes * 5);
437
438           OTF_drive_cmap (otf, &gstring);
439           y = margin + font_height + 1;
440           for (i = 0; i < n_codes; i++, x += max_glyph_width)
441             draw_bitmap (gstring.glyphs[i].glyph_id, x - x0, y - y0);
442
443           OTF_drive_gsub (otf, &gstring, "deva", NULL, NULL);
444           x = margin + font_width * 9;
445           y += max_glyph_height;
446           for (i = 0; i < gstring.used; i++, x += max_glyph_width)
447             draw_bitmap (gstring.glyphs[i].glyph_id, x - x0, y - y0);
448
449           OTF_drive_gpos (otf, &gstring, "deva", NULL, NULL);
450           x = margin + font_width * 9 - x0;
451           y += max_glyph_height - y0;
452           for (i = 0; i < gstring.used; i++)
453             {
454               int xoff = 0, yoff = 0;
455               OTF_Glyph *g = gstring.glyphs + i;
456
457               switch (g->positioning_type)
458                 {
459                 case 1: case 2:
460                   if (g->f.f1.format & OTF_XPlacement)
461                     xoff = pixel_size * ((double) (g->f.f1.value->XPlacement)
462                                          * 100 / otf->head->unitsPerEm);
463                   if (g->f.f1.format & OTF_YPlacement)
464                     yoff = pixel_size * ((double) (g->f.f1.value->YPlacement)
465                                          * 100 / otf->head->unitsPerEm);
466                   break;
467
468                 case 4:
469                   xoff = (pixel_size
470                           * ((double) (g->f.f4.base_anchor->XCoordinate
471                                        - g->f.f4.mark_anchor->XCoordinate)
472                              * 100 / otf->head->unitsPerEm));
473                   yoff = (pixel_size
474                           * ((double) (g->f.f4.base_anchor->YCoordinate
475                                        - g->f.f4.mark_anchor->YCoordinate)
476                              * 100 / otf->head->unitsPerEm));
477                   break;
478                 }
479
480               draw_bitmap (gstring.glyphs[i].glyph_id, x + xoff, y + yoff);
481               x += bitmap[gstring.glyphs[i].glyph_id].advance;
482             }
483         }
484
485       if (update_mask & UPDATE_CHARMAP)
486         {
487           char *p = charmap_line, *pend;
488
489           x = margin + 1;
490           y = margin * 3 + rendering_area_height + font->ascent + 1;
491           XDrawImageString (display, win, gc_norm, x, y, charmap_line,
492                             strlen (charmap_line));
493           if (charmap_index == -1)
494             pend = p + 3;
495           else
496             {
497               p += charmap_rec[charmap_index].index;
498               pend = p + strlen (charmap_rec[charmap_index].name);
499               FT_Set_Charmap (face, face->charmaps[charmap_index]);
500             }
501           x += font_width * (p - charmap_line);
502           *pend = '\0';
503           XDrawImageString (display, win, gc_rev, x, y, p, strlen (p));
504           *pend = ' ';
505         }
506
507       if (update_mask & UPDATE_BITMAP)
508         {
509           x = margin + 1;
510           y = (margin * 2 + rendering_area_height + font_height + 2
511                + (max_glyph_height - font_height) / 2 + font->ascent);
512           for (i = 0; i < rows; i++, y += max_glyph_height + 1)
513             {
514               sprintf (buf, "%03Xx", (first_idx + i * cols) / 16);
515               XDrawImageString (display, win, gc_norm, x, y, buf, 4);
516             }
517           x += font_width * 4 + 1;
518           y = (margin * 3 + rendering_area_height + charmap_area_height
519                + font_height + 2);
520           for (i = 0; i < rows; i++)
521             for (j = 0; j < cols; j++)
522               {
523                 unsigned index = first_idx + i * cols + j;
524
525                 XClearArea (display, win, x + (max_glyph_width + 1) * j,
526                             y + (max_glyph_height + 1) * i,
527                             max_glyph_width, max_glyph_height, False);
528                 if (charmap_index >= 0)
529                   index = FT_Get_Char_Index (face, (FT_ULong) index);
530
531                 draw_bitmap (index,
532                              x + (max_glyph_width + 1) * j - x0,
533                              y + (max_glyph_height + 1) * i - y0);
534               }
535         }
536       update_mask = 0;
537     }
538
539  finish:
540   OTF_close (otf);
541   exit (0);
542 }