X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Frrd_gfx.c;h=2cdf20cced85184bb8aee394694b9aed3be5d9cf;hb=5fc7ff89bdbced9c593c566fea9840a269935dcd;hp=6f83bc561fa88b170f9acc2c98d99d42f5bba38f;hpb=0f913ead7c1e1e7ba4976e8763d73c3167f8cf73;p=rrdtool.git diff --git a/src/rrd_gfx.c b/src/rrd_gfx.c index 6f83bc5..2cdf20c 100644 --- a/src/rrd_gfx.c +++ b/src/rrd_gfx.c @@ -1,16 +1,21 @@ /**************************************************************************** - * RRDtool 1.2.4 Copyright by Tobi Oetiker, 1997-2005 + * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007 **************************************************************************** * rrd_gfx.c graphics wrapper for rrdtool **************************************************************************/ /* #define DEBUG */ -#ifdef DEBUG -# define DPRINT(x) (void)(printf x, printf("\n")) -#else -# define DPRINT(x) -#endif +/* stupid MSVC doesnt support variadic macros = no debug for now! */ +#ifdef _MSC_VER +# define RRDPRINTF() +#else +# ifdef DEBUG +# define RRDPRINTF(...) fprintf(stderr, __VA_ARGS__); +# else +# define RRDPRINTF(...) +# endif /* DEBUG */ +#endif /* _MSC_VER */ #include "rrd_tool.h" #include #include @@ -19,6 +24,7 @@ #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 @@ -37,7 +43,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; @@ -291,7 +297,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; @@ -302,10 +308,15 @@ double gfx_get_text_width_libart ( FT_Init_FreeType( &library ); error = FT_New_Face( library, font, 0, &face ); - if ( error ) return -1; + if ( error ) { + FT_Done_FreeType(library); + return -1; + } error = FT_Set_Char_Size(face, size*64,size*64, 100,100); - if ( error ) return -1; - + if ( error ) { + FT_Done_FreeType(library); + return -1; + } string = gfx_string_create( canvas, face, text, rotation, tabwidth, size ); text_width = string->width; gfx_string_destroy(string); @@ -372,18 +383,36 @@ 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){ + /* conversion did not work, so lets fall back to just use what we got */ + string->count=clen-1; + for(n=0;text[n] != '\0';n++){ + cstr[n]=(unsigned char)text[n]; + } + } +#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); @@ -394,15 +423,16 @@ 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]; + letter = afm_fix_osx_charset(letter); /* unsafe macro */ 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; @@ -418,7 +448,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 */ @@ -437,12 +466,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); + RRDPRINTF("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); + RRDPRINTF("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 @@ -464,7 +493,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"); + RRDPRINTF("couldn't transform glyph id %d\n", letter) continue; } @@ -474,16 +503,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"); + RRDPRINTF("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 */ @@ -493,7 +521,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; } @@ -585,6 +612,7 @@ int gfx_render_png (gfx_canvas_t *canvas, &face ); if ( error ) { rrd_set_error("failed to load %s",node->filename); + break; } error = FT_Set_Char_Size(face, /* handle to face object */ @@ -592,11 +620,16 @@ int gfx_render_png (gfx_canvas_t *canvas, (long)(node->size*64), (long)(100*canvas->zoom), (long)(100*canvas->zoom)); - if ( error ) break; + if ( error ) { + FT_Done_Face(face); + break; + } pen_x = node->x * canvas->zoom; pen_y = node->y * canvas->zoom; string = gfx_string_create (canvas, face, node->text, node->angle, node->tabwidth, node->size); + FT_Done_Face(face); + switch(node->halign){ case GFX_H_RIGHT: vec.x = -string->bbox.xMax; break; @@ -623,19 +656,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"); + RRDPRINTF("no image\n") continue; } error = FT_Glyph_Copy (glyph->image, &image); if (error) { - fprintf (stderr, "couldn't copy image\n"); + RRDPRINTF("couldn't copy image\n") continue; } @@ -963,7 +996,7 @@ static void pdf_calc(int page_height, gfx_node_t *node, pdf_coords *g) */ 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; @@ -1026,37 +1059,50 @@ static void svg_close_tag_empty_node(FILE *fp) static void svg_write_text(FILE *fp, const char *text) { - const unsigned char *p, *start, *last; - unsigned int ch; - p = (const unsigned char*)text; - if (!p) - return; - /* trim leading spaces */ - while (*p == ' ') - p++; - start = p; - /* trim trailing spaces */ - last = p - 1; - while ((ch = *p) != 0) { - if (ch != ' ') - last = p; - p++; - } - /* encode trimmed text */ - p = start; - while (p <= last) { +#ifdef HAVE_MBSTOWCS + size_t clen; + wchar_t *p, *cstr, ch; + int text_count; + if (!text) + return; + clen = strlen(text) + 1; + cstr = malloc(sizeof(wchar_t) * clen); + text_count = mbstowcs(cstr, text, clen); + if (text_count == -1) + text_count = mbstowcs(cstr, "Enc-Err", 6); + p = cstr; +#else + unsigned char *p = text; + unsigned char *cstr; + char ch; + if (!p) + return; +#endif + while (1) { ch = *p++; - ch = afm_host2unicode(ch); /* unsafe macro */ + ch = afm_fix_osx_charset(ch); /* unsafe macro */ switch (ch) { + case 0: +#ifdef HAVE_MBSTOWCS + free(cstr); +#endif + return; case '&': fputs("&", fp); break; case '<': fputs("<", fp); break; case '>': fputs(">", fp); break; case '"': fputs(""", fp); break; default: - if (ch >= 127) - fprintf(fp, "&#%d;", ch); + if (ch == 32) { +#ifdef HAVE_MBSTOWCS + if (p <= cstr + 1 || !*p || *p == 32) + fputs(" ", fp); /* non-breaking space in unicode */ + else +#endif + fputc(32, fp); + } else if (ch < 32 || ch >= 127) + fprintf(fp, "&#%d;", (int)ch); else - putc(ch, fp); + putc((char)ch, fp); } } } @@ -1341,6 +1387,7 @@ static void svg_area(FILE *fp, gfx_node_t *node) static void svg_text(FILE *fp, gfx_node_t *node) { 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 */ @@ -1362,12 +1409,10 @@ static void svg_text(FILE *fp, gfx_node_t *node) 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-size=\"", 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)) @@ -1384,13 +1429,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" "\n", fp); svg_start_tag(fp, "svg"); fputs(" width=\"", fp); - svg_write_number(fp, width * canvas->zoom); + svg_write_number(fp, width * canvas->zoom); fputs("\" height=\"", fp); - svg_write_number(fp, height * canvas->zoom); + svg_write_number(fp, height * canvas->zoom); fputs("\" x=\"0\" y=\"0\" viewBox=\"", fp); svg_write_number(fp, -LINEOFFSET); fputs(" ", fp); @@ -1412,13 +1466,16 @@ int gfx_render_svg (gfx_canvas_t *canvas, fputs(" ", fp); svg_write_number(fp, height - LINEOFFSET); fputs("\" preserveAspectRatio=\"xMidYMid\"", fp); - fprintf(fp, " font-family=\"%s\"", svg_default_font); /* default font */ - fputs(" stroke-linecap=\"round\" stroke-linejoin=\"round\"", fp); + fprintf(fp, " font-family=\"%s\"", svg_default_font); /* default font */ + fputs(" stroke-linecap=\"round\" stroke-linejoin=\"round\"", fp); + fputs(" xmlns=\"http://www.w3.org/2000/svg\"", fp); + fputs(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"", fp); svg_close_tag(fp); svg_start_tag(fp, "rect"); fprintf(fp, " x=\"0\" y=\"0\" width=\"%d\" height=\"%d\"", width, height); svg_write_color(fp, background, "fill"); svg_close_tag_empty_node(fp); + node = canvas->firstnode; while (node) { switch (node->type) { case GFX_LINE: @@ -1557,7 +1614,7 @@ static int eps_prologue(eps_state *state) gfx_node_t *node; fputs( "%!PS-Adobe-3.0 EPSF-3.0\n" - "%%Creator: RRDtool 1.2.4 Tobias Oetiker, http://tobi.oetiker.ch\n" + "%%Creator: RRDtool " PACKAGE_VERSION " Tobias Oetiker, http://tobi.oetiker.ch\n" /* can't like weird chars here */ "%%Title: (RRDtool output)\n" "%%DocumentData: Clean7Bit\n" @@ -1686,10 +1743,27 @@ static void eps_write_linearea(eps_state *state, gfx_node_t *node) static void eps_write_text(eps_state *state, gfx_node_t *node) { FILE *fp = state->fp; - const unsigned char *p; const char *ps_font = afm_get_font_postscript_name(node->filename); int lineLen = 0; pdf_coords g; +#ifdef HAVE_MBSTOWCS + size_t clen; + wchar_t *p, *cstr, ch; + int text_count; + if (!node->text) + return; + clen = strlen(node->text) + 1; + cstr = malloc(sizeof(wchar_t) * clen); + text_count = mbstowcs(cstr, node->text, clen); + if (text_count == -1) + text_count = mbstowcs(cstr, "Enc-Err", 6); + p = cstr; +#else + const unsigned char *p = node->text; + unsigned char ch; + if (!p) + return; +#endif pdf_calc(state->page_height, node, &g); eps_set_color(state, node->color); if (strcmp(ps_font, state->font) || node->size != state->font_size) { @@ -1702,29 +1776,47 @@ static void eps_write_text(eps_state *state, gfx_node_t *node) fputs("T1 ", fp); fputs("(", fp); lineLen = 20; - for (p = (const unsigned char*)node->text; *p; p++) { - if (lineLen > 70) { + while (1) { + ch = *p; + if (!ch) + break; + ch = afm_fix_osx_charset(ch); /* unsafe macro */ + if (++lineLen > 70) { fputs("\\\n", fp); /* backslash and \n */ lineLen = 0; } - switch (*p) { + switch (ch) { + case '%': case '(': case ')': case '\\': + fputc('\\', fp); + fputc(ch, fp); + break; case '\n': + fputs("\\n", fp); + break; case '\r': + fputs("\\r", fp); + break; case '\t': - fputc('\\', fp); - lineLen++; - /* fall-through */ + fputs("\\t", fp); + break; default: - if (*p >= 126) - fprintf(fp, "\\%03o", *p); - else - fputc(*p, fp); - lineLen++; - } + if (ch > 255) { + fputc('?', fp); + } else if (ch >= 126 || ch < 32) { + fprintf(fp, "\\%03o", (unsigned int)ch); + lineLen += 3; + } else { + fputc(ch, fp); + } + } + p++; } +#ifdef HAVE_MBSTOWCS + free(cstr); +#endif if (node->angle) { /* can't use svg_write_number as 2 decimals is far from enough to avoid skewed text */ @@ -1772,6 +1864,7 @@ int gfx_render_eps (gfx_canvas_t *canvas, state.linecap = -1; state.linejoin = -1; state.has_dash = 0; + state.line_width = 1; if (eps_prologue(&state) == -1) return -1; eps_set_color(&state, background); @@ -1883,6 +1976,17 @@ static void pdf_put(pdf_buffer *buf, const char *text, int len) buf->current_size += len; } +static void pdf_put_char(pdf_buffer *buf, char c) +{ + if (buf->alloc_size >= buf->current_size + 1) { + buf->data[buf->current_size++] = c; + } else { + char tmp[1]; + tmp[0] = (char)c; + pdf_put(buf, tmp, 1); + } +} + static void pdf_puts(pdf_buffer *buf, const char *text) { pdf_put(buf, text, strlen(text)); @@ -1913,35 +2017,76 @@ static void pdf_putnumber(pdf_buffer *buf, double d) pdf_puts(buf, tmp); } -static void pdf_put_string_contents(pdf_buffer *buf, const char *text) +static void pdf_put_string_contents_wide(pdf_buffer *buf, const afm_char *text) { - const char *p = text; + const afm_char *p = text; while (1) { - char ch = *p; + afm_char ch = *p; + ch = afm_fix_osx_charset(ch); /* unsafe macro */ switch (ch) { - case 0: return; + case 0: + return; case '(': + pdf_puts(buf, "\\("); + break; case ')': + pdf_puts(buf, "\\)"); + break; case '\\': + pdf_puts(buf, "\\\\"); + break; case '\n': + pdf_puts(buf, "\\n"); + break; case '\r': + pdf_puts(buf, "\\r"); + break; case '\t': - pdf_puts(buf, "\\"); - pdf_put(buf, (char)ch, 1); + pdf_puts(buf, "\\t"); break; default: - if (ch >= 126 || ch < 32) { + if (ch > 255) { + pdf_put_char(buf, '?'); + } else if (ch > 125 || ch < 32) { + pdf_put_char(buf, ch); + } else { char tmp[10]; - snprintf(tmp, sizeof(tmp), "\\%03o", ch); + snprintf(tmp, sizeof(tmp), "\\%03o", (int)ch); pdf_puts(buf, tmp); - } else { - pdf_put(buf, p, 1); } } p++; } } +static void pdf_put_string_contents(pdf_buffer *buf, const char *text) +{ +#ifdef HAVE_MBSTOWCS + size_t clen = strlen(text) + 1; + wchar_t *cstr = malloc(sizeof(wchar_t) * clen); + int text_count = mbstowcs(cstr, text, clen); + if (text_count == -1) + text_count = mbstowcs(cstr, "Enc-Err", 6); + pdf_put_string_contents_wide(buf, cstr); +#if 0 + if (*text == 'W') { + fprintf(stderr, "Decoding utf8 for '%s'\n", text); + wchar_t *p = cstr; + char *pp = text; + fprintf(stderr, "sz wc = %d\n", sizeof(wchar_t)); + while (*p) { + fprintf(stderr, " %d = %c versus %d = %c\n", *p, (char)*p, 255 & (int)*pp, *pp); + p++; + pp++; + } + } +#endif + free(cstr); +#else + pdf_put_string_contents_wide(buf, text); +#endif +} + static void pdf_init_object(pdf_state *state, pdf_buffer *buf) { pdf_init_buffer(state, buf); @@ -2225,7 +2370,7 @@ static void pdf_init_document(pdf_state *state) static void pdf_setup_document(pdf_state *state) { - const char *creator = "RRDtool 1.2.4 Tobias Oetiker, http://tobi.oetiker.ch"; + const char *creator = "RRDtool " PACKAGE_VERSION " Tobias Oetiker, http://tobi.oetiker.ch"; /* all objects created by now, so init code can reference them */ /* HEADER */ pdf_puts(&state->pdf_header, "%PDF-1.3\n"); @@ -2345,7 +2490,7 @@ static void pdf_free_resources(pdf_state *state) int gfx_render_pdf (gfx_canvas_t *canvas, art_u32 width, art_u32 height, - gfx_color_t background, FILE *fp){ + gfx_color_t UNUSED(background), FILE *fp){ struct pdf_state state; memset(&state, 0, sizeof(pdf_state)); state.fp = fp;