X-Git-Url: https://git.octo.it/?p=rrdtool.git;a=blobdiff_plain;f=src%2Frrd_gfx.c;h=d01b90487a5bbb03edc074e0181b503fe460498c;hp=cdb92e103f0625c791e22ef06e04b68d18c487c4;hb=0dc5d6d50c0d95ba4f04b656358b26518d4ce854;hpb=2c188938780b23a4abc14cb70268580ff5b6daca diff --git a/src/rrd_gfx.c b/src/rrd_gfx.c index cdb92e1..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; @@ -45,8 +49,8 @@ struct gfx_string_s { 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); +gfx_string gfx_string_create ( gfx_canvas_t *canvas, FT_Face face, + const char *text, int rotation, double tabwidth, double size); /* create a freetype glyph string */ static void gfx_string_destroy ( gfx_string string ); @@ -88,7 +92,9 @@ gfx_canvas_t *gfx_new_canvas (void) { canvas->imgformat = IF_PNG; /* we default to PNG output */ canvas->interlaced = 0; canvas->zoom = 1.0; - return canvas; + canvas->font_aa_threshold = -1.0; + canvas->aa_type = AA_NORMAL; + return canvas; } /* create a new line */ @@ -275,7 +281,7 @@ double gfx_get_text_width ( gfx_canvas_t *canvas, double tabwidth, char* text, int rotation){ switch (canvas->imgformat) { case IF_PNG: - return gfx_get_text_width_libart (start, font, size, tabwidth, text, rotation); + return gfx_get_text_width_libart (canvas, start, font, size, tabwidth, text, rotation); case IF_SVG: /* fall through */ case IF_EPS: case IF_PDF: @@ -286,8 +292,8 @@ double gfx_get_text_width ( gfx_canvas_t *canvas, } double gfx_get_text_width_libart ( - double start, char* font, double size, - double tabwidth, char* text, int rotation){ + gfx_canvas_t *canvas, double UNUSED(start), char* font, double size, + double tabwidth, char* text, int rotation ){ int error; double text_width=0; @@ -301,7 +307,7 @@ double gfx_get_text_width_libart ( error = FT_Set_Char_Size(face, size*64,size*64, 100,100); if ( error ) return -1; - string = gfx_string_create( face, text, rotation,tabwidth); + string = gfx_string_create( canvas, face, text, rotation, tabwidth, size ); text_width = string->width; gfx_string_destroy(string); FT_Done_FreeType(library); @@ -358,8 +364,8 @@ static void compute_string_bbox(gfx_string string) { } /* create a free type glyph string */ -gfx_string gfx_string_create(FT_Face face,const char *text, - int rotation, double tabwidth) +gfx_string gfx_string_create(gfx_canvas_t *canvas, FT_Face face,const char *text, + int rotation, double tabwidth, double size ) { FT_GlyphSlot slot = face->glyph; /* a small shortcut */ @@ -367,18 +373,32 @@ gfx_string gfx_string_create(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); @@ -389,15 +409,15 @@ gfx_string gfx_string_create(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; @@ -413,7 +433,6 @@ gfx_string gfx_string_create(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 */ @@ -427,14 +446,17 @@ gfx_string gfx_string_create(FT_Face face,const char *text, /* 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); + error = FT_Load_Glyph (face, glyph->index, size > canvas->font_aa_threshold ? + canvas->aa_type == AA_NORMAL ? FT_LOAD_TARGET_NORMAL : + 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 @@ -456,23 +478,25 @@ gfx_string gfx_string_create(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; } /* convert to a bitmap - destroy native image */ - error = FT_Glyph_To_Bitmap (&glyph->image, FT_RENDER_MODE_NORMAL, 0, 1); + error = FT_Glyph_To_Bitmap (&glyph->image, size > canvas->font_aa_threshold ? + canvas->aa_type == AA_NORMAL ? FT_RENDER_MODE_NORMAL : + 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 */ @@ -482,7 +506,6 @@ gfx_string gfx_string_create(FT_Face face,const char *text, string->width = string->bbox.xMax - string->bbox.xMin; } */ string->height = string->bbox.yMax - string->bbox.yMin; - return string; } @@ -506,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); + /* pvec = art_vpath_perturb(vec); + art_free(vec); */ if(node->type == GFX_LINE){ - svp = art_svp_vpath_stroke ( pvec, ART_PATH_STROKE_JOIN_ROUND, + 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 ( pvec ); - svpt = art_svp_uncross( svp ); - art_svp_free(svp); - svp = art_svp_rewind_uncrossed(svpt,ART_WIND_RULE_NONZERO); - art_svp_free(svpt); + svp = art_svp_from_vpath ( vec ); + /* 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(pvec); + art_free(vec); /* 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); @@ -582,7 +608,7 @@ int gfx_render_png (gfx_canvas_t *canvas, pen_x = node->x * canvas->zoom; pen_y = node->y * canvas->zoom; - string = gfx_string_create (face, node->text, node->angle, node->tabwidth); + string = gfx_string_create (canvas, face, node->text, node->angle, node->tabwidth, node->size); switch(node->halign){ case GFX_H_RIGHT: vec.x = -string->bbox.xMax; break; @@ -609,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; } @@ -647,23 +673,46 @@ int gfx_render_png (gfx_canvas_t *canvas, } art_free(letter); */ - - 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.pitch + 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); + switch ( bit->bitmap.pixel_mode ) { + case FT_PIXEL_MODE_GRAY: + 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.pitch + 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); + } + } } - } + break; + + case FT_PIXEL_MODE_MONO: + 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 ; + + if (buf_x < 0 || buf_x >= (long)pys_width) continue; + buf_x *= bytes_per_pixel ; + if ( (fcolor[3] = falpha * ((*(bit->bitmap.buffer + iy * bit->bitmap.pitch + ix/8) >> (7 - (ix % 8))) & 1)) > 0 ) + art_rgba_rgba_composite(buffer + buf_y + buf_x ,fcolor,1); + } + } + break; + + default: + rrd_set_error("unknown freetype pixel mode: %d", bit->bitmap.pixel_mode); + break; } /* @@ -764,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); */ /* @@ -786,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; @@ -940,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); } @@ -1169,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); @@ -1222,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" "