[API] Add hb_direction_from/to_string()
[framework/uifw/harfbuzz.git] / src / hb-view.cc
1 /*
2  * Copyright © 2010  Behdad Esfahbod
3  * Copyright © 2011  Google, Inc.
4  *
5  *  This is part of HarfBuzz, a text shaping library.
6  *
7  * Permission is hereby granted, without written agreement and without
8  * license or royalty fees, to use, copy, modify, and distribute this
9  * software and its documentation for any purpose, provided that the
10  * above copyright notice and the following two paragraphs appear in
11  * all copies of this software.
12  *
13  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17  * DAMAGE.
18  *
19  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
22  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24  *
25  * Google Author(s): Behdad Esfahbod
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <unistd.h>
33 #include <getopt.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <math.h>
38 #include <locale.h>
39
40 #include <glib.h>
41
42 #include <cairo-ft.h>
43 #include <hb-ft.h>
44
45 HB_BEGIN_DECLS
46
47
48 /* Controlled by cmd-line options */
49 static int margin_t = 10;
50 static int margin_b = 10;
51 static int margin_l = 10;
52 static int margin_r = 10;
53 static int line_space = 0;
54 static double font_size = 18;
55 static const char *fore = "#000000";
56 static const char *back = "#ffffff";
57 static const char *text = NULL;
58 static const char *font_file = NULL;
59 static const char *out_file = "/dev/stdout";
60 static const char *direction = NULL;
61 static const char *script = NULL;
62 static const char *language = NULL;
63 static hb_feature_t *features = NULL;
64 static unsigned int num_features;
65 static hb_bool_t debug = FALSE;
66
67 /* Ugh, global vars.  Ugly, but does the job */
68 static int width = 0;
69 static int height = 0;
70 static cairo_surface_t *surface = NULL;
71 static cairo_pattern_t *fore_pattern = NULL;
72 static cairo_pattern_t *back_pattern = NULL;
73 static FT_Library ft_library;
74 static FT_Face ft_face;
75 static cairo_font_face_t *cairo_face;
76
77
78 G_GNUC_NORETURN static void
79 usage (FILE *f, int status)
80 {
81   fprintf (f, "usage: hb-view [OPTS...] font-file text\n");
82   exit (status);
83 }
84
85 G_GNUC_NORETURN static void
86 version (void)
87 {
88   printf ("hb-view (harfbuzz) %s\n", PACKAGE_VERSION);
89   exit (0);
90 }
91
92 static void parse_features (char *s);
93
94 static void
95 parse_opts (int argc, char **argv)
96 {
97   argv[0] = (char *) "hb-view";
98   while (1)
99     {
100       int option_index = 0, c;
101       static struct option long_options[] = {
102         {"background", 1, 0, 'B'},
103         {"debug", 0, &debug, TRUE},
104         {"direction", 1, 0, 'd'},
105         {"features", 1, 0, 'f'},
106         {"font-size", 1, 0, 's'},
107         {"foreground", 1, 0, 'F'},
108         {"help", 0, 0, 'h'},
109         {"language", 1, 0, 'L'},
110         {"line-space", 1, 0, 'l'},
111         {"margin", 1, 0, 'm'},
112         {"output", 1, 0, 'o'},
113         {"script", 1, 0, 'S'},
114         {"version", 0, 0, 'v'},
115         {0, 0, 0, 0}
116       };
117
118       c = getopt_long (argc, argv, "", long_options, &option_index);
119       if (c == -1)
120         break;
121
122       switch (c)
123         {
124         case 0:
125           break;
126         case 'h':
127           usage (stdout, 0);
128           break;
129         case 'v':
130           version ();
131           break;
132         case 'l':
133           line_space = atoi (optarg);
134           break;
135         case 'm':
136           switch (sscanf (optarg, "%d %d %d %d", &margin_t, &margin_r, &margin_b, &margin_l)) {
137             case 1: margin_r = margin_t;
138             case 2: margin_b = margin_t;
139             case 3: margin_l = margin_r;
140           }
141           break;
142         case 's':
143           font_size = strtod (optarg, NULL);
144           break;
145         case 'f':
146           parse_features (optarg);
147           break;
148         case 'F':
149           fore = optarg;
150           break;
151         case 'B':
152           back = optarg;
153           break;
154         case 't':
155           text = optarg;
156           break;
157         case 'd':
158           direction = optarg;
159           break;
160         case 'S':
161           script = optarg;
162           break;
163         case 'L':
164           language = optarg;
165           break;
166         case 'o':
167           out_file = optarg;
168           break;
169         case '?':
170           usage (stdout, 1);
171           break;
172         default:
173           break;
174         }
175     }
176   if (optind + 2 != argc)
177     usage (stderr, 1);
178   font_file = argv[optind++];
179   text = argv[optind++];
180 }
181
182
183 static void
184 parse_space (char **pp)
185 {
186   char c;
187 #define ISSPACE(c) ((c)==' '||(c)=='\f'||(c)=='\n'||(c)=='\r'||(c)=='\t'||(c)=='\v')
188   while (c = **pp, ISSPACE (c))
189     (*pp)++;
190 #undef ISSPACE
191 }
192
193 static hb_bool_t
194 parse_char (char **pp, char c)
195 {
196   parse_space (pp);
197
198   if (**pp != c)
199     return FALSE;
200
201   (*pp)++;
202   return TRUE;
203 }
204
205 static hb_bool_t
206 parse_uint (char **pp, unsigned int *pv)
207 {
208   char *p = *pp;
209   unsigned int v;
210
211   v = strtol (p, pp, 0);
212
213   if (p == *pp)
214     return FALSE;
215
216   *pv = v;
217   return TRUE;
218 }
219
220
221 static hb_bool_t
222 parse_feature_value_prefix (char **pp, hb_feature_t *feature)
223 {
224   if (parse_char (pp, '-'))
225     feature->value = 0;
226   else {
227     parse_char (pp, '+');
228     feature->value = 1;
229   }
230
231   return TRUE;
232 }
233
234 static hb_bool_t
235 parse_feature_tag (char **pp, hb_feature_t *feature)
236 {
237   char *p = *pp, c;
238
239   parse_space (pp);
240
241 #define ISALNUM(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z') || ('0' <= (c) && (c) <= '9'))
242   while (c = **pp, ISALNUM(c))
243     (*pp)++;
244 #undef ISALNUM
245
246   if (p == *pp)
247     return FALSE;
248
249   **pp = '\0';
250   feature->tag = hb_tag_from_string (p);
251   **pp = c;
252
253   return TRUE;
254 }
255
256 static hb_bool_t
257 parse_feature_indices (char **pp, hb_feature_t *feature)
258 {
259   hb_bool_t has_start;
260
261   feature->start = 0;
262   feature->end = (unsigned int) -1;
263
264   if (!parse_char (pp, '['))
265     return TRUE;
266
267   has_start = parse_uint (pp, &feature->start);
268
269   if (parse_char (pp, ':')) {
270     parse_uint (pp, &feature->end);
271   } else {
272     if (has_start)
273       feature->end = feature->start + 1;
274   }
275
276   return parse_char (pp, ']');
277 }
278
279 static hb_bool_t
280 parse_feature_value_postfix (char **pp, hb_feature_t *feature)
281 {
282   return !parse_char (pp, '=') || parse_uint (pp, &feature->value);
283 }
284
285
286 static hb_bool_t
287 parse_one_feature (char **pp, hb_feature_t *feature)
288 {
289   return parse_feature_value_prefix (pp, feature) &&
290          parse_feature_tag (pp, feature) &&
291          parse_feature_indices (pp, feature) &&
292          parse_feature_value_postfix (pp, feature) &&
293          (parse_char (pp, ',') || **pp == '\0');
294 }
295
296 static void
297 skip_one_feature (char **pp)
298 {
299   char *e;
300   e = strchr (*pp, ',');
301   if (e)
302     *pp = e + 1;
303   else
304     *pp = *pp + strlen (*pp);
305 }
306
307 static void parse_features (char *s)
308 {
309   char *p;
310
311   num_features = 0;
312   features = NULL;
313
314   if (!*s)
315     return;
316
317   /* count the features first, so we can allocate memory */
318   p = s;
319   do {
320     num_features++;
321     p = strchr (p, ',');
322     if (p)
323       p++;
324   } while (p);
325
326   features = (hb_feature_t *) calloc (num_features, sizeof (*features));
327
328   /* now do the actual parsing */
329   p = s;
330   num_features = 0;
331   while (*p) {
332     if (parse_one_feature (&p, &features[num_features]))
333       num_features++;
334     else
335       skip_one_feature (&p);
336   }
337 }
338
339
340 static cairo_glyph_t *
341 _hb_cr_text_glyphs (cairo_t *cr,
342                     const char *text, int len,
343                     unsigned int *pnum_glyphs)
344 {
345   cairo_scaled_font_t *scaled_font = cairo_get_scaled_font (cr);
346   FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
347   hb_face_t *hb_face = hb_ft_face_create_cached (ft_face);
348   hb_font_t *hb_font = hb_ft_font_create (ft_face, NULL);
349   hb_buffer_t *hb_buffer;
350   cairo_glyph_t *cairo_glyphs;
351   hb_glyph_info_t *hb_glyph;
352   hb_glyph_position_t *hb_position;
353   unsigned int num_glyphs, i;
354   hb_position_t x;
355
356   hb_buffer = hb_buffer_create (0);
357
358   if (direction)
359     hb_buffer_set_direction (hb_buffer, hb_direction_from_string (direction));
360   if (script)
361     hb_buffer_set_script (hb_buffer, hb_script_from_string (script));
362   if (language)
363     hb_buffer_set_language (hb_buffer, hb_language_from_string (language));
364
365   if (len < 0)
366     len = strlen (text);
367   hb_buffer_add_utf8 (hb_buffer, text, len, 0, len);
368
369   hb_shape (hb_font, hb_face, hb_buffer, features, num_features);
370
371   num_glyphs = hb_buffer_get_length (hb_buffer);
372   hb_glyph = hb_buffer_get_glyph_infos (hb_buffer, NULL);
373   hb_position = hb_buffer_get_glyph_positions (hb_buffer, NULL);
374   cairo_glyphs = cairo_glyph_allocate (num_glyphs + 1);
375   x = 0;
376   for (i = 0; i < num_glyphs; i++)
377     {
378       cairo_glyphs[i].index = hb_glyph->codepoint;
379       cairo_glyphs[i].x = (hb_position->x_offset + x) * (1./64);
380       cairo_glyphs[i].y = -(hb_position->y_offset)    * (1./64);
381       x += hb_position->x_advance;
382
383       hb_glyph++;
384       hb_position++;
385     }
386   cairo_glyphs[i].x = x * (1./64);
387   hb_buffer_destroy (hb_buffer);
388   hb_font_destroy (hb_font);
389   hb_face_destroy (hb_face);
390   cairo_ft_scaled_font_unlock_face (scaled_font);
391
392   if (pnum_glyphs)
393     *pnum_glyphs = num_glyphs;
394   return cairo_glyphs;
395 }
396
397 static cairo_t *
398 create_context (void)
399 {
400   cairo_t *cr;
401   unsigned int fr, fg, fb, fa, br, bg, bb, ba;
402
403   if (surface)
404     cairo_surface_destroy (surface);
405   if (back_pattern)
406     cairo_pattern_destroy (back_pattern);
407   if (fore_pattern)
408     cairo_pattern_destroy (fore_pattern);
409
410   br = bg = bb = ba = 255;
411   sscanf (back + (*back=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba);
412   fr = fg = fb = 0; fa = 255;
413   sscanf (fore + (*fore=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa);
414
415   if (ba == 255 && fa == 255 && br == bg && bg == bb && fr == fg && fg == fb) {
416     /* grayscale.  use A8 surface */
417     surface = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
418     cr = cairo_create (surface);
419     cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
420     cairo_set_source_rgba (cr, 1., 1., 1., br / 255.);
421     cairo_paint (cr);
422     back_pattern = cairo_pattern_reference (cairo_get_source (cr));
423     cairo_set_source_rgba (cr, 1., 1., 1., fr / 255.);
424     fore_pattern = cairo_pattern_reference (cairo_get_source (cr));
425   } else {
426     /* color.  use (A)RGB surface */
427     if (ba != 255)
428       surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
429     else
430       surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
431     cr = cairo_create (surface);
432     cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.);
433     cairo_paint (cr);
434     back_pattern = cairo_pattern_reference (cairo_get_source (cr));
435     cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.);
436     fore_pattern = cairo_pattern_reference (cairo_get_source (cr));
437   }
438
439   cairo_set_font_face (cr, cairo_face);
440
441   return cr;
442 }
443
444 static void
445 draw (void)
446 {
447   cairo_t *cr;
448   cairo_font_extents_t font_extents;
449
450   cairo_glyph_t *glyphs = NULL;
451   unsigned int num_glyphs = 0;
452
453   const char *end, *p = text;
454   double x, y;
455
456   cr= create_context ();
457
458   cairo_set_font_size (cr, font_size);
459   cairo_font_extents (cr, &font_extents);
460
461   height = 0;
462   width = 0;
463
464   x = margin_l;
465   y = margin_t;
466
467   do {
468     cairo_text_extents_t extents;
469
470     end = strchr (p, '\n');
471     if (!end)
472       end = p + strlen (p);
473
474     if (p != text)
475         y += line_space;
476
477     if (p != end) {
478       glyphs = _hb_cr_text_glyphs (cr, p, end - p, &num_glyphs);
479
480       cairo_glyph_extents (cr, glyphs, num_glyphs, &extents);
481
482       y += ceil (font_extents.ascent);
483       width = MAX (width, extents.x_advance);
484       cairo_save (cr);
485       cairo_translate (cr, x, y);
486       cairo_show_glyphs (cr, glyphs, num_glyphs);
487       cairo_restore (cr);
488       y += ceil (font_extents.height - ceil (font_extents.ascent));
489
490       cairo_glyph_free (glyphs);
491     }
492
493     p = end + 1;
494   } while (*end);
495
496   height = y + margin_b;
497   width += margin_l + margin_r;
498
499   cairo_destroy (cr);
500 }
501
502
503
504 int
505 main (int argc, char **argv)
506 {
507   cairo_status_t status;
508
509   setlocale (LC_ALL, "");
510
511   parse_opts (argc, argv);
512
513   FT_Init_FreeType (&ft_library);
514   if (FT_New_Face (ft_library, font_file, 0, &ft_face)) {
515     fprintf (stderr, "Failed to open font file `%s'\n", font_file);
516     exit (1);
517   }
518   cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
519
520   draw ();
521   draw ();
522
523   status = cairo_surface_write_to_png (surface, out_file);
524   if (status != CAIRO_STATUS_SUCCESS) {
525     fprintf (stderr, "Failed to write output file `%s': %s\n",
526              out_file, cairo_status_to_string (status));
527     exit (1);
528   }
529
530   if (debug) {
531     free (features);
532
533     cairo_pattern_destroy (fore_pattern);
534     cairo_pattern_destroy (back_pattern);
535     cairo_surface_destroy (surface);
536     cairo_font_face_destroy (cairo_face);
537     cairo_debug_reset_static_data ();
538
539     FT_Done_Face (ft_face);
540     FT_Done_FreeType (ft_library);
541   }
542
543   return 0;
544 }
545
546
547 HB_END_DECLS