|
Graphviz
2.31.20130617.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 #include <stdarg.h> 00019 #include <stdlib.h> 00020 #include <string.h> 00021 #include <ctype.h> 00022 00023 #ifdef WIN32 00024 #include <io.h> 00025 #include "compat.h" 00026 #endif 00027 00028 #include "macros.h" 00029 #include "const.h" 00030 00031 #include "gvplugin_render.h" 00032 #include "gvplugin_device.h" 00033 #include "gvio.h" 00034 #include "agxbuf.h" 00035 #include "utils.h" 00036 #include "color.h" 00037 00038 /* Number of points to split splines into */ 00039 #define BEZIERSUBDIVISION 6 00040 00041 typedef enum { FORMAT_FIG, } format_type; 00042 00043 static int Depth; 00044 00045 static void figptarray(GVJ_t *job, pointf * A, int n, int close) 00046 { 00047 int i; 00048 point p; 00049 00050 for (i = 0; i < n; i++) { 00051 PF2P(A[i],p); 00052 gvprintf(job, " %d %d", p.x, p.y); 00053 } 00054 if (close) { 00055 PF2P(A[0],p); 00056 gvprintf(job, " %d %d", p.x, p.y); 00057 } 00058 gvputs(job, "\n"); 00059 } 00060 00061 static char *fig_string(char *s) 00062 { 00063 static char *buf = NULL; 00064 static int bufsize = 0; 00065 int pos = 0; 00066 char *p; 00067 unsigned char c; 00068 00069 if (!buf) { 00070 bufsize = 64; 00071 buf = malloc(bufsize * sizeof(char)); 00072 } 00073 00074 p = buf; 00075 while ((c = *s++)) { 00076 if (pos > (bufsize - 8)) { 00077 bufsize *= 2; 00078 buf = realloc(buf, bufsize * sizeof(char)); 00079 p = buf + pos; 00080 } 00081 if (isascii(c)) { 00082 if (c == '\\') { 00083 *p++ = '\\'; 00084 pos++; 00085 } 00086 *p++ = c; 00087 pos++; 00088 } else { 00089 *p++ = '\\'; 00090 sprintf(p, "%03o", c); 00091 p += 3; 00092 pos += 4; 00093 } 00094 } 00095 *p = '\0'; 00096 return buf; 00097 } 00098 00099 static int figColorResolve(int *new, int r, int g, int b) 00100 { 00101 #define maxColors 256 00102 static int top = 0; 00103 static short red[maxColors], green[maxColors], blue[maxColors]; 00104 int c; 00105 int ct = -1; 00106 long rd, gd, bd, dist; 00107 long mindist = 3 * 255 * 255; /* init to max poss dist */ 00108 00109 *new = 0; /* in case it is not a new color */ 00110 for (c = 0; c < top; c++) { 00111 rd = (long) (red[c] - r); 00112 gd = (long) (green[c] - g); 00113 bd = (long) (blue[c] - b); 00114 dist = rd * rd + gd * gd + bd * bd; 00115 if (dist < mindist) { 00116 if (dist == 0) 00117 return c; /* Return exact match color */ 00118 mindist = dist; 00119 ct = c; 00120 } 00121 } 00122 /* no exact match. We now know closest, but first try to allocate exact */ 00123 if (top++ == maxColors) 00124 return ct; /* Return closest available color */ 00125 red[c] = r; 00126 green[c] = g; 00127 blue[c] = b; 00128 *new = 1; /* flag new color */ 00129 return c; /* Return newly allocated color */ 00130 } 00131 00132 /* this table is in xfig color index order */ 00133 static char *figcolor[] = { 00134 "black", "blue", "green", "cyan", "red", "magenta", "yellow", "white", (char *) NULL 00135 }; 00136 00137 static void fig_resolve_color(GVJ_t *job, gvcolor_t * color) 00138 { 00139 int object_code = 0; /* always 0 for color */ 00140 int i, new; 00141 00142 switch (color->type) { 00143 case COLOR_STRING: 00144 for (i = 0; figcolor[i]; i++) { 00145 if (streq(figcolor[i], color->u.string)) { 00146 color->u.index = i; 00147 break; 00148 } 00149 } 00150 break; 00151 case RGBA_BYTE: 00152 i = 32 + figColorResolve(&new, 00153 color->u.rgba[0], 00154 color->u.rgba[1], 00155 color->u.rgba[2]); 00156 if (new) 00157 gvprintf(job, "%d %d #%02x%02x%02x\n", 00158 object_code, i, 00159 color->u.rgba[0], 00160 color->u.rgba[1], 00161 color->u.rgba[2]); 00162 color->u.index = i; 00163 break; 00164 default: 00165 assert(0); /* internal error */ 00166 } 00167 00168 color->type = COLOR_INDEX; 00169 } 00170 00171 static void fig_line_style(obj_state_t *obj, int *line_style, double *style_val) 00172 { 00173 switch (obj->pen) { 00174 case PEN_DASHED: 00175 *line_style = 1; 00176 *style_val = 10.; 00177 break; 00178 case PEN_DOTTED: 00179 *line_style = 2; 00180 *style_val = 10.; 00181 break; 00182 case PEN_SOLID: 00183 default: 00184 *line_style = 0; 00185 *style_val = 0.; 00186 break; 00187 } 00188 } 00189 00190 static void fig_comment(GVJ_t *job, char *str) 00191 { 00192 gvprintf(job, "# %s\n", str); 00193 } 00194 00195 static void fig_begin_graph(GVJ_t * job) 00196 { 00197 obj_state_t *obj = job->obj; 00198 00199 gvputs(job, "#FIG 3.2\n"); 00200 gvprintf(job, "# Generated by %s version %s (%s)\n", 00201 job->common->info[0], job->common->info[1], job->common->info[2]); 00202 gvprintf(job, "# Title: %s\n", agnameof(obj->u.g)); 00203 gvprintf(job, "# Pages: %d\n", job->pagesArraySize.x * job->pagesArraySize.y); 00204 gvputs(job, "Portrait\n"); /* orientation */ 00205 gvputs(job, "Center\n"); /* justification */ 00206 gvputs(job, "Inches\n"); /* units */ 00207 gvputs(job, "Letter\n"); /* papersize */ 00208 gvputs(job, "100.00\n"); /* magnification % */ 00209 gvputs(job, "Single\n"); /* multiple-page */ 00210 gvputs(job, "-2\n"); /* transparent color (none) */ 00211 gvputs(job, "1200"); /* resolution */ 00212 gvputs(job, " 2\n"); /* coordinate system (upper left) */ 00213 } 00214 00215 static void fig_end_graph(GVJ_t * job) 00216 { 00217 gvputs(job, "# end of FIG file\n"); 00218 } 00219 00220 static void fig_begin_page(GVJ_t * job) 00221 { 00222 Depth = 2; 00223 } 00224 00225 static void fig_begin_node(GVJ_t * job) 00226 { 00227 Depth = 1; 00228 } 00229 00230 static void fig_end_node(GVJ_t * job) 00231 { 00232 Depth = 2; 00233 } 00234 00235 static void fig_begin_edge(GVJ_t * job) 00236 { 00237 Depth = 0; 00238 } 00239 00240 static void fig_end_edge(GVJ_t * job) 00241 { 00242 Depth = 2; 00243 } 00244 00245 static void fig_textpara(GVJ_t * job, pointf p, textpara_t * para) 00246 { 00247 obj_state_t *obj = job->obj; 00248 00249 int object_code = 4; /* always 4 for text */ 00250 int sub_type = 0; /* text justification */ 00251 int color = obj->pencolor.u.index; 00252 int depth = Depth; 00253 int pen_style = 0; /* not used */ 00254 int font = -1; /* init to xfig's default font */ 00255 double font_size = para->fontsize * job->zoom; 00256 double angle = job->rotation ? (M_PI / 2.0) : 0.0; 00257 int font_flags = 6; /* PostScript font + Special text */ 00258 /* Special text indicates that latex markup may exist 00259 * in the output - but note that dot knows nothing about latex, 00260 * so the node sizes may be wrong. 00261 */ 00262 double height = 0.0; 00263 double length = 0.0; 00264 00265 if (para->postscript_alias) /* if it is a standard postscript font */ 00266 font = para->postscript_alias->xfig_code; 00267 00268 switch (para->just) { 00269 case 'l': 00270 sub_type = 0; 00271 break; 00272 case 'r': 00273 sub_type = 2; 00274 break; 00275 default: 00276 case 'n': 00277 sub_type = 1; 00278 break; 00279 } 00280 00281 gvprintf(job, 00282 "%d %d %d %d %d %d %.1f %.4f %d %.1f %.1f %d %d %s\\001\n", 00283 object_code, sub_type, color, depth, pen_style, font, 00284 font_size, angle, font_flags, height, length, ROUND(p.x), ROUND(p.y), 00285 fig_string(para->str)); 00286 } 00287 00288 static void fig_ellipse(GVJ_t * job, pointf * A, int filled) 00289 { 00290 obj_state_t *obj = job->obj; 00291 00292 int object_code = 1; /* always 1 for ellipse */ 00293 int sub_type = 1; /* ellipse defined by radii */ 00294 int line_style; /* solid, dotted, dashed */ 00295 int thickness = obj->penwidth; 00296 int pen_color = obj->pencolor.u.index; 00297 int fill_color = obj->fillcolor.u.index; 00298 int depth = Depth; 00299 int pen_style = 0; /* not used */ 00300 int area_fill = filled ? 20 : -1; 00301 double style_val; 00302 int direction = 0; 00303 double angle = 0.0; 00304 int center_x, center_y, radius_x, radius_y; 00305 int start_x, start_y, end_x, end_y; 00306 00307 fig_line_style(obj, &line_style, &style_val); 00308 00309 start_x = center_x = ROUND(A[0].x); 00310 start_y = center_y = ROUND(A[0].y); 00311 radius_x = ROUND(A[1].x - A[0].x); 00312 radius_y = ROUND(A[1].y - A[0].y); 00313 end_x = ROUND(A[1].x); 00314 end_y = ROUND(A[1].y); 00315 00316 gvprintf(job, 00317 "%d %d %d %d %d %d %d %d %d %.3f %d %.4f %d %d %d %d %d %d %d %d\n", 00318 object_code, sub_type, line_style, thickness, pen_color, 00319 fill_color, depth, pen_style, area_fill, style_val, direction, 00320 angle, center_x, center_y, radius_x, radius_y, start_x, 00321 start_y, end_x, end_y); 00322 } 00323 00324 static void fig_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start, 00325 int arrow_at_end, int filled) 00326 { 00327 obj_state_t *obj = job->obj; 00328 00329 int object_code = 3; /* always 3 for spline */ 00330 int sub_type; 00331 int line_style; /* solid, dotted, dashed */ 00332 int thickness = obj->penwidth; 00333 int pen_color = obj->pencolor.u.index; 00334 int fill_color = obj->fillcolor.u.index; 00335 int depth = Depth; 00336 int pen_style = 0; /* not used */ 00337 int area_fill; 00338 double style_val; 00339 int cap_style = 0; 00340 int forward_arrow = 0; 00341 int backward_arrow = 0; 00342 int npoints = n; 00343 int i; 00344 00345 00346 pointf pf, V[4]; 00347 point p; 00348 int j, step; 00349 int count = 0; 00350 int size; 00351 00352 char *buffer; 00353 char *buf; 00354 assert (n >= 4); 00355 00356 buffer = 00357 malloc((npoints + 1) * (BEZIERSUBDIVISION + 00358 1) * 20 * sizeof(char)); 00359 buf = buffer; 00360 00361 fig_line_style(obj, &line_style, &style_val); 00362 00363 if (filled) { 00364 sub_type = 5; /* closed X-spline */ 00365 area_fill = 20; /* fully saturated color */ 00366 fill_color = job->obj->fillcolor.u.index; 00367 } 00368 else { 00369 sub_type = 4; /* opened X-spline */ 00370 area_fill = -1; 00371 fill_color = 0; 00372 } 00373 V[3].x = A[0].x; 00374 V[3].y = A[0].y; 00375 /* Write first point in line */ 00376 count++; 00377 PF2P(A[0], p); 00378 size = sprintf(buf, " %d %d", p.x, p.y); 00379 buf += size; 00380 /* write subsequent points */ 00381 for (i = 0; i + 3 < n; i += 3) { 00382 V[0] = V[3]; 00383 for (j = 1; j <= 3; j++) { 00384 V[j].x = A[i + j].x; 00385 V[j].y = A[i + j].y; 00386 } 00387 for (step = 1; step <= BEZIERSUBDIVISION; step++) { 00388 count++; 00389 pf = Bezier (V, 3, (double) step / BEZIERSUBDIVISION, NULL, NULL); 00390 PF2P(pf, p); 00391 size = sprintf(buf, " %d %d", p.x, p.y); 00392 buf += size; 00393 } 00394 } 00395 00396 gvprintf(job, "%d %d %d %d %d %d %d %d %d %.1f %d %d %d %d\n", 00397 object_code, 00398 sub_type, 00399 line_style, 00400 thickness, 00401 pen_color, 00402 fill_color, 00403 depth, 00404 pen_style, 00405 area_fill, 00406 style_val, cap_style, forward_arrow, backward_arrow, count); 00407 00408 gvprintf(job, " %s\n", buffer); /* print points */ 00409 free(buffer); 00410 for (i = 0; i < count; i++) { 00411 gvprintf(job, " %d", i % (count - 1) ? 1 : 0); /* -1 on all */ 00412 } 00413 gvputs(job, "\n"); 00414 } 00415 00416 static void fig_polygon(GVJ_t * job, pointf * A, int n, int filled) 00417 { 00418 obj_state_t *obj = job->obj; 00419 00420 int object_code = 2; /* always 2 for polyline */ 00421 int sub_type = 3; /* always 3 for polygon */ 00422 int line_style; /* solid, dotted, dashed */ 00423 int thickness = obj->penwidth; 00424 int pen_color = obj->pencolor.u.index; 00425 int fill_color = obj->fillcolor.u.index; 00426 int depth = Depth; 00427 int pen_style = 0; /* not used */ 00428 int area_fill = filled ? 20 : -1; 00429 double style_val; 00430 int join_style = 0; 00431 int cap_style = 0; 00432 int radius = 0; 00433 int forward_arrow = 0; 00434 int backward_arrow = 0; 00435 int npoints = n + 1; 00436 00437 fig_line_style(obj, &line_style, &style_val); 00438 00439 gvprintf(job, 00440 "%d %d %d %d %d %d %d %d %d %.1f %d %d %d %d %d %d\n", 00441 object_code, sub_type, line_style, thickness, pen_color, 00442 fill_color, depth, pen_style, area_fill, style_val, join_style, 00443 cap_style, radius, forward_arrow, backward_arrow, npoints); 00444 figptarray(job, A, n, 1); /* closed shape */ 00445 } 00446 00447 static void fig_polyline(GVJ_t * job, pointf * A, int n) 00448 { 00449 obj_state_t *obj = job->obj; 00450 00451 int object_code = 2; /* always 2 for polyline */ 00452 int sub_type = 1; /* always 1 for polyline */ 00453 int line_style; /* solid, dotted, dashed */ 00454 int thickness = obj->penwidth; 00455 int pen_color = obj->pencolor.u.index; 00456 int fill_color = 0; 00457 int depth = Depth; 00458 int pen_style = 0; /* not used */ 00459 int area_fill = 0; 00460 double style_val; 00461 int join_style = 0; 00462 int cap_style = 0; 00463 int radius = 0; 00464 int forward_arrow = 0; 00465 int backward_arrow = 0; 00466 int npoints = n; 00467 00468 fig_line_style(obj, &line_style, &style_val); 00469 00470 gvprintf(job, 00471 "%d %d %d %d %d %d %d %d %d %.1f %d %d %d %d %d %d\n", 00472 object_code, sub_type, line_style, thickness, pen_color, 00473 fill_color, depth, pen_style, area_fill, style_val, join_style, 00474 cap_style, radius, forward_arrow, backward_arrow, npoints); 00475 figptarray(job, A, n, 0); /* open shape */ 00476 } 00477 00478 gvrender_engine_t fig_engine = { 00479 0, /* fig_begin_job */ 00480 0, /* fig_end_job */ 00481 fig_begin_graph, 00482 fig_end_graph, 00483 0, /* fig_begin_layer */ 00484 0, /* fig_end_layer */ 00485 fig_begin_page, 00486 0, /* fig_end_page */ 00487 0, /* fig_begin_cluster */ 00488 0, /* fig_end_cluster */ 00489 0, /* fig_begin_nodes */ 00490 0, /* fig_end_nodes */ 00491 0, /* fig_begin_edges */ 00492 0, /* fig_end_edges */ 00493 fig_begin_node, 00494 fig_end_node, 00495 fig_begin_edge, 00496 fig_end_edge, 00497 0, /* fig_begin_anchor */ 00498 0, /* fig_end_anchor */ 00499 0, /* fig_begin_label */ 00500 0, /* fig_end_label */ 00501 fig_textpara, 00502 fig_resolve_color, 00503 fig_ellipse, 00504 fig_polygon, 00505 fig_bezier, 00506 fig_polyline, 00507 fig_comment, 00508 0, /* fig_library_shape */ 00509 }; 00510 00511 00512 /* NB. List must be LANG_C sorted */ 00513 static char *fig_knowncolors[] = { 00514 "black", "blue", "cyan", "green", "magenta", "red", "white", "yellow", 00515 }; 00516 00517 00518 gvrender_features_t render_features_fig = { 00519 EMIT_COLORS 00520 | GVRENDER_Y_GOES_DOWN, /* flags */ 00521 4., /* default pad - graph units */ 00522 fig_knowncolors, /* knowncolors */ 00523 sizeof(fig_knowncolors) / sizeof(char *), /* sizeof knowncolors */ 00524 RGBA_BYTE, /* color_type */ 00525 }; 00526 00527 gvdevice_features_t device_features_fig = { 00528 EMIT_COLORS 00529 | GVRENDER_Y_GOES_DOWN, /* flags */ 00530 {0.,0.}, /* default margin - points */ 00531 {0.,0.}, /* default page width, height - points */ 00532 {1440.,1440.}, /* default dpi */ 00533 /* FIXME - this default dpi is a very strange number!!! 00534 * It was picked to make .png usershapes the right size on my screen. 00535 * It happens to be 1.2 * 1200, but I can't explain the 1.2. 00536 * (I was expecting 1.3333 which is 96/72, but thats too big.) 00537 * Also 1200 is hardcoded in fig_begin_graph() instead of using job->dpi 00538 */ 00539 00540 /* It may be TWIPS, i.e. 20 * POINT_PER_INCH 00541 * but that doesn't explain what the 1200 is? */ 00542 }; 00543 00544 gvplugin_installed_t gvrender_fig_types[] = { 00545 {FORMAT_FIG, "fig", 1, &fig_engine, &render_features_fig}, 00546 {0, NULL, 0, NULL, NULL} 00547 }; 00548 00549 gvplugin_installed_t gvdevice_fig_types[] = { 00550 {FORMAT_FIG, "fig:fig", 1, NULL, &device_features_fig}, 00551 {0, NULL, 0, NULL, NULL} 00552 };
1.7.5