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