X-Git-Url: https://git.octo.it/?p=rrdtool.git;a=blobdiff_plain;f=src%2Frrd_gfx.c;h=d01b90487a5bbb03edc074e0181b503fe460498c;hp=faa9c916ccacc2a1d4f12cf9b85339d9154da2e3;hb=0dc5d6d50c0d95ba4f04b656358b26518d4ce854;hpb=104c47f444b8f0747fcd1cba3f0b791410087dcf diff --git a/src/rrd_gfx.c b/src/rrd_gfx.c index faa9c91..d01b904 100644 --- a/src/rrd_gfx.c +++ b/src/rrd_gfx.c @@ -1,5 +1,5 @@ /**************************************************************************** - * RRDtool 1.2.1 Copyright by Tobi Oetiker, 1997-2005 + * RRDtool 1.2.9 Copyright by Tobi Oetiker, 1997-2005 **************************************************************************** * rrd_gfx.c graphics wrapper for rrdtool **************************************************************************/ @@ -7,9 +7,9 @@ /* #define DEBUG */ #ifdef DEBUG -# define DPRINT(x) (void)(printf x, printf("\n")) +# define DPRINTF(x,...) fprintf(stderr, x, ## __VA_ARGS__); #else -# define DPRINT(x) +# define DPRINTF(x,...) #endif #include "rrd_tool.h" #include @@ -19,10 +19,14 @@ #include "rrd_gfx.h" #include "rrd_afm.h" +#include "unused.h" /* lines are better drawn on the pixle than between pixles */ #define LINEOFFSET 0.5 +#define USE_PDF_FAKE_ALPHA 1 +#define USE_EPS_FAKE_ALPHA 1 + typedef struct gfx_char_s *gfx_char; struct gfx_char_s { FT_UInt index; /* glyph index */ @@ -34,7 +38,7 @@ typedef struct gfx_string_s *gfx_string; struct gfx_string_s { unsigned int width; unsigned int height; - size_t count; /* number of characters */ + int count; /* number of characters */ gfx_char glyphs; size_t num_glyphs; FT_BBox bbox; @@ -288,7 +292,7 @@ double gfx_get_text_width ( gfx_canvas_t *canvas, } double gfx_get_text_width_libart ( - gfx_canvas_t *canvas, double start, char* font, double size, + gfx_canvas_t *canvas, double UNUSED(start), char* font, double size, double tabwidth, char* text, int rotation ){ int error; @@ -369,18 +373,32 @@ gfx_string gfx_string_create(gfx_canvas_t *canvas, FT_Face face,const char *text FT_UInt previous; FT_Vector ft_pen; - gfx_string string; + gfx_string string = (gfx_string) malloc (sizeof(struct gfx_string_s)); + gfx_char glyph; /* current glyph in table */ - unsigned int n; + int n; int error; int gottab = 0; + +#ifdef HAVE_MBSTOWCS + wchar_t *cstr; + size_t clen = strlen(text)+1; + cstr = malloc(sizeof(wchar_t) * clen); /* yes we are allocating probably too much here, I know */ + string->count=mbstowcs(cstr,text,clen); + if ( string->count == -1){ + string->count=mbstowcs(cstr,"Enc-Err",6); + } +#else + char *cstr = strdup(text); + string->count = strlen (text); +#endif + 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); @@ -391,15 +409,15 @@ gfx_string gfx_string_create(gfx_canvas_t *canvas, FT_Face face,const char *text use_kerning = FT_HAS_KERNING(face); previous = 0; glyph = string->glyphs; - for (n=0; ncount;glyph++) { + for (n=0; ncount;glyph++,n++) { 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. */ - unsigned char letter = text[n]; + unsigned int letter = cstr[n]; gottab = 0; - if (letter == '\\' && n+1 < string->count && text[n+1] == 't'){ + if (letter == '\\' && n+1 < string->count && cstr[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; @@ -415,7 +433,6 @@ gfx_string gfx_string_create(gfx_canvas_t *canvas, FT_Face face,const char *text glyph->pos.x = 0; glyph->pos.y = 0; glyph->image = NULL; - glyph->index = FT_Get_Char_Index( face, letter ); /* compute glyph origin */ @@ -434,12 +451,12 @@ gfx_string gfx_string_create(gfx_canvas_t *canvas, FT_Face face,const char *text canvas->aa_type == AA_LIGHT ? FT_LOAD_TARGET_LIGHT : FT_LOAD_TARGET_MONO : FT_LOAD_TARGET_MONO); if (error) { - fprintf (stderr, "couldn't load glyph: %c\n", letter); + DPRINTF("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); + DPRINTF("couldn't get glyph %c from slot %d\n", letter, (int)slot) continue; } /* if we are in tabbing mode, we replace the tab with a space and shift the position @@ -461,7 +478,7 @@ gfx_string gfx_string_create(gfx_canvas_t *canvas, FT_Face face,const char *text FT_Vector_Transform (&vec, &string->transform); error = FT_Glyph_Transform (glyph->image, &string->transform, &vec); if (error) { - fprintf (stderr, "couldn't transform glyph\n"); + DPRINTF("couldn't transform glyph id %d\n", letter) continue; } @@ -471,16 +488,15 @@ gfx_string gfx_string_create(gfx_canvas_t *canvas, FT_Face face,const char *text canvas->aa_type == AA_LIGHT ? FT_RENDER_MODE_LIGHT : FT_RENDER_MODE_MONO : FT_RENDER_MODE_MONO, 0, 1); if (error) { - fprintf (stderr, "couldn't convert glyph to bitmap\n"); + DPRINTF("couldn't convert glyph id %d to bitmap\n", letter) continue; } /* increment number of glyphs */ previous = glyph->index; string->num_glyphs++; - n++; - } + free(cstr); /* printf ("number of glyphs = %d\n", string->num_glyphs);*/ compute_string_bbox( string ); /* the last character was a tab */ @@ -490,7 +506,6 @@ gfx_string gfx_string_create(gfx_canvas_t *canvas, FT_Face face,const char *text string->width = string->bbox.xMax - string->bbox.xMin; } */ string->height = string->bbox.yMax - string->bbox.yMin; - return string; } @@ -514,12 +529,12 @@ int gfx_render_png (gfx_canvas_t *canvas, unsigned long pys_height = height * canvas->zoom; 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); + /* fill that buffer with out background color */ - gfx_color_t *buffp; - long i; - for (i=0,buffp=(gfx_color_t *)buffer; - itype) { case GFX_LINE: case GFX_AREA: { - ArtVpath *vec,*pvec; + ArtVpath *vec; double dst[6]; - ArtSVP *svp,*svpt; + ArtSVP *svp; art_affine_scale(dst,canvas->zoom,canvas->zoom); vec = art_vpath_affine_transform(node->path,dst); if (node->closed_path) gfx_libart_close_path(node, &vec); /* gfx_round_scaled_coordinates(vec); */ /* pvec = art_vpath_perturb(vec); - art_free(vec); */ + art_free(vec); */ if(node->type == GFX_LINE){ svp = art_svp_vpath_stroke ( vec, ART_PATH_STROKE_JOIN_ROUND, ART_PATH_STROKE_CAP_ROUND, node->size*canvas->zoom,4,0.25); } else { svp = art_svp_from_vpath ( vec ); -/* svpt = art_svp_uncross( svp ); - art_svp_free(svp); - svp = art_svp_rewind_uncrossed(svpt,ART_WIND_RULE_NONZERO); - art_svp_free(svpt);*/ + /* this takes time and is unnecessary since we make + sure elsewhere that the areas are going clock-whise */ + /* 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); /* this is from gnome since libart does not have this yet */ @@ -617,19 +635,19 @@ int gfx_render_png (gfx_canvas_t *canvas, pen_x += vec.x/64; pen_y += vec.y/64; glyph = string->glyphs; - for(n=0; nnum_glyphs; ++n, ++glyph) { + for(n=0; nnum_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"); + DPRINTF("no image\n") continue; } error = FT_Glyph_Copy (glyph->image, &image); if (error) { - fprintf (stderr, "couldn't copy image\n"); + DPRINTF("couldn't copy image\n") continue; } @@ -795,8 +813,9 @@ 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); */ + /* lets make this fast while ending up with some increass in image size */ + png_set_filter(png_ptr,0,PNG_FILTER_NONE); + /* png_set_filter(png_ptr,0,PNG_FILTER_SUB); */ png_set_compression_level(png_ptr,1); /* png_set_compression_strategy(png_ptr,Z_HUFFMAN_ONLY); */ /* @@ -817,13 +836,146 @@ static int gfx_save_png (art_u8 *buffer, FILE *fp, long width, long height, lon } +/* ----- COMMON ROUTINES for pdf, svg and eps */ +#define min3(a, b, c) (a < b ? (a < c ? a : c) : (b < c ? b : c)) +#define max3(a, b, c) (a > b ? (a > c ? a : c) : (b > c ? b : c)) + +#define PDF_CALC_DEBUG 0 + +typedef struct pdf_point +{ + double x, y; +} pdf_point; + +typedef struct +{ + double ascender, descender, baselineY; + pdf_point sizep, minp, maxp; + double x, y, tdx, tdy; + double r, cos_r, sin_r; + double ma, mb, mc, md, mx, my; /* pdf coord matrix */ + double tmx, tmy; /* last 2 coords of text coord matrix */ +#if PDF_CALC_DEBUG + int debug; +#endif +} pdf_coords; + +#if PDF_CALC_DEBUG +static void pdf_dump_calc(gfx_node_t *node, pdf_coords *g) +{ + fprintf(stderr, "PDF CALC =============================\n"); + fprintf(stderr, " '%s' at %f pt\n", node->text, node->size); + fprintf(stderr, " align h = %s, v = %s, sizep = %f, %f\n", + (node->halign == GFX_H_RIGHT ? "r" : + (node->halign == GFX_H_CENTER ? "c" : + (node->halign == GFX_H_LEFT ? "l" : "N"))), + (node->valign == GFX_V_TOP ? "t" : + (node->valign == GFX_V_CENTER ? "c" : + (node->valign == GFX_V_BOTTOM ? "b" : "N"))), + g->sizep.x, g->sizep.y); + fprintf(stderr, " r = %f = %f, cos = %f, sin = %f\n", + g->r, node->angle, g->cos_r, g->sin_r); + fprintf(stderr, " ascender = %f, descender = %f, baselineY = %f\n", + g->ascender, g->descender, g->baselineY); + fprintf(stderr, " sizep: %f, %f\n", g->sizep.x, g->sizep.y); + fprintf(stderr, " minp: %f, %f maxp = %f, %f\n", + g->minp.x, g->minp.y, g->maxp.x, g->maxp.y); + fprintf(stderr, " x = %f, y = %f\n", g->x, g->y); + fprintf(stderr, " tdx = %f, tdy = %f\n", g->tdx, g->tdy); + fprintf(stderr, " GM = %f, %f, %f, %f, %f, %f\n", + g->ma, g->mb, g->mc, g->md, g->mx, g->my); + fprintf(stderr, " TM = %f, %f, %f, %f, %f, %f\n", + g->ma, g->mb, g->mc, g->md, g->tmx, g->tmy); +} +#endif + +#if PDF_CALC_DEBUG +#define PDF_DD(x) if (g->debug) x; +#else +#define PDF_DD(x) +#endif + +static void pdf_rotate(pdf_coords *g, pdf_point *p) +{ + double x2 = g->cos_r * p->x - g->sin_r * p->y; + double y2 = g->sin_r * p->x + g->cos_r * p->y; + PDF_DD( fprintf(stderr, " rotate(%f, %f) -> %f, %f\n", p->x, p->y, x2, y2)) + p->x = x2; + p->y = y2; +} + + +static void pdf_calc(int page_height, gfx_node_t *node, pdf_coords *g) +{ + pdf_point a, b, c; +#if PDF_CALC_DEBUG + /* g->debug = !!strstr(node->text, "RevProxy-1") || !!strstr(node->text, "08:00"); */ + g->debug = !!strstr(node->text, "sekunder") || !!strstr(node->text, "Web"); +#endif + g->x = node->x; + g->y = page_height - node->y; + if (node->angle) { + g->r = 2 * M_PI * node->angle / 360.0; + g->cos_r = cos(g->r); + g->sin_r = sin(g->r); + } else { + g->r = 0; + g->cos_r = 1; + g->sin_r = 0; + } + g->ascender = afm_get_ascender(node->filename, node->size); + g->descender = afm_get_descender(node->filename, node->size); + g->sizep.x = afm_get_text_width(0, node->filename, node->size, node->tabwidth, node->text); + /* seems like libart ignores the descender when doing vertial-align = bottom, + so we do that too, to get labels v-aligning properly */ + g->sizep.y = -g->ascender; /* + afm_get_descender(font->ps_font, node->size); */ + g->baselineY = -g->ascender - g->sizep.y / 2; + a.x = g->sizep.x; a.y = g->sizep.y; + b.x = g->sizep.x; b.y = 0; + c.x = 0; c.y = g->sizep.y; + if (node->angle) { + pdf_rotate(g, &a); + pdf_rotate(g, &b); + pdf_rotate(g, &c); + } + g->minp.x = min3(a.x, b.x, c.x); + g->minp.y = min3(a.y, b.y, c.y); + g->maxp.x = max3(a.x, b.x, c.x); + g->maxp.y = max3(a.y, b.y, c.y); + /* The alignment parameters in node->valign and node->halign + specifies the alignment in the non-rotated coordinate system + (very unlike pdf/postscript), which complicates matters. + */ + switch (node->halign) { + case GFX_H_RIGHT: g->tdx = -g->maxp.x; break; + case GFX_H_CENTER: g->tdx = -(g->maxp.x + g->minp.x) / 2; break; + case GFX_H_LEFT: g->tdx = -g->minp.x; break; + case GFX_H_NULL: g->tdx = 0; break; + } + switch(node->valign){ + case GFX_V_TOP: g->tdy = -g->maxp.y; break; + case GFX_V_CENTER: g->tdy = -(g->maxp.y + g->minp.y) / 2; break; + case GFX_V_BOTTOM: g->tdy = -g->minp.y; break; + case GFX_V_NULL: g->tdy = 0; break; + } + g->ma = g->cos_r; + g->mb = g->sin_r; + g->mc = -g->sin_r; + g->md = g->cos_r; + g->mx = g->x + g->tdx; + g->my = g->y + g->tdy; + g->tmx = g->mx - g->ascender * g->mc; + g->tmy = g->my - g->ascender * g->md; + PDF_DD(pdf_dump_calc(node, g)) +} + /* ------- SVG ------- SVG reference: http://www.w3.org/TR/SVG/ */ static int svg_indent = 0; static int svg_single_line = 0; -static const char *svg_default_font = "Helvetica"; +static const char *svg_default_font = "-dummy-"; typedef struct svg_dash { int dash_enable; @@ -971,7 +1123,7 @@ static void svg_write_color(FILE *fp, gfx_color_t c, const char *attr) } fputs("\"", fp); if (opacity != 0xFF) { - fprintf(fp, " stroke-opacity=\""); + fprintf(fp, " opacity=\""); svg_write_number(fp, opacity / 255.0); fputs("\"", fp); } @@ -1200,47 +1352,37 @@ static void svg_area(FILE *fp, gfx_node_t *node) static void svg_text(FILE *fp, gfx_node_t *node) { - double x = node->x - LINEOFFSET; - double y = node->y - LINEOFFSET; + pdf_coords g; + const char *fontname; + /* as svg has 0,0 in top-left corner (like most screens) instead of + bottom-left corner like pdf and eps, we have to fake the coords + using offset and inverse sin(r) value */ + int page_height = 1000; + pdf_calc(page_height, node, &g); if (node->angle != 0) { svg_start_tag(fp, "g"); - fputs(" transform=\"translate(", fp); - svg_write_number(fp, x); - fputs(",", fp); - svg_write_number(fp, y); - fputs(") rotate(", fp); - svg_write_number(fp, -node->angle); - fputs(")\"", fp); - x = y = 0; + /* can't use svg_write_number as 2 decimals is far from enough to avoid + skewed text */ + fprintf(fp, " transform=\"matrix(%f,%f,%f,%f,%f,%f)\"", + g.ma, -g.mb, -g.mc, g.md, g.tmx, page_height - g.tmy); svg_close_tag(fp); } - switch (node->valign) { - case GFX_V_TOP: y += node->size; break; - case GFX_V_CENTER: y += node->size / 3; break; - case GFX_V_BOTTOM: break; - case GFX_V_NULL: break; - } svg_start_tag(fp, "text"); - fputs(" x=\"", fp); - svg_write_number(fp, x); - fputs("\" y=\"", fp); - svg_write_number(fp, y); - -/* if (strcmp(node->filename, svg_default_font)) - fprintf(fp, " font-family=\"%s\"", node->filename); - */ - fputs("\" font-family=\"Helvetica", fp); - fputs("\" font-size=\"", fp); + if (!node->angle) { + fputs(" x=\"", fp); + svg_write_number(fp, g.tmx); + fputs("\" y=\"", fp); + svg_write_number(fp, page_height - g.tmy); + fputs("\"", fp); + } + fontname = afm_get_font_name(node->filename); + if (strcmp(fontname, svg_default_font)) + fprintf(fp, " font-family=\"%s\"", fontname); + fputs(" font-size=\"", fp); svg_write_number(fp, node->size); fputs("\"", fp); if (!svg_color_is_black(node->color)) svg_write_color(fp, node->color, "fill"); - switch (node->halign) { - case GFX_H_RIGHT: fputs(" text-anchor=\"end\"", fp); break; - case GFX_H_CENTER: fputs(" text-anchor=\"middle\"", fp); break; - case GFX_H_LEFT: break; - case GFX_H_NULL: break; - } svg_close_tag_single_line(fp); /* support for node->tabwidth missing */ svg_write_text(fp, node->text); @@ -1253,13 +1395,22 @@ int gfx_render_svg (gfx_canvas_t *canvas, art_u32 width, art_u32 height, gfx_color_t background, FILE *fp){ gfx_node_t *node = canvas->firstnode; + /* Find the first font used, and assume it is the mostly used + one. It reduces the number of font-familty attributes. */ + while (node) { + if (node->type == GFX_TEXT && node->filename) { + svg_default_font = afm_get_font_name(node->filename); + break; + } + node = node->next; + } fputs( "\n" "\n" "