]> git.cworth.org Git - spritext/blobdiff - spritext.c
Account for x_bearing/y_bearing to align each glyph properly
[spritext] / spritext.c
index 87feeae273f16fa27c6164a3e2277eb4d7ba70d0..21d77f54df6e9036cd690566c716a97751a62313 100644 (file)
@@ -1,13 +1,16 @@
+/* XXX: Copyright/license blurb needed here */
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <libgen.h>
 #include <cairo.h>
 #include <cairo-ft.h>
-#include <cgic.h>
+
 #ifndef CAIRO_HAS_PNG_FUNCTIONS
 #error This program requires cairo with PNG support
 #endif
+
 #include <ft2build.h>
 #include FT_FREETYPE_H
 
 #define TRUE (1==1)
 #define FALSE (!TRUE)
 
-static cairo_status_t
-stdio_write (void *closure, const unsigned char *data, unsigned int length);
-
-double
-get_max_width (cairo_t *cr, char *characters);
-
-double
-get_max_height (cairo_t *cr, char *characters);
-
-void
-out_stroke (cairo_t *cr)
+static double
+get_max_width (cairo_t *cr, const char *characters, int num_characters)
 {
-    
+    int i;
+    double max_width = 0.;
+    char string[2];
+    string[1] = '\0';
+    for (i = 0; i < num_characters; i++) {
+       cairo_text_extents_t extents;
+       string[0] = characters[i];
+       cairo_text_extents (cr, string, &extents);
+       if ((extents.width + (extents.x_bearing * 2)) > max_width)
+           max_width = extents.width + (extents.x_bearing * 2);
+    }
+    return max_width;
 }
 
-int
-cgiMain ()
+static double
+get_max_height (cairo_t *cr, const char *characters, int num_characters)
 {
-    char outputJson = FALSE;
-
-    char characters[] = {
-      '!', '"', '#', '$', '%', '&','\'', '(',
-      ')', '*', '+', ',', '-', '.', '/', '0',
-      '1', '2', '3', '4', '5', '6', '7', '8',
-      '9', ':', ';', '<', '=', '>', '?', '@',
-      'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
-      'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
-      'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
-      'Y', 'Z', '[','\\', ']', '^', '_', '`',
-      'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
-      'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
-      'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
-      'y', 'z', '{', '|', '}', '~'
-    };
-
     int i;
+    double max_height = 0.;
+    char string[2];
+    string[1] = '\0';
+    for (i = 0; i < num_characters; i++) {
+       cairo_text_extents_t extents;
+       string[0] = characters[i];
+       cairo_text_extents (cr, string, &extents);
+       if ((extents.height) > max_height)
+           max_height = extents.height;
+    }
+    return max_height;
+}
 
+typedef struct {
+    char *family;
+    double size;
+    struct {
+       double red;
+       double green;
+       double blue;
+    } color;
+} args_t;
+
+static void
+parse_args (args_t *args)
+{
+    /* First some defaults */
+    args->family = "Vera";
+    args->size = 40;
+    args->color.red = 0.0;
+    args->color.green = 0.3;
+    args->color.blue = 0.0;
+
+    /* XXX: Next, we should override the defaults based on
+     * command-line arguments. */
+
+    /* XXX: Finally, we'll want to allow CGI-based arguments as
+     * well. */
+#if USE_CGIG
     /* QueryString */
-    char fontname[20];
     int fontsize;
     char format[5];
     char fillcolor[20];
+    char fontname[20];
     cgiFormStringNoNewlines("fontname", fontname, 20);
     cgiFormInteger("fontsize", &fontsize, 50);
     cgiFormStringNoNewlines("format", format, 5);
@@ -67,39 +93,48 @@ cgiMain ()
     int fillcolor_green;
     int fillcolor_blue;
     sscanf (fillcolor, " rgb : %d , %d , %d ", &fillcolor_red, &fillcolor_green, &fillcolor_blue);
-
     if ( format[0] == 'j' && format[1] == 's' && format[2] == 'o' && format[3] == 'n' )
-      outputJson = TRUE;
+       outputJson = TRUE;
+#endif
+}
 
