blob: a78983e38bc955f25d5336f841fcc4ed7ed37c14 [file] [log] [blame]
Behdad Esfahbod5353bf42011-02-22 18:06:19 -05001/*
Behdad Esfahbod2409d5f2011-04-21 17:14:28 -04002 * Copyright © 2010 Behdad Esfahbod
3 * Copyright © 2011 Google, Inc.
Behdad Esfahbod5353bf42011-02-22 18:06:19 -05004 *
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>
Behdad Esfahbodcab6f652011-04-04 15:36:51 -040038#include <locale.h>
Behdad Esfahbod5353bf42011-02-22 18:06:19 -050039
Behdad Esfahbodd4bee9f2011-04-27 09:24:37 -040040#include <glib.h>
Behdad Esfahbod8df90c82011-08-10 15:26:41 +020041#include <glib/gprintf.h>
Behdad Esfahbod5353bf42011-02-22 18:06:19 -050042
43#include <cairo-ft.h>
44#include <hb-ft.h>
45
Behdad Esfahbodc57d4542011-04-20 18:50:27 -040046
47
Behdad Esfahbod5353bf42011-02-22 18:06:19 -050048/* Controlled by cmd-line options */
Behdad Esfahbod8df90c82011-08-10 15:26:41 +020049struct margin_t {
50 double t, r, b, l;
51};
52static margin_t opt_margin = {18, 18, 18, 18};
Behdad Esfahbod25c48302011-08-10 16:28:38 +020053static char **opt_shapers;
Behdad Esfahbod8df90c82011-08-10 15:26:41 +020054static double line_space = 0;
Behdad Esfahbod0c6a9762011-05-31 12:59:17 -040055static int face_index = 0;
Behdad Esfahbod8df90c82011-08-10 15:26:41 +020056static double font_size = 36;
Behdad Esfahbod5353bf42011-02-22 18:06:19 -050057static const char *fore = "#000000";
Behdad Esfahboda4b781e2011-02-23 12:47:56 -050058static const char *back = "#ffffff";
Behdad Esfahbod5353bf42011-02-22 18:06:19 -050059static const char *text = NULL;
60static const char *font_file = NULL;
61static const char *out_file = "/dev/stdout";
Behdad Esfahbod39a840a2011-04-27 14:48:19 -040062static const char *direction = NULL;
Behdad Esfahbod5d91c3d2011-03-16 17:36:32 -030063static const char *script = NULL;
Behdad Esfahbod39a840a2011-04-27 14:48:19 -040064static const char *language = NULL;
Behdad Esfahbode609aeb2011-05-18 10:17:02 -040065static hb_bool_t annotate = FALSE;
Behdad Esfahbod1b4a2cc2011-04-04 14:45:28 -040066static hb_bool_t debug = FALSE;
Behdad Esfahbod5353bf42011-02-22 18:06:19 -050067
Behdad Esfahbod8df90c82011-08-10 15:26:41 +020068static hb_feature_t *features = NULL;
69static unsigned int num_features;
70
Behdad Esfahbod5353bf42011-02-22 18:06:19 -050071/* Ugh, global vars. Ugly, but does the job */
72static int width = 0;
73static int height = 0;
74static cairo_surface_t *surface = NULL;
75static cairo_pattern_t *fore_pattern = NULL;
76static cairo_pattern_t *back_pattern = NULL;
Behdad Esfahbod5353bf42011-02-22 18:06:19 -050077static cairo_font_face_t *cairo_face;
78
79
Behdad Esfahbod25c48302011-08-10 16:28:38 +020080static gchar *
81shapers_to_string (void)
Behdad Esfahbod5353bf42011-02-22 18:06:19 -050082{
Behdad Esfahbod25c48302011-08-10 16:28:38 +020083 GString *shapers = g_string_new (NULL);
84 const char **shaper_list = hb_shape_list_shapers ();
85
86 for (; *shaper_list; shaper_list++) {
87 g_string_append (shapers, *shaper_list);
88 g_string_append_c (shapers, ',');
89 }
90 g_string_truncate (shapers, MAX (0, (gint)shapers->len - 1));
91
92 return g_string_free (shapers, FALSE);
Behdad Esfahbod5353bf42011-02-22 18:06:19 -050093}
94
Behdad Esfahbod25c48302011-08-10 16:28:38 +020095
Behdad Esfahbod8df90c82011-08-10 15:26:41 +020096static G_GNUC_NORETURN gboolean
97show_version (const char *name G_GNUC_UNUSED,
98 const char *arg G_GNUC_UNUSED,
99 gpointer data G_GNUC_UNUSED,
100 GError **error G_GNUC_UNUSED)
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500101{
Behdad Esfahbod25c48302011-08-10 16:28:38 +0200102 g_printf ("%s (%s) %s\n", g_get_prgname (), PACKAGE_NAME, PACKAGE_VERSION);
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200103
Behdad Esfahbod25c48302011-08-10 16:28:38 +0200104 char *shapers = shapers_to_string ();
105 g_printf ("Available shapers: %s\n", shapers);
106 g_free (shapers);
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200107 if (strcmp (HB_VERSION_STRING, hb_version_string ()))
Behdad Esfahbod25c48302011-08-10 16:28:38 +0200108 g_printf ("Linked HarfBuzz library has a different version: %s\n", hb_version_string ());
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200109
110 exit(0);
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500111}
112
Behdad Esfahbodb2da26d2011-04-01 15:48:43 -0400113
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200114static gboolean
115parse_features (const char *name G_GNUC_UNUSED,
116 const char *arg,
117 gpointer data G_GNUC_UNUSED,
118 GError **error G_GNUC_UNUSED);
119
120static gboolean
121parse_margin (const char *name G_GNUC_UNUSED,
122 const char *arg,
123 gpointer data G_GNUC_UNUSED,
124 GError **error G_GNUC_UNUSED)
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500125{
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200126 switch (sscanf (arg, "%f %f %f %f", &opt_margin.t, &opt_margin.r, &opt_margin.b, &opt_margin.l)) {
127 case 1: opt_margin.r = opt_margin.t;
128 case 2: opt_margin.b = opt_margin.t;
129 case 3: opt_margin.l = opt_margin.r;
130 case 4: return TRUE;
131 default:
132 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
133 "%s argument should be one to four space-separated numbers",
134 name);
135 return FALSE;
136 }
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500137}
138
Behdad Esfahbod25c48302011-08-10 16:28:38 +0200139static gboolean
140parse_shapers (const char *name G_GNUC_UNUSED,
141 const char *arg,
142 gpointer data G_GNUC_UNUSED,
143 GError **error G_GNUC_UNUSED)
144{
145 opt_shapers = g_strsplit (arg, ",", 0);
146 return TRUE;
147}
148
Behdad Esfahbodfb9ca1b2011-04-04 14:50:09 -0400149
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200150void
151fail (const char *format, ...)
152{
153 const char *msg;
154
155 va_list vap;
156 va_start (vap, format);
157 msg = g_strdup_vprintf (format, vap);
158 g_printerr ("%s: %s\n", g_get_prgname (), msg);
159
160 exit (1);
161}
162
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200163void
164parse_options (int argc, char *argv[])
165{
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200166 GOptionEntry entries[] =
167 {
Behdad Esfahboda21add62011-08-10 16:07:49 +0200168 {"version", 0, G_OPTION_FLAG_NO_ARG,
169 G_OPTION_ARG_CALLBACK, (gpointer) &show_version, "Show version numbers", NULL},
170 {"debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Free all resources before exit", NULL},
171 {"output", 0, 0, G_OPTION_ARG_STRING, &out_file, "Set output file name", "filename"},
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200172
Behdad Esfahboda21add62011-08-10 16:07:49 +0200173 {"annotate", 0, 0, G_OPTION_ARG_NONE, &annotate, "Annotate output rendering", NULL},
174 {"background", 0, 0, G_OPTION_ARG_STRING, &back, "Set background color", "red/#rrggbb/#rrggbbaa"},
175 {"foreground", 0, 0, G_OPTION_ARG_STRING, &fore, "Set foreground color", "red/#rrggbb/#rrggbbaa"},
176 {"line-space", 0, 0, G_OPTION_ARG_DOUBLE, &font_size, "Set space between lines (default: 0)", "units"},
177 {"margin", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_margin, "Margin around output", "one to four numbers"},
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200178
Behdad Esfahbod25c48302011-08-10 16:28:38 +0200179 {"shapers", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_shapers, "Comma-separated list of shapers", "list"},
Behdad Esfahboda21add62011-08-10 16:07:49 +0200180 {"direction", 0, 0, G_OPTION_ARG_STRING, &direction, "Set text direction (default: auto)", "ltr/rtl/ttb/btt"},
181 {"language", 0, 0, G_OPTION_ARG_STRING, &language, "Set text language (default: $LANG)", "langstr"},
182 {"script", 0, 0, G_OPTION_ARG_STRING, &script, "Set text script (default: auto)", "ISO-15924 tag"},
183 {"features", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_features, "Font features to apply to text", "TODO"},
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200184
Behdad Esfahboda21add62011-08-10 16:07:49 +0200185 {"face-index", 0, 0, G_OPTION_ARG_INT, &face_index, "Face index (default: 0)", "index"},
186 {"font-size", 0, 0, G_OPTION_ARG_DOUBLE, &font_size, "Font size", "size"},
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200187
188 {NULL}
189 };
190 GError *error = NULL;
191 GError *parse_error = NULL;
192 GOptionContext *context;
193 size_t len;
194
195 context = g_option_context_new ("- FONT-FILE TEXT");
196
197 g_option_context_add_main_entries (context, entries, NULL);
198
199 if (!g_option_context_parse (context, &argc, &argv, &parse_error))
200 {
201 if (parse_error != NULL)
202 fail ("%s", parse_error->message);
203 else
204 fail ("Option parse error");
205 exit(1);
206 }
207 g_option_context_free(context);
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200208
209 if (argc != 3) {
210 g_printerr ("Usage: %s [OPTION...] FONT-FILE TEXT\n", g_get_prgname ());
211 exit (1);
212 }
213
214 font_file = argv[1];
215 text = g_locale_to_utf8 (argv[2], -1, NULL, NULL, &error);
216}
217
218
219
Behdad Esfahbodfb9ca1b2011-04-04 14:50:09 -0400220static void
221parse_space (char **pp)
222{
223 char c;
224#define ISSPACE(c) ((c)==' '||(c)=='\f'||(c)=='\n'||(c)=='\r'||(c)=='\t'||(c)=='\v')
225 while (c = **pp, ISSPACE (c))
226 (*pp)++;
227#undef ISSPACE
228}
229
230static hb_bool_t
231parse_char (char **pp, char c)
232{
233 parse_space (pp);
234
235 if (**pp != c)
236 return FALSE;
237
238 (*pp)++;
239 return TRUE;
240}
241
242static hb_bool_t
243parse_uint (char **pp, unsigned int *pv)
244{
245 char *p = *pp;
246 unsigned int v;
247
248 v = strtol (p, pp, 0);
249
250 if (p == *pp)
251 return FALSE;
252
253 *pv = v;
254 return TRUE;
255}
256
257
258static hb_bool_t
259parse_feature_value_prefix (char **pp, hb_feature_t *feature)
260{
261 if (parse_char (pp, '-'))
262 feature->value = 0;
263 else {
264 parse_char (pp, '+');
265 feature->value = 1;
266 }
267
268 return TRUE;
269}
270
271static hb_bool_t
272parse_feature_tag (char **pp, hb_feature_t *feature)
273{
274 char *p = *pp, c;
275
276 parse_space (pp);
277
Behdad Esfahbod08da7a32011-04-21 16:59:10 -0400278#define ISALNUM(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z') || ('0' <= (c) && (c) <= '9'))
279 while (c = **pp, ISALNUM(c))
Behdad Esfahbodfb9ca1b2011-04-04 14:50:09 -0400280 (*pp)++;
Behdad Esfahbod08da7a32011-04-21 16:59:10 -0400281#undef ISALNUM
Behdad Esfahbodfb9ca1b2011-04-04 14:50:09 -0400282
283 if (p == *pp)
284 return FALSE;
285
286 **pp = '\0';
287 feature->tag = hb_tag_from_string (p);
288 **pp = c;
289
290 return TRUE;
291}
292
293static hb_bool_t
294parse_feature_indices (char **pp, hb_feature_t *feature)
295{
296 hb_bool_t has_start;
297
298 feature->start = 0;
299 feature->end = (unsigned int) -1;
300
301 if (!parse_char (pp, '['))
302 return TRUE;
303
304 has_start = parse_uint (pp, &feature->start);
305
306 if (parse_char (pp, ':')) {
307 parse_uint (pp, &feature->end);
308 } else {
309 if (has_start)
310 feature->end = feature->start + 1;
311 }
312
313 return parse_char (pp, ']');
314}
315
316static hb_bool_t
317parse_feature_value_postfix (char **pp, hb_feature_t *feature)
318{
319 return !parse_char (pp, '=') || parse_uint (pp, &feature->value);
320}
321
322
323static hb_bool_t
324parse_one_feature (char **pp, hb_feature_t *feature)
325{
326 return parse_feature_value_prefix (pp, feature) &&
327 parse_feature_tag (pp, feature) &&
328 parse_feature_indices (pp, feature) &&
329 parse_feature_value_postfix (pp, feature) &&
330 (parse_char (pp, ',') || **pp == '\0');
331}
332
333static void
334skip_one_feature (char **pp)
335{
336 char *e;
337 e = strchr (*pp, ',');
338 if (e)
339 *pp = e + 1;
340 else
341 *pp = *pp + strlen (*pp);
342}
343
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200344static gboolean
345parse_features (const char *name G_GNUC_UNUSED,
346 const char *arg,
347 gpointer data G_GNUC_UNUSED,
348 GError **error G_GNUC_UNUSED)
Behdad Esfahbodb2da26d2011-04-01 15:48:43 -0400349{
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200350 char *s = (char *) arg;
Behdad Esfahbodb2da26d2011-04-01 15:48:43 -0400351 char *p;
Behdad Esfahbodb2da26d2011-04-01 15:48:43 -0400352
353 num_features = 0;
354 features = NULL;
355
356 if (!*s)
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200357 return TRUE;
Behdad Esfahbodb2da26d2011-04-01 15:48:43 -0400358
359 /* count the features first, so we can allocate memory */
360 p = s;
361 do {
362 num_features++;
363 p = strchr (p, ',');
364 if (p)
Behdad Esfahbodfb9ca1b2011-04-04 14:50:09 -0400365 p++;
Behdad Esfahbodb2da26d2011-04-01 15:48:43 -0400366 } while (p);
367
Behdad Esfahbodf1425a52011-04-27 12:15:06 -0400368 features = (hb_feature_t *) calloc (num_features, sizeof (*features));
Behdad Esfahbodb2da26d2011-04-01 15:48:43 -0400369
370 /* now do the actual parsing */
371 p = s;
Behdad Esfahbodfb9ca1b2011-04-04 14:50:09 -0400372 num_features = 0;
373 while (*p) {
374 if (parse_one_feature (&p, &features[num_features]))
375 num_features++;
376 else
377 skip_one_feature (&p);
Behdad Esfahbodb2da26d2011-04-01 15:48:43 -0400378 }
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200379
380 return TRUE;
Behdad Esfahbodb2da26d2011-04-01 15:48:43 -0400381}
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500382
383
384static cairo_glyph_t *
385_hb_cr_text_glyphs (cairo_t *cr,
Behdad Esfahbod31f18ab2011-06-15 09:49:58 -0400386 const char *utf8, int len,
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500387 unsigned int *pnum_glyphs)
388{
389 cairo_scaled_font_t *scaled_font = cairo_get_scaled_font (cr);
390 FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500391 hb_font_t *hb_font = hb_ft_font_create (ft_face, NULL);
392 hb_buffer_t *hb_buffer;
393 cairo_glyph_t *cairo_glyphs;
394 hb_glyph_info_t *hb_glyph;
395 hb_glyph_position_t *hb_position;
396 unsigned int num_glyphs, i;
Behdad Esfahbod1883af32011-05-16 15:18:16 -0400397 hb_position_t x, y;
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500398
Behdad Esfahbodd4bee9f2011-04-27 09:24:37 -0400399 hb_buffer = hb_buffer_create (0);
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500400
Behdad Esfahbod39a840a2011-04-27 14:48:19 -0400401 if (direction)
402 hb_buffer_set_direction (hb_buffer, hb_direction_from_string (direction));
Behdad Esfahbod5d91c3d2011-03-16 17:36:32 -0300403 if (script)
Behdad Esfahbod0e8d35c2011-04-15 19:07:10 -0400404 hb_buffer_set_script (hb_buffer, hb_script_from_string (script));
Behdad Esfahbodc0af1932011-04-15 19:26:24 -0400405 if (language)
406 hb_buffer_set_language (hb_buffer, hb_language_from_string (language));
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500407
Behdad Esfahbodd4bee9f2011-04-27 09:24:37 -0400408 if (len < 0)
Behdad Esfahbod31f18ab2011-06-15 09:49:58 -0400409 len = strlen (utf8);
410 hb_buffer_add_utf8 (hb_buffer, utf8, len, 0, len);
Behdad Esfahbodd4bee9f2011-04-27 09:24:37 -0400411
Behdad Esfahbod25c48302011-08-10 16:28:38 +0200412 if (!hb_shape_full (hb_font, hb_buffer, features, num_features, NULL, opt_shapers))
413 fail ("All shapers failed");
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500414
415 num_glyphs = hb_buffer_get_length (hb_buffer);
Ryan Lortie70566be2011-04-15 18:32:36 -0400416 hb_glyph = hb_buffer_get_glyph_infos (hb_buffer, NULL);
417 hb_position = hb_buffer_get_glyph_positions (hb_buffer, NULL);
Behdad Esfahbod9aa6f962011-05-16 15:08:31 -0400418 cairo_glyphs = cairo_glyph_allocate (num_glyphs);
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500419 x = 0;
Behdad Esfahbod1883af32011-05-16 15:18:16 -0400420 y = 0;
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500421 for (i = 0; i < num_glyphs; i++)
422 {
423 cairo_glyphs[i].index = hb_glyph->codepoint;
Behdad Esfahbod1883af32011-05-16 15:18:16 -0400424 cairo_glyphs[i].x = ( hb_position->x_offset + x) * (1./64);
425 cairo_glyphs[i].y = (-hb_position->y_offset + y) * (1./64);
426 x += hb_position->x_advance;
427 y += -hb_position->y_advance;
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500428
429 hb_glyph++;
430 hb_position++;
431 }
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500432 hb_buffer_destroy (hb_buffer);
433 hb_font_destroy (hb_font);
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500434 cairo_ft_scaled_font_unlock_face (scaled_font);
435
436 if (pnum_glyphs)
437 *pnum_glyphs = num_glyphs;
438 return cairo_glyphs;
439}
440
441static cairo_t *
442create_context (void)
443{
444 cairo_t *cr;
445 unsigned int fr, fg, fb, fa, br, bg, bb, ba;
446
447 if (surface)
448 cairo_surface_destroy (surface);
449 if (back_pattern)
450 cairo_pattern_destroy (back_pattern);
451 if (fore_pattern)
452 cairo_pattern_destroy (fore_pattern);
453
454 br = bg = bb = ba = 255;
455 sscanf (back + (*back=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba);
456 fr = fg = fb = 0; fa = 255;
457 sscanf (fore + (*fore=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa);
458
Behdad Esfahbode609aeb2011-05-18 10:17:02 -0400459 if (!annotate && ba == 255 && fa == 255 && br == bg && bg == bb && fr == fg && fg == fb) {
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500460 /* grayscale. use A8 surface */
461 surface = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
462 cr = cairo_create (surface);
463 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
464 cairo_set_source_rgba (cr, 1., 1., 1., br / 255.);
465 cairo_paint (cr);
466 back_pattern = cairo_pattern_reference (cairo_get_source (cr));
467 cairo_set_source_rgba (cr, 1., 1., 1., fr / 255.);
468 fore_pattern = cairo_pattern_reference (cairo_get_source (cr));
469 } else {
470 /* color. use (A)RGB surface */
471 if (ba != 255)
472 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
473 else
474 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
475 cr = cairo_create (surface);
476 cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.);
477 cairo_paint (cr);
478 back_pattern = cairo_pattern_reference (cairo_get_source (cr));
479 cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.);
480 fore_pattern = cairo_pattern_reference (cairo_get_source (cr));
481 }
482
483 cairo_set_font_face (cr, cairo_face);
484
485 return cr;
486}
487
488static void
489draw (void)
490{
491 cairo_t *cr;
492 cairo_font_extents_t font_extents;
493
494 cairo_glyph_t *glyphs = NULL;
495 unsigned int num_glyphs = 0;
496
497 const char *end, *p = text;
498 double x, y;
499
500 cr= create_context ();
501
502 cairo_set_font_size (cr, font_size);
503 cairo_font_extents (cr, &font_extents);
504
505 height = 0;
506 width = 0;
507
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200508 x = opt_margin.l;
509 y = opt_margin.t;
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500510
511 do {
512 cairo_text_extents_t extents;
513
514 end = strchr (p, '\n');
515 if (!end)
516 end = p + strlen (p);
517
518 if (p != text)
519 y += line_space;
520
521 if (p != end) {
522 glyphs = _hb_cr_text_glyphs (cr, p, end - p, &num_glyphs);
Behdad Esfahbod1b4a2cc2011-04-04 14:45:28 -0400523
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500524 cairo_glyph_extents (cr, glyphs, num_glyphs, &extents);
525
526 y += ceil (font_extents.ascent);
527 width = MAX (width, extents.x_advance);
528 cairo_save (cr);
529 cairo_translate (cr, x, y);
Behdad Esfahbode609aeb2011-05-18 10:17:02 -0400530 if (annotate) {
531 unsigned int i;
532 cairo_save (cr);
533
534 /* Draw actual glyph origins */
535 cairo_set_source_rgba (cr, 1., 0., 0., .5);
536 cairo_set_line_width (cr, 5);
537 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
538 for (i = 0; i < num_glyphs; i++) {
539 cairo_move_to (cr, glyphs[i].x, glyphs[i].y);
540 cairo_rel_line_to (cr, 0, 0);
541 }
542 cairo_stroke (cr);
543
544 cairo_restore (cr);
545 }
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500546 cairo_show_glyphs (cr, glyphs, num_glyphs);
547 cairo_restore (cr);
548 y += ceil (font_extents.height - ceil (font_extents.ascent));
Behdad Esfahbod1b4a2cc2011-04-04 14:45:28 -0400549
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500550 cairo_glyph_free (glyphs);
551 }
552
553 p = end + 1;
554 } while (*end);
555
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200556 height = y + opt_margin.b;
557 width += opt_margin.l + opt_margin.r;
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500558
559 cairo_destroy (cr);
560}
561
562
563
564int
565main (int argc, char **argv)
566{
Behdad Esfahbod31f18ab2011-06-15 09:49:58 -0400567 static FT_Library ft_library;
568 static FT_Face ft_face;
Behdad Esfahbodb7b29682011-03-02 01:01:03 -0500569 cairo_status_t status;
570
Behdad Esfahbodcab6f652011-04-04 15:36:51 -0400571 setlocale (LC_ALL, "");
572
Behdad Esfahbod8df90c82011-08-10 15:26:41 +0200573 parse_options (argc, argv);
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500574
575 FT_Init_FreeType (&ft_library);
Behdad Esfahbod0c6a9762011-05-31 12:59:17 -0400576 if (FT_New_Face (ft_library, font_file, face_index, &ft_face)) {
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500577 fprintf (stderr, "Failed to open font file `%s'\n", font_file);
578 exit (1);
579 }
580 cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
581
582 draw ();
583 draw ();
584
Behdad Esfahbodb7b29682011-03-02 01:01:03 -0500585 status = cairo_surface_write_to_png (surface, out_file);
586 if (status != CAIRO_STATUS_SUCCESS) {
587 fprintf (stderr, "Failed to write output file `%s': %s\n",
588 out_file, cairo_status_to_string (status));
589 exit (1);
590 }
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500591
Behdad Esfahbod1b4a2cc2011-04-04 14:45:28 -0400592 if (debug) {
593 free (features);
Behdad Esfahbod25c48302011-08-10 16:28:38 +0200594 g_free (opt_shapers);
Behdad Esfahbod1b4a2cc2011-04-04 14:45:28 -0400595
596 cairo_pattern_destroy (fore_pattern);
597 cairo_pattern_destroy (back_pattern);
598 cairo_surface_destroy (surface);
599 cairo_font_face_destroy (cairo_face);
600 cairo_debug_reset_static_data ();
601
602 FT_Done_Face (ft_face);
603 FT_Done_FreeType (ft_library);
604 }
605
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500606 return 0;
607}
Behdad Esfahbodc57d4542011-04-20 18:50:27 -0400608
609