* we are now creating true RGBA pngs
[rrdtool.git] / src / rrd_gfx.c
index 28007cd..778ba08 100644 (file)
@@ -1,5 +1,5 @@
 /****************************************************************************
- * RRDtool 1.1.x  Copyright Tobias Oetiker, 1997 - 2002
+ * RRDtool 1.2rc6  Copyright by Tobi Oetiker, 1997-2005
  ****************************************************************************
  * rrd_gfx.c  graphics wrapper for rrdtool
   **************************************************************************/
@@ -15,6 +15,7 @@
 #include <png.h>
 #include <ft2build.h>
 #include FT_FREETYPE_H
+#include FT_GLYPH_H
 
 #include "rrd_gfx.h"
 #include "rrd_afm.h"
 /* lines are better drawn on the pixle than between pixles */
 #define LINEOFFSET 0.5
 
+typedef struct gfx_char_s *gfx_char;
+struct gfx_char_s {
+  FT_UInt     index;    /* glyph index */
+  FT_Vector   pos;      /* location from baseline in 26.6 */
+  FT_Glyph    image;    /* glyph bitmap */
+};
+
+typedef struct gfx_string_s *gfx_string;
+struct gfx_string_s {
+  unsigned int    width;
+  unsigned int    height;
+  size_t          count;  /* number of characters */
+  gfx_char        glyphs;
+  size_t          num_glyphs;
+  FT_BBox         bbox;
+  FT_Matrix       transform;
+};
+
+/* compute string bbox */
+static void compute_string_bbox(gfx_string string);
+
+/* create a freetype glyph string */
+gfx_string gfx_string_create ( FT_Face face,
+                               const char *text, int rotation, double tabwidth);
+
+/* create a freetype glyph string */
+static void gfx_string_destroy ( gfx_string string );
+
 static
 gfx_node_t *gfx_new_node( gfx_canvas_t *canvas,enum gfx_en type){
   gfx_node_t *node = art_new(gfx_node_t,1);
@@ -33,7 +62,6 @@ gfx_node_t *gfx_new_node( gfx_canvas_t *canvas,enum gfx_en type){
   node->points = 0;
   node->points_max =0;
   node->closed_path = 0;
-  node->svp = NULL;         /* svp */
   node->filename = NULL;             /* font or image filename */
   node->text = NULL;
   node->x = 0.0;
@@ -85,7 +113,7 @@ gfx_node_t  *gfx_new_dashed_line(gfx_canvas_t *canvas,
   if (vec == NULL) return NULL;
   vec[0].code = ART_MOVETO_OPEN; vec[0].x=X0+LINEOFFSET; vec[0].y=Y0+LINEOFFSET;
   vec[1].code = ART_LINETO; vec[1].x=X1+LINEOFFSET; vec[1].y=Y1+LINEOFFSET;
-  vec[2].code = ART_END;
+  vec[2].code = ART_END; vec[2].x=0;vec[2].y=0;
   
   node->points = 3;
   node->points_max = 3;
@@ -114,7 +142,7 @@ gfx_node_t   *gfx_new_area   (gfx_canvas_t *canvas,
   vec[1].code = ART_LINETO; vec[1].x=X1; vec[1].y=Y1;
   vec[2].code = ART_LINETO; vec[2].x=X2; vec[2].y=Y2;
   vec[3].code = ART_LINETO; vec[3].x=X0; vec[3].y=Y0;
-  vec[4].code = ART_END;
+  vec[4].code = ART_END; vec[4].x=0; vec[4].y=0;
   
   node->points = 5;
   node->points_max = 5;
@@ -182,10 +210,6 @@ gfx_node_t   *gfx_new_text   (gfx_canvas_t *canvas,
                              enum gfx_v_align_en v_align,
                               char* text){
    gfx_node_t *node = gfx_new_node(canvas,GFX_TEXT);
-   if (angle != 0.0){
-       /* currently we only support 0 and 270 */
-       angle = 270.0;
-   }
    
    node->text = strdup(text);
    node->size = size;
@@ -235,12 +259,23 @@ int           gfx_render(gfx_canvas_t *canvas,
   }
 }
 
+static void gfx_string_destroy ( gfx_string string ) {
+  unsigned int n;
+  if (string->glyphs) {
+    for (n=0; n<string->num_glyphs; ++n)
+      FT_Done_Glyph (string->glyphs[n].image);
+    free (string->glyphs);
+  }
+  free (string);
+}
+
+
 double gfx_get_text_width ( gfx_canvas_t *canvas,
                            double start, char* font, double size,
-                           double tabwidth, char* text){
+                           double tabwidth, char* text, int rotation){
   switch (canvas->imgformat) {
   case IF_PNG: 
-    return gfx_get_text_width_libart (canvas, start, font, size, tabwidth, text);
+    return gfx_get_text_width_libart (start, font, size, tabwidth, text, rotation);
   case IF_SVG: /* fall through */ 
   case IF_EPS:
   case IF_PDF:
@@ -250,56 +285,30 @@ double gfx_get_text_width ( gfx_canvas_t *canvas,
   }
 }
 
-double gfx_get_text_width_libart ( gfx_canvas_t *canvas,
+double gfx_get_text_width_libart (
                            double start, char* font, double size,
-                           double tabwidth, char* text){
+                           double tabwidth, char* text, int rotation){
 
-  FT_GlyphSlot  slot;
-  FT_UInt       previous=0;
-  FT_UInt       glyph_index=0;
-  FT_Bool       use_kerning;
   int           error;
+  double        text_width=0;
   FT_Face       face;
   FT_Library    library=NULL;  
-  double        text_width=0;
+  gfx_string    string;
+
   FT_Init_FreeType( &library );
   error = FT_New_Face( library, font, 0, &face );
   if ( error ) return -1;
   error = FT_Set_Char_Size(face,  size*64,size*64,  100,100);
   if ( error ) return -1;
 
-  use_kerning = FT_HAS_KERNING(face);
-  slot = face->glyph;
-  for(;*text;text++) { 
-    previous = glyph_index;
-    glyph_index = FT_Get_Char_Index( face, *text);
-    
-    if (use_kerning && previous && glyph_index){
-      FT_Vector  delta;
-      FT_Get_Kerning( face, previous, glyph_index,
-                     0, &delta );
-      text_width += (double)delta.x / 64.0;
-      
-    }
-    error = FT_Load_Glyph( face, glyph_index, 0 );
-    if ( error ) {
-      FT_Done_FreeType(library);
-      return -1;
-    }
-    if (! previous) {
-      text_width -= (double)slot->metrics.horiBearingX / 64.0; /* add just char width */       
-    }
-    text_width += (double)slot->metrics.horiAdvance / 64.0;
-  }
-  text_width -= (double)slot->metrics.horiAdvance / 64.0; /* remove last step */
-  text_width += (double)slot->metrics.width / 64.0; /* add just char width */
-  text_width += (double)slot->metrics.horiBearingX / 64.0; /* add just char width */
+  string = gfx_string_create( face, text, rotation,tabwidth);
+  text_width = string->width;
+  gfx_string_destroy(string);
   FT_Done_FreeType(library);
-  return text_width;
+  return text_width/64;
 }
-static void gfx_libart_close_path(gfx_canvas_t *canvas,
-       gfx_node_t *node, ArtVpath **vec)
+
+static void gfx_libart_close_path(gfx_node_t *node, ArtVpath **vec)
 {
     /* libart must have end==start for closed paths,
        even if using ART_MOVETO and not ART_MOVETO_OPEN
@@ -311,19 +320,174 @@ static void gfx_libart_close_path(gfx_canvas_t *canvas,
     art_vpath_add_point (vec, &points, &points_max, ART_END, 0, 0);
 }
 
-static void gfx_round_scaled_coordinates(gfx_canvas_t *canvas,
-       gfx_node_t *node, ArtVpath *vec)
+
+/* find bbox of a string */
+static void compute_string_bbox(gfx_string string) {
+    unsigned int n;
+    FT_BBox bbox;
+
+    bbox.xMin = bbox.yMin = 32000;
+    bbox.xMax = bbox.yMax = -32000;
+    for ( n = 0; n < string->num_glyphs; n++ ) {
+      FT_BBox glyph_bbox;
+      FT_Glyph_Get_CBox( string->glyphs[n].image, ft_glyph_bbox_gridfit,
+       &glyph_bbox );
+      if (glyph_bbox.xMin < bbox.xMin) {
+         bbox.xMin = glyph_bbox.xMin;
+      }
+      if (glyph_bbox.yMin < bbox.yMin) {
+        bbox.yMin = glyph_bbox.yMin;
+      }
+      if (glyph_bbox.xMax > bbox.xMax) {
+         bbox.xMax = glyph_bbox.xMax;
+      }
+      if (glyph_bbox.yMax > bbox.yMax) {
+         bbox.yMax = glyph_bbox.yMax;
+      }
+    }
+    if ( bbox.xMin > bbox.xMax ) { 
+      bbox.xMin = 0;
+      bbox.yMin = 0;
+      bbox.xMax = 0;
+      bbox.yMax = 0;
+    }
+    string->bbox.xMin = bbox.xMin;
+    string->bbox.xMax = bbox.xMax;
+    string->bbox.yMin = bbox.yMin;
+    string->bbox.yMax = bbox.yMax;
+} 
+
+/* create a free type glyph string */
+gfx_string gfx_string_create(FT_Face face,const char *text,
+        int rotation, double tabwidth)
 {
-    while (vec->code != ART_END) {
-       vec->x = floor(vec->x - LINEOFFSET + 0.5) + LINEOFFSET;
-       vec->y = floor(vec->y - LINEOFFSET + 0.5) + LINEOFFSET;
-       vec++;
+
+  FT_GlyphSlot  slot = face->glyph;  /* a small shortcut */
+  FT_Bool       use_kerning;
+  FT_UInt       previous;
+  FT_Vector     ft_pen;
+
+  gfx_string    string;
+  gfx_char      glyph;          /* current glyph in table */
+  unsigned int  n;
+  int           error;
+  int        gottab = 0;    
+
+  ft_pen.x = 0;   /* start at (0,0) !! */
+  ft_pen.y = 0;
+
+  string = (gfx_string) malloc (sizeof(struct gfx_string_s));
+  string->width = 0;
+  string->height = 0;
+  string->count = strlen (text);
+  string->glyphs = (gfx_char) calloc (string->count,sizeof(struct gfx_char_s));
+  string->num_glyphs = 0;
+  string->transform.xx = (FT_Fixed)( cos(M_PI*(rotation)/180.0)*0x10000);
+  string->transform.xy = (FT_Fixed)(-sin(M_PI*(rotation)/180.0)*0x10000);
+  string->transform.yx = (FT_Fixed)( sin(M_PI*(rotation)/180.0)*0x10000);
+  string->transform.yy = (FT_Fixed)( cos(M_PI*(rotation)/180.0)*0x10000);
+
+  use_kerning = FT_HAS_KERNING(face);
+  previous    = 0;
+  glyph = string->glyphs;
+  for (n=0; n<string->count; n++, glyph++) {
+    FT_Vector   vec;
+    /* handle the tabs ...
+       have a witespace glyph inserted, but set its width such that the distance
+    of the new right edge is x times tabwidth from 0,0 where x is an integer. */    
+    char letter = text[n];
+    gottab = 0;
+    if (letter == '\\' && n+1 < string->count && text[n+1] == 't'){
+            /* we have a tab here so skip the backslash and
+               set t to ' ' so that we get a white space */
+            gottab = 1;
+            n++;
+            letter  = ' ';            
+    }            
+    if (letter == '\t'){
+       letter = ' ';
+        gottab = 1 ;
+    }            
+    /* initialize each struct gfx_char_s */
+    glyph->index = 0;
+    glyph->pos.x = 0;
+    glyph->pos.y = 0;
+    glyph->image = NULL;
+
+    glyph->index = FT_Get_Char_Index( face, letter );
+
+    /* compute glyph origin */
+    if ( use_kerning && previous && glyph->index ) {
+      FT_Vector kerning;
+      FT_Get_Kerning (face, previous, glyph->index,
+          ft_kerning_default, &kerning);
+      ft_pen.x += kerning.x;
+      ft_pen.y += kerning.y;
+    }
+
+    /* load the glyph image (in its native format) */
+    /* for now, we take a monochrome glyph bitmap */
+    error = FT_Load_Glyph (face, glyph->index, FT_LOAD_DEFAULT);
+    if (error) {
+      fprintf (stderr, "couldn't load glyph:  %c\n", letter);
+      continue;
+    }
+    error = FT_Get_Glyph (slot, &glyph->image);
+    if (error) {
+      fprintf (stderr, "couldn't get glyph from slot:  %c\n", letter);
+      continue;
+    }
+    /* if we are in tabbing mode, we replace the tab with a space and shift the position
+       of the space so that its left edge is where the tab was supposed to land us */
+    if (gottab){
+       /* we are in gridfitting mode so the calculations happen in 1/64 pixles */
+        ft_pen.x = tabwidth*64.0 * (float)(1 + (long)(ft_pen.x / (tabwidth * 64.0))) - slot->advance.x;
     }
+    /* store current pen position */
+    glyph->pos.x = ft_pen.x;
+    glyph->pos.y = ft_pen.y;
+
+
+    ft_pen.x   += slot->advance.x;    
+    ft_pen.y   += slot->advance.y;
+
+    /* rotate glyph */
+    vec = glyph->pos;
+    FT_Vector_Transform (&vec, &string->transform);
+    error = FT_Glyph_Transform (glyph->image, &string->transform, &vec);
+    if (error) {
+      fprintf (stderr, "couldn't transform glyph\n");
+      continue;
+    }
+
+    /* convert to a bitmap - destroy native image */
+    error = FT_Glyph_To_Bitmap (&glyph->image, FT_RENDER_MODE_NORMAL, 0, 1);
+    if (error) {
+      fprintf (stderr, "couldn't convert glyph to bitmap\n");
+      continue;
+    }
+
+    /* increment number of glyphs */
+    previous = glyph->index;
+    string->num_glyphs++;
+  }
+/*  printf ("number of glyphs = %d\n", string->num_glyphs);*/
+  compute_string_bbox( string );
+  /* the last character was a tab */  
+  if (gottab) {
+      string->width = ft_pen.x;
+  } else {
+      string->width = string->bbox.xMax - string->bbox.xMin;
+  }
+  string->height = string->bbox.yMax - string->bbox.yMin;
+  return string;
 }
 
+
 static int gfx_save_png (art_u8 *buffer, FILE *fp,
                      long width, long height, long bytes_per_pixel);
 /* render grafics into png image */
+
 int           gfx_render_png (gfx_canvas_t *canvas, 
                              art_u32 width, art_u32 height, 
                              gfx_color_t background, FILE *fp){
@@ -331,54 +495,68 @@ int           gfx_render_png (gfx_canvas_t *canvas,
     
     FT_Library    library;
     gfx_node_t *node = canvas->firstnode;    
+    /*
     art_u8 red = background >> 24, green = (background >> 16) & 0xff;
     art_u8 blue = (background >> 8) & 0xff, alpha = ( background & 0xff );
+    */
     unsigned long pys_width = width * canvas->zoom;
     unsigned long pys_height = height * canvas->zoom;
-    const int bytes_per_pixel = 3;
+    const int bytes_per_pixel = 4;
     unsigned long rowstride = pys_width*bytes_per_pixel; /* bytes per pixel */
     art_u8 *buffer = art_new (art_u8, rowstride*pys_height);
-    art_rgb_run_alpha (buffer, red, green, blue, alpha, pys_width*pys_height);
+    /* fill that buffer with out background color */
+    gfx_color_t *buffp;
+    long i;
+    for (i=0,buffp=(gfx_color_t *)buffer;
+         i<pys_width*pys_height;
+        i++){
+       *(buffp++)=background;
+    }
     FT_Init_FreeType( &library );
     while(node){
         switch (node->type) {
         case GFX_LINE:
         case GFX_AREA: {   
-            ArtVpath *vec;
+            ArtVpath *vec,*pvec;
             double dst[6];     
-            ArtSVP *svp;
+            ArtSVP *svp,*svpt;
             art_affine_scale(dst,canvas->zoom,canvas->zoom);
             vec = art_vpath_affine_transform(node->path,dst);
            if (node->closed_path)
-               gfx_libart_close_path(canvas, node, &vec);
-           gfx_round_scaled_coordinates(canvas, node, vec);
+               gfx_libart_close_path(node, &vec);
+           /* gfx_round_scaled_coordinates(vec); */
+            pvec = art_vpath_perturb(vec);
+           art_free(vec);
             if(node->type == GFX_LINE){
-                svp = art_svp_vpath_stroke ( vec, ART_PATH_STROKE_JOIN_ROUND,
+                svp = art_svp_vpath_stroke ( pvec, ART_PATH_STROKE_JOIN_ROUND,
                                              ART_PATH_STROKE_CAP_ROUND,
-                                             node->size*canvas->zoom,1,1);
+                                             node->size*canvas->zoom,4,0.25);
             } else {
-                svp = art_svp_from_vpath ( vec );
+                svp  = art_svp_from_vpath ( pvec );
+                svpt = art_svp_uncross( svp );
+                art_svp_free(svp);
+               svp  = art_svp_rewind_uncrossed(svpt,ART_WIND_RULE_NONZERO); 
+                art_svp_free(svpt);
             }
-            art_free(vec);
-            art_rgb_svp_alpha (svp ,0,0, pys_width, pys_height,
-                               node->color, buffer, rowstride, NULL);
-            art_free(svp);
+            art_free(pvec);
+           /* this is from gnome since libart does not have this yet */
+            gnome_print_art_rgba_svp_alpha (svp ,0,0, pys_width, pys_height,
+                                node->color, buffer, rowstride, NULL);
+            art_svp_free(svp);
             break;
         }
         case GFX_TEXT: {
+            unsigned int  n;
             int  error;
-            float text_width=0.0, text_height = 0.0;
-            unsigned char *text;
-            art_u8 fcolor[3],falpha;
+            art_u8 fcolor[4],falpha;
             FT_Face       face;
-            FT_GlyphSlot  slot;
-            FT_UInt       previous=0;
-            FT_UInt       glyph_index=0;
-           FT_Bool       use_kerning;
+            gfx_char      glyph;
+            gfx_string    string;
+            FT_Vector     vec;  /* 26.6 */
 
             float pen_x = 0.0 , pen_y = 0.0;
             /* double x,y; */
-            long   ix,iy,iz;
+            long   ix,iy;
             
             fcolor[0] = node->color >> 24;
             fcolor[1] = (node->color >> 16) & 0xff;
@@ -388,9 +566,10 @@ int           gfx_render_png (gfx_canvas_t *canvas,
                                  (char *)node->filename,
                                  0,
                                  &face );
-           if ( error ) break;
-            use_kerning = FT_HAS_KERNING(face);
-
+           if ( error ) {
+               rrd_set_error("failed to load %s",node->filename);
+               break;
+           }
             error = FT_Set_Char_Size(face,   /* handle to face object            */
                                      (long)(node->size*64),
                                      (long)(node->size*64),
@@ -399,75 +578,103 @@ int           gfx_render_png (gfx_canvas_t *canvas,
             if ( error ) break;
             pen_x = node->x * canvas->zoom;
             pen_y = node->y * canvas->zoom;
-            slot = face->glyph;
-
-            for(text=(unsigned char *)node->text;*text;text++) {       
-                previous = glyph_index;
-                glyph_index = FT_Get_Char_Index( face, *text);
-                
-                if (use_kerning && previous && glyph_index){
-                    FT_Vector  delta;
-                    FT_Get_Kerning( face, previous, glyph_index,
-                                    0, &delta );
-                    text_width += (double)delta.x / 64.0;
-                    
-                }
-                error = FT_Load_Glyph( face, glyph_index, 0 );
-                if ( error ) break;
-               if (previous == 0){
-                 pen_x -= (double)slot->metrics.horiBearingX / 64.0; /* adjust pos for first char */   
-                 text_width -= (double)slot->metrics.horiBearingX / 64.0; /* add just char width */    
-                }
-               if ( text_height < (double)slot->metrics.horiBearingY / 64.0 ) {
-                 text_height = (double)slot->metrics.horiBearingY / 64.0;
-               }
-                text_width += (double)slot->metrics.horiAdvance / 64.0;
-            }
-            text_width -= (double)slot->metrics.horiAdvance / 64.0; /* remove last step */
-            text_width += (double)slot->metrics.width / 64.0; /* add just char width */
-            text_width += (double)slot->metrics.horiBearingX / 64.0; /* add just char width */
-            
+
+            string = gfx_string_create (face, node->text, node->angle, node->tabwidth);
             switch(node->halign){
-            case GFX_H_RIGHT:  pen_x -= text_width; break;
-            case GFX_H_CENTER: pen_x -= text_width / 2.0; break;          
-            case GFX_H_LEFT: break;          
-            case GFX_H_NULL: break;          
+            case GFX_H_RIGHT:  vec.x = -string->bbox.xMax;
+                               break;          
+            case GFX_H_CENTER: vec.x = abs(string->bbox.xMax) >= abs(string->bbox.xMin) ?
+                                       -string->bbox.xMax/2:-string->bbox.xMin/2;
+                               break;          
+            case GFX_H_LEFT:   vec.x = -string->bbox.xMin;
+                              break;
+            case GFX_H_NULL:   vec.x = 0;
+                               break;          
             }
 
             switch(node->valign){
-            case GFX_V_TOP:    pen_y += text_height; break;
-            case GFX_V_CENTER: pen_y += text_height / 2.0; break;          
-            case GFX_V_BOTTOM: break;          
-            case GFX_V_NULL: break;          
+            case GFX_V_TOP:    vec.y = string->bbox.yMax;
+                               break;
+            case GFX_V_CENTER: vec.y = abs(string->bbox.yMax) >= abs(string->bbox.yMin) ?
+                                       string->bbox.yMax/2:string->bbox.yMin/2;
+                               break;
+            case GFX_V_BOTTOM: vec.y = 0;
+                               break;
+            case GFX_V_NULL:   vec.y = 0;
+                               break;
             }
+           pen_x += vec.x/64;
+           pen_y += vec.y/64;
+            glyph = string->glyphs;
+            for(n=0; n<string->num_glyphs; ++n, ++glyph) {
+                int gr;
+                FT_Glyph        image;
+                FT_BitmapGlyph  bit;
+               /* long buf_x,comp_n; */
+               /* make copy to transform */
+                if (! glyph->image) {
+                  fprintf (stderr, "no image\n");
+                  continue;
+                }
+                error = FT_Glyph_Copy (glyph->image, &image);
+                if (error) {
+                  fprintf (stderr, "couldn't copy image\n");
+                  continue;
+                }
+
+                /* transform it */
+                vec = glyph->pos;
+                FT_Vector_Transform (&vec, &string->transform);
+
+                bit = (FT_BitmapGlyph) image;
+                gr = bit->bitmap.num_grays -1;
+/* 
+               buf_x = (pen_x + 0.5) + (double)bit->left;
+               comp_n = buf_x + bit->bitmap.width > pys_width ? pys_width - buf_x : bit->bitmap.width;
+                if (buf_x < 0 || buf_x >= (long)pys_width) continue;
+               buf_x *=  bytes_per_pixel ;
+               for (iy=0; iy < bit->bitmap.rows; iy++){                    
+                   long buf_y = iy+(pen_y+0.5)-(double)bit->top;
+                   if (buf_y < 0 || buf_y >= (long)pys_height) continue;
+                    buf_y *= rowstride;
+                   for (ix=0;ix < bit->bitmap.width;ix++){             
+                       *(letter + (ix*bytes_per_pixel+3)) = *(bit->bitmap.buffer + iy * bit->bitmap.width + ix);
+                   }
+                   art_rgba_rgba_composite(buffer + buf_y + buf_x ,letter,comp_n);
+                }
+                art_free(letter);
+*/
 
-            glyph_index=0;
-            for(text=(unsigned char *)node->text;*text;text++) {
-                int gr;          
-                previous = glyph_index;
-                glyph_index = FT_Get_Char_Index( face, *text);
-                
-                if (use_kerning && previous && glyph_index){
-                    FT_Vector  delta;
-                    FT_Get_Kerning( face, previous, glyph_index,
-                                    0, &delta );
-                    pen_x += (double)delta.x / 64.0;
-                    
+                for (iy=0; iy < bit->bitmap.rows; iy++){                   
+                    long buf_y = iy+(pen_y+0.5)-bit->top;
+                    if (buf_y < 0 || buf_y >= (long)pys_height) continue;
+                    buf_y *= rowstride;
+                    for (ix=0;ix < bit->bitmap.width;ix++){
+                        long buf_x = ix + (pen_x + 0.5) + (double)bit->left ;
+                        art_u8 font_alpha;
+                        
+                        if (buf_x < 0 || buf_x >= (long)pys_width) continue;
+                        buf_x *=  bytes_per_pixel ;
+                        font_alpha =  *(bit->bitmap.buffer + iy * bit->bitmap.width + ix);
+                       if (font_alpha > 0){
+                               fcolor[3] =  (art_u8)((double)font_alpha / gr * falpha);
+                               art_rgba_rgba_composite(buffer + buf_y + buf_x ,fcolor,1);
+                        }
+                    }
                 }
-                error = FT_Load_Glyph( face, glyph_index, FT_LOAD_RENDER );
-                if ( error ) break;
-                gr = slot->bitmap.num_grays -1;
-                for (iy=0; iy < slot->bitmap.rows; iy++){
-                    long buf_y = iy+(pen_y+0.5)-slot->bitmap_top;
-                    if (buf_y < 0 || buf_y >= pys_height) continue;
+
+/*
+                for (iy=0; iy < bit->bitmap.rows; iy++){                   
+                    long buf_y = iy+(pen_y+0.5)-bit->top;
+                    if (buf_y < 0 || buf_y >= (long)pys_height) continue;
                     buf_y *= rowstride;
-                    for (ix=0;ix < slot->bitmap.width;ix++){
-                        long buf_x = ix + (pen_x + 0.5) + (double)slot->bitmap_left ;
+                    for (ix=0;ix < bit->bitmap.width;ix++){
+                        long buf_x = ix + (pen_x + 0.5) + (double)bit->left ;
                         art_u8 font_alpha;
                         
-                        if (buf_x < 0 || buf_x >= pys_width) continue;
+                        if (buf_x < 0 || buf_x >= (long)pys_width) continue;
                         buf_x *=  bytes_per_pixel ;
-                        font_alpha =  *(slot->bitmap.buffer + iy * slot->bitmap.width + ix);
+                        font_alpha =  *(bit->bitmap.buffer + iy * bit->bitmap.width + ix);
                         font_alpha =  (art_u8)((double)font_alpha / gr * falpha);
                         for (iz = 0; iz < 3; iz++){
                             art_u8 *orig = buffer + buf_y + buf_x + iz;
@@ -476,8 +683,10 @@ int           gfx_render_png (gfx_canvas_t *canvas,
                         }
                     }
                 }
-                pen_x += (double)slot->metrics.horiAdvance / 64.0;
+*/
+                FT_Done_Glyph (image);
             }
+            gfx_string_destroy(string);
         }
         }
         node = node->next;
@@ -496,12 +705,12 @@ gfx_destroy    (gfx_canvas_t *canvas){
   while(node){
     next = node->next;
     art_free(node->path);
-    art_free(node->svp);
     free(node->text);
     free(node->filename);
     art_free(node);
     node = next;
   }
+  art_free(canvas);
   return 0;
 }
  
@@ -528,6 +737,7 @@ static int gfx_save_png (art_u8 *buffer, FILE *fp,  long width, long height, lon
 
   if (info_ptr == NULL)
     {
+      png_free(png_ptr,row_pointers);
       png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
       return (1);
     }
@@ -541,7 +751,7 @@ static int gfx_save_png (art_u8 *buffer, FILE *fp,  long width, long height, lon
 
   png_init_io(png_ptr, fp);
   png_set_IHDR (png_ptr, info_ptr,width, height,
-                8, PNG_COLOR_TYPE_RGB,
+                8, PNG_COLOR_TYPE_RGB_ALPHA,
                 PNG_INTERLACE_NONE,
                 PNG_COMPRESSION_TYPE_DEFAULT,
                 PNG_FILTER_TYPE_DEFAULT);
@@ -551,14 +761,23 @@ static int gfx_save_png (art_u8 *buffer, FILE *fp,  long width, long height, lon
   text[0].compression = PNG_TEXT_COMPRESSION_NONE;
   png_set_text (png_ptr, info_ptr, text, 1);
 
+  /* lets make this fast */
+  png_set_filter(png_ptr,0,PNG_FILTER_NONE);
+  png_set_compression_level(png_ptr,1);
+  png_set_compression_strategy(png_ptr,Z_HUFFMAN_ONLY);
+  /* 
+  png_set_filter(png_ptr,PNG_FILTER_TYPE_BASE,PNG_FILTER_SUB);
+  png_set_compression_strategy(png_ptr,Z_HUFFMAN_ONLY);
+  png_set_compression_level(png_ptr,Z_BEST_SPEED); */
+  
   /* Write header data */
   png_write_info (png_ptr, info_ptr);
-
   for (i = 0; i < height; i++)
     row_pointers[i] = (png_bytep) (buffer + i*rowstride);
-
+  
   png_write_image(png_ptr, row_pointers);
   png_write_end(png_ptr, info_ptr);
+  png_free(png_ptr,row_pointers);
   png_destroy_write_struct(&png_ptr, &info_ptr);
   return 1;
 }
@@ -972,8 +1191,11 @@ static void svg_text(FILE *fp, gfx_node_t *node)
    svg_write_number(fp, x);
    fputs("\" y=\"", fp);
    svg_write_number(fp, y);
-  if (strcmp(node->filename, svg_default_font))
+
+/*  if (strcmp(node->filename, svg_default_font))
     fprintf(fp, " font-family=\"%s\"", node->filename);
+    */
+   fputs("\" font-family=\"Helvetica", fp);
    fputs("\" font-size=\"", fp);
    svg_write_number(fp, node->size);
    fputs("\"", fp);
@@ -1157,9 +1379,9 @@ static int eps_prologue(eps_state *state)
   gfx_node_t *node;
   fputs(
     "%!PS-Adobe-3.0 EPSF-3.0\n"
-    "%%Creator: RRDtool 1.1.x, Tobias Oetiker, http://tobi.oetiker.ch\n"
+    "%%Creator: RRDtool 1.2rc6 Tobias Oetiker, http://tobi.oetiker.ch\n"
     /* can't like weird chars here */
-    "%%Title: (RRDTool output)\n"
+    "%%Title: (RRDtool output)\n"
     "%%DocumentData: Clean7Bit\n"
     "", state->fp);
   fprintf(state->fp, "%%%%BoundingBox: 0 0 %d %d\n",