common_utils.c 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632
  1. /// @file
  2. /// @ingroup common_utils
  3. /*************************************************************************
  4. * Copyright (c) 2011 AT&T Intellectual Property
  5. * All rights reserved. This program and the accompanying materials
  6. * are made available under the terms of the Eclipse Public License v1.0
  7. * which accompanies this distribution, and is available at
  8. * https://www.eclipse.org/legal/epl-v10.html
  9. *
  10. * Contributors: Details at https://graphviz.org
  11. *************************************************************************/
  12. #include <common/render.h>
  13. #include <cgraph/gv_ctype.h>
  14. #include <cgraph/gv_math.h>
  15. #include <cgraph/strview.h>
  16. #include <cgraph/tokenize.h>
  17. #include <common/htmltable.h>
  18. #include <common/entities.h>
  19. #include <limits.h>
  20. #include <math.h>
  21. #include <gvc/gvc.h>
  22. #include <cgraph/strview.h>
  23. #include <stddef.h>
  24. #include <stdbool.h>
  25. #include <stdint.h>
  26. #include <unistd.h>
  27. #include <util/agxbuf.h>
  28. #include <util/alloc.h>
  29. #include <util/startswith.h>
  30. #include <util/strcasecmp.h>
  31. #include <util/streq.h>
  32. int late_int(void *obj, attrsym_t *attr, int defaultValue, int minimum) {
  33. if (attr == NULL)
  34. return defaultValue;
  35. char *p = ag_xget(obj, attr);
  36. if (!p || p[0] == '\0')
  37. return defaultValue;
  38. char *endp;
  39. long rv = strtol(p, &endp, 10);
  40. if (p == endp || rv > INT_MAX)
  41. return defaultValue; /* invalid int format */
  42. if (rv < minimum)
  43. return minimum;
  44. return (int)rv;
  45. }
  46. double late_double(void *obj, attrsym_t *attr, double defaultValue,
  47. double minimum) {
  48. if (!attr || !obj)
  49. return defaultValue;
  50. char *p = ag_xget(obj, attr);
  51. if (!p || p[0] == '\0')
  52. return defaultValue;
  53. char *endp;
  54. double rv = strtod(p, &endp);
  55. if (p == endp)
  56. return defaultValue; /* invalid double format */
  57. if (rv < minimum)
  58. return minimum;
  59. return rv;
  60. }
  61. /** Return value for PSinputscale. If this is > 0, it has been set on the
  62. * command line and this value is used.
  63. * Otherwise, we check the graph's inputscale attribute. If this is not set
  64. * or has a bad value, we return -1.
  65. * If the value is 0, we return the default. Otherwise, we return the value.
  66. * Set but negative values are treated like 0.
  67. */
  68. double get_inputscale(graph_t *g) {
  69. if (PSinputscale > 0) return PSinputscale; /* command line flag prevails */
  70. double d = late_double(g, agfindgraphattr(g, "inputscale"), -1, 0);
  71. if (is_exactly_zero(d)) return POINTS_PER_INCH;
  72. return d;
  73. }
  74. char *late_string(void *obj, attrsym_t *attr, char *defaultValue) {
  75. if (!attr || !obj)
  76. return defaultValue;
  77. return agxget(obj, attr);
  78. }
  79. char *late_nnstring(void *obj, attrsym_t *attr, char *defaultValue) {
  80. char *rv = late_string(obj, attr, defaultValue);
  81. if (!rv || (rv[0] == '\0'))
  82. return defaultValue;
  83. return rv;
  84. }
  85. bool late_bool(void *obj, attrsym_t *attr, bool defaultValue) {
  86. if (attr == NULL)
  87. return defaultValue;
  88. return mapbool(agxget(obj, attr));
  89. }
  90. node_t *UF_find(node_t * n)
  91. {
  92. while (ND_UF_parent(n) && ND_UF_parent(n) != n) {
  93. if (ND_UF_parent(ND_UF_parent(n)))
  94. ND_UF_parent(n) = ND_UF_parent(ND_UF_parent(n));
  95. n = ND_UF_parent(n);
  96. }
  97. return n;
  98. }
  99. node_t *UF_union(node_t * u, node_t * v)
  100. {
  101. if (u == v)
  102. return u;
  103. if (ND_UF_parent(u) == NULL) {
  104. ND_UF_parent(u) = u;
  105. ND_UF_size(u) = 1;
  106. } else
  107. u = UF_find(u);
  108. if (ND_UF_parent(v) == NULL) {
  109. ND_UF_parent(v) = v;
  110. ND_UF_size(v) = 1;
  111. } else
  112. v = UF_find(v);
  113. /* if we have two copies of the same node, their union is just that node */
  114. if (u == v)
  115. return u;
  116. if (ND_id(u) > ND_id(v)) {
  117. ND_UF_parent(u) = v;
  118. ND_UF_size(v) += ND_UF_size(u);
  119. } else {
  120. ND_UF_parent(v) = u;
  121. ND_UF_size(u) += ND_UF_size(v);
  122. v = u;
  123. }
  124. return v;
  125. }
  126. void UF_singleton(node_t * u)
  127. {
  128. ND_UF_size(u) = 1;
  129. ND_UF_parent(u) = NULL;
  130. ND_ranktype(u) = NORMAL;
  131. }
  132. void UF_setname(node_t * u, node_t * v)
  133. {
  134. assert(u == UF_find(u));
  135. ND_UF_parent(u) = v;
  136. ND_UF_size(v) += ND_UF_size(u);
  137. }
  138. pointf coord(node_t * n)
  139. {
  140. pointf r;
  141. r.x = POINTS_PER_INCH * ND_pos(n)[0];
  142. r.y = POINTS_PER_INCH * ND_pos(n)[1];
  143. return r;
  144. }
  145. /* from Glassner's Graphics Gems */
  146. #define W_DEGREE 5
  147. /*
  148. * Evaluate a Bezier curve at a particular parameter value
  149. * Fill in control points for resulting sub-curves if "Left" and
  150. * "Right" are non-null.
  151. *
  152. */
  153. pointf Bezier(pointf *V, double t, pointf *Left, pointf *Right) {
  154. const int degree = 3;
  155. int i, j; /* Index variables */
  156. pointf Vtemp[W_DEGREE + 1][W_DEGREE + 1];
  157. /* Copy control points */
  158. for (j = 0; j <= degree; j++) {
  159. Vtemp[0][j] = V[j];
  160. }
  161. /* Triangle computation */
  162. for (i = 1; i <= degree; i++) {
  163. for (j = 0; j <= degree - i; j++) {
  164. Vtemp[i][j].x =
  165. (1.0 - t) * Vtemp[i - 1][j].x + t * Vtemp[i - 1][j + 1].x;
  166. Vtemp[i][j].y =
  167. (1.0 - t) * Vtemp[i - 1][j].y + t * Vtemp[i - 1][j + 1].y;
  168. }
  169. }
  170. if (Left != NULL)
  171. for (j = 0; j <= degree; j++)
  172. Left[j] = Vtemp[j][0];
  173. if (Right != NULL)
  174. for (j = 0; j <= degree; j++)
  175. Right[j] = Vtemp[degree - j][j];
  176. return Vtemp[degree][0];
  177. }
  178. #ifdef DEBUG
  179. edge_t *debug_getedge(graph_t * g, char *s0, char *s1)
  180. {
  181. node_t *n0, *n1;
  182. n0 = agfindnode(g, s0);
  183. n1 = agfindnode(g, s1);
  184. if (n0 && n1)
  185. return agfindedge(g, n0, n1);
  186. return NULL;
  187. }
  188. Agraphinfo_t* GD_info(graph_t * g) { return ((Agraphinfo_t*)AGDATA(g));}
  189. Agnodeinfo_t* ND_info(node_t * n) { return ((Agnodeinfo_t*)AGDATA(n));}
  190. #endif
  191. /* safefile:
  192. * Check to make sure it is okay to read in files.
  193. * It returns NULL if the filename is trivial.
  194. *
  195. * If the application has set the SERVER_NAME environment variable,
  196. * this indicates it is web-active.
  197. *
  198. * If filename contains multiple components, the user is
  199. * warned, once, that everything to the left is ignored.
  200. *
  201. * For non-server applications, we use the path list in Gvimagepath to
  202. * resolve relative pathnames.
  203. *
  204. * N.B. safefile uses a fixed buffer, so functions using it should use the
  205. * value immediately or make a copy.
  206. */
  207. #ifdef _WIN32
  208. #define PATHSEP ";"
  209. #else
  210. #define PATHSEP ":"
  211. #endif
  212. static strview_t *mkDirlist(const char *list) {
  213. size_t cnt = 0;
  214. strview_t *dirs = gv_calloc(1, sizeof(strview_t));
  215. for (tok_t t = tok(list, PATHSEP); !tok_end(&t); tok_next(&t)) {
  216. strview_t dir = tok_get(&t);
  217. dirs = gv_recalloc(dirs, cnt + 1, cnt + 2, sizeof(strview_t));
  218. dirs[cnt++] = dir;
  219. }
  220. return dirs;
  221. }
  222. static char *findPath(const strview_t *dirs, const char *str) {
  223. static agxbuf safefilename;
  224. for (const strview_t *dp = dirs; dp != NULL && dp->data != NULL; dp++) {
  225. agxbprint(&safefilename, "%.*s%s%s", (int)dp->size, dp->data, DIRSEP, str);
  226. char *filename = agxbuse(&safefilename);
  227. if (access(filename, R_OK) == 0)
  228. return filename;
  229. }
  230. return NULL;
  231. }
  232. const char *safefile(const char *filename)
  233. {
  234. static bool onetime = true;
  235. static char *pathlist = NULL;
  236. static strview_t *dirs;
  237. if (!filename || !filename[0])
  238. return NULL;
  239. if (HTTPServerEnVar) { /* If used as a server */
  240. if (onetime) {
  241. agwarningf(
  242. "file loading is disabled because the environment contains SERVER_NAME=\"%s\"\n",
  243. HTTPServerEnVar);
  244. onetime = false;
  245. }
  246. return NULL;
  247. }
  248. if (Gvfilepath != NULL) {
  249. if (pathlist == NULL) {
  250. free(dirs);
  251. pathlist = Gvfilepath;
  252. dirs = mkDirlist(pathlist);
  253. }
  254. const char *str = filename;
  255. for (const char *sep = "/\\:"; *sep != '\0'; ++sep) {
  256. const char *p = strrchr(str, *sep);
  257. if (p != NULL) {
  258. str = ++p;
  259. }
  260. }
  261. return findPath(dirs, str);
  262. }
  263. if (pathlist != Gvimagepath) {
  264. free (dirs);
  265. dirs = NULL;
  266. pathlist = Gvimagepath;
  267. if (pathlist && *pathlist)
  268. dirs = mkDirlist(pathlist);
  269. }
  270. if (*filename == DIRSEP[0] || !dirs)
  271. return filename;
  272. return findPath(dirs, filename);
  273. }
  274. int maptoken(char *p, char **name, int *val) {
  275. char *q;
  276. int i = 0;
  277. for (; (q = name[i]) != 0; i++)
  278. if (p && streq(p, q))
  279. break;
  280. return val[i];
  281. }
  282. bool mapBool(const char *p, bool defaultValue) {
  283. if (!p || *p == '\0')
  284. return defaultValue;
  285. if (!strcasecmp(p, "false"))
  286. return false;
  287. if (!strcasecmp(p, "no"))
  288. return false;
  289. if (!strcasecmp(p, "true"))
  290. return true;
  291. if (!strcasecmp(p, "yes"))
  292. return true;
  293. if (gv_isdigit(*p))
  294. return atoi(p) != 0;
  295. return defaultValue;
  296. }
  297. bool mapbool(const char *p)
  298. {
  299. return mapBool(p, false);
  300. }
  301. pointf dotneato_closest(splines * spl, pointf pt)
  302. {
  303. double bestdist2, d2, dlow2, dhigh2; /* squares of distances */
  304. double low, high, t;
  305. pointf c[4], pt2;
  306. bezier bz;
  307. size_t besti = SIZE_MAX;
  308. size_t bestj = SIZE_MAX;
  309. bestdist2 = 1e+38;
  310. for (size_t i = 0; i < spl->size; i++) {
  311. bz = spl->list[i];
  312. for (size_t j = 0; j < bz.size; j++) {
  313. pointf b;
  314. b.x = bz.list[j].x;
  315. b.y = bz.list[j].y;
  316. d2 = DIST2(b, pt);
  317. if (bestj == SIZE_MAX || d2 < bestdist2) {
  318. besti = i;
  319. bestj = j;
  320. bestdist2 = d2;
  321. }
  322. }
  323. }
  324. bz = spl->list[besti];
  325. /* Pick best Bezier. If bestj is the last point in the B-spline, decrement.
  326. * Then set j to be the first point in the corresponding Bezier by dividing
  327. * then multiplying be 3. Thus, 0,1,2 => 0; 3,4,5 => 3, etc.
  328. */
  329. if (bestj == bz.size-1)
  330. bestj--;
  331. const size_t j = 3 * (bestj / 3);
  332. for (size_t k = 0; k < 4; k++) {
  333. c[k].x = bz.list[j + k].x;
  334. c[k].y = bz.list[j + k].y;
  335. }
  336. low = 0.0;
  337. high = 1.0;
  338. dlow2 = DIST2(c[0], pt);
  339. dhigh2 = DIST2(c[3], pt);
  340. do {
  341. t = (low + high) / 2.0;
  342. pt2 = Bezier(c, t, NULL, NULL);
  343. if (fabs(dlow2 - dhigh2) < 1.0)
  344. break;
  345. if (fabs(high - low) < .00001)
  346. break;
  347. if (dlow2 < dhigh2) {
  348. high = t;
  349. dhigh2 = DIST2(pt2, pt);
  350. } else {
  351. low = t;
  352. dlow2 = DIST2(pt2, pt);
  353. }
  354. } while (1);
  355. return pt2;
  356. }
  357. static int Tflag;
  358. void gvToggle(int s)
  359. {
  360. (void)s;
  361. Tflag = !Tflag;
  362. #if !defined(_WIN32)
  363. signal(SIGUSR1, gvToggle);
  364. #endif
  365. }
  366. int test_toggle(void)
  367. {
  368. return Tflag;
  369. }
  370. struct fontinfo {
  371. double fontsize;
  372. char *fontname;
  373. char *fontcolor;
  374. };
  375. void common_init_node(node_t * n)
  376. {
  377. struct fontinfo fi;
  378. char *str;
  379. ND_width(n) =
  380. late_double(n, N_width, DEFAULT_NODEWIDTH, MIN_NODEWIDTH);
  381. ND_height(n) =
  382. late_double(n, N_height, DEFAULT_NODEHEIGHT, MIN_NODEHEIGHT);
  383. ND_shape(n) =
  384. bind_shape(late_nnstring(n, N_shape, DEFAULT_NODESHAPE), n);
  385. str = agxget(n, N_label);
  386. fi.fontsize = late_double(n, N_fontsize, DEFAULT_FONTSIZE, MIN_FONTSIZE);
  387. fi.fontname = late_nnstring(n, N_fontname, DEFAULT_FONTNAME);
  388. fi.fontcolor = late_nnstring(n, N_fontcolor, DEFAULT_COLOR);
  389. ND_label(n) = make_label(n, str,
  390. (aghtmlstr(str) ? LT_HTML : LT_NONE) | ( (shapeOf(n) == SH_RECORD) ? LT_RECD : LT_NONE),
  391. fi.fontsize, fi.fontname, fi.fontcolor);
  392. if (N_xlabel && (str = agxget(n, N_xlabel)) && str[0]) {
  393. ND_xlabel(n) = make_label(n, str, aghtmlstr(str) ? LT_HTML : LT_NONE,
  394. fi.fontsize, fi.fontname, fi.fontcolor);
  395. GD_has_labels(agraphof(n)) |= NODE_XLABEL;
  396. }
  397. {
  398. const int showboxes = imin(late_int(n, N_showboxes, 0, 0), UCHAR_MAX);
  399. ND_showboxes(n) = (unsigned char)showboxes;
  400. }
  401. ND_shape(n)->fns->initfn(n);
  402. }
  403. static void initFontEdgeAttr(edge_t * e, struct fontinfo *fi)
  404. {
  405. fi->fontsize = late_double(e, E_fontsize, DEFAULT_FONTSIZE, MIN_FONTSIZE);
  406. fi->fontname = late_nnstring(e, E_fontname, DEFAULT_FONTNAME);
  407. fi->fontcolor = late_nnstring(e, E_fontcolor, DEFAULT_COLOR);
  408. }
  409. static void
  410. initFontLabelEdgeAttr(edge_t * e, struct fontinfo *fi,
  411. struct fontinfo *lfi)
  412. {
  413. if (!fi->fontname) initFontEdgeAttr(e, fi);
  414. lfi->fontsize = late_double(e, E_labelfontsize, fi->fontsize, MIN_FONTSIZE);
  415. lfi->fontname = late_nnstring(e, E_labelfontname, fi->fontname);
  416. lfi->fontcolor = late_nnstring(e, E_labelfontcolor, fi->fontcolor);
  417. }
  418. /// Return true if head/tail end of edge should not be clipped to node.
  419. static bool
  420. noClip(edge_t *e, attrsym_t* sym)
  421. {
  422. char *str;
  423. bool rv = false;
  424. if (sym) { /* mapbool isn't a good fit, because we want "" to mean true */
  425. str = agxget(e,sym);
  426. if (str && str[0]) rv = !mapbool(str);
  427. else rv = false;
  428. }
  429. return rv;
  430. }
  431. static port
  432. chkPort (port (*pf)(node_t*, char*, char*), node_t* n, char* s)
  433. {
  434. port pt;
  435. char* cp=NULL;
  436. if(s)
  437. cp= strchr(s,':');
  438. if (cp) {
  439. *cp = '\0';
  440. pt = pf(n, s, cp+1);
  441. *cp = ':';
  442. pt.name = cp+1;
  443. }
  444. else {
  445. pt = pf(n, s, NULL);
  446. pt.name = s;
  447. }
  448. return pt;
  449. }
  450. /* return true if edge has label */
  451. void common_init_edge(edge_t *e) {
  452. char *str;
  453. struct fontinfo fi;
  454. struct fontinfo lfi;
  455. graph_t *sg = agraphof(agtail(e));
  456. fi.fontname = NULL;
  457. lfi.fontname = NULL;
  458. if (E_label && (str = agxget(e, E_label)) && str[0]) {
  459. initFontEdgeAttr(e, &fi);
  460. ED_label(e) = make_label(e, str, aghtmlstr(str) ? LT_HTML : LT_NONE,
  461. fi.fontsize, fi.fontname, fi.fontcolor);
  462. GD_has_labels(sg) |= EDGE_LABEL;
  463. ED_label_ontop(e) = mapbool(late_string(e, E_label_float, "false"));
  464. }
  465. if (E_xlabel && (str = agxget(e, E_xlabel)) && str[0]) {
  466. if (!fi.fontname)
  467. initFontEdgeAttr(e, &fi);
  468. ED_xlabel(e) = make_label(e, str, aghtmlstr(str) ? LT_HTML : LT_NONE,
  469. fi.fontsize, fi.fontname, fi.fontcolor);
  470. GD_has_labels(sg) |= EDGE_XLABEL;
  471. }
  472. if (E_headlabel && (str = agxget(e, E_headlabel)) && str[0]) {
  473. initFontLabelEdgeAttr(e, &fi, &lfi);
  474. ED_head_label(e) = make_label(e, str, aghtmlstr(str) ? LT_HTML : LT_NONE,
  475. lfi.fontsize, lfi.fontname, lfi.fontcolor);
  476. GD_has_labels(sg) |= HEAD_LABEL;
  477. }
  478. if (E_taillabel && (str = agxget(e, E_taillabel)) && str[0]) {
  479. if (!lfi.fontname)
  480. initFontLabelEdgeAttr(e, &fi, &lfi);
  481. ED_tail_label(e) = make_label(e, str, aghtmlstr(str) ? LT_HTML : LT_NONE,
  482. lfi.fontsize, lfi.fontname, lfi.fontcolor);
  483. GD_has_labels(sg) |= TAIL_LABEL;
  484. }
  485. /* We still accept ports beginning with colons but this is deprecated
  486. * That is, we allow tailport = ":abc" as well as the preferred
  487. * tailport = "abc".
  488. */
  489. str = agget(e, TAIL_ID);
  490. /* libgraph always defines tailport/headport; libcgraph doesn't */
  491. if (!str) str = "";
  492. if (str && str[0])
  493. ND_has_port(agtail(e)) = true;
  494. ED_tail_port(e) = chkPort (ND_shape(agtail(e))->fns->portfn, agtail(e), str);
  495. if (noClip(e, E_tailclip))
  496. ED_tail_port(e).clip = false;
  497. str = agget(e, HEAD_ID);
  498. /* libgraph always defines tailport/headport; libcgraph doesn't */
  499. if (!str) str = "";
  500. if (str && str[0])
  501. ND_has_port(aghead(e)) = true;
  502. ED_head_port(e) = chkPort(ND_shape(aghead(e))->fns->portfn, aghead(e), str);
  503. if (noClip(e, E_headclip))
  504. ED_head_port(e).clip = false;
  505. }
  506. static boxf addLabelBB(boxf bb, textlabel_t * lp, bool flipxy)
  507. {
  508. double width, height;
  509. pointf p = lp->pos;
  510. double min, max;
  511. if (flipxy) {
  512. height = lp->dimen.x;
  513. width = lp->dimen.y;
  514. }
  515. else {
  516. width = lp->dimen.x;
  517. height = lp->dimen.y;
  518. }
  519. min = p.x - width / 2.;
  520. max = p.x + width / 2.;
  521. if (min < bb.LL.x)
  522. bb.LL.x = min;
  523. if (max > bb.UR.x)
  524. bb.UR.x = max;
  525. min = p.y - height / 2.;
  526. max = p.y + height / 2.;
  527. if (min < bb.LL.y)
  528. bb.LL.y = min;
  529. if (max > bb.UR.y)
  530. bb.UR.y = max;
  531. return bb;
  532. }
  533. /** Compute the bounding box of a polygon.
  534. * We only need to use the outer periphery.
  535. */
  536. boxf
  537. polyBB (polygon_t* poly)
  538. {
  539. const size_t sides = poly->sides;
  540. const size_t peris = MAX(poly->peripheries, 1ul);
  541. pointf* verts = poly->vertices + (peris-1)*sides;
  542. boxf bb;
  543. bb.LL = bb.UR = verts[0];
  544. for (size_t i = 1; i < sides; i++) {
  545. bb.LL.x = MIN(bb.LL.x,verts[i].x);
  546. bb.LL.y = MIN(bb.LL.y,verts[i].y);
  547. bb.UR.x = MAX(bb.UR.x,verts[i].x);
  548. bb.UR.y = MAX(bb.UR.y,verts[i].y);
  549. }
  550. return bb;
  551. }
  552. /** Reset graph's bounding box to include bounding box of the given label.
  553. * Assume the label's position has been set.
  554. */
  555. void updateBB(graph_t * g, textlabel_t * lp)
  556. {
  557. GD_bb(g) = addLabelBB(GD_bb(g), lp, GD_flip(g));
  558. }
  559. /** Compute bounding box of g using nodes, splines, and clusters.
  560. * Assumes bb of clusters already computed.
  561. * store in GD_bb.
  562. */
  563. void compute_bb(graph_t * g)
  564. {
  565. node_t *n;
  566. edge_t *e;
  567. boxf b, bb;
  568. boxf BF;
  569. pointf ptf, s2;
  570. if (agnnodes(g) == 0 && GD_n_cluster(g) == 0) {
  571. bb.LL = (pointf){0};
  572. bb.UR = (pointf){0};
  573. return;
  574. }
  575. bb.LL = (pointf){INT_MAX, INT_MAX};
  576. bb.UR = (pointf){-INT_MAX, -INT_MAX};
  577. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  578. ptf = coord(n);
  579. s2.x = ND_xsize(n) / 2.0;
  580. s2.y = ND_ysize(n) / 2.0;
  581. b.LL = sub_pointf(ptf, s2);
  582. b.UR = add_pointf(ptf, s2);
  583. EXPANDBB(bb,b);
  584. if (ND_xlabel(n) && ND_xlabel(n)->set) {
  585. bb = addLabelBB(bb, ND_xlabel(n), GD_flip(g));
  586. }
  587. for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
  588. if (ED_spl(e) == 0)
  589. continue;
  590. for (size_t i = 0; i < ED_spl(e)->size; i++) {
  591. for (size_t j = 0; j < (((Agedgeinfo_t*)AGDATA(e))->spl)->list[i].size; j++) {
  592. ptf = ED_spl(e)->list[i].list[j];
  593. EXPANDBP(bb,ptf);
  594. }
  595. }
  596. if (ED_label(e) && ED_label(e)->set) {
  597. bb = addLabelBB(bb, ED_label(e), GD_flip(g));
  598. }
  599. if (ED_head_label(e) && ED_head_label(e)->set) {
  600. bb = addLabelBB(bb, ED_head_label(e), GD_flip(g));
  601. }
  602. if (ED_tail_label(e) && ED_tail_label(e)->set) {
  603. bb = addLabelBB(bb, ED_tail_label(e), GD_flip(g));
  604. }
  605. if (ED_xlabel(e) && ED_xlabel(e)->set) {
  606. bb = addLabelBB(bb, ED_xlabel(e), GD_flip(g));
  607. }
  608. }
  609. }
  610. for (int i = 1; i <= GD_n_cluster(g); i++) {
  611. B2BF(GD_bb(GD_clust(g)[i]), BF);
  612. EXPANDBB(bb,BF);
  613. }
  614. if (GD_label(g) && GD_label(g)->set) {
  615. bb = addLabelBB(bb, GD_label(g), GD_flip(g));
  616. }
  617. GD_bb(g) = bb;
  618. }
  619. bool is_a_cluster (Agraph_t* g)
  620. {
  621. return g == g->root || !strncasecmp(agnameof(g), "cluster", 7) ||
  622. mapbool(agget(g, "cluster"));
  623. }
  624. /** Sets object's name attribute to the given value.
  625. * Creates the attribute if not already set.
  626. */
  627. Agsym_t *setAttr(graph_t * g, void *obj, char *name, char *value,
  628. Agsym_t * ap)
  629. {
  630. if (ap == NULL) {
  631. switch (agobjkind(obj)) {
  632. case AGRAPH:
  633. ap = agattr(g, AGRAPH,name, "");
  634. break;
  635. case AGNODE:
  636. ap = agattr(g,AGNODE, name, "");
  637. break;
  638. case AGEDGE:
  639. ap = agattr(g,AGEDGE, name, "");
  640. break;
  641. }
  642. }
  643. agxset(obj, ap, value);
  644. return ap;
  645. }
  646. /** Generate a special cluster node representing the end node
  647. * of an edge to the cluster cg. n is a node whose name is the same
  648. * as the cluster cg. clg is the subgraph of all of
  649. * the original nodes, which will be deleted later.
  650. */
  651. static node_t *clustNode(node_t * n, graph_t * cg, agxbuf * xb,
  652. graph_t * clg)
  653. {
  654. node_t *cn;
  655. static int idx = 0;
  656. agxbprint(xb, "__%d:%s", idx++, agnameof(cg));
  657. cn = agnode(agroot(cg), agxbuse(xb), 1);
  658. agbindrec(cn, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true);
  659. SET_CLUST_NODE(cn);
  660. agsubnode(cg,cn,1);
  661. agsubnode(clg,n,1);
  662. /* set attributes */
  663. N_label = setAttr(agraphof(cn), cn, "label", "", N_label);
  664. N_style = setAttr(agraphof(cn), cn, "style", "invis", N_style);
  665. N_shape = setAttr(agraphof(cn), cn, "shape", "box", N_shape);
  666. return cn;
  667. }
  668. typedef struct {
  669. Dtlink_t link; /* cdt data */
  670. void *p[2]; /* key */
  671. node_t *t;
  672. node_t *h;
  673. } item;
  674. static int cmpItem(void *pp1, void *pp2) {
  675. const void **p1 = pp1;
  676. const void **p2 = pp2;
  677. if (p1[0] < p2[0])
  678. return -1;
  679. if (p1[0] > p2[0])
  680. return 1;
  681. if (p1[1] < p2[1])
  682. return -1;
  683. if (p1[1] > p2[1])
  684. return 1;
  685. return 0;
  686. }
  687. static void *newItem(void *p, Dtdisc_t *disc) {
  688. item *objp = p;
  689. item *newp = gv_alloc(sizeof(item));
  690. (void)disc;
  691. newp->p[0] = objp->p[0];
  692. newp->p[1] = objp->p[1];
  693. newp->t = objp->t;
  694. newp->h = objp->h;
  695. return newp;
  696. }
  697. static Dtdisc_t mapDisc = {
  698. .key = offsetof(item, p),
  699. .size = sizeof(2 * sizeof(void *)),
  700. .link = offsetof(item, link),
  701. .makef = newItem,
  702. .freef = free,
  703. .comparf = cmpItem,
  704. };
  705. /// Make a copy of e in e's graph but using ct and ch as nodes
  706. static edge_t *cloneEdge(edge_t * e, node_t * ct, node_t * ch)
  707. {
  708. graph_t *g = agraphof(ct);
  709. edge_t *ce = agedge(g, ct, ch,NULL,1);
  710. agbindrec(ce, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true);
  711. agcopyattr(e, ce);
  712. ED_compound(ce) = true;
  713. return ce;
  714. }
  715. static void insertEdge(Dt_t * map, void *t, void *h, edge_t * e)
  716. {
  717. item dummy;
  718. dummy.p[0] = t;
  719. dummy.p[1] = h;
  720. dummy.t = agtail(e);
  721. dummy.h = aghead(e);
  722. dtinsert(map, &dummy);
  723. dummy.p[0] = h;
  724. dummy.p[1] = t;
  725. dummy.t = aghead(e);
  726. dummy.h = agtail(e);
  727. dtinsert(map, &dummy);
  728. }
  729. /// Check if we already have cluster edge corresponding to t->h, and return it.
  730. static item *mapEdge(Dt_t * map, edge_t * e)
  731. {
  732. void *key[2];
  733. key[0] = agtail(e);
  734. key[1] = aghead(e);
  735. return dtmatch(map, &key);
  736. }
  737. static graph_t *mapc(Dt_t *cmap, node_t *n) {
  738. if (startswith(agnameof(n), "cluster")) {
  739. return findCluster(cmap, agnameof(n));
  740. }
  741. return NULL;
  742. }
  743. /** If endpoint names a cluster, mark for temporary deletion and create
  744. * special node and insert into cluster. Then clone the edge. Real edge
  745. * will be deleted when we delete the original node.
  746. * Invariant: new edge has same sense as old. That is, given t->h with
  747. * t and h mapped to ct and ch, the new edge is ct->ch.
  748. *
  749. * In the current model, we create a cluster node for each cluster edge
  750. * between the cluster and some other node or cluster, treating the
  751. * cluster node as a port on the cluster. This should help with better
  752. * routing to avoid edge crossings. At present, this is not implemented,
  753. * so we could use a simpler model in which we create a single cluster
  754. * node for each cluster used in a cluster edge.
  755. *
  756. * Return 1 if cluster edge is created.
  757. */
  758. static int
  759. checkCompound(edge_t * e, graph_t * clg, agxbuf * xb, Dt_t * map, Dt_t* cmap)
  760. {
  761. node_t *cn;
  762. node_t *cn1;
  763. node_t *t = agtail(e);
  764. node_t *h = aghead(e);
  765. edge_t *ce;
  766. item *ip;
  767. if (IS_CLUST_NODE(h)) return 0;
  768. graph_t *const tg = mapc(cmap, t);
  769. graph_t *const hg = mapc(cmap, h);
  770. if (!tg && !hg)
  771. return 0;
  772. if (tg == hg) {
  773. agwarningf("cluster cycle %s -- %s not supported\n", agnameof(t),
  774. agnameof(t));
  775. return 0;
  776. }
  777. ip = mapEdge(map, e);
  778. if (ip) {
  779. cloneEdge(e, ip->t, ip->h);
  780. return 1;
  781. }
  782. if (hg) {
  783. if (tg) {
  784. if (agcontains(hg, tg)) {
  785. agwarningf("tail cluster %s inside head cluster %s\n",
  786. agnameof(tg), agnameof(hg));
  787. return 0;
  788. }
  789. if (agcontains(tg, hg)) {
  790. agwarningf("head cluster %s inside tail cluster %s\n",
  791. agnameof(hg),agnameof(tg));
  792. return 0;
  793. }
  794. cn = clustNode(t, tg, xb, clg);
  795. cn1 = clustNode(h, hg, xb, clg);
  796. ce = cloneEdge(e, cn, cn1);
  797. insertEdge(map, t, h, ce);
  798. } else {
  799. if (agcontains(hg, t)) {
  800. agwarningf("tail node %s inside head cluster %s\n",
  801. agnameof(t), agnameof(hg));
  802. return 0;
  803. }
  804. cn = clustNode(h, hg, xb, clg);
  805. ce = cloneEdge(e, t, cn);
  806. insertEdge(map, t, h, ce);
  807. }
  808. } else {
  809. if (agcontains(tg, h)) {
  810. agwarningf("head node %s inside tail cluster %s\n", agnameof(h),
  811. agnameof(tg));
  812. return 0;
  813. }
  814. cn = clustNode(t, tg, xb, clg);
  815. ce = cloneEdge(e, cn, h);
  816. insertEdge(map, t, h, ce);
  817. }
  818. return 1;
  819. }
  820. typedef struct {
  821. Agrec_t hdr;
  822. int n_cluster_edges;
  823. } cl_edge_t;
  824. static int
  825. num_clust_edges(graph_t * g)
  826. {
  827. cl_edge_t* cl_info = (cl_edge_t*)HAS_CLUST_EDGE(g);
  828. if (cl_info)
  829. return cl_info->n_cluster_edges;
  830. return 0;
  831. }
  832. /** Look for cluster edges. Replace cluster edge endpoints
  833. * corresponding to a cluster with special cluster nodes.
  834. * Delete original nodes.
  835. * If cluster edges are found, a cl_edge_t record will be
  836. * attached to the graph, containing the count of such edges.
  837. */
  838. void processClusterEdges(graph_t * g)
  839. {
  840. int num_cl_edges = 0;
  841. node_t *n;
  842. node_t *nxt;
  843. edge_t *e;
  844. graph_t *clg;
  845. agxbuf xb = {0};
  846. Dt_t *map;
  847. Dt_t *cmap = mkClustMap (g);
  848. map = dtopen(&mapDisc, Dtoset);
  849. clg = agsubg(g, "__clusternodes",1);
  850. agbindrec(clg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
  851. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  852. if (IS_CLUST_NODE(n)) continue;
  853. for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
  854. num_cl_edges += checkCompound(e, clg, &xb, map, cmap);
  855. }
  856. }
  857. agxbfree(&xb);
  858. dtclose(map);
  859. for (n = agfstnode(clg); n; n = nxt) {
  860. nxt = agnxtnode(clg, n);
  861. agdelete(g, n);
  862. }
  863. agclose(clg);
  864. if (num_cl_edges) {
  865. cl_edge_t* cl_info;
  866. cl_info = agbindrec(g, CL_EDGE_TAG, sizeof(cl_edge_t), false);
  867. cl_info->n_cluster_edges = num_cl_edges;
  868. }
  869. dtclose(cmap);
  870. }
  871. /** Convert cluster nodes back to ordinary nodes
  872. * If n is already ordinary, return it.
  873. * Otherwise, we know node's name is "__i:xxx"
  874. * where i is some number and xxx is the nodes's original name.
  875. * Create new node of name xxx if it doesn't exist and add n to clg
  876. * for later deletion.
  877. */
  878. static node_t *mapN(node_t * n, graph_t * clg)
  879. {
  880. node_t *nn;
  881. char *name;
  882. graph_t *g = agraphof(n);
  883. Agsym_t *sym;
  884. if (!IS_CLUST_NODE(n))
  885. return n;
  886. agsubnode(clg, n, 1);
  887. name = strchr(agnameof(n), ':');
  888. assert(name);
  889. name++;
  890. if ((nn = agfindnode(g, name)))
  891. return nn;
  892. nn = agnode(g, name, 1);
  893. agbindrec(nn, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true);
  894. SET_CLUST_NODE(nn);
  895. /* Set all attributes to default */
  896. for (sym = agnxtattr(g, AGNODE, NULL); sym; (sym = agnxtattr(g, AGNODE, sym))) {
  897. if (agxget(nn, sym) != sym->defval)
  898. agxset(nn, sym, sym->defval);
  899. }
  900. return nn;
  901. }
  902. static void undoCompound(edge_t * e, graph_t * clg)
  903. {
  904. node_t *t = agtail(e);
  905. node_t *h = aghead(e);
  906. node_t *ntail;
  907. node_t *nhead;
  908. edge_t* ce;
  909. ntail = mapN(t, clg);
  910. nhead = mapN(h, clg);
  911. ce = cloneEdge(e, ntail, nhead);
  912. /* transfer drawing information */
  913. ED_spl(ce) = ED_spl(e);
  914. ED_spl(e) = NULL;
  915. ED_label(ce) = ED_label(e);
  916. ED_label(e) = NULL;
  917. ED_xlabel(ce) = ED_xlabel(e);
  918. ED_xlabel(e) = NULL;
  919. ED_head_label(ce) = ED_head_label(e);
  920. ED_head_label(e) = NULL;
  921. ED_tail_label(ce) = ED_tail_label(e);
  922. ED_tail_label(e) = NULL;
  923. gv_cleanup_edge(e);
  924. }
  925. /** Replace cluster nodes with originals. Make sure original has
  926. * no attributes. Replace original edges. Delete cluster nodes,
  927. * which will also delete cluster edges.
  928. */
  929. void undoClusterEdges(graph_t * g)
  930. {
  931. node_t *n;
  932. node_t *nextn;
  933. edge_t *e;
  934. graph_t *clg;
  935. int ecnt = num_clust_edges(g);
  936. int i = 0;
  937. if (!ecnt) return;
  938. clg = agsubg(g, "__clusternodes",1);
  939. agbindrec(clg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
  940. edge_t **edgelist = gv_calloc(ecnt, sizeof(edge_t*));
  941. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  942. for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
  943. if (ED_compound(e))
  944. edgelist[i++] = e;
  945. }
  946. }
  947. assert(i == ecnt);
  948. for (i = 0; i < ecnt; i++)
  949. undoCompound(edgelist[i], clg);
  950. free (edgelist);
  951. for (n = agfstnode(clg); n; n = nextn) {
  952. nextn = agnxtnode(clg, n);
  953. gv_cleanup_node(n);
  954. agdelete(g, n);
  955. }
  956. agclose(clg);
  957. }
  958. /** Find the attribute belonging to graph g for objects like obj
  959. * with given name. If one does not exist, create it with the
  960. * default value defaultValue.
  961. */
  962. attrsym_t *safe_dcl(graph_t *g, int obj_kind, char *name, char *defaultValue) {
  963. attrsym_t *a = agattr(g,obj_kind,name, NULL);
  964. if (!a) /* attribute does not exist */
  965. a = agattr(g, obj_kind, name, defaultValue);
  966. return a;
  967. }
  968. static int comp_entities(const void *e1, const void *e2) {
  969. const strview_t *key = e1;
  970. const struct entities_s *candidate = e2;
  971. return strview_cmp(*key, strview(candidate->name, '\0'));
  972. }
  973. /** Scan non-numeric entity, convert to &#...; form and store in xbuf.
  974. * t points to first char after '&'. Return after final semicolon.
  975. * If unknown, we return t and let libexpat flag the error.
  976. * */
  977. char* scanEntity (char* t, agxbuf* xb)
  978. {
  979. const strview_t key = strview(t, ';');
  980. struct entities_s *res;
  981. agxbputc(xb, '&');
  982. if (key.data[key.size] == '\0') return t;
  983. if (key.size > ENTITY_NAME_LENGTH_MAX || key.size < 2) return t;
  984. res = bsearch(&key, entities, NR_OF_ENTITIES,
  985. sizeof(entities[0]), comp_entities);
  986. if (!res) return t;
  987. agxbprint(xb, "#%d;", res->value);
  988. return t + key.size + 1;
  989. }
  990. /** Check for an HTML entity for a special character.
  991. * Assume *s points to first byte after '&'.
  992. * If successful, return the corresponding value and update s to
  993. * point after the terminating ';'.
  994. * On failure, return 0 and leave s unchanged.
  995. */
  996. static int
  997. htmlEntity (char** s)
  998. {
  999. struct entities_s *res;
  1000. unsigned char* str = *(unsigned char**)s;
  1001. unsigned int byte;
  1002. int i, n = 0;
  1003. byte = *str;
  1004. if (byte == '#') {
  1005. byte = *(str + 1);
  1006. if (byte == 'x' || byte == 'X') {
  1007. for (i = 2; i < 8; i++) {
  1008. byte = *(str + i);
  1009. if (byte >= 'A' && byte <= 'F')
  1010. byte = byte - 'A' + 10;
  1011. else if (byte >= 'a' && byte <= 'f')
  1012. byte = byte - 'a' + 10;
  1013. else if (byte >= '0' && byte <= '9')
  1014. byte = byte - '0';
  1015. else
  1016. break;
  1017. n = n * 16 + (int)byte;
  1018. }
  1019. }
  1020. else {
  1021. for (i = 1; i < 8; i++) {
  1022. byte = *(str + i);
  1023. if (byte >= '0' && byte <= '9')
  1024. n = n * 10 + ((int)byte - '0');
  1025. else
  1026. break;
  1027. }
  1028. }
  1029. if (byte == ';') {
  1030. str += i+1;
  1031. }
  1032. else {
  1033. n = 0;
  1034. }
  1035. }
  1036. else {
  1037. strview_t key = {.data = (char *)str};
  1038. for (i = 0; i < ENTITY_NAME_LENGTH_MAX; i++) {
  1039. byte = *(str + i);
  1040. if (byte == '\0') break;
  1041. if (byte == ';') {
  1042. res = bsearch(&key, entities, NR_OF_ENTITIES,
  1043. sizeof(entities[0]), comp_entities);
  1044. if (res) {
  1045. n = res->value;
  1046. str += i+1;
  1047. }
  1048. break;
  1049. }
  1050. ++key.size;
  1051. }
  1052. }
  1053. *s = (char*)str;
  1054. return n;
  1055. }
  1056. static unsigned char
  1057. cvtAndAppend (unsigned char c, agxbuf* xb)
  1058. {
  1059. char buf[2];
  1060. buf[0] = c;
  1061. buf[1] = '\0';
  1062. char *s = latin1ToUTF8(buf);
  1063. char *p = s;
  1064. size_t len = strlen(s);
  1065. while (len-- > 1)
  1066. agxbputc(xb, *p++);
  1067. c = *p;
  1068. free (s);
  1069. return c;
  1070. }
  1071. /** substitute html entities like: &#123; and: &amp; with the UTF8 equivalents
  1072. * check for invalid utf8. If found, treat a single byte as Latin-1, convert it to
  1073. * utf8 and warn the user.
  1074. */
  1075. char* htmlEntityUTF8 (char* s, graph_t* g)
  1076. {
  1077. static graph_t* lastg;
  1078. static bool warned;
  1079. unsigned char c;
  1080. unsigned int v;
  1081. int uc;
  1082. int ui;
  1083. if (lastg != g) {
  1084. lastg = g;
  1085. warned = false;
  1086. }
  1087. agxbuf xb = {0};
  1088. while ((c = *(unsigned char*)s++)) {
  1089. if (c < 0xC0)
  1090. /*
  1091. * Handles properly formed UTF-8 characters between
  1092. * 0x01 and 0x7F. Also treats \0 and naked trail
  1093. * bytes 0x80 to 0xBF as valid characters representing
  1094. * themselves.
  1095. */
  1096. uc = 0;
  1097. else if (c < 0xE0)
  1098. uc = 1;
  1099. else if (c < 0xF0)
  1100. uc = 2;
  1101. else if (c < 0xF8)
  1102. uc = 3;
  1103. else {
  1104. uc = -1;
  1105. if (!warned) {
  1106. agwarningf("UTF8 codes > 4 bytes are not currently supported (graph %s) - treated as Latin-1. Perhaps \"-Gcharset=latin1\" is needed?\n", agnameof(g));
  1107. warned = true;
  1108. }
  1109. c = cvtAndAppend (c, &xb);
  1110. }
  1111. if (uc == 0 && c == '&') {
  1112. /* replace html entity sequences like: &amp;
  1113. * and: &#123; with their UTF8 equivalents */
  1114. v = htmlEntity (&s);
  1115. if (v) {
  1116. if (v < 0x7F) /* entity needs 1 byte in UTF8 */
  1117. c = v;
  1118. else if (v < 0x07FF) { /* entity needs 2 bytes in UTF8 */
  1119. agxbputc(&xb, (char)((v >> 6) | 0xC0));
  1120. c = (v & 0x3F) | 0x80;
  1121. }
  1122. else { /* entity needs 3 bytes in UTF8 */
  1123. agxbputc(&xb, (char)((v >> 12) | 0xE0));
  1124. agxbputc(&xb, (char)(((v >> 6) & 0x3F) | 0x80));
  1125. c = (v & 0x3F) | 0x80;
  1126. }
  1127. }
  1128. }
  1129. else /* copy n byte UTF8 characters */
  1130. for (ui = 0; ui < uc; ++ui)
  1131. if ((*s & 0xC0) == 0x80) {
  1132. agxbputc(&xb, (char)c);
  1133. c = *(unsigned char*)s++;
  1134. }
  1135. else {
  1136. if (!warned) {
  1137. agwarningf("Invalid %d-byte UTF8 found in input of graph %s - treated as Latin-1. Perhaps \"-Gcharset=latin1\" is needed?\n", uc + 1, agnameof(g));
  1138. warned = true;
  1139. }
  1140. c = cvtAndAppend (c, &xb);
  1141. break;
  1142. }
  1143. agxbputc(&xb, (char)c);
  1144. }
  1145. return agxbdisown(&xb);
  1146. }
  1147. /// Converts string from Latin1 encoding to utf8. Also translates HTML entities.
  1148. char* latin1ToUTF8 (char* s)
  1149. {
  1150. agxbuf xb = {0};
  1151. unsigned int v;
  1152. /* Values are either a byte (<= 256) or come from htmlEntity, whose
  1153. * values are all less than 0x07FF, so we need at most 3 bytes.
  1154. */
  1155. while ((v = *(unsigned char*)s++)) {
  1156. if (v == '&') {
  1157. v = htmlEntity (&s);
  1158. if (!v) v = '&';
  1159. }
  1160. if (v < 0x7F)
  1161. agxbputc(&xb, (char)v);
  1162. else if (v < 0x07FF) {
  1163. agxbputc(&xb, (char)((v >> 6) | 0xC0));
  1164. agxbputc(&xb, (char)((v & 0x3F) | 0x80));
  1165. }
  1166. else {
  1167. agxbputc(&xb, (char)((v >> 12) | 0xE0));
  1168. agxbputc(&xb, (char)(((v >> 6) & 0x3F) | 0x80));
  1169. agxbputc(&xb, (char)((v & 0x3F) | 0x80));
  1170. }
  1171. }
  1172. return agxbdisown(&xb);
  1173. }
  1174. /** Converts string from utf8 encoding to Latin1
  1175. * Note that it does not attempt to reproduce HTML entities.
  1176. * We assume the input string comes from latin1ToUTF8.
  1177. */
  1178. char*
  1179. utf8ToLatin1 (char* s)
  1180. {
  1181. agxbuf xb = {0};
  1182. unsigned char c;
  1183. while ((c = *(unsigned char*)s++)) {
  1184. if (c < 0x7F)
  1185. agxbputc(&xb, (char)c);
  1186. else {
  1187. unsigned char outc = (c & 0x03) << 6;
  1188. c = *(unsigned char *)s++;
  1189. outc = outc | (c & 0x3F);
  1190. agxbputc(&xb, (char)outc);
  1191. }
  1192. }
  1193. return agxbdisown(&xb);
  1194. }
  1195. bool overlap_node(node_t *n, boxf b) {
  1196. if (! OVERLAP(b, ND_bb(n)))
  1197. return false;
  1198. /* FIXME - need to do something better about CLOSEENOUGH */
  1199. pointf p = sub_pointf(ND_coord(n), mid_pointf(b.UR, b.LL));
  1200. inside_t ictxt = {.s.n = n};
  1201. return ND_shape(n)->fns->insidefn(&ictxt, p);
  1202. }
  1203. bool overlap_label(textlabel_t *lp, boxf b)
  1204. {
  1205. const pointf s = {.x = lp->dimen.x / 2.0, .y = lp->dimen.y / 2.0};
  1206. boxf bb = {.LL = sub_pointf(lp->pos, s), .UR = add_pointf(lp->pos, s)};
  1207. return OVERLAP(b, bb);
  1208. }
  1209. static bool overlap_arrow(pointf p, pointf u, double scale, boxf b)
  1210. {
  1211. // FIXME - check inside arrow shape
  1212. return OVERLAP(b, arrow_bb(p, u, scale));
  1213. }
  1214. static bool overlap_bezier(bezier bz, boxf b) {
  1215. assert(bz.size);
  1216. pointf u = bz.list[0];
  1217. for (size_t i = 1; i < bz.size; i++) {
  1218. pointf p = bz.list[i];
  1219. if (lineToBox(p, u, b) != -1)
  1220. return true;
  1221. u = p;
  1222. }
  1223. /* check arrows */
  1224. if (bz.sflag) {
  1225. if (overlap_arrow(bz.sp, bz.list[0], 1, b))
  1226. return true;
  1227. }
  1228. if (bz.eflag) {
  1229. if (overlap_arrow(bz.ep, bz.list[bz.size - 1], 1, b))
  1230. return true;
  1231. }
  1232. return false;
  1233. }
  1234. bool overlap_edge(edge_t *e, boxf b)
  1235. {
  1236. splines *spl = ED_spl(e);
  1237. if (spl && boxf_overlap(spl->bb, b))
  1238. for (size_t i = 0; i < spl->size; i++)
  1239. if (overlap_bezier(spl->list[i], b))
  1240. return true;
  1241. textlabel_t *lp = ED_label(e);
  1242. if (lp && overlap_label(lp, b))
  1243. return true;
  1244. return false;
  1245. }
  1246. /// Convert string to edge type.
  1247. static int edgeType(const char *s, int defaultValue) {
  1248. if (s == NULL || strcmp(s, "") == 0) {
  1249. return defaultValue;
  1250. }
  1251. if (*s == '0') { /* false */
  1252. return EDGETYPE_LINE;
  1253. } else if (*s >= '1' && *s <= '9') { /* true */
  1254. return EDGETYPE_SPLINE;
  1255. } else if (strcasecmp(s, "curved") == 0) {
  1256. return EDGETYPE_CURVED;
  1257. } else if (strcasecmp(s, "compound") == 0) {
  1258. return EDGETYPE_COMPOUND;
  1259. } else if (strcasecmp(s, "false") == 0) {
  1260. return EDGETYPE_LINE;
  1261. } else if (strcasecmp(s, "line") == 0) {
  1262. return EDGETYPE_LINE;
  1263. } else if (strcasecmp(s, "none") == 0) {
  1264. return EDGETYPE_NONE;
  1265. } else if (strcasecmp(s, "no") == 0) {
  1266. return EDGETYPE_LINE;
  1267. } else if (strcasecmp(s, "ortho") == 0) {
  1268. return EDGETYPE_ORTHO;
  1269. } else if (strcasecmp(s, "polyline") == 0) {
  1270. return EDGETYPE_PLINE;
  1271. } else if (strcasecmp(s, "spline") == 0) {
  1272. return EDGETYPE_SPLINE;
  1273. } else if (strcasecmp(s, "true") == 0) {
  1274. return EDGETYPE_SPLINE;
  1275. } else if (strcasecmp(s, "yes") == 0) {
  1276. return EDGETYPE_SPLINE;
  1277. }
  1278. agwarningf("Unknown \"splines\" value: \"%s\" - ignored\n", s);
  1279. return defaultValue;
  1280. }
  1281. /** Sets graph's edge type based on the "splines" attribute.
  1282. * If the attribute is not defined, use defaultValue.
  1283. * If the attribute is "", use NONE.
  1284. * If attribute value matches (case indepedent), use match.
  1285. * ortho => EDGETYPE_ORTHO
  1286. * none => EDGETYPE_NONE
  1287. * line => EDGETYPE_LINE
  1288. * polyline => EDGETYPE_PLINE
  1289. * spline => EDGETYPE_SPLINE
  1290. * If attribute is boolean, true means EDGETYPE_SPLINE, false means
  1291. * EDGETYPE_LINE. Else warn and use default.
  1292. */
  1293. void setEdgeType(graph_t *g, int defaultValue) {
  1294. char* s = agget(g, "splines");
  1295. int et;
  1296. if (!s) {
  1297. et = defaultValue;
  1298. }
  1299. else if (*s == '\0') {
  1300. et = EDGETYPE_NONE;
  1301. } else {
  1302. et = edgeType(s, defaultValue);
  1303. }
  1304. GD_flags(g) |= et;
  1305. }
  1306. /** Evaluates the extreme points of an ellipse or polygon
  1307. * Determines the point at the center of the extreme points
  1308. * If isRadial is true,sets the inner radius to half the distance to the min point;
  1309. * else uses the angle parameter to identify two points on a line that defines the
  1310. * gradient direction
  1311. * By default, this assumes a left-hand coordinate system (for svg); if RHS = 2 flag
  1312. * is set, use standard coordinate system.
  1313. */
  1314. void get_gradient_points(pointf *A, pointf *G, size_t n, double angle, int flags) {
  1315. pointf min,max,center;
  1316. int isRadial = flags & 1;
  1317. int isRHS = flags & 2;
  1318. if (n == 2) {
  1319. double rx = A[1].x - A[0].x;
  1320. double ry = A[1].y - A[0].y;
  1321. min.x = A[0].x - rx;
  1322. max.x = A[0].x + rx;
  1323. min.y = A[0].y - ry;
  1324. max.y = A[0].y + ry;
  1325. }
  1326. else {
  1327. min.x = max.x = A[0].x;
  1328. min.y = max.y = A[0].y;
  1329. for (size_t i = 0; i < n; i++) {
  1330. min.x = MIN(A[i].x, min.x);
  1331. min.y = MIN(A[i].y, min.y);
  1332. max.x = MAX(A[i].x, max.x);
  1333. max.y = MAX(A[i].y, max.y);
  1334. }
  1335. }
  1336. center.x = min.x + (max.x - min.x)/2;
  1337. center.y = min.y + (max.y - min.y)/2;
  1338. if (isRadial) {
  1339. double inner_r, outer_r;
  1340. outer_r = hypot(center.x - min.x, center.y - min.y);
  1341. inner_r = outer_r /4.;
  1342. if (isRHS) {
  1343. G[0].y = center.y;
  1344. }
  1345. else {
  1346. G[0].y = -center.y;
  1347. }
  1348. G[0].x = center.x;
  1349. G[1].x = inner_r;
  1350. G[1].y = outer_r;
  1351. }
  1352. else {
  1353. double half_x = max.x - center.x;
  1354. double half_y = max.y - center.y;
  1355. double sina = sin(angle);
  1356. double cosa = cos(angle);
  1357. if (isRHS) {
  1358. G[0].y = center.y - half_y * sina;
  1359. G[1].y = center.y + half_y * sina;
  1360. }
  1361. else {
  1362. G[0].y = -center.y + (max.y - center.y) * sin(angle);
  1363. G[1].y = -center.y - (center.y - min.y) * sin(angle);
  1364. }
  1365. G[0].x = center.x - half_x * cosa;
  1366. G[1].x = center.x + half_x * cosa;
  1367. }
  1368. }
  1369. void gv_free_splines(edge_t *e) {
  1370. if (ED_spl(e)) {
  1371. for (size_t i = 0; i < ED_spl(e)->size; i++)
  1372. free(ED_spl(e)->list[i].list);
  1373. free(ED_spl(e)->list);
  1374. free(ED_spl(e));
  1375. }
  1376. ED_spl(e) = NULL;
  1377. }
  1378. void gv_cleanup_edge(edge_t * e)
  1379. {
  1380. free(ED_path(e).ps);
  1381. gv_free_splines(e);
  1382. free_label(ED_label(e));
  1383. free_label(ED_xlabel(e));
  1384. free_label(ED_head_label(e));
  1385. free_label(ED_tail_label(e));
  1386. /*FIX HERE , shallow cleaning may not be enough here */
  1387. agdelrec(e, "Agedgeinfo_t");
  1388. }
  1389. void gv_cleanup_node(node_t * n)
  1390. {
  1391. free(ND_pos(n));
  1392. if (ND_shape(n))
  1393. ND_shape(n)->fns->freefn(n);
  1394. free_label(ND_label(n));
  1395. free_label(ND_xlabel(n));
  1396. /*FIX HERE , shallow cleaning may not be enough here */
  1397. agdelrec(n, "Agnodeinfo_t");
  1398. }
  1399. void gv_nodesize(node_t *n, bool flip) {
  1400. if (flip) {
  1401. double w = INCH2PS(ND_height(n));
  1402. ND_lw(n) = ND_rw(n) = w / 2;
  1403. ND_ht(n) = INCH2PS(ND_width(n));
  1404. }
  1405. else {
  1406. double w = INCH2PS(ND_width(n));
  1407. ND_lw(n) = ND_rw(n) = w / 2;
  1408. ND_ht(n) = INCH2PS(ND_height(n));
  1409. }
  1410. }
  1411. #ifndef HAVE_DRAND48
  1412. double drand48(void)
  1413. {
  1414. double d;
  1415. d = rand();
  1416. d = d / RAND_MAX;
  1417. return d;
  1418. }
  1419. #endif
  1420. typedef struct {
  1421. Dtlink_t link;
  1422. char* name;
  1423. Agraph_t* clp;
  1424. } clust_t;
  1425. static Dtdisc_t strDisc = {
  1426. .key = offsetof(clust_t, name),
  1427. .size = -1,
  1428. .link = offsetof(clust_t, link),
  1429. .freef = free,
  1430. };
  1431. static void fillMap (Agraph_t* g, Dt_t* map)
  1432. {
  1433. for (int c = 1; c <= GD_n_cluster(g); c++) {
  1434. Agraph_t *cl = GD_clust(g)[c];
  1435. char *s = agnameof(cl);
  1436. if (dtmatch(map, s)) {
  1437. agwarningf("Two clusters named %s - the second will be ignored\n", s);
  1438. } else {
  1439. clust_t *ip = gv_alloc(sizeof(clust_t));
  1440. ip->name = s;
  1441. ip->clp = cl;
  1442. dtinsert (map, ip);
  1443. }
  1444. fillMap (cl, map);
  1445. }
  1446. }
  1447. /** Generates a dictionary mapping cluster names to corresponding cluster.
  1448. * Used with cgraph as the latter does not support a flat namespace of clusters.
  1449. * Assumes G has already built a cluster tree using GD_n_cluster and GD_clust.
  1450. */
  1451. Dt_t* mkClustMap (Agraph_t* g)
  1452. {
  1453. Dt_t* map = dtopen (&strDisc, Dtoset);
  1454. fillMap (g, map);
  1455. return map;
  1456. }
  1457. Agraph_t*
  1458. findCluster (Dt_t* map, char* name)
  1459. {
  1460. clust_t* clp = dtmatch (map, name);
  1461. if (clp)
  1462. return clp->clp;
  1463. return NULL;
  1464. }
  1465. /**
  1466. * @dir lib/common
  1467. * @brief common code for layout engines
  1468. * @ingroup engines
  1469. *
  1470. * @ref common_utils
  1471. *
  1472. * @defgroup common_utils utilities
  1473. * @brief low level utilities for layout engines and rendering
  1474. * @ingroup engines
  1475. */