Update Copyright headers
[apps/home/video-player.git] / src / hb-view.c
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 <hb-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 *language = NULL;
61 static const char *script = NULL;
62 static hb_feature_t *features = NULL;
63 static unsigned int num_features;
64 static hb_bool_t debug = FALSE;
65
66 /* Ugh, global vars.  Ugly, but does the job */
67 static int width = 0;
68 static int height = 0;
69 static cairo_surface_t *surface = NULL;
70 static cairo_pattern_t *fore_pattern = NULL;
71 static cairo_pattern_t *back_pattern = NULL;
72 static FT_Library ft_library;
73 static FT_Face ft_face;
74 static cairo_font_face_t *cairo_face;
75
76
77 G_GNUC_NORETURN static void
78 usage (FILE *f, int status)
79 {
80   fprintf (f, "usage: hb-view [OPTS...] font-file text\n");
81   exit (status);
82 }
83
84 G_GNUC_NORETURN static void
85 version (void)
86 {
87   printf ("hb-view (harfbuzz) %s\n", PACKAGE_VERSION);
88   exit (0);
89 }
90
91 static void parse_features (char *s);
92
93 static void
94 parse_opts (int argc, char **argv)
95 {
96   argv[0] = (char *) "hb-view";
97   while (1)
98     {
99       int option_index = 0, c;
100       static struct option long_options[] = {
101         {"background", 1, 0, 'B'},
102         {"debug", 0, &debug, TRUE},
103         {"features", 1, 0, 'f'},
104         {"font-size", 1, 0, 's'},
105         {"foreground", 1, 0, 'F'},
106         {"help", 0, 0, 'h'},
107         {"language", 1, 0, 'L'},
108         {"line-space", 1, 0, 'l'},
109         {"margin", 1, 0, 'm'},
110         {"output", 1, 0, 'o'},
111         {"script", 1, 0, 'S'},
112         {"version", 0, 0, 'v'},
113         {0, 0, 0, 0}
114       };
115
116       c = getopt_long (argc, argv, "", long_options, &option_index);
117       if (c == -1)
118         break;
119
120       switch (c)
121         {
122         case 0:
123           break;
124         case 'h':
125           usage (stdout, 0);
126           break;
127         case 'v':
128           version ();
129           break;
130         case 'l':
131           line_space = atoi (optarg);
132           break;
133         case 'm':
134           switch (sscanf (optarg, "%d %d %d %d", &margin_t, &margin_r, &margin_b, &margin_l)) {
135             case 1: margin_r = margin_t;
136             case 2: margin_b = margin_t;
137             case 3: margin_l = margin_r;
138           }
139           break;
140         case 's':
141           font_size = strtod (optarg, NULL);
142           break;
143         case 'f':
144           parse_features (optarg);
145           break;
146         case 'F':
147           fore = optarg;
148           break;
149         case 'B':
150           back = optarg;
151           break;
152         case 't':
153           text = optarg;
154           break;
155         case 'L':
156           language = optarg;
157           break;
158         case 'S':
159           script = optarg;
160           break;
161         case 'o':
162           out_file = optarg;
163           break;
164         case '?':
165           usage (stdout, 1);
166           break;
167         default:
168           break;
169         }
170     }
171   if (optind + 2 != argc)
172     usage (stderr, 1);
173   font_file = argv[optind++];
174   text = argv[optind++];
175 }
176
177
178 static void
179 parse_space (char **pp)
180 {
181   char c;
182 #define ISSPACE(c) ((c)==' '||(c)=='\f'||(c)=='\n'||(c)=='\r'||(c)=='\t'||(c)=='\v')
183   while (c = **pp, ISSPACE (c))
184     (*pp)++;
185 #undef ISSPACE
186 }
187
188 static hb_bool_t
189 parse_char (char **pp, char c)
190 {
191   parse_space (pp);
192
193   if (**pp != c)
194     return FALSE;
195
196   (*pp)++;
197   return TRUE;
198 }
199
200 static hb_bool_t
201 parse_uint (char **pp, unsigned int *pv)
202 {
203   char *p = *pp;
204   unsigned int v;
205
206   v = strtol (p, pp, 0);
207
208   if (p == *pp)
209     return FALSE;
210
211   *pv = v;
212   return TRUE;
213 }
214
215
216 static hb_bool_t
217 parse_feature_value_prefix (char **pp, hb_feature_t *feature)
218 {
219   if (parse_char (pp, '-'))
220     feature->value = 0;
221   else {
222     parse_char (pp, '+');
223     feature->value = 1;
224   }
225
226   return TRUE;
227 }
228
229 static hb_bool_t
230 parse_feature_tag (char **pp, hb_feature_t *feature)
231 {
232   char *p = *pp, c;
233
234   parse_space (pp);
235
236 #define ISALNUM(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z') || ('0' <= (c) && (c) <= '9'))
237   while (c = **pp, ISALNUM(c))
238     (*pp)++;
239 #undef ISALNUM
240
241   if (p == *pp)
242     return FALSE;
243
244   **pp = '\0';
245   feature->tag = hb_tag_from_string (p);
246   **pp = c;
247
248   return TRUE;
249 }
250
251 static hb_bool_t
252 parse_feature_indices (char **pp, hb_feature_t *feature)
253 {
254   hb_bool_t has_start;
255
256   feature->start = 0;
257   feature->end = (unsigned int) -1;
258
259   if (!parse_char (pp, '['))
260     return TRUE;
261
262   has_start = parse_uint (pp, &feature->start);
263
264   if (parse_char (pp, ':')) {
265     parse_uint (pp, &feature->end);
266   } else {
267     if (has_start)
268       feature->end = feature->start + 1;
269   }
270
271   return parse_char (pp, ']');
272 }
273
274 static hb_bool_t
275 parse_feature_value_postfix (char **pp, hb_feature_t *feature)
276 {
277   return !parse_char (pp, '=') || parse_uint (pp, &feature->value);
278 }
279
280
281 static hb_bool_t
282 parse_one_feature (char **pp, hb_feature_t *feature)
283 {
284   return parse_feature_value_prefix (pp, feature) &&
285          parse_feature_tag (pp, feature) &&
286          parse_feature_indices (pp, feature) &&
287          parse_feature_value_postfix (pp, feature) &&
288          (parse_char (pp, ',') || **pp == '\0');
289 }
290
291 static void
292 skip_one_feature (char **pp)
293 {
294   char *e;
295   e = strchr (*pp, ',');
296   if (e)
297     *pp = e + 1;
298   else
299     *pp = *pp + strlen (*pp);
300 }
301
302 static void parse_features (char *s)
303 {
304   char *p;
305
306   num_features = 0;
307   features = NULL;
308
309   if (!*s)
310     return;
311
312   /* count the features first, so we can allocate memory */
313   p = s;
314   do {
315     num_features++;
316     p = strchr (p, ',');
317     if (p)
318       p++;
319   } while (p);
320
321   features = calloc (num_features, sizeof (*features));
322
323   /* now do the actual parsing */
324   p = s;
325   num_features = 0;
326   while (*p) {
327     if (parse_one_feature (&p, &features[num_features]))
328       num_features++;
329     else
330       skip_one_feature (&p);
331   }
332 }
333
334
335 static cairo_glyph_t *
336 _hb_cr_text_glyphs (cairo_t *cr,
337                     const char *text, int len,
338                     unsigned int *pnum_glyphs)
339 {
340   cairo_scaled_font_t *scaled_font = cairo_get_scaled_font (cr);
341   FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
342   hb_face_t *hb_face = hb_ft_face_create_cached (ft_face);
343   hb_font_t *hb_font = hb_ft_font_create (ft_face, NULL);
344   hb_buffer_t *hb_buffer;
345   cairo_glyph_t *cairo_glyphs;
346   hb_glyph_info_t *hb_glyph;
347   hb_glyph_position_t *hb_position;
348   unsigned int num_glyphs, i;
349   hb_position_t x;
350
351   if (len < 0)
352     len = strlen (text);
353   hb_buffer = hb_buffer_create (len);
354
355   hb_buffer_set_unicode_funcs (hb_buffer, hb_glib_get_unicode_funcs ());
356
357   hb_buffer_add_utf8 (hb_buffer, text, len, 0, len);
358   if (script)
359     hb_buffer_set_script (hb_buffer, hb_script_from_string (script));
360   if (language)
361     hb_buffer_set_language (hb_buffer, hb_language_from_string (language));
362
363   hb_shape (hb_font, hb_face, hb_buffer, features, num_features);
364
365   num_glyphs = hb_buffer_get_length (hb_buffer);
366   hb_glyph = hb_buffer_get_glyph_infos (hb_buffer, NULL);
367   hb_position = hb_buffer_get_glyph_positions (hb_buffer, NULL);
368   cairo_glyphs = cairo_glyph_allocate (num_glyphs + 1);
369   x = 0;
370   for (i = 0; i < num_glyphs; i++)
371     {
372       cairo_glyphs[i].index = hb_glyph->codepoint;
373       cairo_glyphs[i].x = (hb_position->x_offset + x) * (1./64);
374       cairo_glyphs[i].y = -(hb_position->y_offset)    * (1./64);
375       x += hb_position->x_advance;
376
377       hb_glyph++;
378       hb_position++;
379     }
380   cairo_glyphs[i].x = x * (1./64);
381   hb_buffer_destroy (hb_buffer);
382   hb_font_destroy (hb_font);
383   hb_face_destroy (hb_face);
384   cairo_ft_scaled_font_unlock_face (scaled_font);
385
386   if (pnum_glyphs)
387     *pnum_glyphs = num_glyphs;
388   return cairo_glyphs;
389 }
390
391 static cairo_t *
392 create_context (void)
393 {
394   cairo_t *cr;
395   unsigned int fr, fg, fb, fa, br, bg, bb, ba;
396
397   if (surface)
398     cairo_surface_destroy (surface);
399   if (back_pattern)
400     cairo_pattern_destroy (back_pattern);
401   if (fore_pattern)
402     cairo_pattern_destroy (fore_pattern);
403
404   br = bg = bb = ba = 255;
405   sscanf (back + (*back=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba);
406   fr = fg = fb = 0; fa = 255;
407   sscanf (fore + (*fore=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa);
408
409   if (ba == 255 && fa == 255 && br == bg && bg == bb && fr == fg && fg == fb) {
410     /* grayscale.  use A8 surface */
411     surface = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
412     cr = cairo_create (surface);
413     cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
414     cairo_set_source_rgba (cr, 1., 1., 1., br / 255.);
415     cairo_paint (cr);
416     back_pattern = cairo_pattern_reference (cairo_get_source (cr));
417     cairo_set_source_rgba (cr, 1., 1., 1., fr / 255.);
418     fore_pattern = cairo_pattern_reference (cairo_get_source (cr));
419   } else {
420     /* color.  use (A)RGB surface */
421     if (ba != 255)
422       surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
423     else
424       surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
425     cr = cairo_create (surface);
426     cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.);
427     cairo_paint (cr);
428     back_pattern = cairo_pattern_reference (cairo_get_source (cr));
429     cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.);
430     fore_pattern = cairo_pattern_reference (cairo_get_source (cr));
431   }
432
433   cairo_set_font_face (cr, cairo_face);
434
435   return cr;
436 }
437
438 static void
439 draw (void)
440 {
441   cairo_t *cr;
442   cairo_font_extents_t font_extents;
443
444   cairo_glyph_t *glyphs = NULL;
445   unsigned int num_glyphs = 0;
446
447   const char *end, *p = text;
448   double x, y;
449
450   cr= create_context ();
451
452   cairo_set_font_size (cr, font_size);
453   cairo_font_extents (cr, &font_extents);
454
455   height = 0;
456   width = 0;
457
458   x = margin_l;
459   y = margin_t;
460
461   do {
462     cairo_text_extents_t extents;
463
464     end = strchr (p, '\n');
465     if (!end)
466       end = p + strlen (p);
467
468     if (p != text)
469         y += line_space;
470
471     if (p != end) {
472       glyphs = _hb_cr_text_glyphs (cr, p, end - p, &num_glyphs);
473
474       cairo_glyph_extents (cr, glyphs, num_glyphs, &extents);
475
476       y += ceil (font_extents.ascent);
477       width = MAX (width, extents.x_advance);
478       cairo_save (cr);
479       cairo_translate (cr, x, y);
480       cairo_show_glyphs (cr, glyphs, num_glyphs);
481       cairo_restore (cr);
482       y += ceil (font_extents.height - ceil (font_extents.ascent));
483
484       cairo_glyph_free (glyphs);
485     }
486
487     p = end + 1;
488   } while (*end);
489
490   height = y + margin_b;
491   width += margin_l + margin_r;
492
493   cairo_destroy (cr);
494 }
495
496
497
498 int
499 main (int argc, char **argv)
500 {
501   cairo_status_t status;
502
503   setlocale (LC_ALL, "");
504
505   parse_opts (argc, argv);
506
507   FT_Init_FreeType (&ft_library);
508   if (FT_New_Face (ft_library, font_file, 0, &ft_face)) {
509     fprintf (stderr, "Failed to open font file `%s'\n", font_file);
510     exit (1);
511   }
512   cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
513
514   draw ();
515   draw ();
516
517   status = cairo_surface_write_to_png (surface, out_file);
518   if (status != CAIRO_STATUS_SUCCESS) {
519     fprintf (stderr, "Failed to write output file `%s': %s\n",
520              out_file, cairo_status_to_string (status));
521     exit (1);
522   }
523
524   if (debug) {
525     free (features);
526
527     cairo_pattern_destroy (fore_pattern);
528     cairo_pattern_destroy (back_pattern);
529     cairo_surface_destroy (surface);
530     cairo_font_face_destroy (cairo_face);
531     cairo_debug_reset_static_data ();
532
533     FT_Done_Face (ft_face);
534     FT_Done_FreeType (ft_library);
535   }
536
537   return 0;
538 }
539
540
541 HB_END_DECLS