blob: fd9fec7f2fb220ec1a6f5827c4f4c070bed47edf [file] [log] [blame]
Behdad Esfahbod5353bf42011-02-22 18:06:19 -05001/*
2 * Copyright (C) 2010 Behdad Esfahbod
3 * Copyright (C) 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
39#include <glib.h>
40#include <hb-glib.h>
41
42#include <cairo-ft.h>
43#include <hb-ft.h>
44
45/* Controlled by cmd-line options */
46static int margin_t = 10;
47static int margin_b = 10;
48static int margin_l = 10;
49static int margin_r = 10;
50static int line_space = 0;
51static double font_size = 18;
52static const char *fore = "#000000";
Behdad Esfahboda4b781e2011-02-23 12:47:56 -050053static const char *back = "#ffffff";
Behdad Esfahbod5353bf42011-02-22 18:06:19 -050054static const char *text = NULL;
55static const char *font_file = NULL;
56static const char *out_file = "/dev/stdout";
57static const char *language = NULL;
Behdad Esfahbod5d91c3d2011-03-16 17:36:32 -030058static const char *script = NULL;
Behdad Esfahbod5353bf42011-02-22 18:06:19 -050059
60/* Ugh, global vars. Ugly, but does the job */
61static int width = 0;
62static int height = 0;
63static cairo_surface_t *surface = NULL;
64static cairo_pattern_t *fore_pattern = NULL;
65static cairo_pattern_t *back_pattern = NULL;
66static FT_Library ft_library;
67static FT_Face ft_face;
68static cairo_font_face_t *cairo_face;
69
70
71G_GNUC_NORETURN static void
72usage (FILE *f, int status)
73{
74 fprintf (f, "usage: hb-view [OPTS...] font-file text\n");
75 exit (status);
76}
77
78G_GNUC_NORETURN static void
79version (void)
80{
81 printf ("hb-view (harfbuzz) %s\n", PACKAGE_VERSION);
82 exit (0);
83}
84
85static void
86parse_opts (int argc, char **argv)
87{
88 argv[0] = (char *) "hb-view";
89 while (1)
90 {
91 int option_index = 0, c;
92 static struct option long_options[] = {
93 {"help", 0, 0, 'h'},
94 {"version", 0, 0, 'v'},
95 {"margin", 1, 0, 'm'},
96 {"line-space", 1, 0, 'l'},
97 {"font-size", 1, 0, 's'},
98 {"foreground", 1, 0, 'F'},
99 {"background", 1, 0, 'B'},
100 {"language", 1, 0, 'L'},
Behdad Esfahbod5d91c3d2011-03-16 17:36:32 -0300101 {"script", 1, 0, 'S'},
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500102 {"output", 1, 0, 'o'},
103 {0, 0, 0, 0}
104 };
105
106 c = getopt_long (argc, argv, "", long_options, &option_index);
107 if (c == -1)
108 break;
109
110 switch (c)
111 {
112 case 0:
113 break;
114 case 'h':
115 usage (stdout, 0);
116 break;
117 case 'v':
118 version ();
119 break;
120 case 'l':
121 line_space = atoi (optarg);
122 break;
123 case 'm':
124 switch (sscanf (optarg, "%d %d %d %d", &margin_t, &margin_r, &margin_b, &margin_l)) {
125 case 1: margin_r = margin_t;
126 case 2: margin_b = margin_t;
127 case 3: margin_l = margin_r;
128 }
129 break;
130 case 's':
131 font_size = strtod (optarg, NULL);
132 break;
133 case 'f':
134 font_file = optarg;
135 break;
136 case 'F':
137 fore = optarg;
138 break;
139 case 'B':
140 back = optarg;
141 break;
142 case 't':
143 text = optarg;
144 break;
145 case 'L':
146 language = optarg;
147 break;
Behdad Esfahbod5d91c3d2011-03-16 17:36:32 -0300148 case 'S':
149 script = optarg;
150 break;
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500151 case 'o':
152 out_file = optarg;
153 break;
154 case '?':
155 usage (stdout, 1);
156 break;
157 default:
158 break;
159 }
160 }
161 if (optind + 2 != argc)
162 usage (stderr, 1);
163 font_file = argv[optind++];
164 text = argv[optind++];
165}
166
167
168
169static cairo_glyph_t *
170_hb_cr_text_glyphs (cairo_t *cr,
171 const char *text, int len,
172 unsigned int *pnum_glyphs)
173{
174 cairo_scaled_font_t *scaled_font = cairo_get_scaled_font (cr);
175 FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font);
176 hb_face_t *hb_face = hb_ft_face_create (ft_face, NULL);
177 hb_font_t *hb_font = hb_ft_font_create (ft_face, NULL);
178 hb_buffer_t *hb_buffer;
179 cairo_glyph_t *cairo_glyphs;
180 hb_glyph_info_t *hb_glyph;
181 hb_glyph_position_t *hb_position;
182 unsigned int num_glyphs, i;
183 hb_position_t x;
184
185 if (len < 0)
186 len = strlen (text);
187 hb_buffer = hb_buffer_create (len);
188
189 hb_buffer_set_unicode_funcs (hb_buffer, hb_glib_get_unicode_funcs ());
190
191 hb_buffer_add_utf8 (hb_buffer, text, len, 0, len);
Behdad Esfahbod5d91c3d2011-03-16 17:36:32 -0300192 if (script)
193 hb_buffer_set_script (hb_buffer, hb_script_from_iso15924_tag (hb_tag_from_string (script)));
194 else
195 hb_buffer_set_script (hb_buffer, HB_SCRIPT_INVALID);
Behdad Esfahbod3286fc02011-03-16 14:53:32 -0300196 hb_buffer_set_direction (hb_buffer, HB_DIRECTION_INVALID);
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500197 hb_buffer_set_language (hb_buffer, hb_language_from_string (language));
198
199 hb_shape (hb_font, hb_face, hb_buffer, NULL, 0);
200
201 num_glyphs = hb_buffer_get_length (hb_buffer);
202 hb_glyph = hb_buffer_get_glyph_infos (hb_buffer);
203 hb_position = hb_buffer_get_glyph_positions (hb_buffer);
204 cairo_glyphs = cairo_glyph_allocate (num_glyphs + 1);
205 x = 0;
206 for (i = 0; i < num_glyphs; i++)
207 {
208 cairo_glyphs[i].index = hb_glyph->codepoint;
209 cairo_glyphs[i].x = (hb_position->x_offset + x) * (1./64);
210 cairo_glyphs[i].y = -(hb_position->y_offset) * (1./64);
211 x += hb_position->x_advance;
212
213 hb_glyph++;
214 hb_position++;
215 }
216 cairo_glyphs[i].x = x * (1./64);
217 hb_buffer_destroy (hb_buffer);
218 hb_font_destroy (hb_font);
219 hb_face_destroy (hb_face);
220 cairo_ft_scaled_font_unlock_face (scaled_font);
221
222 if (pnum_glyphs)
223 *pnum_glyphs = num_glyphs;
224 return cairo_glyphs;
225}
226
227static cairo_t *
228create_context (void)
229{
230 cairo_t *cr;
231 unsigned int fr, fg, fb, fa, br, bg, bb, ba;
232
233 if (surface)
234 cairo_surface_destroy (surface);
235 if (back_pattern)
236 cairo_pattern_destroy (back_pattern);
237 if (fore_pattern)
238 cairo_pattern_destroy (fore_pattern);
239
240 br = bg = bb = ba = 255;
241 sscanf (back + (*back=='#'), "%2x%2x%2x%2x", &br, &bg, &bb, &ba);
242 fr = fg = fb = 0; fa = 255;
243 sscanf (fore + (*fore=='#'), "%2x%2x%2x%2x", &fr, &fg, &fb, &fa);
244
245 if (ba == 255 && fa == 255 && br == bg && bg == bb && fr == fg && fg == fb) {
246 /* grayscale. use A8 surface */
247 surface = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
248 cr = cairo_create (surface);
249 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
250 cairo_set_source_rgba (cr, 1., 1., 1., br / 255.);
251 cairo_paint (cr);
252 back_pattern = cairo_pattern_reference (cairo_get_source (cr));
253 cairo_set_source_rgba (cr, 1., 1., 1., fr / 255.);
254 fore_pattern = cairo_pattern_reference (cairo_get_source (cr));
255 } else {
256 /* color. use (A)RGB surface */
257 if (ba != 255)
258 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
259 else
260 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
261 cr = cairo_create (surface);
262 cairo_set_source_rgba (cr, br / 255., bg / 255., bb / 255., ba / 255.);
263 cairo_paint (cr);
264 back_pattern = cairo_pattern_reference (cairo_get_source (cr));
265 cairo_set_source_rgba (cr, fr / 255., fg / 255., fb / 255., fa / 255.);
266 fore_pattern = cairo_pattern_reference (cairo_get_source (cr));
267 }
268
269 cairo_set_font_face (cr, cairo_face);
270
271 return cr;
272}
273
274static void
275draw (void)
276{
277 cairo_t *cr;
278 cairo_font_extents_t font_extents;
279
280 cairo_glyph_t *glyphs = NULL;
281 unsigned int num_glyphs = 0;
282
283 const char *end, *p = text;
284 double x, y;
285
286 cr= create_context ();
287
288 cairo_set_font_size (cr, font_size);
289 cairo_font_extents (cr, &font_extents);
290
291 height = 0;
292 width = 0;
293
294 x = margin_l;
295 y = margin_t;
296
297 do {
298 cairo_text_extents_t extents;
299
300 end = strchr (p, '\n');
301 if (!end)
302 end = p + strlen (p);
303
304 if (p != text)
305 y += line_space;
306
307 if (p != end) {
308 glyphs = _hb_cr_text_glyphs (cr, p, end - p, &num_glyphs);
309 cairo_glyph_extents (cr, glyphs, num_glyphs, &extents);
310
311 y += ceil (font_extents.ascent);
312 width = MAX (width, extents.x_advance);
313 cairo_save (cr);
314 cairo_translate (cr, x, y);
315 cairo_show_glyphs (cr, glyphs, num_glyphs);
316 cairo_restore (cr);
317 y += ceil (font_extents.height - ceil (font_extents.ascent));
318 cairo_glyph_free (glyphs);
319 }
320
321 p = end + 1;
322 } while (*end);
323
324 height = y + margin_b;
325 width += margin_l + margin_r;
326
327 cairo_destroy (cr);
328}
329
330
331
332int
333main (int argc, char **argv)
334{
Behdad Esfahbodb7b29682011-03-02 01:01:03 -0500335 cairo_status_t status;
336
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500337 parse_opts (argc, argv);
338
339 FT_Init_FreeType (&ft_library);
340 if (FT_New_Face (ft_library, font_file, 0, &ft_face)) {
341 fprintf (stderr, "Failed to open font file `%s'\n", font_file);
342 exit (1);
343 }
344 cairo_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
345
346 draw ();
347 draw ();
348
Behdad Esfahbodb7b29682011-03-02 01:01:03 -0500349 status = cairo_surface_write_to_png (surface, out_file);
350 if (status != CAIRO_STATUS_SUCCESS) {
351 fprintf (stderr, "Failed to write output file `%s': %s\n",
352 out_file, cairo_status_to_string (status));
353 exit (1);
354 }
Behdad Esfahbod5353bf42011-02-22 18:06:19 -0500355
356 return 0;
357}