-    if (outputJson)
-    {
-      cgiHeaderContentType("application/json");
-    } else {
-      cgiHeaderContentType("image/png");
-    }
+static FT_Face
+load_ft_face_for_family (const char *family)
+{
+    /* XXX: Instead of a hard-coded filename here, we should really be
+     * taking family and using fontconfig to map that to a
+     * filename. */
+    const char font_filename[] = "./Vera.ttf";
 
-    int error;  
+    int error;
+    FT_Face     ft_face;
     FT_Library         library;
-    FT_Face    ft_face;
-    FT_Bool     use_kerning;
-    FT_UInt     left_index, right_index;
-    FT_Vector   kerning;
 
     error = FT_Init_FreeType( &library );
-    if ( error )
-      printf("error");
+    if ( error ) {
+       fprintf (stderr, "Fatal error initializing freetype.\n");
+       exit (1);
+    }
 
-    error = FT_New_Face( library,"/srv/rdworth.org/cgi-bin/Verdana.ttf",
+    error = FT_New_Face( library, font_filename,
                          0,
                          &ft_face );
-    if ( error == FT_Err_Unknown_File_Format )
-        printf("File opened. Font format unsupported.");
-    else if ( error )
-        printf("Could not be open or read or broken.");
+    if ( error == FT_Err_Unknown_File_Format ) {
+        fprintf (stderr, "Unsupported font format: %s\n", font_filename);
+       exit (1);
+    } else if ( error ) {
+        fprintf (stderr, "Failed to open file: %s\n", font_filename);
+       exit (1);
+    }
 
-    use_kerning = FT_HAS_KERNING( ft_face );
+    return ft_face;
+}
 
-    /* Compute max-width and max-height */
+static void
+get_characters_max_width_height (FT_Face ft_face, double size,
+                                const char *characters, int num_characters,
+                                double *max_width, double *max_height)
+{
     cairo_surface_t *surface;
     cairo_t *cr;
     cairo_font_face_t *cr_face;
@@ -109,180 +144,124 @@ cgiMain ()
 
     cr = cairo_create (surface);
 
-/*
-    cairo_select_font_face (cr, fontname,
-                      CAIRO_FONT_SLANT_NORMAL,
-                      CAIRO_FONT_WEIGHT_NORMAL);
-*/
     cr_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
     cairo_set_font_face (cr, cr_face);
 
-    cairo_set_font_size (cr, fontsize);
+    cairo_set_font_size (cr, size);
 
     cairo_set_line_width (cr, 1.0);
 
-    double max_width = get_max_width(cr, characters);
-    double max_height = get_max_height(cr, characters);
+    *max_width = get_max_width(cr, characters, num_characters);
+    *max_height = get_max_height(cr, characters, num_characters);
 
     cairo_font_face_destroy (cr_face);
 
     cairo_destroy (cr);
 
     cairo_surface_destroy(surface);
+}
 
-    /* Draw */
-    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
-                                        (max_width + 8) * 10,
-                                         (max_height + 8) * 10);
-
-    cr = cairo_create (surface);
+static void
+draw_character_table (cairo_t *cr,
+                     FT_Face ft_face, double size,
+                     const char *characters, int num_characters,
+                     double character_width, double character_height)
+{
+    cairo_font_face_t *cr_face;
+    int i;
 
-/*
-    cairo_select_font_face (cr, fontname,
-                      CAIRO_FONT_SLANT_NORMAL,
-                      CAIRO_FONT_WEIGHT_NORMAL);
-*/
     cr_face = cairo_ft_font_face_create_for_ft_face (ft_face, 0);
     cairo_set_font_face (cr, cr_face);
 
-    cairo_set_font_size (cr, fontsize);
+    cairo_set_font_size (cr, size);
 
     cairo_text_extents_t extents;
     double x = 0.0;
     double y = 0.0;
 
-    if (outputJson)
-      printf("{");
-
-    cairo_translate (cr, 0, -(max_height / 2.50));
-
-//    cairo_translate (cr, 0, -5.0);
-
     char string[2];
     string[1] = '\0';
-    for (i = 0; i < ARRAY_SIZE(characters); i++)
-    {
-      if (i % 10 == 0)
-      {
-        x = 0.;
-        y += max_height + 8;
-      }
-      string[0] = characters[i];
-
-      cairo_text_extents (cr, string, &extents);
-
-      if (outputJson)
-      {
-        if (i > 0)
-          printf (",\n");
-        printf ("u%i:{", characters[i]);
-        printf ("x:%.1f,y:%.1f,w:%.1f,h:%.1f",
-                x,
-                y - (max_height * 1.15),
-                extents.width + extents.x_bearing,
-                extents.height);
-        int j;
-        for ( j = 0; j < ARRAY_SIZE(characters); j++ )
-        {
-          right_index = FT_Get_Char_Index( ft_face, characters[i] );
-          left_index = FT_Get_Char_Index( ft_face, characters[j] );
-    
-          FT_Get_Kerning( ft_face, left_index, right_index,
-                          FT_KERNING_UNSCALED, &kerning );
-    
-          if ( kerning.x )
-            printf(",k%d:%d", characters[j], kerning.x);
-        }
-
-        printf ("}");
-      }
-
-      x -= extents.x_bearing;
-  
-      cairo_move_to (cr, x, y);
-      cairo_set_source_rgb (cr, 0., 0., 0.); // black
-      if ( 0 <= fillcolor_red && fillcolor_red <= 255.0
-        && 0 <= fillcolor_green && fillcolor_green <= 255.0
-        && 0 <= fillcolor_blue && fillcolor_blue <= 255.0 )
-        cairo_set_source_rgb (cr, fillcolor_red / 255.0, fillcolor_green / 255.0, fillcolor_blue / 255.0);
-      cairo_text_path (cr, string);
-      cairo_fill (cr);
-
-      x += extents.x_bearing;
-      x += max_width + 8;
-    }
-
-/*
-    int thick_width = 4;
-    int thin_width = 0;
 
-    for (c = 'A'; c <= 'I'; c++)
+    cairo_set_line_width (cr, 2.0);
+    for (i = 0; i < num_characters; i++)
     {
-      string[0] = c;
-      int spacing = ((c - 'A') * letterspacing) + 1;
-
-
-      cairo_save(cr);
-
-      cairo_move_to (cr, spacing, 0);
-      cairo_text_path (cr, string);
-//      cairo_set_source_rgb (cr, 0.1, 0.2, 0.2); // gray
-      cairo_set_source_rgb (cr, 0.8, 0.8, 0.85); // silver
-      cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+       cairo_rectangle (cr, x, y, character_width, character_height);
+       cairo_stroke_preserve (cr);
+       cairo_save (cr);
+       cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+       cairo_fill (cr);
+       cairo_restore (cr);
+
+       string[0] = characters[i];
+
+       cairo_text_extents (cr, string, &extents);
+
+       cairo_move_to (cr, x - extents.x_bearing, y - extents.y_bearing);
+       cairo_show_text (cr, string);
+
+       if ((i+1) % 10 == 0)
+       {
+           x = 0.;
+           y += character_height;
+       } else {
+           x += character_width;
+       }
+    }
 
-      cairo_push_group (cr);
-      cairo_set_line_width (cr, thick_width);
-      cairo_stroke_preserve (cr);
-      cairo_set_line_width (cr, thin_width);
-      cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
-      cairo_stroke_preserve (cr);
-      cairo_fill (cr);
-      cairo_pop_group_to_source (cr);
+    cairo_scale (cr, 1, -1);
+    cairo_push_group (cr);
+}
 
-      cairo_paint (cr);
+int
+main (void)
+{
+    char characters[] = {
+       '!', '"', '#', '$', '%', '&','\'', '(',
+       ')', '*', '+', ',', '-', '.', '/', '0',
+       '1', '2', '3', '4', '5', '6', '7', '8',
+       '9', ':', ';', '<', '=', '>', '?', '@',
+       'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+       'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+       'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+       'Y', 'Z', '[','\\', ']', '^', '_', '`',
+       'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
+       'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
+       'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
+       'y', 'z', '{', '|', '}', '~'
+    };
 
-      cairo_restore(cr);
+    args_t args;
+    FT_Face ft_face;
+    double max_width, max_height;
+    cairo_surface_t *surface;
+    cairo_t *cr;
 
-      cairo_move_to (cr, spacing, 0);
-      cairo_text_path (cr, string);
-      cairo_set_source_rgb (cr, 0.4, 0.45, 0.7); // blue
+    parse_args (&args);
 
-      cairo_fill (cr);
-    }
-*/
+    ft_face = load_ft_face_for_family (args.family);
 
-    cairo_scale (cr, 1, -1);
-    cairo_push_group (cr);
-/*
+    get_characters_max_width_height (ft_face, args.size,
+                                    characters, ARRAY_SIZE (characters),
+                                    &max_width, &max_height);
 
-    cairo_pattern_t *gradient;
-
-    for (i = 0; i < 9; i++)
-    {
-      char c[2];
-      strncpy(c, STRING + i, 1);
-      c[1] = '\0';
-      int spacing = (i * letterspacing) + 1;
-      cairo_move_to (cr, spacing, -3);
-      cairo_show_text (cr, c);
+    /* Draw */
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                         max_width * 10,
+                                         max_height * 10);
 
