]> git.cworth.org Git - spritext/blob - spritext.c
More refactoring out of main (get_max_width_height)
[spritext] / spritext.c
1 /* XXX: Copyright/license blurb needed here */
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <libgen.h>
7 #include <cairo.h>
8 #include <cairo-ft.h>
9
10 #ifndef CAIRO_HAS_PNG_FUNCTIONS
11 #error This program requires cairo with PNG support
12 #endif
13
14 #include <ft2build.h>
15 #include FT_FREETYPE_H
16
17 #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
18
19 #define TRUE (1==1)
20 #define FALSE (!TRUE)
21
22 #if USE_CGIC
23 static cairo_status_t
24 stdio_write (void *closure, const unsigned char *data, unsigned int length);
25 #endif
26
27 static double
28 get_max_width (cairo_t *cr, const char *characters)
29 {
30     int i;
31     double max_width = 0.;
32     char string[2];
33     string[1] = '\0';
34     for (i = 0; i < ARRAY_SIZE(characters); i++) {
35         cairo_text_extents_t extents;
36         string[0] = characters[i];
37         cairo_text_extents (cr, string, &extents);
38         if ((extents.width + (extents.x_bearing * 2)) > max_width)
39             max_width = extents.width + (extents.x_bearing * 2);
40     }
41     return max_width;
42 }
43
44 static double
45 get_max_height (cairo_t *cr, const char *characters)
46 {
47     int i;
48     double max_height = 0.;
49     char string[2];
50     string[1] = '\0';
51     for (i = 0; i < ARRAY_SIZE(characters); i++) {
52         cairo_text_extents_t extents;
53         string[0] = characters[i];
54         cairo_text_extents (cr, string, &extents);
55 //    if ((extents.height - extents.y_bearing) > max_height)
56 //      max_height = extents.height - extents.y_bearing;
57         if ((extents.height) > max_height)
58             max_height = extents.height;
59     }
60     return max_height;
61 }
62
63 typedef struct {
64     char *family;
65     double size;
66     struct {
67         double red;
68         double green;
69         double blue;
70     } color;
71 } args_t;
72
73 static void
74 parse_args (args_t *args)
75 {
76     /* First some defaults */
77     args->family = "Vera";
78     args->size = 20;
79     args->color.red = 0.0;
80     args->color.green = 0.0;
81     args->color.blue = 0.0;
82
83     /* XXX: Next, we should override the defaults based on
84      * command-line arguments. */
85
86     /* XXX: Finally, we'll want to allow CGI-based arguments as
87      * well. */
88 #if USE_CGIG
89     /* QueryString */
90     int fontsize;
91     char format[5];
92     char fillcolor[20];
93     char fontname[20];
94     cgiFormStringNoNewlines("fontname", fontname, 20);
95     cgiFormInteger("fontsize", &fontsize, 50);
96     cgiFormStringNoNewlines("format", format, 5);
97     cgiFormStringNoNewlines("fillcolor", fillcolor, 20);
98
99     int fillcolor_red;
100     int fillcolor_green;
101     int fillcolor_blue;
102     sscanf (fillcolor, " rgb : %d , %d , %d ", &fillcolor_red, &fillcolor_green, &fillcolor_blue);
103     if ( format[0] == 'j' && format[1] == 's' && format[2] == 'o' && format[3] == 'n' )
104         outputJson = TRUE;
105 #endif
106 }
107
108 static FT_Face
109 load_ft_face_for_family (const char *family)
110 {
111     /* XXX: Instead of a hard-coded filename here, we should really be
112      * taking family and using fontconfig to map that to a
113      * filename. */
114     const char font_filename[] = "./Vera.ttf";
115
116     int error;
117     FT_Face     ft_face;
118     FT_Library  library;
119
120     error = FT_Init_FreeType( &library );
121     if ( error ) {
122         fprintf (stderr, "Fatal error initializing freetype.\n");
123         exit (1);
124     }
125
126     error = FT_New_Face( library, font_filename,
127                          0,
128                          &ft_face );
129     if ( error == FT_Err_Unknown_File_Format ) {
130         fprintf (stderr, "Unsupported font format: %s\n", font_filename);
131         exit (1);
132     } else if ( error ) {
133         fprintf (stderr, "Failed to open file: %s\n", font_filename);
134         exit (1);
135     }
136
137     return ft_face;
138 }
139
140 static void
141 get_characters_max_width_height (FT_Face ft_face, double size,
142                                  const char *characters,
143                                  double *max_width, double *max_height)
144 {
145     cairo_surface_t *surface;
146     cairo_t *cr;
147     cairo_font_face_t *cr_face;
148
149     surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
150                                           1, 1);
151
152     cr = cairo_create (surface);
153
154 /*
155   cairo_select_font_face (cr, fontname,
156   CAIRO_FONT_SLANT_NORMAL,
157   CAIRO_FONT_WEIGHT_NORMAL);
158 */
159     cr_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
160     cairo_set_font_face (cr, cr_face);
161
162     cairo_set_font_size (cr, size);
163
164     cairo_set_line_width (cr, 1.0);
165
166     *max_width = get_max_width(cr, characters);
167     *max_height = get_max_height(cr, characters);
168
169     cairo_font_face_destroy (cr_face);
170
171     cairo_destroy (cr);
172
173     cairo_surface_destroy(surface);
174 }
175
176 int
177 main (void)
178 {
179     char outputJson = FALSE;
180
181     char characters[] = {
182         '!', '"', '#', '$', '%', '&','\'', '(',
183         ')', '*', '+', ',', '-', '.', '/', '0',
184         '1', '2', '3', '4', '5', '6', '7', '8',
185         '9', ':', ';', '<', '=', '>', '?', '@',
186         'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
187         'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
188         'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
189         'Y', 'Z', '[','\\', ']', '^', '_', '`',
190         'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
191         'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
192         'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
193         'y', 'z', '{', '|', '}', '~'
194     };
195
196     int i;
197     args_t args;
198     FT_Face ft_face;
199     FT_UInt     left_index, right_index;
200     FT_Bool     use_kerning;
201     FT_Vector   kerning;
202     double max_width, max_height;
203     cairo_surface_t *surface;
204     cairo_t *cr;
205     cairo_font_face_t *cr_face;
206
207     parse_args (&args);
208
209 #if USE_CGIC
210     if (outputJson)
211     {
212         cgiHeaderContentType("application/json");
213     } else {
214         cgiHeaderContentType("image/png");
215     }
216 #endif
217
218     ft_face = load_ft_face_for_family (args.family);
219
220     use_kerning = FT_HAS_KERNING( ft_face );
221
222     get_characters_max_width_height (ft_face, args.size,
223                                      characters,
224                                      &max_width, &max_height);
225
226     /* Draw */
227     surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
228                                           (max_width + 8) * 10,
229                                           (max_height + 8) * 10);
230
231     cr = cairo_create (surface);
232
233 /*
234   cairo_select_font_face (cr, fontname,
235   CAIRO_FONT_SLANT_NORMAL,
236   CAIRO_FONT_WEIGHT_NORMAL);
237 */
238     cr_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
239     cairo_set_font_face (cr, cr_face);
240
241     cairo_set_font_size (cr, args.size);
242
243     cairo_text_extents_t extents;
244     double x = 0.0;
245     double y = 0.0;
246
247     if (outputJson)
248         printf("{");
249
250     cairo_translate (cr, 0, -(max_height / 2.50));
251
252 //    cairo_translate (cr, 0, -5.0);
253
254     char string[2];
255     string[1] = '\0';
256     for (i = 0; i < ARRAY_SIZE(characters); i++)
257     {
258         if (i % 10 == 0)
259         {
260             x = 0.;
261             y += max_height + 8;
262         }
263         string[0] = characters[i];
264
265         cairo_text_extents (cr, string, &extents);
266
267         if (outputJson)
268         {
269             if (i > 0)
270                 printf (",\n");
271             printf ("u%i:{", characters[i]);
272             printf ("x:%.1f,y:%.1f,w:%.1f,h:%.1f",
273                     x,
274                     y - (max_height * 1.15),
275                     extents.width + extents.x_bearing,
276                     extents.height);
277             int j;
278             for ( j = 0; j < ARRAY_SIZE(characters); j++ )
279             {
280                 right_index = FT_Get_Char_Index( ft_face, characters[i] );
281                 left_index = FT_Get_Char_Index( ft_face, characters[j] );
282     
283                 FT_Get_Kerning( ft_face, left_index, right_index,
284                                 FT_KERNING_UNSCALED, &kerning );
285     
286                 if ( kerning.x )
287                     printf(",k%d:%ld", characters[j], kerning.x);
288             }
289
290             printf ("}");
291         }
292
293         x -= extents.x_bearing;
294   
295         cairo_move_to (cr, x, y);
296         cairo_set_source_rgb (cr, 0., 0., 0.); // black
297         cairo_set_source_rgb (cr,
298                               args.color.red,
299                               args.color.green,
300                               args.color.blue);
301         cairo_text_path (cr, string);
302         cairo_fill (cr);
303
304         x += extents.x_bearing;
305         x += max_width + 8;
306     }
307
308 /*
309   int thick_width = 4;
310   int thin_width = 0;
311
312   for (c = 'A'; c <= 'I'; c++)
313   {
314   string[0] = c;
315   int spacing = ((c - 'A') * letterspacing) + 1;
316
317
318   cairo_save(cr);
319
320   cairo_move_to (cr, spacing, 0);
321   cairo_text_path (cr, string);
322 //      cairo_set_source_rgb (cr, 0.1, 0.2, 0.2); // gray
323 cairo_set_source_rgb (cr, 0.8, 0.8, 0.85); // silver
324 cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
325
326 cairo_push_group (cr);
327 cairo_set_line_width (cr, thick_width);
328 cairo_stroke_preserve (cr);
329 cairo_set_line_width (cr, thin_width);
330 cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
331 cairo_stroke_preserve (cr);
332 cairo_fill (cr);
333 cairo_pop_group_to_source (cr);
334
335 cairo_paint (cr);
336
337 cairo_restore(cr);
338
339 cairo_move_to (cr, spacing, 0);
340 cairo_text_path (cr, string);
341 cairo_set_source_rgb (cr, 0.4, 0.45, 0.7); // blue
342
343 cairo_fill (cr);
344 }
345 */
346
347     cairo_scale (cr, 1, -1);
348     cairo_push_group (cr);
349 /*
350
351 cairo_pattern_t *gradient;
352
353 for (i = 0; i < 9; i++)
354 {
355 char c[2];
356 strncpy(c, STRING + i, 1);
357 c[1] = '\0';
358 int spacing = (i * letterspacing) + 1;
359 cairo_move_to (cr, spacing, -3);
360 cairo_show_text (cr, c);
361
362 }
363 cairo_pop_group_to_source (cr);
364
365 gradient = cairo_pattern_create_linear (0, 0, 0, -fontsize);
366 cairo_pattern_add_color_stop_rgba (gradient, 0.0, 1, 1, 1, 0.5);
367 cairo_pattern_add_color_stop_rgba (gradient, 0.7, 1, 1, 1, 0.0);
368
369 cairo_mask (cr, gradient);
370
371 cairo_pattern_destroy (gradient);
372 */
373
374 #if USE_CGIC
375     if (outputJson)
376     {
377         printf("}");
378     } else {
379         cairo_surface_write_to_png_stream (surface, stdio_write, cgiOut);
380     }
381 #endif
382
383     cairo_surface_write_to_png (surface, "spritext-output.png");
384     printf ("Result written to spritext-output.png\n");
385
386     cairo_destroy (cr);
387
388     cairo_surface_destroy (surface);
389
390     return 0;
391 }
392
393 #if USE_CGIC
394 static cairo_status_t
395 stdio_write (void *closure, const unsigned char *data, unsigned int length)
396 {
397     FILE *file = closure;
398     if (fwrite (data, 1, length, file) == length)
399         return CAIRO_STATUS_SUCCESS;
400     else
401         return CAIRO_STATUS_WRITE_ERROR;
402 }
403 #endif