X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Frrd_gfx.c;h=6f83bc561fa88b170f9acc2c98d99d42f5bba38f;hb=0f913ead7c1e1e7ba4976e8763d73c3167f8cf73;hp=fb9324040383c32ee6b554014e396cc222405d2b;hpb=554df7224e8b3fec9ec5981812b453c43072f150;p=rrdtool.git diff --git a/src/rrd_gfx.c b/src/rrd_gfx.c index fb93240..6f83bc5 100644 --- a/src/rrd_gfx.c +++ b/src/rrd_gfx.c @@ -23,6 +23,9 @@ /* 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 */ @@ -821,6 +824,139 @@ 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/ @@ -975,7 +1111,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 +1340,38 @@ 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; + /* 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 (!node->angle) { + fputs(" x=\"", fp); + svg_write_number(fp, g.tmx); + fputs("\" y=\"", fp); + svg_write_number(fp, page_height - g.tmy); + fputs("\"", fp); + } /* if (strcmp(node->filename, svg_default_font)) fprintf(fp, " font-family=\"%s\"", node->filename); */ - fputs("\" font-family=\"Helvetica", fp); + fputs(" font-family=\"Helvetica", fp); 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); @@ -1262,8 +1389,8 @@ int gfx_render_svg (gfx_canvas_t *canvas, "\n" "