-    }
-      cairo_pop_group_to_source (cr);
+    cr = cairo_create (surface);
 
-      gradient = cairo_pattern_create_linear (0, 0, 0, -fontsize);
-      cairo_pattern_add_color_stop_rgba (gradient, 0.0, 1, 1, 1, 0.5);
-      cairo_pattern_add_color_stop_rgba (gradient, 0.7, 1, 1, 1, 0.0);
+    cairo_set_source_rgb (cr,
+                         args.color.red,
+                         args.color.green,
+                         args.color.blue);
 
-      cairo_mask (cr, gradient);
+    draw_character_table (cr, ft_face, args.size,
+                         characters, ARRAY_SIZE (characters),
+                         max_width, max_height);
 
-      cairo_pattern_destroy (gradient);
-*/
-    if (outputJson)
-    {
-      printf("}");
-    } else {
-      cairo_surface_write_to_png_stream (surface, stdio_write, cgiOut);
-    }
+    cairo_surface_write_to_png (surface, "spritext-output.png");
+    printf ("Result written to spritext-output.png\n");
 
     cairo_destroy (cr);
 
@@ -290,50 +269,3 @@ cgiMain ()
 
     return 0;
 }
-
-static cairo_status_t
-stdio_write (void *closure, const unsigned char *data, unsigned int length)
-{
-    FILE *file = closure;
-    if (fwrite (data, 1, length, file) == length)
-       return CAIRO_STATUS_SUCCESS;
-    else
-       return CAIRO_STATUS_WRITE_ERROR;
-}
-
-double
-get_max_width(cairo_t *cr, char *characters)
-{
-  int i;
-  double max_width = 0.;
-  char string[2];
-  string[1] = '\0';
-  for (i = 0; i < ARRAY_SIZE(characters); i++) {
-    cairo_text_extents_t extents;
-    string[0] = characters[i];
-    cairo_text_extents (cr, string, &extents);
-    if ((extents.width + (extents.x_bearing * 2)) > max_width)
-      max_width = extents.width + (extents.x_bearing * 2);
-  }
-  return max_width;
-}
-
-double
-get_max_height(cairo_t *cr, char *characters)
-{
-  int i;
-  double max_height = 0.;
-  char string[2];
-  string[1] = '\0';
-  for (i = 0; i < ARRAY_SIZE(characters); i++) {
-    cairo_text_extents_t extents;
-    string[0] = characters[i];
-    cairo_text_extents (cr, string, &extents);
-//    if ((extents.height - extents.y_bearing) > max_height)
-//      max_height = extents.height - extents.y_bearing;
-    if ((extents.height) > max_height)
-      max_height = extents.height;
-  }
-  return max_height;
-}
-