|
Graphviz
2.31.20130618.0446
|
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 };
1.7.5