|
Graphviz
2.29.20120524.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 <stddef.h> 00019 #include <string.h> 00020 #include <errno.h> 00021 00022 #ifdef WIN32 00023 #include <windows.h> 00024 #define GLOB_NOSPACE 1 /* Ran out of memory. */ 00025 #define GLOB_ABORTED 2 /* Read error. */ 00026 #define GLOB_NOMATCH 3 /* No matches found. */ 00027 #define GLOB_NOSORT 4 00028 #define DMKEY "Software\\Microsoft" //key to look for library dir 00029 #include "regex_win32.h" 00030 #else 00031 #include <regex.h> 00032 #endif 00033 00034 #include "types.h" 00035 #include "logic.h" 00036 #include "memory.h" 00037 #include "agxbuf.h" 00038 00039 #define _BLD_gvc 1 00040 #include "utils.h" 00041 #include "gvplugin_loadimage.h" 00042 00043 extern char *Gvimagepath; 00044 extern char *HTTPServerEnVar; 00045 extern shape_desc *find_user_shape(const char *); 00046 00047 static Dict_t *ImageDict; 00048 00049 typedef struct { 00050 char *template; 00051 int size; 00052 int type; 00053 char *stringtype; 00054 } knowntype_t; 00055 00056 #define HDRLEN 20 00057 00058 #define PNG_MAGIC "\x89PNG\x0D\x0A\x1A\x0A" 00059 #define PS_MAGIC "%!PS-Adobe-" 00060 #define BMP_MAGIC "BM" 00061 #define GIF_MAGIC "GIF8" 00062 #define JPEG_MAGIC "\xFF\xD8\xFF\xE0" 00063 #define PDF_MAGIC "%PDF-" 00064 #define EPS_MAGIC "\xC5\xD0\xD3\xC6" 00065 #define XML_MAGIC "<?xml" 00066 #define SVG_MAGIC "<svg" 00067 #define RIFF_MAGIC "RIFF" 00068 #define WEBP_MAGIC "WEBP" 00069 00070 static knowntype_t knowntypes[] = { 00071 { PNG_MAGIC, sizeof(PNG_MAGIC)-1, FT_PNG, "png", }, 00072 { PS_MAGIC, sizeof(PS_MAGIC)-1, FT_PS, "ps", }, 00073 { BMP_MAGIC, sizeof(BMP_MAGIC)-1, FT_BMP, "bmp", }, 00074 { GIF_MAGIC, sizeof(GIF_MAGIC)-1, FT_GIF, "gif", }, 00075 { JPEG_MAGIC, sizeof(JPEG_MAGIC)-1, FT_JPEG, "jpeg", }, 00076 { PDF_MAGIC, sizeof(PDF_MAGIC)-1, FT_PDF, "pdf", }, 00077 { EPS_MAGIC, sizeof(EPS_MAGIC)-1, FT_EPS, "eps", }, 00078 /* { SVG_MAGIC, sizeof(SVG_MAGIC)-1, FT_SVG, "svg", }, - viewers expect xml preamble */ 00079 { XML_MAGIC, sizeof(XML_MAGIC)-1, FT_XML, "xml", }, 00080 { RIFF_MAGIC, sizeof(RIFF_MAGIC)-1, FT_RIFF, "riff", }, 00081 }; 00082 00083 static int imagetype (usershape_t *us) 00084 { 00085 char header[HDRLEN]; 00086 char line[200]; 00087 int i; 00088 00089 if (us->f && fread(header, 1, HDRLEN, us->f) == HDRLEN) { 00090 for (i = 0; i < sizeof(knowntypes) / sizeof(knowntype_t); i++) { 00091 if (!memcmp (header, knowntypes[i].template, knowntypes[i].size)) { 00092 us->stringtype = knowntypes[i].stringtype; 00093 us->type = knowntypes[i].type; 00094 if (us->type == FT_XML) { 00095 /* check for SVG in case of XML */ 00096 while (fgets(line, sizeof(line), us->f) != NULL) { 00097 if (!memcmp(line, SVG_MAGIC, sizeof(SVG_MAGIC)-1)) { 00098 us->stringtype = "svg"; 00099 return (us->type = FT_SVG); 00100 } 00101 } 00102 } 00103 else if (us->type == FT_RIFF) { 00104 /* check for WEBP in case of RIFF */ 00105 if (!memcmp(header+8, WEBP_MAGIC, sizeof(WEBP_MAGIC)-1)) { 00106 us->stringtype = "webp"; 00107 return (us->type = FT_WEBP); 00108 } 00109 } 00110 return us->type; 00111 } 00112 } 00113 } 00114 00115 us->stringtype = "(lib)"; 00116 us->type = FT_NULL; 00117 00118 return FT_NULL; 00119 } 00120 00121 static boolean get_int_lsb_first (FILE *f, unsigned int sz, unsigned int *val) 00122 { 00123 int ch, i; 00124 00125 *val = 0; 00126 for (i = 0; i < sz; i++) { 00127 ch = fgetc(f); 00128 if (feof(f)) 00129 return FALSE; 00130 *val |= (ch << 8*i); 00131 } 00132 return TRUE; 00133 } 00134 00135 static boolean get_int_msb_first (FILE *f, unsigned int sz, unsigned int *val) 00136 { 00137 int ch, i; 00138 00139 *val = 0; 00140 for (i = 0; i < sz; i++) { 00141 ch = fgetc(f); 00142 if (feof(f)) 00143 return FALSE; 00144 *val <<= 8; 00145 *val |= ch; 00146 } 00147 return TRUE; 00148 } 00149 00150 static unsigned int svg_units_convert(double n, char *u) 00151 { 00152 if (strcmp(u, "in") == 0) 00153 return ROUND(n * POINTS_PER_INCH); 00154 if (strcmp(u, "px") == 0) 00155 return ROUND(n * POINTS_PER_INCH / 96); 00156 if (strcmp(u, "pc") == 0) 00157 return ROUND(n * POINTS_PER_INCH / 6); 00158 if (strcmp(u, "pt") == 0 || strcmp(u, "\"") == 0) /* ugly!! - if there are no inits then the %2s get the trailing '"' */ 00159 return ROUND(n); 00160 if (strcmp(u, "cm") == 0) 00161 return ROUND(n * POINTS_PER_CM); 00162 if (strcmp(u, "mm") == 0) 00163 return ROUND(n * POINTS_PER_MM); 00164 return 0; 00165 } 00166 00167 static char* svg_attr_value_re = "([a-z][a-zA-Z]*)=\"([^\"]*)\""; 00168 static regex_t re, *pre = NULL; 00169 00170 static void svg_size (usershape_t *us) 00171 { 00172 unsigned int w = 0, h = 0; 00173 double n, x0, y0, x1, y1; 00174 char u[10]; 00175 char *attribute, *value, *re_string; 00176 char line[200]; 00177 boolean wFlag = FALSE, hFlag = FALSE; 00178 #define RE_NMATCH 4 00179 regmatch_t re_pmatch[RE_NMATCH]; 00180 00181 /* compile on first use */ 00182 if (! pre) { 00183 if (regcomp(&re, svg_attr_value_re, REG_EXTENDED) != 0) { 00184 agerr(AGERR,"cannot compile regular expression %s", svg_attr_value_re); 00185 } 00186 pre = &re; 00187 } 00188 00189 fseek(us->f, 0, SEEK_SET); 00190 while (fgets(line, sizeof(line), us->f) != NULL && (!wFlag || !hFlag)) { 00191 re_string = line; 00192 while (regexec(&re, re_string, RE_NMATCH, re_pmatch, 0) == 0) { 00193 re_string[re_pmatch[1].rm_eo] = '\0'; 00194 re_string[re_pmatch[2].rm_eo] = '\0'; 00195 attribute = re_string + re_pmatch[1].rm_so; 00196 value = re_string + re_pmatch[2].rm_so; 00197 re_string += re_pmatch[0].rm_eo + 1; 00198 00199 if (strcmp(attribute,"width") == 0 00200 && sscanf(value, "%lf%2s", &n, u) == 2) { 00201 w = svg_units_convert(n, u); 00202 wFlag = TRUE; 00203 if (hFlag) 00204 break; 00205 } 00206 else if (strcmp(attribute,"height") == 0 00207 && sscanf(value, "%lf%2s", &n, u) == 2) { 00208 h = svg_units_convert(n, u); 00209 hFlag = TRUE; 00210 if (wFlag) 00211 break; 00212 } 00213 else if (strcmp(attribute,"viewBox") == 0 00214 && sscanf(value, "%lf %lf %lf %lf", &x0,&y0,&x1,&y1) == 4) { 00215 w = x1 - x0 + 1; 00216 h = y1 - y0 + 1; 00217 wFlag = TRUE; 00218 hFlag = TRUE; 00219 break; 00220 } 00221 } 00222 } 00223 us->dpi = 72; 00224 us->w = w; 00225 us->h = h; 00226 } 00227 00228 static void png_size (usershape_t *us) 00229 { 00230 unsigned int w, h; 00231 00232 us->dpi = 0; 00233 fseek(us->f, 16, SEEK_SET); 00234 if (get_int_msb_first(us->f, 4, &w) && get_int_msb_first(us->f, 4, &h)) { 00235 us->w = w; 00236 us->h = h; 00237 } 00238 } 00239 00240 static void webp_size (usershape_t *us) 00241 { 00242 unsigned int w, h; 00243 00244 us->dpi = 0; 00245 fseek(us->f, 15, SEEK_SET); 00246 if (fgetc(us->f) == 'X') { //VP8X 00247 fseek(us->f, 24, SEEK_SET); 00248 if (get_int_lsb_first(us->f, 4, &w) && get_int_lsb_first(us->f, 4, &h)) { 00249 us->w = w; 00250 us->h = h; 00251 } 00252 } 00253 else { //VP8 00254 fseek(us->f, 26, SEEK_SET); 00255 if (get_int_lsb_first(us->f, 2, &w) && get_int_lsb_first(us->f, 2, &h)) { 00256 us->w = w; 00257 us->h = h; 00258 } 00259 } 00260 } 00261 00262 static void gif_size (usershape_t *us) 00263 { 00264 unsigned int w, h; 00265 00266 us->dpi = 0; 00267 fseek(us->f, 6, SEEK_SET); 00268 if (get_int_lsb_first(us->f, 2, &w) && get_int_lsb_first(us->f, 2, &h)) { 00269 us->w = w; 00270 us->h = h; 00271 } 00272 } 00273 00274 static void bmp_size (usershape_t *us) { 00275 unsigned int size_x_msw, size_x_lsw, size_y_msw, size_y_lsw; 00276 00277 us->dpi = 0; 00278 fseek (us->f, 16, SEEK_SET); 00279 if ( get_int_lsb_first (us->f, 2, &size_x_msw) && 00280 get_int_lsb_first (us->f, 2, &size_x_lsw) && 00281 get_int_lsb_first (us->f, 2, &size_y_msw) && 00282 get_int_lsb_first (us->f, 2, &size_y_lsw) ) { 00283 us->w = size_x_msw << 16 | size_x_lsw; 00284 us->h = size_y_msw << 16 | size_y_lsw; 00285 } 00286 } 00287 00288 static void jpeg_size (usershape_t *us) { 00289 unsigned int marker, length, size_x, size_y, junk; 00290 00291 /* These are the markers that follow 0xff in the file. 00292 * Other markers implicitly have a 2-byte length field that follows. 00293 */ 00294 static unsigned char standalone_markers [] = { 00295 0x01, /* Temporary */ 00296 0xd0, 0xd1, 0xd2, 0xd3, /* Reset */ 00297 0xd4, 0xd5, 0xd6, 00298 0xd7, 00299 0xd8, /* Start of image */ 00300 0xd9, /* End of image */ 00301 0 00302 }; 00303 00304 us->dpi = 0; 00305 while (TRUE) { 00306 /* Now we must be at a 0xff or at a series of 0xff's. 00307 * If that is not the case, or if we're at EOF, then there's 00308 * a parsing error. 00309 */ 00310 if (! get_int_msb_first (us->f, 1, &marker)) 00311 return; 00312 00313 if (marker == 0xff) 00314 continue; 00315 00316 /* Ok.. marker now read. If it is not a stand-alone marker, 00317 * then continue. If it's a Start Of Frame (0xc?), then we're there. 00318 * If it's another marker with a length field, then skip ahead 00319 * over that length field. 00320 */ 00321 00322 /* A stand-alone... */ 00323 if (strchr ((char*)standalone_markers, marker)) 00324 continue; 00325 00326 /* Incase of a 0xc0 marker: */ 00327 if (marker == 0xc0) { 00328 /* Skip length and 2 lengths. */ 00329 if ( get_int_msb_first (us->f, 3, &junk) && 00330 get_int_msb_first (us->f, 2, &size_x) && 00331 get_int_msb_first (us->f, 2, &size_y) ) { 00332 00333 /* Store length. */ 00334 us->h = size_x; 00335 us->w = size_y; 00336 } 00337 return; 00338 } 00339 00340 /* Incase of a 0xc2 marker: */ 00341 if (marker == 0xc2) { 00342 /* Skip length and one more byte */ 00343 if (! get_int_msb_first (us->f, 3, &junk)) 00344 return; 00345 00346 /* Get length and store. */ 00347 if ( get_int_msb_first (us->f, 2, &size_x) && 00348 get_int_msb_first (us->f, 2, &size_y) ) { 00349 us->h = size_x; 00350 us->w = size_y; 00351 } 00352 return; 00353 } 00354 00355 /* Any other marker is assumed to be followed by 2 bytes length. */ 00356 if (! get_int_msb_first (us->f, 2, &length)) 00357 return; 00358 00359 fseek (us->f, length - 2, SEEK_CUR); 00360 } 00361 } 00362 00363 static void ps_size (usershape_t *us) 00364 { 00365 char line[BUFSIZ]; 00366 boolean saw_bb; 00367 int lx, ly, ux, uy; 00368 char* linep; 00369 00370 us->dpi = POINTS_PER_INCH; 00371 fseek(us->f, 0, SEEK_SET); 00372 saw_bb = FALSE; 00373 while (fgets(line, sizeof(line), us->f)) { 00374 /* PostScript accepts \r as EOL, so using fgets () and looking for a 00375 * bounding box comment at the beginning doesn't work in this case. 00376 * As a heuristic, we first search for a bounding box comment in line. 00377 * This obviously fails if not all of the numbers make it into the 00378 * current buffer. This shouldn't be a problem, as the comment is 00379 * typically near the beginning, and so should be read within the first 00380 * BUFSIZ bytes (even on Windows where this is 512). 00381 */ 00382 if (!(linep = strstr (line, "%%BoundingBox:"))) 00383 continue; 00384 if (sscanf (linep, "%%%%BoundingBox: %d %d %d %d", &lx, &ly, &ux, &uy) == 4) { 00385 saw_bb = TRUE; 00386 break; 00387 } 00388 } 00389 if (saw_bb) { 00390 us->x = lx; 00391 us->y = ly; 00392 us->w = ux - lx; 00393 us->h = uy - ly; 00394 } 00395 } 00396 00397 static void usershape_close (Dict_t * dict, Void_t * p, Dtdisc_t * disc) 00398 { 00399 usershape_t *us = (usershape_t *)p; 00400 00401 if (us->f) 00402 fclose(us->f); 00403 if (us->data && us->datafree) 00404 us->datafree(us); 00405 free (us); 00406 } 00407 00408 static Dtdisc_t ImageDictDisc = { 00409 offsetof(usershape_t, name), /* key */ 00410 -1, /* size */ 00411 0, /* link offset */ 00412 NIL(Dtmake_f), 00413 usershape_close, 00414 NIL(Dtcompar_f), 00415 NIL(Dthash_f), 00416 NIL(Dtmemory_f), 00417 NIL(Dtevent_f) 00418 }; 00419 00420 usershape_t *gvusershape_find(char *name) 00421 { 00422 usershape_t probe; 00423 00424 if (!ImageDict) 00425 return NULL; 00426 00427 probe.name = name; 00428 return (dtsearch(ImageDict, &probe)); 00429 } 00430 00431 #define MAX_USERSHAPE_FILES_OPEN 50 00432 boolean gvusershape_file_access(usershape_t *us) 00433 { 00434 static int usershape_files_open_cnt; 00435 const char *fn; 00436 00437 assert(us); 00438 assert(us->name); 00439 00440 if (us->f) 00441 fseek(us->f, 0, SEEK_SET); 00442 else { 00443 if ((fn = safefile(us->name))) { 00444 #ifndef WIN32 00445 us->f = fopen(fn, "r"); 00446 #else 00447 us->f = fopen(fn, "rb"); 00448 #endif 00449 if (us->f == NULL) { 00450 agerr(AGWARN, "%s while opening %s\n", strerror(errno), fn); 00451 return FALSE; 00452 } 00453 if (usershape_files_open_cnt >= MAX_USERSHAPE_FILES_OPEN) 00454 us->nocache = TRUE; 00455 else 00456 usershape_files_open_cnt++; 00457 } 00458 } 00459 return TRUE; 00460 } 00461 00462 void gvusershape_file_release(usershape_t *us) 00463 { 00464 if (us->nocache) { 00465 if (us->f) { 00466 fclose(us->f); 00467 us->f = NULL; 00468 } 00469 } 00470 } 00471 00472 static usershape_t *gvusershape_open (char *name) 00473 { 00474 usershape_t *us; 00475 00476 if (!ImageDict) 00477 ImageDict = dtopen(&ImageDictDisc, Dttree); 00478 00479 if (! (us = gvusershape_find(name))) { 00480 if (! (us = zmalloc(sizeof(usershape_t)))) 00481 return NULL; 00482 00483 us->name = name; 00484 if (!gvusershape_file_access(us)) 00485 return NULL; 00486 00487 switch(imagetype(us)) { 00488 case FT_NULL: 00489 if (!(us->data = (void*)find_user_shape(us->name))) 00490 agerr(AGWARN, "\"%s\" was not found as a file or as a shape library member\n", us->name); 00491 free(us); 00492 return NULL; 00493 break; 00494 case FT_GIF: 00495 gif_size(us); 00496 break; 00497 case FT_PNG: 00498 png_size(us); 00499 break; 00500 case FT_BMP: 00501 bmp_size(us); 00502 break; 00503 case FT_JPEG: 00504 jpeg_size(us); 00505 break; 00506 case FT_PS: 00507 ps_size(us); 00508 break; 00509 case FT_WEBP: 00510 webp_size(us); 00511 break; 00512 case FT_SVG: 00513 svg_size(us); 00514 break; 00515 case FT_PDF: /* no pdf_size code available */ 00516 case FT_EPS: /* no eps_size code available */ 00517 default: 00518 break; 00519 } 00520 dtinsert(ImageDict, us); 00521 } 00522 00523 gvusershape_file_release(us); 00524 00525 return us; 00526 } 00527 00528 /* gvusershape_size_dpi: 00529 * Return image size in points. 00530 */ 00531 point 00532 gvusershape_size_dpi (usershape_t* us, pointf dpi) 00533 { 00534 point rv; 00535 00536 if (!us) { 00537 rv.x = rv.y = -1; 00538 } 00539 else { 00540 if (us->dpi != 0) { 00541 dpi.x = dpi.y = us->dpi; 00542 } 00543 rv.x = us->w * POINTS_PER_INCH / dpi.x; 00544 rv.y = us->h * POINTS_PER_INCH / dpi.y; 00545 } 00546 return rv; 00547 } 00548 00549 /* gvusershape_size: 00550 * Loads user image from file name if not already loaded. 00551 * Return image size in points. 00552 */ 00553 point gvusershape_size(graph_t * g, char *name) 00554 { 00555 point rv; 00556 pointf dpi; 00557 static char* oldpath; 00558 00559 /* no shape file, no shape size */ 00560 if (!name || (*name == '\0')) { 00561 rv.x = rv.y = -1; 00562 return rv; 00563 } 00564 00565 if (!HTTPServerEnVar && (oldpath != Gvimagepath)) { 00566 oldpath = Gvimagepath; 00567 if (ImageDict) { 00568 dtclose(ImageDict); 00569 ImageDict = NULL; 00570 } 00571 } 00572 00573 if ((dpi.y = GD_drawing(g)->dpi) >= 1.0) 00574 dpi.x = dpi.y; 00575 else 00576 dpi.x = dpi.y = (double)DEFAULT_DPI; 00577 00578 return gvusershape_size_dpi (gvusershape_open (name), dpi); 00579 }
1.7.5