X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Frrd_gfx.c;h=ac33ba208ad9681afe4806d38b6e8cc37bc2ca17;hb=d3061079f2399ecb1e8af5c132106157a03aa843;hp=93341e709657c8a53dde176dafa16559e2ed47eb;hpb=87617d608f152fd298bcace0d881c92b5538d65d;p=rrdtool.git diff --git a/src/rrd_gfx.c b/src/rrd_gfx.c index 93341e7..ac33ba2 100644 --- a/src/rrd_gfx.c +++ b/src/rrd_gfx.c @@ -1,5 +1,5 @@ /**************************************************************************** - * RRDtool 1.2.3 Copyright by Tobi Oetiker, 1997-2005 + * RRDtool 1.2.8 Copyright by Tobi Oetiker, 1997-2005 **************************************************************************** * rrd_gfx.c graphics wrapper for rrdtool **************************************************************************/ @@ -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 */ @@ -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 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); @@ -396,10 +414,10 @@ gfx_string gfx_string_create(gfx_canvas_t *canvas, FT_Face face,const char *text /* 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 */ @@ -481,6 +498,7 @@ gfx_string gfx_string_create(gfx_canvas_t *canvas, FT_Face face,const char *text n++; } + free(cstr); /* printf ("number of glyphs = %d\n", string->num_glyphs);*/ compute_string_bbox( string ); /* the last character was a tab */ @@ -490,7 +508,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; } @@ -821,13 +838,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; @@ -975,7 +1125,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); } @@ -1204,47 +1354,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); @@ -1257,13 +1397,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" "