Graphviz  2.31.20130618.0446
plugin/pango/gvrender_pango.c
Go to the documentation of this file.
00001 /* $Id$ $Revision$ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /*************************************************************************
00005  * Copyright (c) 2011 AT&T Intellectual Property 
00006  * All rights reserved. This program and the accompanying materials
00007  * are made available under the terms of the Eclipse Public License v1.0
00008  * which accompanies this distribution, and is available at
00009  * http://www.eclipse.org/legal/epl-v10.html
00010  *
00011  * Contributors: See CVS logs. Details at http://www.graphviz.org/
00012  *************************************************************************/
00013 
00014 #ifdef HAVE_CONFIG_H
00015 #include "config.h"
00016 #endif
00017 
00018 #ifdef HAVE_STDLIB_H
00019 #include <stdlib.h>
00020 #endif
00021 #ifdef HAVE_STRING_H
00022 #include <string.h>
00023 #endif
00024 
00025 #include "const.h"
00026 #include "gvplugin_render.h"
00027 #include "agxbuf.h"
00028 #include "utils.h"
00029 #include "gvplugin_device.h"
00030 #include "gvio.h"
00031 
00032 #include "gvplugin_pango.h"
00033 
00034 #ifdef HAVE_PANGOCAIRO
00035 #include <pango/pangocairo.h>
00036 
00037 typedef enum {
00038                 FORMAT_CAIRO,
00039                 FORMAT_PNG,
00040                 FORMAT_PS,
00041                 FORMAT_PDF,
00042                 FORMAT_SVG,
00043     } format_type;
00044 
00045 #define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0]))
00046 
00047 static double dashed[] = {6.};
00048 static int dashed_len = ARRAY_SIZE(dashed);
00049 
00050 static double dotted[] = {2., 6.};
00051 static int dotted_len = ARRAY_SIZE(dotted);
00052 
00053 #ifdef CAIRO_HAS_PS_SURFACE
00054 #include <cairo-ps.h>
00055 #endif
00056 
00057 #ifdef CAIRO_HAS_PDF_SURFACE
00058 #include <cairo-pdf.h>
00059 #endif
00060 
00061 #ifdef CAIRO_HAS_SVG_SURFACE
00062 #include <cairo-svg.h>
00063 #endif
00064 
00065 static void cairogen_set_color(cairo_t * cr, gvcolor_t * color)
00066 {
00067     cairo_set_source_rgba(cr, color->u.RGBA[0], color->u.RGBA[1],
00068                         color->u.RGBA[2], color->u.RGBA[3]);
00069 }
00070 
00071 static void cairogen_add_color_stop_rgba(cairo_pattern_t *pat,int stop , gvcolor_t * color) 
00072 {
00073   cairo_pattern_add_color_stop_rgba (pat, stop,color->u.RGBA[0], color->u.RGBA[1],
00074                         color->u.RGBA[2], color->u.RGBA[3]);
00075 }
00076 
00077 
00078 static cairo_status_t
00079 writer (void *closure, const unsigned char *data, unsigned int length)
00080 {
00081     if (length == gvwrite((GVJ_t *)closure, (const char*)data, length))
00082         return CAIRO_STATUS_SUCCESS;
00083     return CAIRO_STATUS_WRITE_ERROR;
00084 }
00085 
00086 static void cairogen_begin_job(GVJ_t * job)
00087 {
00088     if (job->external_context && job->context)
00089         cairo_save((cairo_t *) job->context);
00090 }
00091 
00092 static void cairogen_end_job(GVJ_t * job)
00093 {
00094     cairo_t *cr = (cairo_t *) job->context;
00095     
00096     if (job->external_context)
00097         cairo_restore(cr);
00098     else {
00099         cairo_destroy(cr);
00100         job->context = NULL;
00101     }
00102 }
00103 
00104 #define CAIRO_XMAX 32767
00105 #define CAIRO_YMAX 32767
00106 
00107 static void cairogen_begin_page(GVJ_t * job)
00108 {
00109     cairo_t *cr = (cairo_t *) job->context;
00110     cairo_surface_t *surface;
00111     cairo_status_t status;
00112 
00113     if (cr == NULL) {
00114         switch (job->render.id) {
00115         case FORMAT_PS:
00116 #ifdef CAIRO_HAS_PS_SURFACE
00117             surface = cairo_ps_surface_create_for_stream (writer,
00118                         job, job->width, job->height);
00119 #endif
00120             break;
00121         case FORMAT_PDF:
00122 #ifdef CAIRO_HAS_PDF_SURFACE
00123             surface = cairo_pdf_surface_create_for_stream (writer,
00124                         job, job->width, job->height);
00125 #endif
00126             break;
00127         case FORMAT_SVG:
00128 #ifdef CAIRO_HAS_SVG_SURFACE
00129             surface = cairo_svg_surface_create_for_stream (writer,
00130                         job, job->width, job->height);
00131 #endif
00132             break;
00133         case FORMAT_CAIRO:
00134         case FORMAT_PNG:
00135         default:
00136             if (job->width >= CAIRO_XMAX || job->height >= CAIRO_YMAX) {
00137                 double scale = MIN((double)CAIRO_XMAX / job->width,
00138                         (double)CAIRO_YMAX / job->height);
00139                 job->width *= scale;
00140                 job->height *= scale;
00141                 job->scale.x *= scale;
00142                 job->scale.y *= scale;
00143                 fprintf(stderr,
00144                         "%s: graph is too large for cairo-renderer bitmaps. Scaling by %g to fit\n", 
00145                         job->common->cmdname, scale);
00146             }
00147             surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
00148                         job->width, job->height);
00149             if (job->common->verbose)
00150                 fprintf(stderr,
00151                         "%s: allocating a %dK cairo image surface (%d x %d pixels)\n",
00152                         job->common->cmdname,
00153                         ROUND(job->width * job->height * 4 / 1024.),
00154                         job->width, job->height);
00155             break;
00156         }
00157         status = cairo_surface_status(surface);
00158         if (status != CAIRO_STATUS_SUCCESS)  {
00159                 fprintf(stderr, "%s: failure to create cairo surface: %s\n",
00160                         job->common->cmdname,
00161                         cairo_status_to_string(status));
00162                 cairo_surface_destroy (surface);
00163                 return;
00164         }
00165         cr = cairo_create(surface);
00166         cairo_surface_destroy (surface);
00167         job->context = (void *) cr;
00168     }
00169 
00170     cairo_scale(cr, job->scale.x, job->scale.y);
00171     cairo_rotate(cr, -job->rotation * M_PI / 180.);
00172     cairo_translate(cr, job->translation.x, -job->translation.y);
00173 
00174     cairo_rectangle(cr, job->clip.LL.x, - job->clip.LL.y,
00175             job->clip.UR.x - job->clip.LL.x, - (job->clip.UR.y - job->clip.LL.y));
00176     cairo_clip(cr);
00177     cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
00178 }
00179 
00180 static void cairogen_end_page(GVJ_t * job)
00181 {
00182     cairo_t *cr = (cairo_t *) job->context;
00183     cairo_surface_t *surface;
00184     cairo_status_t status;
00185 
00186     switch (job->render.id) {
00187 
00188 #ifdef CAIRO_HAS_PNG_FUNCTIONS
00189     case FORMAT_PNG:
00190         surface = cairo_get_target(cr);
00191         cairo_surface_write_to_png_stream(surface, writer, job);
00192         break;
00193 #endif
00194 
00195     case FORMAT_PS:
00196     case FORMAT_PDF:
00197     case FORMAT_SVG:
00198         cairo_show_page(cr);
00199         surface = cairo_surface_reference(cairo_get_target(cr));
00200         cairo_surface_finish(surface);
00201         status = cairo_surface_status(surface);
00202         cairo_surface_destroy(surface);
00203         if (status != CAIRO_STATUS_SUCCESS)
00204             fprintf(stderr, "cairo: %s\n", cairo_status_to_string(status));
00205         break;
00206 
00207     case FORMAT_CAIRO:
00208     default:
00209         surface = cairo_get_target(cr);
00210         if (cairo_image_surface_get_width(surface) == 0 || cairo_image_surface_get_height(surface) == 0) {
00211             /* apparently cairo never allocates a surface if nothing was ever written to it */
00212 /* but suppress this error message since a zero area surface seems to happen during normal operations, particular in -Tx11
00213             fprintf(stderr, "ERROR: cairo surface has zero area, this may indicate some problem during rendering shapes.\n");
00214  - jce */
00215         }
00216         job->imagedata = (char *)(cairo_image_surface_get_data(surface));       
00217         break;
00218         /* formatting will be done by gvdevice_format() */
00219     }
00220 }
00221 
00222 static void cairogen_textpara(GVJ_t * job, pointf p, textpara_t * para)
00223 {
00224     obj_state_t *obj = job->obj;
00225     cairo_t *cr = (cairo_t *) job->context;
00226 
00227     cairo_set_dash (cr, dashed, 0, 0.0);  /* clear any dashing */
00228     cairogen_set_color(cr, &(obj->pencolor));
00229 
00230     switch (para->just) {
00231     case 'r':
00232         p.x -= para->width;
00233         break;
00234     case 'l':
00235         p.x -= 0.0;
00236         break;
00237     case 'n':
00238     default:
00239         p.x -= para->width / 2.0;
00240         break;
00241     }
00242     p.y += para->yoffset_centerline + para->yoffset_layout;
00243 
00244     cairo_move_to (cr, p.x, -p.y);
00245     cairo_save(cr);
00246     cairo_scale(cr, POINTS_PER_INCH / FONT_DPI, POINTS_PER_INCH / FONT_DPI);
00247     pango_cairo_show_layout(cr, (PangoLayout*)(para->layout));
00248     cairo_restore(cr);
00249 }
00250 
00251 static void cairogen_set_penstyle(GVJ_t *job, cairo_t *cr)
00252 {
00253     obj_state_t *obj = job->obj;
00254 
00255     if (obj->pen == PEN_DASHED) {
00256         cairo_set_dash (cr, dashed, dashed_len, 0.0);
00257     } else if (obj->pen == PEN_DOTTED) {
00258         cairo_set_dash (cr, dotted, dotted_len, 0.0);
00259     } else {
00260         cairo_set_dash (cr, dashed, 0, 0.0);
00261     }
00262     cairo_set_line_width (cr, obj->penwidth);
00263 
00264 }
00265 
00266 static void cairo_gradient_fill (cairo_t* cr, obj_state_t* obj, int filled, pointf* A, int n)
00267 {
00268     cairo_pattern_t* pat;
00269     float angle = obj->gradient_angle * M_PI / 180;
00270     float r1,r2;
00271     pointf G[2],c1,c2;
00272 
00273     if (filled == GRADIENT) {
00274           get_gradient_points(A, G, n, angle, 0);
00275           pat = cairo_pattern_create_linear (G[0].x,G[0].y,G[1].x,G[1].y);
00276     }
00277     else {
00278         get_gradient_points(A, G, n, 0, 1);
00279           //r1 is inner radius, r2 is outer radius
00280         r1 = G[1].x;
00281         r2 = G[1].y;
00282         if (angle == 0) {
00283             c1.x = G[0].x;
00284             c1.y = G[0].y;
00285         }
00286         else {
00287             c1.x = G[0].x +  (r2/4) * cos(angle);
00288             c1.y = G[0].y -  (r2/4) * sin(angle);
00289         }
00290         c2.x = G[0].x;
00291         c2.y = G[0].y;
00292         r1 = r2/4;
00293         //r1 is inner radius, r2 is outter radius
00294         pat = cairo_pattern_create_radial(c1.x,c1.y,r1,c2.x,c2.y,r2); 
00295     }
00296     cairogen_add_color_stop_rgba(pat,0,&(obj->fillcolor));
00297     cairogen_add_color_stop_rgba(pat,1,&(obj->stopcolor));
00298     cairo_set_source (cr, pat);
00299     cairo_fill_preserve (cr);
00300     cairo_pattern_destroy (pat);
00301 }
00302 
00303 static void cairogen_ellipse(GVJ_t * job, pointf * A, int filled)
00304 {
00305     obj_state_t *obj = job->obj;
00306     cairo_t *cr = (cairo_t *) job->context;
00307     cairo_matrix_t matrix;
00308     double rx, ry;
00309 
00310     cairogen_set_penstyle(job, cr);
00311 
00312     cairo_get_matrix(cr, &matrix);
00313 
00314     rx = A[1].x - A[0].x;
00315     ry = A[1].y - A[0].y;
00316 
00317 #define RMIN 0.01
00318 if (rx < RMIN) rx = RMIN;
00319 if (ry < RMIN) ry = RMIN;
00320 
00321     cairo_translate(cr, A[0].x, -A[0].y);
00322     cairo_scale(cr, rx, ry);
00323     cairo_move_to(cr, 1., 0.);
00324     cairo_arc(cr, 0., 0., 1., 0., 2 * M_PI);
00325 
00326     cairo_set_matrix(cr, &matrix);
00327 
00328     if (filled == GRADIENT || filled == (RGRADIENT)) {
00329         cairo_gradient_fill (cr, obj, filled, A, 2);
00330     }
00331     else if (filled) {
00332         cairogen_set_color(cr, &(obj->fillcolor));
00333         cairo_fill_preserve(cr);
00334     }
00335     cairogen_set_color(cr, &(obj->pencolor));
00336     cairo_stroke(cr);
00337 }
00338 
00339 static void
00340 cairogen_polygon(GVJ_t * job, pointf * A, int n, int filled)
00341 {
00342     obj_state_t *obj = job->obj;
00343     cairo_t *cr = (cairo_t *) job->context;
00344     int i;
00345 
00346     cairogen_set_penstyle(job, cr);
00347 
00348     cairo_move_to(cr, A[0].x, -A[0].y);
00349     for (i = 1; i < n; i++)
00350     cairo_line_to(cr, A[i].x, -A[i].y);
00351     cairo_close_path(cr);
00352     if (filled == GRADIENT || filled == (RGRADIENT)) {
00353         cairo_gradient_fill (cr, obj, filled, A, n);
00354     }
00355     else if (filled) {
00356         cairogen_set_color(cr, &(obj->fillcolor));
00357         cairo_fill_preserve(cr);
00358     }
00359     cairogen_set_color(cr, &(obj->pencolor));
00360     cairo_stroke(cr);
00361 }
00362 
00363 static void
00364 cairogen_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start,
00365                 int arrow_at_end, int filled)
00366 {
00367     obj_state_t *obj = job->obj;
00368     cairo_t *cr = (cairo_t *) job->context;
00369     int i;
00370 
00371     cairogen_set_penstyle(job, cr);
00372 
00373     cairo_move_to(cr, A[0].x, -A[0].y);
00374     for (i = 1; i < n; i += 3)
00375         cairo_curve_to(cr, A[i].x, -A[i].y, A[i + 1].x, -A[i + 1].y,
00376                        A[i + 2].x, -A[i + 2].y);
00377     if (filled == GRADIENT || filled == (RGRADIENT)) {
00378         cairo_gradient_fill (cr, obj, filled, A, n);
00379     }
00380     else if (filled) {
00381         cairogen_set_color(cr, &(obj->fillcolor));
00382         cairo_fill_preserve(cr);
00383     }
00384     cairogen_set_color(cr, &(obj->pencolor));
00385     cairo_stroke(cr);
00386 }
00387 
00388 static void
00389 cairogen_polyline(GVJ_t * job, pointf * A, int n)
00390 {
00391     obj_state_t *obj = job->obj;
00392     cairo_t *cr = (cairo_t *) job->context;
00393     int i;
00394 
00395     cairogen_set_penstyle(job, cr);
00396 
00397     cairo_move_to(cr, A[0].x, -A[0].y);
00398     for (i = 1; i < n; i++)
00399         cairo_line_to(cr, A[i].x, -A[i].y);
00400     cairogen_set_color(cr, &(obj->pencolor));
00401     cairo_stroke(cr);
00402 }
00403 
00404 static gvrender_engine_t cairogen_engine = {
00405     cairogen_begin_job,
00406     cairogen_end_job,
00407     0,                          /* cairogen_begin_graph */
00408     0,                          /* cairogen_end_graph */
00409     0,                          /* cairogen_begin_layer */
00410     0,                          /* cairogen_end_layer */
00411     cairogen_begin_page,
00412     cairogen_end_page,
00413     0,                          /* cairogen_begin_cluster */
00414     0,                          /* cairogen_end_cluster */
00415     0,                          /* cairogen_begin_nodes */
00416     0,                          /* cairogen_end_nodes */
00417     0,                          /* cairogen_begin_edges */
00418     0,                          /* cairogen_end_edges */
00419     0,                          /* cairogen_begin_node */
00420     0,                          /* cairogen_end_node */
00421     0,                          /* cairogen_begin_edge */
00422     0,                          /* cairogen_end_edge */
00423     0,                          /* cairogen_begin_anchor */
00424     0,                          /* cairogen_end_anchor */
00425     0,                          /* cairogen_begin_label */
00426     0,                          /* cairogen_end_label */
00427     cairogen_textpara,
00428     0,                          /* cairogen_resolve_color */
00429     cairogen_ellipse,
00430     cairogen_polygon,
00431     cairogen_bezier,
00432     cairogen_polyline,
00433     0,                          /* cairogen_comment */
00434     0,                          /* cairogen_library_shape */
00435 };
00436 
00437 static gvrender_features_t render_features_cairo = {
00438     GVRENDER_Y_GOES_DOWN
00439         | GVRENDER_DOES_TRANSFORM, /* flags */
00440     4.,                         /* default pad - graph units */
00441     0,                          /* knowncolors */
00442     0,                          /* sizeof knowncolors */
00443     RGBA_DOUBLE,                /* color_type */
00444 };
00445 
00446 static gvdevice_features_t device_features_png = {
00447     GVDEVICE_BINARY_FORMAT
00448       | GVDEVICE_DOES_TRUECOLOR,/* flags */
00449     {0.,0.},                    /* default margin - points */
00450     {0.,0.},                    /* default page width, height - points */
00451     {96.,96.},                  /* typical monitor dpi */
00452 };
00453 
00454 static gvdevice_features_t device_features_ps = {
00455     GVDEVICE_DOES_TRUECOLOR,    /* flags */
00456     {36.,36.},                  /* default margin - points */
00457     {0.,0.},                    /* default page width, height - points */
00458     {72.,72.},                  /* postscript 72 dpi */
00459 };
00460 
00461 static gvdevice_features_t device_features_pdf = {
00462     GVDEVICE_BINARY_FORMAT
00463       | GVDEVICE_DOES_TRUECOLOR,/* flags */
00464     {36.,36.},                  /* default margin - points */
00465     {0.,0.},                    /* default page width, height - points */
00466     {72.,72.},                  /* postscript 72 dpi */
00467 };
00468 
00469 static gvdevice_features_t device_features_svg = {
00470     GVDEVICE_DOES_TRUECOLOR,    /* flags */
00471     {0.,0.},                    /* default margin - points */
00472     {0.,0.},                    /* default page width, height - points */
00473     {72.,72.},                  /* svg 72 dpi */
00474 };
00475 #endif
00476 
00477 gvplugin_installed_t gvrender_pango_types[] = {
00478 #ifdef HAVE_PANGOCAIRO
00479     {FORMAT_CAIRO, "cairo", 10, &cairogen_engine, &render_features_cairo},
00480 #endif
00481     {0, NULL, 0, NULL, NULL}
00482 };
00483 
00484 gvplugin_installed_t gvdevice_pango_types[] = {
00485 #ifdef HAVE_PANGOCAIRO
00486 #ifdef CAIRO_HAS_PNG_FUNCTIONS
00487     {FORMAT_PNG, "png:cairo", 10, NULL, &device_features_png},
00488 #endif
00489 #ifdef CAIRO_HAS_PS_SURFACE
00490     {FORMAT_PS, "ps:cairo", -10, NULL, &device_features_ps},
00491 #endif
00492 #ifdef CAIRO_HAS_PDF_SURFACE
00493     {FORMAT_PDF, "pdf:cairo", 10, NULL, &device_features_pdf},
00494 #endif
00495 #ifdef CAIRO_HAS_SVG_SURFACE
00496     {FORMAT_SVG, "svg:cairo", -10, NULL, &device_features_svg},
00497 #endif
00498 #endif
00499     {0, NULL, 0, NULL, NULL}
00500 };