-
-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 */
-
- /* fill that buffer with out background color */
- gfx_color_t *buffp = art_new (gfx_color_t, pys_width*pys_height);
- art_u8 *buffer = (art_u8 *)buffp;
- unsigned long i;
- for (i=0;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;
- double dst[6];
- 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); */
- if(node->type == GFX_LINE){
- 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 ( 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(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);
- 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 (canvas, face, node->text, node->angle, node->tabwidth, node->size);
- 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) {
- DPRINTF("no image\n")
- continue;
- }
- error = FT_Glyph_Copy (glyph->image, &image);
- if (error) {
- DPRINTF("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);
-*/
- 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;
- }
-
-/*
- 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 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); */
- /*
- 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;
-}
-
-
-/* ----- 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 = "-dummy-";
-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)