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