-int gfx_add_point (gfx_node_t *node,
- double x, double y){
- if (node == NULL) return 1;
- if (node->type == GFX_AREA) {
- double X0 = node->path[0].x;
- double Y0 = node->path[0].y;
- node->points -= 2;
- art_vpath_add_point (&(node->path),
- &(node->points),
- &(node->points_max),
- ART_LINETO,
- x,y);
- art_vpath_add_point (&(node->path),
- &(node->points),
- &(node->points_max),
- ART_LINETO,
- X0,Y0);
- art_vpath_add_point (&(node->path),
- &(node->points),
- &(node->points_max),
- ART_END,
- 0,0);
- } else if (node->type == GFX_LINE) {
- node->points -= 1;
- art_vpath_add_point (&(node->path),
- &(node->points),
- &(node->points_max),
- ART_LINETO,
- x+LINEOFFSET,y+LINEOFFSET);
- art_vpath_add_point (&(node->path),
- &(node->points),
- &(node->points_max),
- ART_END,
- 0,0);
-
- } else {
- /* can only add point to areas and lines */
- return 1;
- }
- return 0;
-}
-
-void gfx_close_path (gfx_node_t *node) {
- node->closed_path = 1;
- if (node->path[0].code == ART_MOVETO_OPEN)
- node->path[0].code = ART_MOVETO;
-}
-
-/* create a text node */
-gfx_node_t *gfx_new_text (gfx_canvas_t *canvas,
- double x, double y, gfx_color_t color,
- char* font, double size,
- double tabwidth, double angle,
- enum gfx_h_align_en h_align,
- enum gfx_v_align_en v_align,
- char* text){
- gfx_node_t *node = gfx_new_node(canvas,GFX_TEXT);
-
- node->text = strdup(text);
- node->size = size;
- node->filename = strdup(font);
- node->x = x;
- node->y = y;
- node->angle = angle;
- node->color = color;
- node->tabwidth = tabwidth;
- node->halign = h_align;
- node->valign = v_align;
-#if 0
- /* debugging: show text anchor
- green is along x-axis, red is downward y-axis */
- if (1) {
- double a = 2 * M_PI * -node->angle / 360.0;
- double cos_a = cos(a);
- double sin_a = sin(a);
- double len = 3;
- gfx_new_line(canvas,
- x, y,
- x + len * cos_a, y - len * sin_a,
- 0.2, 0x00FF0000);
- gfx_new_line(canvas,
- x, y,
- x + len * sin_a, y + len * cos_a,
- 0.2, 0xFF000000);
- }
-#endif
- return node;
-}
-
-int gfx_render(gfx_canvas_t *canvas,
- art_u32 width, art_u32 height,
- gfx_color_t background, FILE *fp){
- switch (canvas->imgformat) {
- case IF_PNG:
- return gfx_render_png (canvas, width, height, background, fp);
- case IF_SVG:
- return gfx_render_svg (canvas, width, height, background, fp);
- case IF_EPS:
- return gfx_render_eps (canvas, width, height, background, fp);
- case IF_PDF:
- return gfx_render_pdf (canvas, width, height, background, fp);
- default:
- return -1;
- }
-}
-
-static void gfx_string_destroy ( gfx_string string ) {
- unsigned int n;
- if (string->glyphs) {
- for (n=0; n<string->num_glyphs; ++n)
- FT_Done_Glyph (string->glyphs[n].image);
- free (string->glyphs);
- }
- free (string);
-}
-
-
-double gfx_get_text_width ( gfx_canvas_t *canvas,
- double start, char* font, double size,
- double tabwidth, char* text, int rotation){
- switch (canvas->imgformat) {
- case IF_PNG:
- return gfx_get_text_width_libart (start, font, size, tabwidth, text, rotation);
- case IF_SVG: /* fall through */
- case IF_EPS:
- case IF_PDF:
- return afm_get_text_width(start, font, size, tabwidth, text);
- default:
- return size * strlen(text);
- }
-}
-
-double gfx_get_text_width_libart (
- double start, char* font, double size,
- double tabwidth, char* text, int rotation){
-
- int error;
- double text_width=0;
- FT_Face face;
- FT_Library library=NULL;
- gfx_string string;
-
- FT_Init_FreeType( &library );
- error = FT_New_Face( library, font, 0, &face );
- if ( error ) return -1;
- error = FT_Set_Char_Size(face, size*64,size*64, 100,100);
- if ( error ) return -1;
-
- string = gfx_string_create( face, text, rotation,tabwidth);
- text_width = string->width;
- gfx_string_destroy(string);
- FT_Done_FreeType(library);
- return text_width/64;
-}
-
-static void gfx_libart_close_path(gfx_node_t *node, ArtVpath **vec)
-{
- /* libart must have end==start for closed paths,
- even if using ART_MOVETO and not ART_MOVETO_OPEN
- so add extra point which is the same as the starting point */
- int points_max = node->points; /* scaled array has exact size */
- int points = node->points - 1;
- art_vpath_add_point (vec, &points, &points_max, ART_LINETO,
- (**vec).x, (**vec).y);
- art_vpath_add_point (vec, &points, &points_max, ART_END, 0, 0);
-}
-
-
-/* find bbox of a string */
-static void compute_string_bbox(gfx_string string) {
- unsigned int n;
- FT_BBox bbox;
-
- bbox.xMin = bbox.yMin = 32000;
- bbox.xMax = bbox.yMax = -32000;
- for ( n = 0; n < string->num_glyphs; n++ ) {
- FT_BBox glyph_bbox;
- FT_Glyph_Get_CBox( string->glyphs[n].image, ft_glyph_bbox_gridfit,
- &glyph_bbox );
- if (glyph_bbox.xMin < bbox.xMin) {
- bbox.xMin = glyph_bbox.xMin;
- }
- if (glyph_bbox.yMin < bbox.yMin) {
- bbox.yMin = glyph_bbox.yMin;
- }
- if (glyph_bbox.xMax > bbox.xMax) {
- bbox.xMax = glyph_bbox.xMax;
- }
- if (glyph_bbox.yMax > bbox.yMax) {
- bbox.yMax = glyph_bbox.yMax;
- }
- }
- if ( bbox.xMin > bbox.xMax ) {
- bbox.xMin = 0;
- bbox.yMin = 0;
- bbox.xMax = 0;
- bbox.yMax = 0;
- }
- string->bbox.xMin = bbox.xMin;
- string->bbox.xMax = bbox.xMax;
- string->bbox.yMin = bbox.yMin;
- string->bbox.yMax = bbox.yMax;
-}
-
-/* create a free type glyph string */
-gfx_string gfx_string_create(FT_Face face,const char *text,
- int rotation, double tabwidth)
-{
-
- FT_GlyphSlot slot = face->glyph; /* a small shortcut */
- FT_Bool use_kerning;
- FT_UInt previous;
- FT_Vector ft_pen;
-
- gfx_string string;
- gfx_char glyph; /* current glyph in table */
- unsigned int n;
- int error;
- int gottab = 0;
- 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);
- string->transform.xy = (FT_Fixed)(-sin(M_PI*(rotation)/180.0)*0x10000);
- string->transform.yx = (FT_Fixed)( sin(M_PI*(rotation)/180.0)*0x10000);
- string->transform.yy = (FT_Fixed)( cos(M_PI*(rotation)/180.0)*0x10000);
-
- use_kerning = FT_HAS_KERNING(face);
- previous = 0;
- glyph = string->glyphs;
- for (n=0; n<string->count;glyph++) {
- 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];
-
- gottab = 0;
- if (letter == '\\' && n+1 < string->count && text[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;
- n++;
- letter = ' ';
- }
- if (letter == '\t'){
- letter = ' ';
- gottab = 1 ;
- }
- /* initialize each struct gfx_char_s */
- glyph->index = 0;
- glyph->pos.x = 0;
- glyph->pos.y = 0;
- glyph->image = NULL;
-
- glyph->index = FT_Get_Char_Index( face, letter );
-
- /* compute glyph origin */
- if ( use_kerning && previous && glyph->index ) {
- FT_Vector kerning;
- FT_Get_Kerning (face, previous, glyph->index,
- ft_kerning_default, &kerning);
- ft_pen.x += kerning.x;
- ft_pen.y += kerning.y;
- }
-
- /* 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);
- if (error) {
- fprintf (stderr, "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);
- continue;
- }
- /* if we are in tabbing mode, we replace the tab with a space and shift the position
- of the space so that its left edge is where the tab was supposed to land us */
- if (gottab){
- /* we are in gridfitting mode so the calculations happen in 1/64 pixles */
- ft_pen.x = tabwidth*64.0 * (float)(1 + (long)(ft_pen.x / (tabwidth * 64.0))) - slot->advance.x;
- }
- /* store current pen position */
- glyph->pos.x = ft_pen.x;
- glyph->pos.y = ft_pen.y;
-
-
- ft_pen.x += slot->advance.x;
- ft_pen.y += slot->advance.y;
-
- /* rotate glyph */
- vec = glyph->pos;
- FT_Vector_Transform (&vec, &string->transform);
- error = FT_Glyph_Transform (glyph->image, &string->transform, &vec);
- if (error) {
- fprintf (stderr, "couldn't transform glyph\n");
- continue;
- }
-
- /* convert to a bitmap - destroy native image */
- error = FT_Glyph_To_Bitmap (&glyph->image, FT_RENDER_MODE_NORMAL, 0, 1);
- if (error) {
- fprintf (stderr, "couldn't convert glyph to bitmap\n");
- continue;
- }
-
- /* increment number of glyphs */
- previous = glyph->index;
- string->num_glyphs++;
- n++;
-
- }
-/* printf ("number of glyphs = %d\n", string->num_glyphs);*/
- compute_string_bbox( string );
- /* the last character was a tab */
- /* if (gottab) { */
- string->width = ft_pen.x;
- /* } else {
- string->width = string->bbox.xMax - string->bbox.xMin;
- } */
- string->height = string->bbox.yMax - string->bbox.yMin;
-
- return string;
-}
-
-
-static int gfx_save_png (art_u8 *buffer, FILE *fp,
- long width, long height, long bytes_per_pixel);
-/* render grafics into png image */
-
-int gfx_render_png (gfx_canvas_t *canvas,
- art_u32 width, art_u32 height,
- gfx_color_t background, FILE *fp){
-
-
- FT_Library library;
- gfx_node_t *node = canvas->firstnode;
- /*
- art_u8 red = background >> 24, green = (background >> 16) & 0xff;
- art_u8 blue = (background >> 8) & 0xff, alpha = ( background & 0xff );
- */
- unsigned long pys_width = width * canvas->zoom;
- 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;
- i<pys_width*pys_height;
- i++){
- *(buffp++)=background;
- }
- FT_Init_FreeType( &library );
- while(node){
- switch (node->type) {
- case GFX_LINE:
- case GFX_AREA: {
- ArtVpath *vec,*pvec;
- double dst[6];
- ArtSVP *svp,*svpt;
- 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);
- if(node->type == GFX_LINE){
- svp = art_svp_vpath_stroke ( pvec, 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);
- }
- art_free(pvec);
- /* 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);
- art_svp_free(svp);
- break;
- }
- case GFX_TEXT: {
- unsigned int n;
- int error;
- art_u8 fcolor[4],falpha;
- FT_Face face;
- gfx_char glyph;
- gfx_string string;
- FT_Vector vec; /* 26.6 */
-
- float pen_x = 0.0 , pen_y = 0.0;
- /* double x,y; */
- long ix,iy;
-
- fcolor[0] = node->color >> 24;
- fcolor[1] = (node->color >> 16) & 0xff;
- fcolor[2] = (node->color >> 8) & 0xff;
- falpha = node->color & 0xff;
- error = FT_New_Face( library,
- (char *)node->filename,
- 0,
- &face );
- if ( error ) {
- rrd_set_error("failed to load %s",node->filename);
- break;
- }
- error = FT_Set_Char_Size(face, /* handle to face object */
- (long)(node->size*64),
- (long)(node->size*64),
- (long)(100*canvas->zoom),
- (long)(100*canvas->zoom));
- if ( error ) break;
- pen_x = node->x * canvas->zoom;
- pen_y = node->y * canvas->zoom;
-
- string = gfx_string_create (face, node->text, node->angle, node->tabwidth);
- switch(node->halign){
- case GFX_H_RIGHT: vec.x = -string->bbox.xMax;
- break;
- case GFX_H_CENTER: vec.x = abs(string->bbox.xMax) >= abs(string->bbox.xMin) ?
- -string->bbox.xMax/2:-string->bbox.xMin/2;
- break;
- case GFX_H_LEFT: vec.x = -string->bbox.xMin;
- break;
- case GFX_H_NULL: vec.x = 0;
- break;
- }
-
- switch(node->valign){
- case GFX_V_TOP: vec.y = string->bbox.yMax;
- break;
- case GFX_V_CENTER: vec.y = abs(string->bbox.yMax) >= abs(string->bbox.yMin) ?
- string->bbox.yMax/2:string->bbox.yMin/2;
- break;
- case GFX_V_BOTTOM: vec.y = 0;
- break;
- case GFX_V_NULL: vec.y = 0;
- break;
- }
- pen_x += vec.x/64;
- pen_y += vec.y/64;
- glyph = string->glyphs;
- for(n=0; n<string->num_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");
- continue;
- }
- error = FT_Glyph_Copy (glyph->image, &image);
- if (error) {
- fprintf (stderr, "couldn't copy image\n");
- continue;
- }
-
- /* transform it */
- vec = glyph->pos;
- FT_Vector_Transform (&vec, &string->transform);
-
- bit = (FT_BitmapGlyph) image;
- gr = bit->bitmap.num_grays -1;
-/*
- buf_x = (pen_x + 0.5) + (double)bit->left;
- comp_n = buf_x + bit->bitmap.width > pys_width ? pys_width - buf_x : bit->bitmap.width;
- if (buf_x < 0 || buf_x >= (long)pys_width) continue;
- buf_x *= bytes_per_pixel ;
- for (iy=0; iy < bit->bitmap.rows; iy++){
- long buf_y = iy+(pen_y+0.5)-(double)bit->top;
- if (buf_y < 0 || buf_y >= (long)pys_height) continue;
- buf_y *= rowstride;
- for (ix=0;ix < bit->bitmap.width;ix++){
- *(letter + (ix*bytes_per_pixel+3)) = *(bit->bitmap.buffer + iy * bit->bitmap.width + ix);
- }
- art_rgba_rgba_composite(buffer + buf_y + buf_x ,letter,comp_n);
- }
- 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.width + 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);
- }
- }
- }
-
-/*
- 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.width + ix);
- font_alpha = (art_u8)((double)font_alpha / gr * falpha);
- for (iz = 0; iz < 3; iz++){
- art_u8 *orig = buffer + buf_y + buf_x + iz;
- *orig = (art_u8)((double)*orig / gr * ( gr - font_alpha) +
- (double)fcolor[iz] / gr * (font_alpha));
- }
- }
- }
-*/
- FT_Done_Glyph (image);
- }
- gfx_string_destroy(string);
- }
- }
- node = node->next;
- }
- gfx_save_png(buffer,fp , pys_width,pys_height,bytes_per_pixel);
- art_free(buffer);
- FT_Done_FreeType( library );
- return 0;
-}
-
-/* free memory used by nodes this will also remove memory required for
- associated paths and svcs ... but not for text strings */
-int
-gfx_destroy (gfx_canvas_t *canvas){
- gfx_node_t *next,*node = canvas->firstnode;
- while(node){
- next = node->next;
- art_free(node->path);
- free(node->text);
- free(node->filename);
- art_free(node);
- node = next;
- }
- art_free(canvas);
- return 0;
-}
-
-static int gfx_save_png (art_u8 *buffer, FILE *fp, long width, long height, long bytes_per_pixel){
- png_structp png_ptr = NULL;
- png_infop info_ptr = NULL;
- int i;
- png_bytep *row_pointers;
- int rowstride = width * bytes_per_pixel;
- png_text text[2];
-
- if (fp == NULL)
- return (1);
-
- png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
- if (png_ptr == NULL)
- {
- return (1);
- }
- row_pointers = (png_bytepp)png_malloc(png_ptr,
- height*sizeof(png_bytep));
-
- info_ptr = png_create_info_struct(png_ptr);
-
- if (info_ptr == NULL)
- {
- png_free(png_ptr,row_pointers);
- png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
- return (1);
- }
-
- if (setjmp(png_jmpbuf(png_ptr)))
- {
- /* If we get here, we had a problem writing the file */
- png_destroy_write_struct(&png_ptr, &info_ptr);
- return (1);
- }
-
- png_init_io(png_ptr, fp);
- png_set_IHDR (png_ptr, info_ptr,width, height,
- 8, PNG_COLOR_TYPE_RGB_ALPHA,
- PNG_INTERLACE_NONE,
- PNG_COMPRESSION_TYPE_DEFAULT,
- PNG_FILTER_TYPE_DEFAULT);
-
- text[0].key = "Software";
- text[0].text = "RRDtool, Tobias Oetiker <tobi@oetike.ch>, http://tobi.oetiker.ch";
- 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); */
- png_set_compression_level(png_ptr,1);
- /* png_set_compression_strategy(png_ptr,Z_HUFFMAN_ONLY); */
- /*
- png_set_filter(png_ptr,PNG_FILTER_TYPE_BASE,PNG_FILTER_SUB);
- png_set_compression_strategy(png_ptr,Z_HUFFMAN_ONLY);
- png_set_compression_level(png_ptr,Z_BEST_SPEED); */
-
- /* Write header data */
- png_write_info (png_ptr, info_ptr);
- for (i = 0; i < height; i++)
- row_pointers[i] = (png_bytep) (buffer + i*rowstride);
-
- png_write_image(png_ptr, row_pointers);
- png_write_end(png_ptr, info_ptr);
- png_free(png_ptr,row_pointers);
- png_destroy_write_struct(&png_ptr, &info_ptr);
- return 1;
-}
-
-
-/* ------- 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";
-typedef struct svg_dash
-{
- int dash_enable;
- double dash_adjust, dash_len, dash_offset;
- double adjusted_on, adjusted_off;
-} svg_dash;
-
-
-static void svg_print_indent(FILE *fp)
-{
- int i;
- for (i = svg_indent - svg_single_line; i > 0; i--) {
- putc(' ', fp);
- putc(' ', fp);
- }
-}
-
-static void svg_start_tag(FILE *fp, const char *name)
-{
- svg_print_indent(fp);
- putc('<', fp);
- fputs(name, fp);
- svg_indent++;
-}
-
-static void svg_close_tag_single_line(FILE *fp)
-{
- svg_single_line++;
- putc('>', fp);
-}
-
-static void svg_close_tag(FILE *fp)
-{
- putc('>', fp);
- if (!svg_single_line)
- putc('\n', fp);
-}
-
-static void svg_end_tag(FILE *fp, const char *name)
-{
- /* name is NULL if closing empty-node tag */
- svg_indent--;
- if (svg_single_line)
- svg_single_line--;
- else if (name)
- svg_print_indent(fp);
- if (name != NULL) {
- fputs("</", fp);
- fputs(name, fp);
- } else {
- putc('/', fp);
- }
- svg_close_tag(fp);
-}
-
-static void svg_close_tag_empty_node(FILE *fp)
-{
- svg_end_tag(fp, NULL);
-}
-
-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) {
- ch = *p++;
- ch = afm_host2unicode(ch); /* unsafe macro */
- switch (ch) {
- case '&': fputs("&", fp); break;
- case '<': fputs("<", fp); break;
- case '>': fputs(">", fp); break;
- case '"': fputs(""", fp); break;
- default:
- if (ch >= 127)
- fprintf(fp, "&#%d;", ch);
- else
- putc(ch, fp);
- }
- }
-}
-
-static void svg_format_number(char *buf, int bufsize, double d)
-{
- /* omit decimals if integer to reduce filesize */
- char *p;
- snprintf(buf, bufsize, "%.2f", d);
- p = buf; /* doesn't trust snprintf return value */
- while (*p)
- p++;
- while (--p > buf) {
- char ch = *p;
- if (ch == '0') {
- *p = '\0'; /* zap trailing zeros */
- continue;
- }
- if (ch == '.')
- *p = '\0'; /* zap trailing dot */
- break;
- }
-}
-
-static void svg_write_number(FILE *fp, double d)