|
Graphviz
2.31.20130525.0447
|
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 if (sscanf(value, "%lf%2s", &n, u) == 2) { 00201 w = svg_units_convert(n, u); 00202 wFlag = TRUE; 00203 } 00204 else if (sscanf(value, "%lf", &n) == 1) { 00205 w = svg_units_convert(n, "pt"); 00206 wFlag = TRUE; 00207 } 00208 if (hFlag) 00209 break; 00210 } 00211 else if (strcmp(attribute,"height") == 0) { 00212 if (sscanf(value, "%lf%2s", &n, u) == 2) { 00213 h = svg_units_convert(n, u); 00214 hFlag = TRUE; 00215 } 00216 else if (sscanf(value, "%lf", &n) == 1) { 00217 h = svg_units_convert(n, "pt"); 00218 hFlag = TRUE; 00219 } 00220 if (wFlag) 00221 break; 00222 } 00223 else if (strcmp(attribute,"viewBox") == 0 00224 && sscanf(value, "%lf %lf %lf %lf", &x0,&y0,&x1,&y1) == 4) { 00225 w = x1 - x0 + 1; 00226 h = y1 - y0 + 1; 00227 wFlag = TRUE; 00228 hFlag = TRUE; 00229 break; 00230 } 00231 } 00232 } 00233 us->dpi = 72; 00234 us->w = w; 00235 us->h = h; 00236 } 00237 00238 static void png_size (usershape_t *us) 00239 { 00240 unsigned int w, h; 00241 00242 us->dpi = 0; 00243 fseek(us->f, 16, SEEK_SET); 00244 if (get_int_msb_first(us->f, 4, &w) && get_int_msb_first(us->f, 4, &h)) { 00245 us->w = w; 00246 us->h = h; 00247 } 00248 } 00249 00250 static void webp_size (usershape_t *us) 00251 { 00252 unsigned int w, h; 00253 00254 us->dpi = 0; 00255 fseek(us->f, 15, SEEK_SET); 00256 if (fgetc(us->f) == 'X') { //VP8X 00257 fseek(us->f, 24, SEEK_SET); 00258 if (get_int_lsb_first(us->f, 4, &w) && get_int_lsb_first(us->f, 4, &h)) { 00259 us->w = w; 00260 us->h = h; 00261 } 00262 } 00263 else { //VP8 00264 fseek(us->f, 26, SEEK_SET); 00265 if (get_int_lsb_first(us->f, 2, &w) && get_int_lsb_first(us->f, 2, &h)) { 00266 us->w = w; 00267 us->h = h; 00268 } 00269 } 00270 } 00271 00272 static void gif_size (usershape_t *us) 00273 { 00274 unsigned int w, h; 00275 00276 us->dpi = 0; 00277 fseek(us->f, 6, SEEK_SET); 00278 if (get_int_lsb_first(us->f, 2, &w) && get_int_lsb_first(us->f, 2, &h)) { 00279 us->w = w; 00280 us->h = h; 00281 } 00282 } 00283 00284 static void bmp_size (usershape_t *us) { 00285 unsigned int size_x_msw, size_x_lsw, size_y_msw, size_y_lsw; 00286 00287 us->dpi = 0; 00288 fseek (us->f, 16, SEEK_SET); 00289 if ( get_int_lsb_first (us->f, 2, &size_x_msw) && 00290 get_int_lsb_first (us->f, 2, &size_x_lsw) && 00291 get_int_lsb_first (us->f, 2, &size_y_msw) && 00292 get_int_lsb_first (us->f, 2, &size_y_lsw) ) { 00293 us->w = size_x_msw << 16 | size_x_lsw; 00294 us->h = size_y_msw << 16 | size_y_lsw; 00295 } 00296 } 00297 00298 static void jpeg_size (usershape_t *us) { 00299 unsigned int marker, length, size_x, size_y, junk; 00300 00301 /* These are the markers that follow 0xff in the file. 00302 * Other markers implicitly have a 2-byte length field that follows. 00303 */ 00304 static unsigned char standalone_markers [] = { 00305 0x01, /* Temporary */ 00306 0xd0, 0xd1, 0xd2, 0xd3, /* Reset */ 00307 0xd4, 0xd5, 0xd6, 00308 0xd7, 00309 0xd8, /* Start of image */ 00310 0xd9, /* End of image */ 00311 0 00312 }; 00313 00314 us->dpi = 0; 00315 while (TRUE) { 00316 /* Now we must be at a 0xff or at a series of 0xff's. 00317 * If that is not the case, or if we're at EOF, then there's 00318 * a parsing error. 00319 */ 00320 if (! get_int_msb_first (us->f, 1, &marker)) 00321 return; 00322 00323 if (marker == 0xff) 00324 continue; 00325 00326 /* Ok.. marker now read. If it is not a stand-alone marker, 00327 * then continue. If it's a Start Of Frame (0xc?), then we're there. 00328 * If it's another marker with a length field, then skip ahead 00329 * over that length field. 00330 */ 00331 00332 /* A stand-alone... */ 00333 if (strchr ((char*)standalone_markers, marker)) 00334 continue; 00335 00336 /* Incase of a 0xc0 marker: */ 00337 if (marker == 0xc0) { 00338 /* Skip length and 2 lengths. */ 00339 if ( get_int_msb_first (us->f, 3, &junk) && 00340 get_int_msb_first (us->f, 2, &size_x) && 00341 get_int_msb_first (us->f, 2, &size_y) ) { 00342 00343 /* Store length. */ 00344 us->h = size_x; 00345 us->w = size_y; 00346 } 00347 return; 00348 } 00349 00350 /* Incase of a 0xc2 marker: */ 00351 if (marker == 0xc2) { 00352 /* Skip length and one more byte */ 00353 if (! get_int_msb_first (us->f, 3, &junk)) 00354 return; 00355 00356 /* Get length and store. */ 00357 if ( get_int_msb_first (us->f, 2, &size_x) && 00358 get_int_msb_first (us->f, 2, &size_y) ) { 00359 us->h = size_x; 00360 us->w = size_y; 00361 } 00362 return; 00363 } 00364 00365 /* Any other marker is assumed to be followed by 2 bytes length. */ 00366 if (! get_int_msb_first (us->f, 2, &length)) 00367 return; 00368 00369 fseek (us->f, length - 2, SEEK_CUR); 00370 } 00371 } 00372 00373 static void ps_size (usershape_t *us) 00374 { 00375 char line[BUFSIZ]; 00376 boolean saw_bb; 00377 int lx, ly, ux, uy; 00378 char* linep; 00379 00380 us->dpi = POINTS_PER_INCH; 00381 fseek(us->f, 0, SEEK_SET); 00382 saw_bb = FALSE; 00383 while (fgets(line, sizeof(line), us->f)) { 00384 /* PostScript accepts \r as EOL, so using fgets () and looking for a 00385 * bounding box comment at the beginning doesn't work in this case. 00386 * As a heuristic, we first search for a bounding box comment in line. 00387 * This obviously fails if not all of the numbers make it into the 00388 * current buffer. This shouldn't be a problem, as the comment is 00389 * typically near the beginning, and so should be read within the first 00390 * BUFSIZ bytes (even on Windows where this is 512). 00391 */ 00392 if (!(linep = strstr (line, "%%BoundingBox:"))) 00393 continue; 00394 if (sscanf (linep, "%%%%BoundingBox: %d %d %d %d", &lx, &ly, &ux, &uy) == 4) { 00395 saw_bb = TRUE; 00396 break; 00397 } 00398 } 00399 if (saw_bb) { 00400 us->x = lx; 00401 us->y = ly; 00402 us->w = ux - lx; 00403 us->h = uy - ly; 00404 } 00405 } 00406 00407 static void usershape_close (Dict_t * dict, Void_t * p, Dtdisc_t * disc) 00408 { 00409 usershape_t *us = (usershape_t *)p; 00410 00411 if (us->f) 00412 fclose(us->f); 00413 if (us->data && us->datafree) 00414 us->datafree(us); 00415 free (us); 00416 } 00417 00418 static Dtdisc_t ImageDictDisc = { 00419 offsetof(usershape_t, name), /* key */ 00420 -1, /* size */ 00421 0, /* link offset */ 00422 NIL(Dtmake_f), 00423 usershape_close, 00424 NIL(Dtcompar_f), 00425 NIL(Dthash_f), 00426 NIL(Dtmemory_f), 00427 NIL(Dtevent_f) 00428 }; 00429 00430 usershape_t *gvusershape_find(char *name) 00431 { 00432 usershape_t probe; 00433 00434 if (!ImageDict) 00435 return NULL; 00436 00437 probe.name = name; 00438 return (dtsearch(ImageDict, &probe)); 00439 } 00440 00441 #define MAX_USERSHAPE_FILES_OPEN 50 00442 boolean gvusershape_file_access(usershape_t *us) 00443 { 00444 static int usershape_files_open_cnt; 00445 const char *fn; 00446 00447 assert(us); 00448 assert(us->name); 00449 00450 if (us->f) 00451 fseek(us->f, 0, SEEK_SET); 00452 else { 00453 if ((fn = safefile(us->name))) { 00454 #ifndef WIN32 00455 us->f = fopen(fn, "r"); 00456 #else 00457 us->f = fopen(fn, "rb"); 00458 #endif 00459 if (us->f == NULL) { 00460 agerr(AGWARN, "%s while opening %s\n", strerror(errno), fn); 00461 return FALSE; 00462 } 00463 if (usershape_files_open_cnt >= MAX_USERSHAPE_FILES_OPEN) 00464 us->nocache = TRUE; 00465 else 00466 usershape_files_open_cnt++; 00467 } 00468 } 00469 return TRUE; 00470 } 00471 00472 void gvusershape_file_release(usershape_t *us) 00473 { 00474 if (us->nocache) { 00475 if (us->f) { 00476 fclose(us->f); 00477 us->f = NULL; 00478 } 00479 } 00480 } 00481 00482 static usershape_t *gvusershape_open (char *name) 00483 { 00484 usershape_t *us; 00485 00486 if (!ImageDict) 00487 ImageDict = dtopen(&ImageDictDisc, Dttree); 00488 00489 if (! (us = gvusershape_find(name))) { 00490 if (! (us = zmalloc(sizeof(usershape_t)))) 00491 return NULL; 00492 00493 us->name = name; 00494 if (!gvusershape_file_access(us)) 00495 return NULL; 00496 00497 switch(imagetype(us)) { 00498 case FT_NULL: 00499 if (!(us->data = (void*)find_user_shape(us->name))) 00500 agerr(AGWARN, "\"%s\" was not found as a file or as a shape library member\n", us->name); 00501 free(us); 00502 return NULL; 00503 break; 00504 case FT_GIF: 00505 gif_size(us); 00506 break; 00507 case FT_PNG: 00508 png_size(us); 00509 break; 00510 case FT_BMP: 00511 bmp_size(us); 00512 break; 00513 case FT_JPEG: 00514 jpeg_size(us); 00515 break; 00516 case FT_PS: 00517 ps_size(us); 00518 break; 00519 case FT_WEBP: 00520 webp_size(us); 00521 break; 00522 case FT_SVG: 00523 svg_size(us); 00524 break; 00525 case FT_PDF: /* no pdf_size code available */ 00526 case FT_EPS: /* no eps_size code available */ 00527 default: 00528 break; 00529 } 00530 dtinsert(ImageDict, us); 00531 } 00532 00533 gvusershape_file_release(us); 00534 00535 return us; 00536 } 00537 00538 /* gvusershape_size_dpi: 00539 * Return image size in points. 00540 */ 00541 point 00542 gvusershape_size_dpi (usershape_t* us, pointf dpi) 00543 { 00544 point rv; 00545 00546 if (!us) { 00547 rv.x = rv.y = -1; 00548 } 00549 else { 00550 if (us->dpi != 0) { 00551 dpi.x = dpi.y = us->dpi; 00552 } 00553 rv.x = us->w * POINTS_PER_INCH / dpi.x; 00554 rv.y = us->h * POINTS_PER_INCH / dpi.y; 00555 } 00556 return rv; 00557 } 00558 00559 /* gvusershape_size: 00560 * Loads user image from file name if not already loaded. 00561 * Return image size in points. 00562 */ 00563 point gvusershape_size(graph_t * g, char *name) 00564 { 00565 point rv; 00566 pointf dpi; 00567 static char* oldpath; 00568 usershape_t* us; 00569 00570 /* no shape file, no shape size */ 00571 if (!name || (*name == '\0')) { 00572 rv.x = rv.y = -1; 00573 return rv; 00574 } 00575 00576 if (!HTTPServerEnVar && (oldpath != Gvimagepath)) { 00577 oldpath = Gvimagepath; 00578 if (ImageDict) { 00579 dtclose(ImageDict); 00580 ImageDict = NULL; 00581 } 00582 } 00583 00584 if ((dpi.y = GD_drawing(g)->dpi) >= 1.0) 00585 dpi.x = dpi.y; 00586 else 00587 dpi.x = dpi.y = (double)DEFAULT_DPI; 00588 00589 us = gvusershape_open (name); 00590 rv = gvusershape_size_dpi (us, dpi); 00591 return rv; 00592 }
1.7.5