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