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