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