Graphviz 13.1.2~dev.20250723.1036
Loading...
Searching...
No Matches
gvusershape.c
Go to the documentation of this file.
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
11#include "config.h"
12#include <assert.h>
13#include <errno.h>
14#include <limits.h>
15#include <math.h>
16#include <stdbool.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <util/agxbuf.h>
21#include <util/gv_ctype.h>
22#include <util/gv_fopen.h>
23#include <util/optional.h>
24
25#include <common/types.h>
26#include <common/usershape.h>
27#include <common/utils.h>
28#include <gvc/gvcint.h>
29#include <gvc/gvcproc.h>
30#include <gvc/gvplugin.h>
32#include <util/agxbuf.h>
33#include <util/alloc.h>
34#include <util/gv_ctype.h>
35#include <util/strview.h>
36
37extern char *Gvimagepath;
38extern char *HTTPServerEnVar;
39extern shape_desc *find_user_shape(const char *);
40
42
43typedef struct {
44 char *template;
45 size_t size;
49
50#define HDRLEN 20
51
52#define PNG_MAGIC "\x89PNG\x0D\x0A\x1A\x0A"
53#define PS_MAGIC "%!PS-Adobe-"
54#define BMP_MAGIC "BM"
55#define GIF_MAGIC "GIF8"
56#define JPEG_MAGIC "\xFF\xD8\xFF"
57#define PDF_MAGIC "%PDF-"
58#define EPS_MAGIC "\xC5\xD0\xD3\xC6"
59#define XML_MAGIC "<?xml"
60#define SVG_MAGIC "<svg"
61#define RIFF_MAGIC "RIFF"
62#define WEBP_MAGIC "WEBP"
63#define ICO_MAGIC "\x00\x00\x01\x00"
64
66 {
68 sizeof(PNG_MAGIC) - 1,
69 FT_PNG,
70 "png",
71 },
72 {
74 sizeof(PS_MAGIC) - 1,
75 FT_PS,
76 "ps",
77 },
78 {
80 sizeof(BMP_MAGIC) - 1,
81 FT_BMP,
82 "bmp",
83 },
84 {
86 sizeof(GIF_MAGIC) - 1,
87 FT_GIF,
88 "gif",
89 },
90 {
92 sizeof(JPEG_MAGIC) - 1,
93 FT_JPEG,
94 "jpeg",
95 },
96 {
98 sizeof(PDF_MAGIC) - 1,
99 FT_PDF,
100 "pdf",
101 },
102 {
103 EPS_MAGIC,
104 sizeof(EPS_MAGIC) - 1,
105 FT_EPS,
106 "eps",
107 },
108 {
109 XML_MAGIC,
110 sizeof(XML_MAGIC) - 1,
111 FT_XML,
112 "xml",
113 },
114 {
116 sizeof(RIFF_MAGIC) - 1,
117 FT_RIFF,
118 "riff",
119 },
120 {
121 ICO_MAGIC,
122 sizeof(ICO_MAGIC) - 1,
123 FT_ICO,
124 "ico",
125 },
126};
127
129 char header[HDRLEN] = {0};
130
131 if (us->f && fread(header, 1, HDRLEN, us->f) == HDRLEN) {
132 for (size_t i = 0; i < sizeof(knowntypes) / sizeof(knowntype_t); i++) {
133 if (!memcmp(header, knowntypes[i].template, knowntypes[i].size)) {
135 us->type = knowntypes[i].type;
136 if (us->type == FT_XML) {
137 // if we did not see the closing of the XML declaration, scan for it
138 if (memchr(header, '>', HDRLEN) == NULL) {
139 while (true) {
140 int c = fgetc(us->f);
141 if (c == EOF) {
142 return us->type;
143 } else if (c == '>') {
144 break;
145 }
146 }
147 }
148 /* check for SVG in case of XML */
149 char tag[sizeof(SVG_MAGIC) - 1] = {0};
150 if (fread(tag, 1, sizeof(tag), us->f) != sizeof(tag)) {
151 return us->type;
152 }
153 while (true) {
154 if (memcmp(tag, SVG_MAGIC, sizeof(SVG_MAGIC) - 1) == 0) {
155 us->stringtype = "svg";
156 return (us->type = FT_SVG);
157 }
158 int c = fgetc(us->f);
159 if (c == EOF) {
160 return us->type;
161 }
162 memmove(&tag[0], &tag[1], sizeof(tag) - 1);
163 tag[sizeof(tag) - 1] = (char)c;
164 }
165 } else if (us->type == FT_RIFF) {
166 /* check for WEBP in case of RIFF */
167 if (!memcmp(header + 8, WEBP_MAGIC, sizeof(WEBP_MAGIC) - 1)) {
168 us->stringtype = "webp";
169 return (us->type = FT_WEBP);
170 }
171 }
172 return us->type;
173 }
174 }
175 }
176
177 us->stringtype = "(lib)";
178 us->type = FT_NULL;
179
180 return FT_NULL;
181}
182
183static bool get_int_lsb_first(FILE *f, size_t sz, int *val) {
184 int ch;
185
186 unsigned value = 0;
187 for (size_t i = 0; i < sz; i++) {
188 ch = fgetc(f);
189 if (feof(f))
190 return false;
191 value |= (unsigned)ch << 8 * i;
192 }
193 if (value > INT_MAX) {
194 return false;
195 }
196 *val = (int)value;
197 return true;
198}
199
200static bool get_int_msb_first(FILE *f, size_t sz, int *val) {
201 int ch;
202
203 unsigned value = 0;
204 for (size_t i = 0; i < sz; i++) {
205 ch = fgetc(f);
206 if (feof(f))
207 return false;
208 value <<= 8;
209 value |= (unsigned)ch;
210 }
211 if (value > INT_MAX) {
212 return false;
213 }
214 *val = (int)value;
215 return true;
216}
217
218static double svg_units_convert(double n, char *u) {
219 if (strcmp(u, "in") == 0)
220 return round(n * POINTS_PER_INCH);
221 if (strcmp(u, "px") == 0)
222 return round(n * POINTS_PER_INCH / 96);
223 if (strcmp(u, "pc") == 0)
224 return round(n * POINTS_PER_INCH / 6);
225 if (strcmp(u, "pt") == 0 ||
226 strcmp(u, "\"") == 0) /* ugly!! - if there are no inits then the %2s get
227 the trailing '"' */
228 return round(n);
229 if (strcmp(u, "cm") == 0)
230 return round(n * POINTS_PER_CM);
231 if (strcmp(u, "mm") == 0)
232 return round(n * POINTS_PER_MM);
233 return 0;
234}
235
236typedef struct {
239} match_t;
240
241static int find_attribute(const char *s, match_t *result) {
242
243 // look for an attribute string matching ([a-z][a-zA-Z]*)="([^"]*)"
244 for (size_t i = 0; s[i] != '\0';) {
245 if (gv_islower(s[i])) {
246 result->key.data = &s[i];
247 result->key.size = 1;
248 ++i;
249 while (gv_isalpha(s[i])) {
250 ++i;
251 ++result->key.size;
252 }
253 if (s[i] == '=' && s[i + 1] == '"') {
254 i += 2;
255 result->value.data = &s[i];
256 result->value.size = 0;
257 while (s[i] != '"' && s[i] != '\0') {
258 ++i;
259 ++result->value.size;
260 }
261 if (s[i] == '"') {
262 // found a valid attribute
263 return 0;
264 }
265 }
266 } else {
267 ++i;
268 }
269 }
270
271 // no attribute found
272 return -1;
273}
274
275static void svg_size(usershape_t *us) {
276 double n;
277 char u[3];
278 agxbuf line = {0};
279 bool eof = false;
280
281 // authoritative constraints we learned from `height` and `width`
282 optional_double_t hard_height = {0};
283 optional_double_t hard_width = {0};
284
285 // fallback constraints we learned from `viewBox`
286 optional_double_t soft_height = {0};
287 optional_double_t soft_width = {0};
288
289 rewind(us->f);
290 while (!eof && (!hard_width.has_value || !hard_height.has_value)) {
291 // read next line
292 while (true) {
293 int c = fgetc(us->f);
294 if (c == EOF) {
295 eof = true;
296 break;
297 } else if (c == '\n') {
298 break;
299 }
300 agxbputc(&line, (char)c);
301 }
302
303 const char *re_string = agxbuse(&line);
305 while (find_attribute(re_string, &match) == 0) {
306 re_string = match.value.data + match.value.size + 1;
307
308 if (strview_str_eq(match.key, "width")) {
309 char *value = strview_str(match.value);
310 if (sscanf(value, "%lf%2s", &n, u) == 2) {
311 optional_double_set(&hard_width, svg_units_convert(n, u));
312 } else if (sscanf(value, "%lf", &n) == 1) {
313 optional_double_set(&hard_width, svg_units_convert(n, "pt"));
314 }
315 free(value);
316 if (hard_height.has_value)
317 break;
318 } else if (strview_str_eq(match.key, "height")) {
319 char *value = strview_str(match.value);
320 if (sscanf(value, "%lf%2s", &n, u) == 2) {
321 optional_double_set(&hard_height, svg_units_convert(n, u));
322 } else if (sscanf(value, "%lf", &n) == 1) {
323 optional_double_set(&hard_height, svg_units_convert(n, "pt"));
324 }
325 free(value);
326 if (hard_width.has_value)
327 break;
328 } else if (strview_str_eq(match.key, "viewBox")) {
329 char *value = strview_str(match.value);
330 double w, h;
331 if (sscanf(value, "%*f %*f %lf %lf", &w, &h) == 2) {
332 optional_double_set(&soft_width, w);
333 optional_double_set(&soft_height, h);
334 }
335 free(value);
336 }
337 }
338
339 // if we have reached the end of a line and have seen `viewBox` but not
340 // `height` and/or `width`, let `viewBox` determine the dimensions
341 if (soft_height.has_value && soft_width.has_value) {
342 if (!hard_height.has_value) {
343 optional_double_set(&hard_height, soft_height.value);
344 }
345 if (!hard_width.has_value) {
346 optional_double_set(&hard_width, soft_width.value);
347 }
348 break;
349 }
350 }
351 us->dpi = 0;
352 const double h = optional_double_value_or(hard_height, 0);
353 const double w = optional_double_value_or(hard_width, 0);
354 assert(w >= 0 && w <= INT_MAX);
355 us->w = (int)w;
356 assert(h >= 0 && h <= INT_MAX);
357 us->h = (int)h;
358 agxbfree(&line);
359}
360
361static void png_size(usershape_t *us) {
362 int w, h;
363
364 us->dpi = 0;
365 fseek(us->f, 16, SEEK_SET);
366 if (get_int_msb_first(us->f, 4, &w) && get_int_msb_first(us->f, 4, &h)) {
367 us->w = w;
368 us->h = h;
369 }
370}
371
372static void ico_size(usershape_t *us) {
373 int w, h;
374
375 us->dpi = 0;
376 fseek(us->f, 6, SEEK_SET);
377 if (get_int_msb_first(us->f, 1, &w) && get_int_msb_first(us->f, 1, &h)) {
378 us->w = w;
379 us->h = h;
380 }
381}
382
383static void webp_size(usershape_t *us) {
384 int w, h;
385
386 us->dpi = 0;
387 fseek(us->f, 15, SEEK_SET);
388 if (fgetc(us->f) == 'X') { // VP8X
389 fseek(us->f, 24, SEEK_SET);
390 if (get_int_lsb_first(us->f, 4, &w) && get_int_lsb_first(us->f, 4, &h)) {
391 us->w = w;
392 us->h = h;
393 }
394 } else { // VP8
395 fseek(us->f, 26, SEEK_SET);
396 if (get_int_lsb_first(us->f, 2, &w) && get_int_lsb_first(us->f, 2, &h)) {
397 us->w = w;
398 us->h = h;
399 }
400 }
401}
402
403static void gif_size(usershape_t *us) {
404 int w, h;
405
406 us->dpi = 0;
407 fseek(us->f, 6, SEEK_SET);
408 if (get_int_lsb_first(us->f, 2, &w) && get_int_lsb_first(us->f, 2, &h)) {
409 us->w = w;
410 us->h = h;
411 }
412}
413
414static void bmp_size(usershape_t *us) {
415 int size_x_msw, size_x_lsw, size_y_msw, size_y_lsw;
416
417 us->dpi = 0;
418 fseek(us->f, 16, SEEK_SET);
419 if (get_int_lsb_first(us->f, 2, &size_x_msw) &&
420 get_int_lsb_first(us->f, 2, &size_x_lsw) &&
421 get_int_lsb_first(us->f, 2, &size_y_msw) &&
422 get_int_lsb_first(us->f, 2, &size_y_lsw)) {
423 us->w = size_x_msw << 16 | size_x_lsw;
424 us->h = size_y_msw << 16 | size_y_lsw;
425 }
426}
427
428static void jpeg_size(usershape_t *us) {
429 int marker, length, size_x, size_y;
430
431 /* These are the markers that follow 0xff in the file.
432 * Other markers implicitly have a 2-byte length field that follows.
433 */
434 static const unsigned char standalone_markers[] = {
435 0x01, /* Temporary */
436 0xd0, 0xd1, 0xd2, 0xd3, /* Reset */
437 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, /* Start of image */
438 0xd9, /* End of image */
439 };
440
441 us->dpi = 0;
442 rewind(us->f);
443 while (true) {
444 /* Now we must be at a 0xff or at a series of 0xff's.
445 * If that is not the case, or if we're at EOF, then there's
446 * a parsing error.
447 */
448 if (!get_int_msb_first(us->f, 1, &marker)) {
449 agwarningf("Parsing of \"%s\" failed\n", us->name);
450 return;
451 }
452
453 if (marker == 0xff)
454 continue;
455
456 /* Ok.. marker now read. If it is not a stand-alone marker,
457 * then continue. If it's a Start Of Frame (0xc?), then we're there.
458 * If it's another marker with a length field, then skip ahead
459 * over that length field.
460 */
461
462 /* A stand-alone... */
463 if (memchr(standalone_markers, marker, sizeof(standalone_markers)))
464 continue;
465
466 /* Incase of a 0xc0 marker: */
467 if (marker == 0xc0) {
468 /* Skip length and 2 lengths. */
469 if (fseek(us->f, 3, SEEK_CUR) == 0 &&
470 get_int_msb_first(us->f, 2, &size_x) &&
471 get_int_msb_first(us->f, 2, &size_y)) {
472
473 /* Store length. */
474 us->h = size_x;
475 us->w = size_y;
476 }
477 return;
478 }
479
480 /* Incase of a 0xc2 marker: */
481 if (marker == 0xc2) {
482 /* Skip length and one more byte */
483 if (fseek(us->f, 3, SEEK_CUR) != 0)
484 return;
485
486 /* Get length and store. */
487 if (get_int_msb_first(us->f, 2, &size_x) &&
488 get_int_msb_first(us->f, 2, &size_y)) {
489 us->h = size_x;
490 us->w = size_y;
491 }
492 return;
493 }
494
495 /* Any other marker is assumed to be followed by 2 bytes length. */
496 if (!get_int_msb_first(us->f, 2, &length))
497 return;
498
499 fseek(us->f, length - 2, SEEK_CUR);
500 }
501}
502
503static void ps_size(usershape_t *us) {
504 char line[BUFSIZ];
505 int lx, ly, ux, uy;
506 char *linep;
507
508 us->dpi = 72;
509 rewind(us->f);
510 bool saw_bb = false;
511 while (fgets(line, sizeof(line), us->f)) {
512 /* PostScript accepts \r as EOL, so using fgets () and looking for a
513 * bounding box comment at the beginning doesn't work in this case.
514 * As a heuristic, we first search for a bounding box comment in line.
515 * This obviously fails if not all of the numbers make it into the
516 * current buffer. This shouldn't be a problem, as the comment is
517 * typically near the beginning, and so should be read within the first
518 * BUFSIZ bytes (even on Windows where this is 512).
519 */
520 if (!(linep = strstr(line, "%%BoundingBox:")))
521 continue;
522 if (sscanf(linep, "%%%%BoundingBox: %d %d %d %d", &lx, &ly, &ux, &uy) ==
523 4) {
524 saw_bb = true;
525 break;
526 }
527 }
528 if (saw_bb) {
529 us->x = lx;
530 us->y = ly;
531 us->w = ux - lx;
532 us->h = uy - ly;
533 }
534}
535
536typedef struct {
537 FILE *fp;
539} stream_t;
540
541static void skipWS(stream_t *str) {
542 while (true) {
543 const int c = getc(str->fp);
544 if (gv_isspace(c)) {
545 continue;
546 }
547 if (c != EOF) {
548 (void)ungetc(c, str->fp);
549 }
550 break;
551 }
552}
553
554static int scanNum(char *tok, double *dp) {
555 char *endp;
556 double d = strtod(tok, &endp);
557
558 if (tok == endp)
559 return 1;
560 *dp = d;
561 return 0;
562}
563
564static char *getNum(stream_t *str) {
565 skipWS(str);
566 while (true) {
567 const int c = getc(str->fp);
568 if (gv_isdigit(c) || c == '.') {
569 agxbputc(&str->scratch, (char)c);
570 continue;
571 }
572 if (c != EOF) {
573 (void)ungetc(c, str->fp);
574 }
575 break;
576 }
577 return agxbuse(&str->scratch);
578}
579
580static int boxof(stream_t *str, boxf *bp) {
581 skipWS(str);
582 if (getc(str->fp) != '[')
583 return 1;
584 char *tok = getNum(str);
585 if (scanNum(tok, &bp->LL.x))
586 return 1;
587 tok = getNum(str);
588 if (scanNum(tok, &bp->LL.y))
589 return 1;
590 tok = getNum(str);
591 if (scanNum(tok, &bp->UR.x))
592 return 1;
593 tok = getNum(str);
594 if (scanNum(tok, &bp->UR.y))
595 return 1;
596 return 0;
597}
598
608static bool fstr(FILE *f, const char *needle) {
609 assert(f != NULL);
610 assert(needle != NULL);
611
612 // the algorithm in this function only works if the needle’s characters are
613 // distinct
614 for (size_t i = 0; needle[i] != '\0'; ++i) {
615 for (size_t j = i + 1; needle[j] != '\0'; ++j) {
616 assert(needle[i] != needle[j]);
617 }
618 }
619
620 for (size_t offset = 0;;) {
621 if (needle[offset] == '\0') {
622 return true;
623 }
624 const int c = getc(f);
625 if (c == EOF) {
626 break;
627 }
628 if (needle[offset] == c) {
629 ++offset;
630 } else if (needle[0] == c) {
631 offset = 1;
632 } else {
633 offset = 0;
634 }
635 }
636
637 return false;
638}
639
640static int bboxPDF(FILE *fp, boxf *bp) {
641 static const char KEY[] = "/MediaBox";
642 if (fstr(fp, KEY)) {
643 stream_t str = {.fp = fp};
644 const int rc = boxof(&str, bp);
645 agxbfree(&str.scratch);
646 return rc;
647 }
648
649 return 1;
650}
651
652static void pdf_size(usershape_t *us) {
653 boxf bb;
654
655 us->dpi = 0;
656 rewind(us->f);
657 if (!bboxPDF(us->f, &bb)) {
658 us->x = bb.LL.x;
659 us->y = bb.LL.y;
660 us->w = bb.UR.x - bb.LL.x;
661 us->h = bb.UR.y - bb.LL.y;
662 }
663}
664
665static void usershape_close(void *p) {
666 usershape_t *us = p;
667
668 if (us->f)
669 fclose(us->f);
670 if (us->data && us->datafree)
671 us->datafree(us);
672 free(us);
673}
674
676 .key = offsetof(usershape_t, name),
677 .size = -1,
678 .freef = usershape_close,
679};
680
681usershape_t *gvusershape_find(const char *name) {
682 usershape_t *us;
683
684 assert(name);
685 assert(name[0]);
686
687 if (!ImageDict)
688 return NULL;
689
690 us = dtmatch(ImageDict, name);
691 return us;
692}
693
694#define MAX_USERSHAPE_FILES_OPEN 50
696 static int usershape_files_open_cnt;
697 const char *fn;
698
699 assert(us);
700 assert(us->name);
701 assert(us->name[0]);
702
703 if (us->f)
704 rewind(us->f);
705 else {
706 if (!(fn = safefile(us->name))) {
707 agwarningf("Filename \"%s\" is unsafe\n", us->name);
708 return false;
709 }
710 us->f = gv_fopen(fn, "rb");
711 if (us->f == NULL) {
712 agwarningf("%s while opening %s\n", strerror(errno), fn);
713 return false;
714 }
715 if (usershape_files_open_cnt >= MAX_USERSHAPE_FILES_OPEN)
716 us->nocache = true;
717 else
718 usershape_files_open_cnt++;
719 }
720 assert(us->f);
721 return true;
722}
723
725 if (us->nocache) {
726 if (us->f) {
727 fclose(us->f);
728 us->f = NULL;
729 }
730 }
731}
732
733static void freeUsershape(usershape_t *us) {
734 if (us->name)
735 agstrfree(0, us->name, false);
736 free(us);
737}
738
739static usershape_t *gvusershape_open(const char *name) {
740 usershape_t *us;
741
742 assert(name);
743
744 if (!ImageDict)
746
747 if (!(us = gvusershape_find(name))) {
748 us = gv_alloc(sizeof(usershape_t));
749
750 us->name = agstrdup(0, name);
751 if (!gvusershape_file_access(us)) {
752 freeUsershape(us);
753 return NULL;
754 }
755
756 assert(us->f);
757
758 switch (imagetype(us)) {
759 case FT_NULL:
760 if (!(us->data = find_user_shape(us->name))) {
762 "\"%s\" was not found as a file or as a shape library member\n",
763 us->name);
764 freeUsershape(us);
765 return NULL;
766 }
767 break;
768 case FT_GIF:
769 gif_size(us);
770 break;
771 case FT_PNG:
772 png_size(us);
773 break;
774 case FT_BMP:
775 bmp_size(us);
776 break;
777 case FT_JPEG:
778 jpeg_size(us);
779 break;
780 case FT_PS:
781 ps_size(us);
782 break;
783 case FT_WEBP:
784 webp_size(us);
785 break;
786 case FT_SVG:
787 svg_size(us);
788 break;
789 case FT_PDF:
790 pdf_size(us);
791 break;
792 case FT_ICO:
793 ico_size(us);
794 break;
795 case FT_EPS: /* no eps_size code available */
796 default:
797 break;
798 }
800 dtinsert(ImageDict, us);
801 return us;
802 }
804 return us;
805}
806
807/* gvusershape_size_dpi:
808 * Return image size in points.
809 */
811 point rv;
812
813 if (!us) {
814 rv.x = rv.y = -1;
815 } else {
816 if (us->dpi != 0) {
817 dpi.x = dpi.y = us->dpi;
818 }
819 rv.x = (int)(us->w * POINTS_PER_INCH / dpi.x);
820 rv.y = (int)(us->h * POINTS_PER_INCH / dpi.y);
821 }
822 return rv;
823}
824
825/* gvusershape_size:
826 * Loads user image from file name if not already loaded.
827 * Return image size in points.
828 */
830 point rv;
831 pointf dpi;
832 static char *oldpath;
833 usershape_t *us;
834
835 /* no shape file, no shape size */
836 if (!name || (*name == '\0')) {
837 rv.x = rv.y = -1;
838 return rv;
839 }
840
841 if (!HTTPServerEnVar && (oldpath != Gvimagepath)) {
842 oldpath = Gvimagepath;
843 if (ImageDict) {
845 ImageDict = NULL;
846 }
847 }
848
849 if ((dpi.y = GD_drawing(g)->dpi) >= 1.0)
850 dpi.x = dpi.y;
851 else
852 dpi.x = dpi.y = DEFAULT_DPI;
853
854 us = gvusershape_open(name);
855 rv = gvusershape_size_dpi(us, dpi);
856 return rv;
857}
size_t match(char *str, char *pat)
Definition actions.c:95
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:78
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:307
static int agxbputc(agxbuf *xb, char c)
add character to buffer
Definition agxbuf.h:277
Memory allocation wrappers that exit on failure.
static void * gv_alloc(size_t size)
Definition alloc.h:47
#define dtmatch(d, o)
Definition cdt.h:184
#define dtinsert(d, o)
Definition cdt.h:185
CDT_API Dtmethod_t * Dttree
Definition dttree.c:308
CDT_API int dtclose(Dt_t *)
Definition dtclose.c:8
CDT_API Dt_t * dtopen(Dtdisc_t *, Dtmethod_t *)
Definition dtopen.c:9
const char * safefile(const char *filename)
Definition utils.c:262
static Extype_t length(Exid_t *rhs, Exdisc_t *disc)
Definition compile.c:1588
#define POINTS_PER_MM
Definition geom.h:60
#define POINTS_PER_CM
Definition geom.h:59
#define POINTS_PER_INCH
Definition geom.h:58
void free(void *)
node NULL
Definition grammar.y:180
void agwarningf(const char *fmt,...)
Definition agerror.c:173
#define GD_drawing(g)
Definition types.h:353
int agstrfree(Agraph_t *, const char *, bool is_html)
Definition refstr.c:415
char * agstrdup(Agraph_t *, const char *)
returns a pointer to a reference-counted copy of the argument string, creating one if necessary
Definition refstr.c:399
bool gvusershape_file_access(usershape_t *us)
replacements for ctype.h functions
static bool gv_islower(int c)
Definition gv_ctype.h:25
static bool gv_isdigit(int c)
Definition gv_ctype.h:41
static bool gv_isalpha(int c)
Definition gv_ctype.h:29
static bool gv_isspace(int c)
Definition gv_ctype.h:55
FILE * gv_fopen(const char *filename, const char *mode)
Definition gv_fopen.c:32
wrapper around fopen for internal library usage
static void ico_size(usershape_t *us)
static void freeUsershape(usershape_t *us)
#define XML_MAGIC
Definition gvusershape.c:59
#define HDRLEN
Definition gvusershape.c:50
#define MAX_USERSHAPE_FILES_OPEN
static bool get_int_msb_first(FILE *f, size_t sz, int *val)
#define RIFF_MAGIC
Definition gvusershape.c:61
shape_desc * find_user_shape(const char *)
Definition shapes.c:3962
#define PNG_MAGIC
Definition gvusershape.c:52
static bool get_int_lsb_first(FILE *f, size_t sz, int *val)
#define JPEG_MAGIC
Definition gvusershape.c:56
static char * getNum(stream_t *str)
static void skipWS(stream_t *str)
static Dtdisc_t ImageDictDisc
#define PS_MAGIC
Definition gvusershape.c:53
static int find_attribute(const char *s, match_t *result)
char * HTTPServerEnVar
Definition globals.h:53
static void webp_size(usershape_t *us)
static bool fstr(FILE *f, const char *needle)
#define BMP_MAGIC
Definition gvusershape.c:54
char * Gvimagepath
Definition globals.h:49
usershape_t * gvusershape_find(const char *name)
void gvusershape_file_release(usershape_t *us)
point gvusershape_size(graph_t *g, char *name)
point gvusershape_size_dpi(usershape_t *us, pointf dpi)
#define EPS_MAGIC
Definition gvusershape.c:58
#define ICO_MAGIC
Definition gvusershape.c:63
#define GIF_MAGIC
Definition gvusershape.c:55
static int boxof(stream_t *str, boxf *bp)
static void jpeg_size(usershape_t *us)
static usershape_t * gvusershape_open(const char *name)
static int bboxPDF(FILE *fp, boxf *bp)
static knowntype_t knowntypes[]
Definition gvusershape.c:65
static void png_size(usershape_t *us)
static void gif_size(usershape_t *us)
static imagetype_t imagetype(usershape_t *us)
static int scanNum(char *tok, double *dp)
#define PDF_MAGIC
Definition gvusershape.c:57
static Dict_t * ImageDict
Definition gvusershape.c:41
#define SVG_MAGIC
Definition gvusershape.c:60
static double svg_units_convert(double n, char *u)
static void pdf_size(usershape_t *us)
static void ps_size(usershape_t *us)
static void svg_size(usershape_t *us)
static void bmp_size(usershape_t *us)
#define WEBP_MAGIC
Definition gvusershape.c:62
static void usershape_close(void *p)
textitem scanner parser str
Definition htmlparse.y:224
C analog of C++’s std::optional
static void optional_double_set(optional_double_t *me, double value)
Definition optional.h:23
static double optional_double_value_or(optional_double_t me, double fallback)
Definition optional.h:34
graph or subgraph
Definition cgraph.h:424
Definition geom.h:41
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
Definition cdt.h:100
int key
Definition cdt.h:85
imagetype_t type
Definition gvusershape.c:46
size_t size
Definition gvusershape.c:45
char * stringtype
Definition gvusershape.c:47
strview_t value
strview_t key
a container that may or may not contain a double value
Definition optional.h:11
double value
the value if has_value is true
Definition optional.h:13
bool has_value
does this have a value?
Definition optional.h:12
Definition geom.h:27
int y
Definition geom.h:27
int x
Definition geom.h:27
double x
Definition geom.h:29
double y
Definition geom.h:29
FILE * fp
agxbuf scratch
a non-owning string reference
Definition strview.h:20
const char * data
start of the pointed to string
Definition strview.h:21
size_t size
extent of the string in bytes
Definition strview.h:22
const char * name
Definition usershape.h:54
char * stringtype
Definition usershape.h:60
FILE * f
Definition usershape.h:58
void(* datafree)(usershape_t *us)
Definition usershape.h:65
bool nocache
Definition usershape.h:57
void * data
Definition usershape.h:63
imagetype_t type
Definition usershape.h:59
double x
Definition usershape.h:61
double y
Definition usershape.h:61
double h
Definition usershape.h:61
double w
Definition usershape.h:61
Non-owning string references.
static bool strview_str_eq(strview_t a, const char *b)
compare a string reference to a string for equality
Definition strview.h:98
static char * strview_str(strview_t source)
make a heap-allocated string from this string view
Definition strview.h:41
static tok_t tok(const char *input, const char *separators)
begin tokenization of a new string
Definition tokenize.h:43
graphs, nodes and edges info: Agraphinfo_t, Agnodeinfo_t and Agedgeinfo_t
Definition grammar.c:89
imagetype_t
Definition usershape.h:24
@ FT_BMP
Definition usershape.h:25
@ FT_PS
Definition usershape.h:26
@ FT_ICO
Definition usershape.h:27
@ FT_NULL
Definition usershape.h:24
@ FT_EPS
Definition usershape.h:26
@ FT_SVG
Definition usershape.h:26
@ FT_WEBP
Definition usershape.h:27
@ FT_GIF
Definition usershape.h:25
@ FT_JPEG
Definition usershape.h:25
@ FT_RIFF
Definition usershape.h:27
@ FT_PNG
Definition usershape.h:25
@ FT_XML
Definition usershape.h:26
@ FT_PDF
Definition usershape.h:26