gvusershape.c 18 KB

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