1 | /* $Id$ $Revision$ */ |
2 | /* vim:set shiftwidth=4 ts=8: */ |
3 | |
4 | /************************************************************************* |
5 | * Copyright (c) 2011 AT&T Intellectual Property |
6 | * All rights reserved. This program and the accompanying materials |
7 | * are made available under the terms of the Eclipse Public License v1.0 |
8 | * which accompanies this distribution, and is available at |
9 | * http://www.eclipse.org/legal/epl-v10.html |
10 | * |
11 | * Contributors: See CVS logs. Details at http://www.graphviz.org/ |
12 | *************************************************************************/ |
13 | |
14 | #include "config.h" |
15 | |
16 | #include <stddef.h> |
17 | #include <string.h> |
18 | #include <stdlib.h> |
19 | #include <ctype.h> |
20 | #include <errno.h> |
21 | |
22 | #ifdef _WIN32 |
23 | #include <windows.h> |
24 | #define GLOB_NOSPACE 1 /* Ran out of memory. */ |
25 | #define GLOB_ABORTED 2 /* Read error. */ |
26 | #define GLOB_NOMATCH 3 /* No matches found. */ |
27 | #define GLOB_NOSORT 4 |
28 | #define DMKEY "Software\\Microsoft" //key to look for library dir |
29 | #endif |
30 | |
31 | #include <regex.h> |
32 | #include "types.h" |
33 | #include "logic.h" |
34 | #include "memory.h" |
35 | #include "agxbuf.h" |
36 | |
37 | #define _BLD_gvc 1 |
38 | #include "utils.h" |
39 | #include "gvplugin_loadimage.h" |
40 | |
41 | extern char *Gvimagepath; |
42 | extern char *HTTPServerEnVar; |
43 | extern shape_desc *find_user_shape(const char *); |
44 | |
45 | static Dict_t *ImageDict; |
46 | |
47 | typedef struct { |
48 | char *template; |
49 | int size; |
50 | int type; |
51 | char *stringtype; |
52 | } knowntype_t; |
53 | |
54 | #define HDRLEN 20 |
55 | |
56 | #define PNG_MAGIC "\x89PNG\x0D\x0A\x1A\x0A" |
57 | #define PS_MAGIC "%!PS-Adobe-" |
58 | #define BMP_MAGIC "BM" |
59 | #define GIF_MAGIC "GIF8" |
60 | #define JPEG_MAGIC "\xFF\xD8\xFF\xE0" |
61 | #define PDF_MAGIC "%PDF-" |
62 | #define EPS_MAGIC "\xC5\xD0\xD3\xC6" |
63 | #define XML_MAGIC "<?xml" |
64 | #define SVG_MAGIC "<svg" |
65 | #define RIFF_MAGIC "RIFF" |
66 | #define WEBP_MAGIC "WEBP" |
67 | //#define TIFF_MAGIC "II" |
68 | #define ICO_MAGIC "\x00\x00\x01\x00" |
69 | |
70 | static knowntype_t knowntypes[] = { |
71 | { PNG_MAGIC, sizeof(PNG_MAGIC)-1, FT_PNG, "png" , }, |
72 | { PS_MAGIC, sizeof(PS_MAGIC)-1, FT_PS, "ps" , }, |
73 | { BMP_MAGIC, sizeof(BMP_MAGIC)-1, FT_BMP, "bmp" , }, |
74 | { GIF_MAGIC, sizeof(GIF_MAGIC)-1, FT_GIF, "gif" , }, |
75 | { JPEG_MAGIC, sizeof(JPEG_MAGIC)-1, FT_JPEG, "jpeg" , }, |
76 | { PDF_MAGIC, sizeof(PDF_MAGIC)-1, FT_PDF, "pdf" , }, |
77 | { EPS_MAGIC, sizeof(EPS_MAGIC)-1, FT_EPS, "eps" , }, |
78 | /* { SVG_MAGIC, sizeof(SVG_MAGIC)-1, FT_SVG, "svg", }, - viewers expect xml preamble */ |
79 | { XML_MAGIC, sizeof(XML_MAGIC)-1, FT_XML, "xml" , }, |
80 | { RIFF_MAGIC, sizeof(RIFF_MAGIC)-1, FT_RIFF, "riff" , }, |
81 | { ICO_MAGIC, sizeof(ICO_MAGIC)-1, FT_ICO, "ico" , }, |
82 | // { TIFF_MAGIC, sizeof(TIFF_MAGIC)-1, FT_TIFF, "tiff", }, |
83 | }; |
84 | |
85 | static int imagetype (usershape_t *us) |
86 | { |
87 | char [HDRLEN]; |
88 | char line[200]; |
89 | int i; |
90 | |
91 | if (us->f && fread(header, 1, HDRLEN, us->f) == HDRLEN) { |
92 | for (i = 0; i < sizeof(knowntypes) / sizeof(knowntype_t); i++) { |
93 | if (!memcmp (header, knowntypes[i].template, knowntypes[i].size)) { |
94 | us->stringtype = knowntypes[i].stringtype; |
95 | us->type = knowntypes[i].type; |
96 | if (us->type == FT_XML) { |
97 | /* check for SVG in case of XML */ |
98 | while (fgets(line, sizeof(line), us->f) != NULL) { |
99 | if (!memcmp(line, SVG_MAGIC, sizeof(SVG_MAGIC)-1)) { |
100 | us->stringtype = "svg" ; |
101 | return (us->type = FT_SVG); |
102 | } |
103 | } |
104 | } |
105 | else if (us->type == FT_RIFF) { |
106 | /* check for WEBP in case of RIFF */ |
107 | if (!memcmp(header+8, WEBP_MAGIC, sizeof(WEBP_MAGIC)-1)) { |
108 | us->stringtype = "webp" ; |
109 | return (us->type = FT_WEBP); |
110 | } |
111 | } |
112 | return us->type; |
113 | } |
114 | } |
115 | } |
116 | |
117 | us->stringtype = "(lib)" ; |
118 | us->type = FT_NULL; |
119 | |
120 | return FT_NULL; |
121 | } |
122 | |
123 | static boolean get_int_lsb_first (FILE *f, unsigned int sz, unsigned int *val) |
124 | { |
125 | int ch, i; |
126 | |
127 | *val = 0; |
128 | for (i = 0; i < sz; i++) { |
129 | ch = fgetc(f); |
130 | if (feof(f)) |
131 | return FALSE; |
132 | *val |= (ch << 8*i); |
133 | } |
134 | return TRUE; |
135 | } |
136 | |
137 | static boolean get_int_msb_first (FILE *f, unsigned int sz, unsigned int *val) |
138 | { |
139 | int ch, i; |
140 | |
141 | *val = 0; |
142 | for (i = 0; i < sz; i++) { |
143 | ch = fgetc(f); |
144 | if (feof(f)) |
145 | return FALSE; |
146 | *val <<= 8; |
147 | *val |= ch; |
148 | } |
149 | return TRUE; |
150 | } |
151 | |
152 | static unsigned int svg_units_convert(double n, char *u) |
153 | { |
154 | if (strcmp(u, "in" ) == 0) |
155 | return ROUND(n * POINTS_PER_INCH); |
156 | if (strcmp(u, "px" ) == 0) |
157 | return ROUND(n * POINTS_PER_INCH / 96); |
158 | if (strcmp(u, "pc" ) == 0) |
159 | return ROUND(n * POINTS_PER_INCH / 6); |
160 | if (strcmp(u, "pt" ) == 0 || strcmp(u, "\"" ) == 0) /* ugly!! - if there are no inits then the %2s get the trailing '"' */ |
161 | return ROUND(n); |
162 | if (strcmp(u, "cm" ) == 0) |
163 | return ROUND(n * POINTS_PER_CM); |
164 | if (strcmp(u, "mm" ) == 0) |
165 | return ROUND(n * POINTS_PER_MM); |
166 | return 0; |
167 | } |
168 | |
169 | static char* svg_attr_value_re = "([a-z][a-zA-Z]*)=\"([^\"]*)\"" ; |
170 | static regex_t re, *pre = NULL; |
171 | |
172 | static void svg_size (usershape_t *us) |
173 | { |
174 | unsigned int w = 0, h = 0; |
175 | double n, x0, y0, x1, y1; |
176 | char u[10]; |
177 | char *attribute, *value, *re_string; |
178 | char line[200]; |
179 | boolean wFlag = FALSE, hFlag = FALSE; |
180 | #define RE_NMATCH 4 |
181 | regmatch_t re_pmatch[RE_NMATCH]; |
182 | |
183 | /* compile on first use */ |
184 | if (! pre) { |
185 | if (regcomp(&re, svg_attr_value_re, REG_EXTENDED) != 0) { |
186 | agerr(AGERR,"cannot compile regular expression %s" , svg_attr_value_re); |
187 | } |
188 | pre = &re; |
189 | } |
190 | |
191 | fseek(us->f, 0, SEEK_SET); |
192 | while (fgets(line, sizeof(line), us->f) != NULL && (!wFlag || !hFlag)) { |
193 | re_string = line; |
194 | while (regexec(&re, re_string, RE_NMATCH, re_pmatch, 0) == 0) { |
195 | re_string[re_pmatch[1].rm_eo] = '\0'; |
196 | re_string[re_pmatch[2].rm_eo] = '\0'; |
197 | attribute = re_string + re_pmatch[1].rm_so; |
198 | value = re_string + re_pmatch[2].rm_so; |
199 | re_string += re_pmatch[0].rm_eo + 1; |
200 | |
201 | if (strcmp(attribute,"width" ) == 0) { |
202 | if (sscanf(value, "%lf%2s" , &n, u) == 2) { |
203 | w = svg_units_convert(n, u); |
204 | wFlag = TRUE; |
205 | } |
206 | else if (sscanf(value, "%lf" , &n) == 1) { |
207 | w = svg_units_convert(n, "pt" ); |
208 | wFlag = TRUE; |
209 | } |
210 | if (hFlag) |
211 | break; |
212 | } |
213 | else if (strcmp(attribute,"height" ) == 0) { |
214 | if (sscanf(value, "%lf%2s" , &n, u) == 2) { |
215 | h = svg_units_convert(n, u); |
216 | hFlag = TRUE; |
217 | } |
218 | else if (sscanf(value, "%lf" , &n) == 1) { |
219 | h = svg_units_convert(n, "pt" ); |
220 | hFlag = TRUE; |
221 | } |
222 | if (wFlag) |
223 | break; |
224 | } |
225 | else if (strcmp(attribute,"viewBox" ) == 0 |
226 | && sscanf(value, "%lf %lf %lf %lf" , &x0,&y0,&x1,&y1) == 4) { |
227 | w = x1 - x0 + 1; |
228 | h = y1 - y0 + 1; |
229 | wFlag = TRUE; |
230 | hFlag = TRUE; |
231 | break; |
232 | } |
233 | } |
234 | } |
235 | us->dpi = 0; |
236 | us->w = w; |
237 | us->h = h; |
238 | } |
239 | |
240 | static void png_size (usershape_t *us) |
241 | { |
242 | unsigned int w, h; |
243 | |
244 | us->dpi = 0; |
245 | fseek(us->f, 16, SEEK_SET); |
246 | if (get_int_msb_first(us->f, 4, &w) && get_int_msb_first(us->f, 4, &h)) { |
247 | us->w = w; |
248 | us->h = h; |
249 | } |
250 | } |
251 | |
252 | static void ico_size (usershape_t *us) |
253 | { |
254 | unsigned int w, h; |
255 | |
256 | us->dpi = 0; |
257 | fseek(us->f, 6, SEEK_SET); |
258 | if (get_int_msb_first(us->f, 1, &w) && get_int_msb_first(us->f, 1, &h)) { |
259 | us->w = w; |
260 | us->h = h; |
261 | } |
262 | } |
263 | |
264 | |
265 | // FIXME - how to get the size of a tiff image? |
266 | #if 0 |
267 | static void tiff_size (usershape_t *us) |
268 | { |
269 | unsigned int w, h; |
270 | |
271 | us->dpi = 0; |
272 | fseek(us->f, 6, SEEK_SET); |
273 | if (get_int_msb_first(us->f, 1, &w) && get_int_msb_first(us->f, 1, &h)) { |
274 | us->w = w; |
275 | us->h = h; |
276 | } |
277 | } |
278 | #endif |
279 | |
280 | static void webp_size (usershape_t *us) |
281 | { |
282 | unsigned int w, h; |
283 | |
284 | us->dpi = 0; |
285 | fseek(us->f, 15, SEEK_SET); |
286 | if (fgetc(us->f) == 'X') { //VP8X |
287 | fseek(us->f, 24, SEEK_SET); |
288 | if (get_int_lsb_first(us->f, 4, &w) && get_int_lsb_first(us->f, 4, &h)) { |
289 | us->w = w; |
290 | us->h = h; |
291 | } |
292 | } |
293 | else { //VP8 |
294 | fseek(us->f, 26, SEEK_SET); |
295 | if (get_int_lsb_first(us->f, 2, &w) && get_int_lsb_first(us->f, 2, &h)) { |
296 | us->w = w; |
297 | us->h = h; |
298 | } |
299 | } |
300 | } |
301 | |
302 | static void gif_size (usershape_t *us) |
303 | { |
304 | unsigned int w, h; |
305 | |
306 | us->dpi = 0; |
307 | fseek(us->f, 6, SEEK_SET); |
308 | if (get_int_lsb_first(us->f, 2, &w) && get_int_lsb_first(us->f, 2, &h)) { |
309 | us->w = w; |
310 | us->h = h; |
311 | } |
312 | } |
313 | |
314 | static void bmp_size (usershape_t *us) { |
315 | unsigned int size_x_msw, size_x_lsw, size_y_msw, size_y_lsw; |
316 | |
317 | us->dpi = 0; |
318 | fseek (us->f, 16, SEEK_SET); |
319 | if ( get_int_lsb_first (us->f, 2, &size_x_msw) && |
320 | get_int_lsb_first (us->f, 2, &size_x_lsw) && |
321 | get_int_lsb_first (us->f, 2, &size_y_msw) && |
322 | get_int_lsb_first (us->f, 2, &size_y_lsw) ) { |
323 | us->w = size_x_msw << 16 | size_x_lsw; |
324 | us->h = size_y_msw << 16 | size_y_lsw; |
325 | } |
326 | } |
327 | |
328 | static void jpeg_size (usershape_t *us) { |
329 | unsigned int marker, length, size_x, size_y, junk; |
330 | |
331 | /* These are the markers that follow 0xff in the file. |
332 | * Other markers implicitly have a 2-byte length field that follows. |
333 | */ |
334 | static unsigned char standalone_markers [] = { |
335 | 0x01, /* Temporary */ |
336 | 0xd0, 0xd1, 0xd2, 0xd3, /* Reset */ |
337 | 0xd4, 0xd5, 0xd6, |
338 | 0xd7, |
339 | 0xd8, /* Start of image */ |
340 | 0xd9, /* End of image */ |
341 | 0 |
342 | }; |
343 | |
344 | us->dpi = 0; |
345 | while (TRUE) { |
346 | /* Now we must be at a 0xff or at a series of 0xff's. |
347 | * If that is not the case, or if we're at EOF, then there's |
348 | * a parsing error. |
349 | */ |
350 | if (! get_int_msb_first (us->f, 1, &marker)) |
351 | return; |
352 | |
353 | if (marker == 0xff) |
354 | continue; |
355 | |
356 | /* Ok.. marker now read. If it is not a stand-alone marker, |
357 | * then continue. If it's a Start Of Frame (0xc?), then we're there. |
358 | * If it's another marker with a length field, then skip ahead |
359 | * over that length field. |
360 | */ |
361 | |
362 | /* A stand-alone... */ |
363 | if (strchr ((char*)standalone_markers, marker)) |
364 | continue; |
365 | |
366 | /* Incase of a 0xc0 marker: */ |
367 | if (marker == 0xc0) { |
368 | /* Skip length and 2 lengths. */ |
369 | if ( get_int_msb_first (us->f, 3, &junk) && |
370 | get_int_msb_first (us->f, 2, &size_x) && |
371 | get_int_msb_first (us->f, 2, &size_y) ) { |
372 | |
373 | /* Store length. */ |
374 | us->h = size_x; |
375 | us->w = size_y; |
376 | } |
377 | return; |
378 | } |
379 | |
380 | /* Incase of a 0xc2 marker: */ |
381 | if (marker == 0xc2) { |
382 | /* Skip length and one more byte */ |
383 | if (! get_int_msb_first (us->f, 3, &junk)) |
384 | return; |
385 | |
386 | /* Get length and store. */ |
387 | if ( get_int_msb_first (us->f, 2, &size_x) && |
388 | get_int_msb_first (us->f, 2, &size_y) ) { |
389 | us->h = size_x; |
390 | us->w = size_y; |
391 | } |
392 | return; |
393 | } |
394 | |
395 | /* Any other marker is assumed to be followed by 2 bytes length. */ |
396 | if (! get_int_msb_first (us->f, 2, &length)) |
397 | return; |
398 | |
399 | fseek (us->f, length - 2, SEEK_CUR); |
400 | } |
401 | } |
402 | |
403 | static void ps_size (usershape_t *us) |
404 | { |
405 | char line[BUFSIZ]; |
406 | boolean saw_bb; |
407 | int lx, ly, ux, uy; |
408 | char* linep; |
409 | |
410 | us->dpi = 72; |
411 | fseek(us->f, 0, SEEK_SET); |
412 | saw_bb = FALSE; |
413 | while (fgets(line, sizeof(line), us->f)) { |
414 | /* PostScript accepts \r as EOL, so using fgets () and looking for a |
415 | * bounding box comment at the beginning doesn't work in this case. |
416 | * As a heuristic, we first search for a bounding box comment in line. |
417 | * This obviously fails if not all of the numbers make it into the |
418 | * current buffer. This shouldn't be a problem, as the comment is |
419 | * typically near the beginning, and so should be read within the first |
420 | * BUFSIZ bytes (even on Windows where this is 512). |
421 | */ |
422 | if (!(linep = strstr (line, "%%BoundingBox:" ))) |
423 | continue; |
424 | if (sscanf (linep, "%%%%BoundingBox: %d %d %d %d" , &lx, &ly, &ux, &uy) == 4) { |
425 | saw_bb = TRUE; |
426 | break; |
427 | } |
428 | } |
429 | if (saw_bb) { |
430 | us->x = lx; |
431 | us->y = ly; |
432 | us->w = ux - lx; |
433 | us->h = uy - ly; |
434 | } |
435 | } |
436 | |
437 | #define KEY "/MediaBox" |
438 | |
439 | typedef struct { |
440 | char* s; |
441 | char* buf; |
442 | FILE* fp; |
443 | } stream_t; |
444 | |
445 | static unsigned char |
446 | nxtc (stream_t* str) |
447 | { |
448 | if (fgets(str->buf, BUFSIZ, str->fp)) { |
449 | str->s = str->buf; |
450 | return *(str->s); |
451 | } |
452 | return '\0'; |
453 | |
454 | } |
455 | |
456 | #define strc(x) (*(x->s)?*(x->s):nxtc(x)) |
457 | #define stradv(x) (x->s++) |
458 | |
459 | static void |
460 | skipWS (stream_t* str) |
461 | { |
462 | unsigned char c; |
463 | while ((c = strc(str))) { |
464 | if (isspace(c)) stradv(str); |
465 | else return; |
466 | } |
467 | } |
468 | |
469 | static int |
470 | scanNum (char* tok, double* dp) |
471 | { |
472 | char* endp; |
473 | double d = strtod(tok, &endp); |
474 | |
475 | if (tok == endp) return 1; |
476 | *dp = d; |
477 | return 0; |
478 | } |
479 | |
480 | static void |
481 | getNum (stream_t* str, char* buf) |
482 | { |
483 | int len = 0; |
484 | char c; |
485 | skipWS(str); |
486 | while ((c = strc(str)) && (isdigit(c) || (c == '.'))) { |
487 | buf[len++] = c; |
488 | stradv(str); |
489 | if (len == BUFSIZ-1) break; |
490 | } |
491 | buf[len] = '\0'; |
492 | |
493 | return; |
494 | } |
495 | |
496 | static int |
497 | boxof (stream_t* str, boxf* bp) |
498 | { |
499 | char tok[BUFSIZ]; |
500 | |
501 | skipWS(str); |
502 | if (strc(str) != '[') return 1; |
503 | stradv(str); |
504 | getNum(str, tok); |
505 | if (scanNum(tok,&bp->LL.x)) return 1; |
506 | getNum(str, tok); |
507 | if (scanNum(tok,&bp->LL.y)) return 1; |
508 | getNum(str, tok); |
509 | if (scanNum(tok,&bp->UR.x)) return 1; |
510 | getNum(str, tok); |
511 | if (scanNum(tok,&bp->UR.y)) return 1; |
512 | return 0; |
513 | } |
514 | |
515 | static int |
516 | bboxPDF (FILE* fp, boxf* bp) |
517 | { |
518 | stream_t str; |
519 | char* s; |
520 | char buf[BUFSIZ]; |
521 | while (fgets(buf, BUFSIZ, fp)) { |
522 | if ((s = strstr(buf,KEY))) { |
523 | str.buf = buf; |
524 | str.s = s+(sizeof(KEY)-1); |
525 | str.fp = fp; |
526 | return boxof(&str,bp); |
527 | } |
528 | } |
529 | return 1; |
530 | } |
531 | |
532 | static void pdf_size (usershape_t *us) |
533 | { |
534 | boxf bb; |
535 | |
536 | us->dpi = 0; |
537 | fseek(us->f, 0, SEEK_SET); |
538 | if ( ! bboxPDF (us->f, &bb)) { |
539 | us->x = bb.LL.x; |
540 | us->y = bb.LL.y; |
541 | us->w = bb.UR.x - bb.LL.x; |
542 | us->h = bb.UR.y - bb.LL.y; |
543 | } |
544 | } |
545 | |
546 | static void usershape_close (Dict_t * dict, void * p, Dtdisc_t * disc) |
547 | { |
548 | usershape_t *us = (usershape_t *)p; |
549 | |
550 | if (us->f) |
551 | fclose(us->f); |
552 | if (us->data && us->datafree) |
553 | us->datafree(us); |
554 | free (us); |
555 | } |
556 | |
557 | static Dtdisc_t ImageDictDisc = { |
558 | offsetof(usershape_t, name), /* key */ |
559 | -1, /* size */ |
560 | 0, /* link offset */ |
561 | NIL(Dtmake_f), |
562 | usershape_close, |
563 | NIL(Dtcompar_f), |
564 | NIL(Dthash_f), |
565 | NIL(Dtmemory_f), |
566 | NIL(Dtevent_f) |
567 | }; |
568 | |
569 | usershape_t *gvusershape_find(const char *name) |
570 | { |
571 | usershape_t *us; |
572 | |
573 | assert(name); |
574 | assert(name[0]); |
575 | |
576 | if (!ImageDict) |
577 | return NULL; |
578 | |
579 | us = dtmatch(ImageDict, name); |
580 | return us; |
581 | } |
582 | |
583 | #define MAX_USERSHAPE_FILES_OPEN 50 |
584 | boolean gvusershape_file_access(usershape_t *us) |
585 | { |
586 | static int usershape_files_open_cnt; |
587 | const char *fn; |
588 | |
589 | assert(us); |
590 | assert(us->name); |
591 | assert(us->name[0]); |
592 | |
593 | if (us->f) |
594 | fseek(us->f, 0, SEEK_SET); |
595 | else { |
596 | if (! (fn = safefile(us->name))) { |
597 | agerr(AGWARN, "Filename \"%s\" is unsafe\n" , us->name); |
598 | return FALSE; |
599 | } |
600 | #ifndef _WIN32 |
601 | us->f = fopen(fn, "r" ); |
602 | #else |
603 | us->f = fopen(fn, "rb" ); |
604 | #endif |
605 | if (us->f == NULL) { |
606 | agerr(AGWARN, "%s while opening %s\n" , strerror(errno), fn); |
607 | return FALSE; |
608 | } |
609 | if (usershape_files_open_cnt >= MAX_USERSHAPE_FILES_OPEN) |
610 | us->nocache = TRUE; |
611 | else |
612 | usershape_files_open_cnt++; |
613 | } |
614 | assert(us->f); |
615 | return TRUE; |
616 | } |
617 | |
618 | void gvusershape_file_release(usershape_t *us) |
619 | { |
620 | if (us->nocache) { |
621 | if (us->f) { |
622 | fclose(us->f); |
623 | us->f = NULL; |
624 | } |
625 | } |
626 | } |
627 | |
628 | static void freeUsershape (usershape_t* us) |
629 | { |
630 | if (us->name) agstrfree(0, (char*)us->name); |
631 | free (us); |
632 | } |
633 | |
634 | static usershape_t *gvusershape_open (const char *name) |
635 | { |
636 | usershape_t *us; |
637 | |
638 | assert(name); |
639 | |
640 | if (!ImageDict) |
641 | ImageDict = dtopen(&ImageDictDisc, Dttree); |
642 | |
643 | if (! (us = gvusershape_find(name))) { |
644 | if (! (us = zmalloc(sizeof(usershape_t)))) |
645 | return NULL; |
646 | |
647 | us->name = agstrdup (0, (char*)name); |
648 | if (!gvusershape_file_access(us)) { |
649 | freeUsershape (us); |
650 | return NULL; |
651 | } |
652 | |
653 | assert(us->f); |
654 | |
655 | switch(imagetype(us)) { |
656 | case FT_NULL: |
657 | if (!(us->data = (void*)find_user_shape(us->name))) { |
658 | agerr(AGWARN, "\"%s\" was not found as a file or as a shape library member\n" , us->name); |
659 | freeUsershape (us); |
660 | return NULL; |
661 | } |
662 | break; |
663 | case FT_GIF: |
664 | gif_size(us); |
665 | break; |
666 | case FT_PNG: |
667 | png_size(us); |
668 | break; |
669 | case FT_BMP: |
670 | bmp_size(us); |
671 | break; |
672 | case FT_JPEG: |
673 | jpeg_size(us); |
674 | break; |
675 | case FT_PS: |
676 | ps_size(us); |
677 | break; |
678 | case FT_WEBP: |
679 | webp_size(us); |
680 | break; |
681 | case FT_SVG: |
682 | svg_size(us); |
683 | break; |
684 | case FT_PDF: |
685 | pdf_size(us); |
686 | break; |
687 | case FT_ICO: |
688 | ico_size(us); |
689 | break; |
690 | // case FT_TIFF: |
691 | // tiff_size(us); |
692 | // break; |
693 | case FT_EPS: /* no eps_size code available */ |
694 | default: |
695 | break; |
696 | } |
697 | gvusershape_file_release(us); |
698 | dtinsert(ImageDict, us); |
699 | return us; |
700 | } |
701 | gvusershape_file_release(us); |
702 | return us; |
703 | } |
704 | |
705 | /* gvusershape_size_dpi: |
706 | * Return image size in points. |
707 | */ |
708 | point |
709 | gvusershape_size_dpi (usershape_t* us, pointf dpi) |
710 | { |
711 | point rv; |
712 | |
713 | if (!us) { |
714 | rv.x = rv.y = -1; |
715 | } |
716 | else { |
717 | if (us->dpi != 0) { |
718 | dpi.x = dpi.y = us->dpi; |
719 | } |
720 | rv.x = us->w * POINTS_PER_INCH / dpi.x; |
721 | rv.y = us->h * POINTS_PER_INCH / dpi.y; |
722 | } |
723 | return rv; |
724 | } |
725 | |
726 | /* gvusershape_size: |
727 | * Loads user image from file name if not already loaded. |
728 | * Return image size in points. |
729 | */ |
730 | point gvusershape_size(graph_t * g, char *name) |
731 | { |
732 | point rv; |
733 | pointf dpi; |
734 | static char* oldpath; |
735 | usershape_t* us; |
736 | |
737 | /* no shape file, no shape size */ |
738 | if (!name || (*name == '\0')) { |
739 | rv.x = rv.y = -1; |
740 | return rv; |
741 | } |
742 | |
743 | if (!HTTPServerEnVar && (oldpath != Gvimagepath)) { |
744 | oldpath = Gvimagepath; |
745 | if (ImageDict) { |
746 | dtclose(ImageDict); |
747 | ImageDict = NULL; |
748 | } |
749 | } |
750 | |
751 | if ((dpi.y = GD_drawing(g)->dpi) >= 1.0) |
752 | dpi.x = dpi.y; |
753 | else |
754 | dpi.x = dpi.y = (double)DEFAULT_DPI; |
755 | |
756 | us = gvusershape_open (name); |
757 | rv = gvusershape_size_dpi (us, dpi); |
758 | return rv; |
759 | } |
760 | |