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