Graphviz  2.41.20170921.2350
labels.c
Go to the documentation of this file.
1 /* $Id$ $Revision$ */
2 /* vim:set shiftwidth=4 ts=8: */
3 
4 /*************************************************************************
5  * Copyright (c) 2011 AT&T Intellectual Property
6  * All rights reserved. This program and the accompanying materials
7  * are made available under the terms of the Eclipse Public License v1.0
8  * which accompanies this distribution, and is available at
9  * http://www.eclipse.org/legal/epl-v10.html
10  *
11  * Contributors: See CVS logs. Details at http://www.graphviz.org/
12  *************************************************************************/
13 
14 
15 #include "render.h"
16 #include "htmltable.h"
17 #include <limits.h>
18 
19 static char *strdup_and_subst_obj0 (char *str, void *obj, int escBackslash);
20 
21 static void storeline(GVC_t *gvc, textlabel_t *lp, char *line, char terminator)
22 {
23  pointf size;
24  textspan_t *span;
25  static textfont_t tf;
26  int oldsz = lp->u.txt.nspans + 1;
27 
28  lp->u.txt.span = ZALLOC(oldsz + 1, lp->u.txt.span, textspan_t, oldsz);
29  span = &(lp->u.txt.span[lp->u.txt.nspans]);
30  span->str = line;
31  span->just = terminator;
32  if (line && line[0]) {
33  tf.name = lp->fontname;
34  tf.size = lp->fontsize;
35  span->font = dtinsert(gvc->textfont_dt, &tf);
36  size = textspan_size(gvc, span);
37  }
38  else {
39  size.x = 0.0;
40  span->size.y = size.y = (int)(lp->fontsize * LINESPACING);
41  }
42 
43  lp->u.txt.nspans++;
44  /* width = max line width */
45  lp->dimen.x = MAX(lp->dimen.x, size.x);
46  /* accumulate height */
47  lp->dimen.y += size.y;
48 }
49 
50 /* compiles <str> into a label <lp> */
52 {
53  char c, *p, *line, *lineptr, *str = lp->text;
54  unsigned char byte = 0x00;
55 
56  lp->dimen.x = lp->dimen.y = 0.0;
57  if (*str == '\0')
58  return;
59 
60  line = lineptr = NULL;
61  p = str;
62  line = lineptr = N_GNEW(strlen(p) + 1, char);
63  *line = 0;
64  while ((c = *p++)) {
65  byte = (unsigned char) c;
66  /* wingraphviz allows a combination of ascii and big-5. The latter
67  * is a two-byte encoding, with the first byte in 0xA1-0xFE, and
68  * the second in 0x40-0x7e or 0xa1-0xfe. We assume that the input
69  * is well-formed, but check that we don't go past the ending '\0'.
70  */
71  if ((lp->charset == CHAR_BIG5) && 0xA1 <= byte && byte <= 0xFE) {
72  *lineptr++ = c;
73  c = *p++;
74  *lineptr++ = c;
75  if (!c) /* NB. Protect against unexpected string end here */
76  break;
77  } else {
78  if (c == '\\') {
79  switch (*p) {
80  case 'n':
81  case 'l':
82  case 'r':
83  *lineptr++ = '\0';
84  storeline(gvc, lp, line, *p);
85  line = lineptr;
86  break;
87  default:
88  *lineptr++ = *p;
89  }
90  if (*p)
91  p++;
92  /* tcldot can enter real linend characters */
93  } else if (c == '\n') {
94  *lineptr++ = '\0';
95  storeline(gvc, lp, line, 'n');
96  line = lineptr;
97  } else {
98  *lineptr++ = c;
99  }
100  }
101  }
102 
103  if (line != lineptr) {
104  *lineptr++ = '\0';
105  storeline(gvc, lp, line, 'n');
106  }
107 
108  lp->space = lp->dimen;
109 }
110 
111 /* make_label:
112  * Assume str is freshly allocated for this instance, so it
113  * can be freed in free_label.
114  */
115 textlabel_t *make_label(void *obj, char *str, int kind, double fontsize, char *fontname, char *fontcolor)
116 {
117  textlabel_t *rv = NEW(textlabel_t);
118  graph_t *g = NULL, *sg = NULL;
119  node_t *n = NULL;
120  edge_t *e = NULL;
121  char *s;
122 
123  switch (agobjkind(obj)) {
124  case AGRAPH:
125  sg = (graph_t*)obj;
126  g = sg->root;
127  break;
128  case AGNODE:
129  n = (node_t*)obj;
130  g = agroot(agraphof(n));
131  break;
132  case AGEDGE:
133  e = (edge_t*)obj;
134  g = agroot(agraphof(aghead(e)));
135  break;
136  }
137  rv->fontname = fontname;
138  rv->fontcolor = fontcolor;
139  rv->fontsize = fontsize;
140  rv->charset = GD_charset(g);
141  if (kind & LT_RECD) {
142  rv->text = strdup(str);
143  if (kind & LT_HTML) {
144  rv->html = TRUE;
145  }
146  }
147  else if (kind == LT_HTML) {
148  rv->text = strdup(str);
149  rv->html = TRUE;
150  if (make_html_label(obj, rv)) {
151  switch (agobjkind(obj)) {
152  case AGRAPH:
153  agerr(AGPREV, "in label of graph %s\n",agnameof(sg));
154  break;
155  case AGNODE:
156  agerr(AGPREV, "in label of node %s\n", agnameof(n));
157  break;
158  case AGEDGE:
159  agerr(AGPREV, "in label of edge %s %s %s\n",
160  agnameof(agtail(e)), agisdirected(g)?"->":"--", agnameof(aghead(e)));
161  break;
162  }
163  }
164  }
165  else {
166  assert(kind == LT_NONE);
167  /* This call just processes the graph object based escape sequences. The formatting escape
168  * sequences (\n, \l, \r) are processed in make_simple_label. That call also replaces \\ with \.
169  */
170  rv->text = strdup_and_subst_obj0(str, obj, 0);
171  switch (rv->charset) {
172  case CHAR_LATIN1:
173  s = latin1ToUTF8(rv->text);
174  break;
175  default: /* UTF8 */
176  s = htmlEntityUTF8(rv->text, g);
177  break;
178  }
179  free(rv->text);
180  rv->text = s;
181  make_simple_label(GD_gvc(g), rv);
182  }
183  return rv;
184 }
185 
186 /* free_textspan:
187  * Free resources related to textspan_t.
188  * tl is an array of cnt textspan_t's.
189  * It is also assumed that the text stored in the str field
190  * is all stored in one large buffer shared by all of the textspan_t,
191  * so only the first one needs to free its tlp->str.
192  */
193 void free_textspan(textspan_t * tl, int cnt)
194 {
195  int i;
196  textspan_t* tlp = tl;
197 
198  if (!tl) return;
199  for (i = 0; i < cnt; i++) {
200  if ((i == 0) && tlp->str)
201  free(tlp->str);
202  if (tlp->layout && tlp->free_layout)
203  tlp->free_layout (tlp->layout);
204  tlp++;
205  }
206  free(tl);
207 }
208 
210 {
211  if (p) {
212  free(p->text);
213  if (p->html) {
214  if (p->u.html) free_html_label(p->u.html, 1);
215  } else {
216  free_textspan(p->u.txt.span, p->u.txt.nspans);
217  }
218  free(p);
219  }
220 }
221 
222 void emit_label(GVJ_t * job, emit_state_t emit_state, textlabel_t * lp)
223 {
224  obj_state_t *obj = job->obj;
225  int i;
226  pointf p;
227  emit_state_t old_emit_state;
228 
229  old_emit_state = obj->emit_state;
230  obj->emit_state = emit_state;
231 
232  if (lp->html) {
233  emit_html_label(job, lp->u.html, lp);
234  obj->emit_state = old_emit_state;
235  return;
236  }
237 
238  /* make sure that there is something to do */
239  if (lp->u.txt.nspans < 1)
240  return;
241 
244 
245  /* position for first span */
246  switch (lp->valign) {
247  case 't':
248  p.y = lp->pos.y + lp->space.y / 2.0 - lp->fontsize;
249  break;
250  case 'b':
251  p.y = lp->pos.y - lp->space.y / 2.0 + lp->dimen.y - lp->fontsize;
252  break;
253  case 'c':
254  default:
255  p.y = lp->pos.y + lp->dimen.y / 2.0 - lp->fontsize;
256  break;
257  }
258  if (obj->labeledgealigned)
259  p.y -= lp->pos.y;
260  for (i = 0; i < lp->u.txt.nspans; i++) {
261  switch (lp->u.txt.span[i].just) {
262  case 'l':
263  p.x = lp->pos.x - lp->space.x / 2.0;
264  break;
265  case 'r':
266  p.x = lp->pos.x + lp->space.x / 2.0;
267  break;
268  default:
269  case 'n':
270  p.x = lp->pos.x;
271  break;
272  }
273  gvrender_textspan(job, p, &(lp->u.txt.span[i]));
274 
275  /* UL position for next span */
276  p.y -= lp->u.txt.span[i].size.y;
277  }
278 
279  gvrender_end_label(job);
280  obj->emit_state = old_emit_state;
281 }
282 
283 /* strdup_and_subst_obj0:
284  * Replace various escape sequences with the name of the associated
285  * graph object. A double backslash \\ can be used to avoid a replacement.
286  * If escBackslash is true, convert \\ to \; else leave alone. All other dyads
287  * of the form \. are passed through unchanged.
288  */
289 static char *strdup_and_subst_obj0 (char *str, void *obj, int escBackslash)
290 {
291  char c, *s, *p, *t, *newstr;
292  char *tp_str = "", *hp_str = "";
293  char *g_str = "\\G", *n_str = "\\N", *e_str = "\\E",
294  *h_str = "\\H", *t_str = "\\T", *l_str = "\\L";
295  size_t g_len = 2, n_len = 2, e_len = 2,
296  h_len = 2, t_len = 2, l_len = 2,
297  tp_len = 0, hp_len = 0;
298  size_t newlen = 0;
299  int isEdge = 0;
300  textlabel_t *tl;
301  port pt;
302 
303  /* prepare substitution strings */
304  switch (agobjkind(obj)) {
305  case AGRAPH:
306  g_str = agnameof((graph_t *)obj);
307  g_len = strlen(g_str);
308  tl = GD_label((graph_t *)obj);
309  if (tl) {
310  l_str = tl->text;
311  if (str) l_len = strlen(l_str);
312  }
313  break;
314  case AGNODE:
315  g_str = agnameof(agraphof((node_t *)obj));
316  g_len = strlen(g_str);
317  n_str = agnameof((node_t *)obj);
318  n_len = strlen(n_str);
319  tl = ND_label((node_t *)obj);
320  if (tl) {
321  l_str = tl->text;
322  if (str) l_len = strlen(l_str);
323  }
324  break;
325  case AGEDGE:
326  isEdge = 1;
327  g_str = agnameof(agroot(agraphof(agtail(((edge_t *)obj)))));
328  g_len = strlen(g_str);
329  t_str = agnameof(agtail(((edge_t *)obj)));
330  t_len = strlen(t_str);
331  pt = ED_tail_port((edge_t *)obj);
332  if ((tp_str = pt.name))
333  tp_len = strlen(tp_str);
334  h_str = agnameof(aghead(((edge_t *)obj)));
335  h_len = strlen(h_str);
336  pt = ED_head_port((edge_t *)obj);
337  if ((hp_str = pt.name))
338  hp_len = strlen(hp_str);
339  h_len = strlen(h_str);
340  tl = ED_label((edge_t *)obj);
341  if (tl) {
342  l_str = tl->text;
343  if (str) l_len = strlen(l_str);
344  }
345  if (agisdirected(agroot(agraphof(agtail(((edge_t*)obj))))))
346  e_str = "->";
347  else
348  e_str = "--";
349  e_len = t_len + (tp_len?tp_len+1:0) + 2 + h_len + (hp_len?hp_len+1:0);
350  break;
351  }
352 
353  /* two passes over str.
354  *
355  * first pass prepares substitution strings and computes
356  * total length for newstring required from malloc.
357  */
358  for (s = str; (c = *s++);) {
359  if (c == '\\') {
360  switch (c = *s++) {
361  case 'G':
362  newlen += g_len;
363  break;
364  case 'N':
365  newlen += n_len;
366  break;
367  case 'E':
368  newlen += e_len;
369  break;
370  case 'H':
371  newlen += h_len;
372  break;
373  case 'T':
374  newlen += t_len;
375  break;
376  case 'L':
377  newlen += l_len;
378  break;
379  case '\\':
380  if (escBackslash) {
381  newlen += 1;
382  break;
383  }
384  /* Fall through */
385  default: /* leave other escape sequences unmodified, e.g. \n \l \r */
386  newlen += 2;
387  }
388  } else {
389  newlen++;
390  }
391  }
392  /* allocate new string */
393  newstr = gmalloc(newlen + 1);
394 
395  /* second pass over str assembles new string */
396  for (s = str, p = newstr; (c = *s++);) {
397  if (c == '\\') {
398  switch (c = *s++) {
399  case 'G':
400  for (t = g_str; (*p = *t++); p++);
401  break;
402  case 'N':
403  for (t = n_str; (*p = *t++); p++);
404  break;
405  case 'E':
406  if (isEdge) {
407  for (t = t_str; (*p = *t++); p++);
408  if (tp_len) {
409  *p++ = ':';
410  for (t = tp_str; (*p = *t++); p++);
411  }
412  for (t = e_str; (*p = *t++); p++);
413  for (t = h_str; (*p = *t++); p++);
414  if (hp_len) {
415  *p++ = ':';
416  for (t = hp_str; (*p = *t++); p++);
417  }
418  }
419  break;
420  case 'T':
421  for (t = t_str; (*p = *t++); p++);
422  break;
423  case 'H':
424  for (t = h_str; (*p = *t++); p++);
425  break;
426  case 'L':
427  for (t = l_str; (*p = *t++); p++);
428  break;
429  case '\\':
430  if (escBackslash) {
431  *p++ = '\\';
432  break;
433  }
434  /* Fall through */
435  default: /* leave other escape sequences unmodified, e.g. \n \l \r */
436  *p++ = '\\';
437  *p++ = c;
438  break;
439  }
440  } else {
441  *p++ = c;
442  }
443  }
444  *p++ = '\0';
445  return newstr;
446 }
447 
448 /* strdup_and_subst_obj:
449  * Processes graph object escape sequences; also collapses \\ to \.
450  */
451 char *strdup_and_subst_obj(char *str, void *obj)
452 {
453  return strdup_and_subst_obj0 (str, obj, 1);
454 }
455 
456 /* return true if *s points to &[A-Za-z]*; (e.g. &Ccedil; )
457  * or &#[0-9]*; (e.g. &#38; )
458  * or &#x[0-9a-fA-F]*; (e.g. &#x6C34; )
459  */
460 static int xml_isentity(char *s)
461 {
462  s++; /* already known to be '&' */
463  if (*s == '#') {
464  s++;
465  if (*s == 'x' || *s == 'X') {
466  s++;
467  while ((*s >= '0' && *s <= '9')
468  || (*s >= 'a' && *s <= 'f')
469  || (*s >= 'A' && *s <= 'F'))
470  s++;
471  } else {
472  while (*s >= '0' && *s <= '9')
473  s++;
474  }
475  } else {
476  while ((*s >= 'a' && *s <= 'z')
477  || (*s >= 'A' && *s <= 'Z'))
478  s++;
479  }
480  if (*s == ';')
481  return 1;
482  return 0;
483 }
484 
485 char *xml_string(char *s)
486 {
487  return xml_string0 (s, FALSE);
488 }
489 
490 /* xml_string0:
491  * Encode input string as an xml string.
492  * If raw is true, the input is interpreted as having no
493  * embedded escape sequences, and \n and \r are changed
494  * into &#10; and &#13;, respectively.
495  * Uses a static buffer, so non-re-entrant.
496  */
497 char *xml_string0(char *s, boolean raw)
498 {
499  static char *buf = NULL;
500  static int bufsize = 0;
501  char *p, *sub, *prev = NULL;
502  int len, pos = 0;
503 
504  if (!buf) {
505  bufsize = 64;
506  buf = gmalloc(bufsize);
507  }
508 
509  p = buf;
510  while (s && *s) {
511  if (pos > (bufsize - 8)) {
512  bufsize *= 2;
513  buf = grealloc(buf, bufsize);
514  p = buf + pos;
515  }
516  /* escape '&' only if not part of a legal entity sequence */
517  if (*s == '&' && (raw || !(xml_isentity(s)))) {
518  sub = "&amp;";
519  len = 5;
520  }
521  /* '<' '>' are safe to substitute even if string is already UTF-8 coded
522  * since UTF-8 strings won't contain '<' or '>' */
523  else if (*s == '<') {
524  sub = "&lt;";
525  len = 4;
526  }
527  else if (*s == '>') {
528  sub = "&gt;";
529  len = 4;
530  }
531  else if (*s == '-') { /* can't be used in xml comment strings */
532  sub = "&#45;";
533  len = 5;
534  }
535  else if (*s == ' ' && prev && *prev == ' ') {
536  /* substitute 2nd and subsequent spaces with required_spaces */
537  sub = "&#160;"; /* inkscape doesn't recognise &nbsp; */
538  len = 6;
539  }
540  else if (*s == '"') {
541  sub = "&quot;";
542  len = 6;
543  }
544  else if (*s == '\'') {
545  sub = "&#39;";
546  len = 5;
547  }
548  else if ((*s == '\n') && raw) {
549  sub = "&#10;";
550  len = 5;
551  }
552  else if ((*s == '\r') && raw) {
553  sub = "&#13;";
554  len = 5;
555  }
556  else {
557  sub = s;
558  len = 1;
559  }
560  while (len--) {
561  *p++ = *sub++;
562  pos++;
563  }
564  prev = s;
565  s++;
566  }
567  *p = '\0';
568  return buf;
569 }
570 
571 /* a variant of xml_string for urls in hrefs */
572 char *xml_url_string(char *s)
573 {
574  static char *buf = NULL;
575  static int bufsize = 0;
576  char *p, *sub;
577 #if 0
578  char *prev = NULL;
579 #endif
580  int len, pos = 0;
581 
582  if (!buf) {
583  bufsize = 64;
584  buf = gmalloc(bufsize);
585  }
586 
587  p = buf;
588  while (s && *s) {
589  if (pos > (bufsize - 8)) {
590  bufsize *= 2;
591  buf = grealloc(buf, bufsize);
592  p = buf + pos;
593  }
594  /* escape '&' only if not part of a legal entity sequence */
595  if (*s == '&' && !(xml_isentity(s))) {
596  sub = "&amp;";
597  len = 5;
598  }
599  /* '<' '>' are safe to substitute even if string is already UTF-8 coded
600  * since UTF-8 strings won't contain '<' or '>' */
601  else if (*s == '<') {
602  sub = "&lt;";
603  len = 4;
604  }
605  else if (*s == '>') {
606  sub = "&gt;";
607  len = 4;
608  }
609 #if 0
610  else if (*s == '-') { /* can't be used in xml comment strings */
611  sub = "&#45;";
612  len = 5;
613  }
614  else if (*s == ' ' && prev && *prev == ' ') {
615  /* substitute 2nd and subsequent spaces with required_spaces */
616  sub = "&#160;"; /* inkscape doesn't recognise &nbsp; */
617  len = 6;
618  }
619 #endif
620  else if (*s == '"') {
621  sub = "&quot;";
622  len = 6;
623  }
624  else if (*s == '\'') {
625  sub = "&#39;";
626  len = 5;
627  }
628  else {
629  sub = s;
630  len = 1;
631  }
632  while (len--) {
633  *p++ = *sub++;
634  pos++;
635  }
636 #if 0
637  prev = s;
638 #endif
639  s++;
640  }
641  *p = '\0';
642  return buf;
643 }
#define GD_label(g)
Definition: types.h:381
#define MAX(a, b)
Definition: agerror.c:17
void free_label(textlabel_t *p)
Definition: labels.c:209
pointf size
Definition: textspan.h:64
CGRAPH_API int agobjkind(void *)
Definition: obj.c:264
Definition: types.h:67
char * latin1ToUTF8(char *s)
Definition: utils.c:1561
void * grealloc(void *ptr, size_t size)
Definition: memory.c:54
htmllabel_t * html
Definition: types.h:139
char * htmlEntityUTF8(char *s, graph_t *g)
Definition: utils.c:1473
emit_state_t
Definition: gvcjob.h:182
char * xml_url_string(char *s)
Definition: labels.c:572
Dt_t * textfont_dt
Definition: gvcint.h:96
double size
Definition: textspan.h:52
#define ED_head_port(e)
Definition: types.h:591
#define assert(x)
Definition: cghdr.h:47
Definition: geom.h:28
char * text
Definition: types.h:124
CGRAPH_API int agisdirected(Agraph_t *g)
Definition: graph.c:182
char * fontcolor
Definition: types.h:126
#define ED_label(e)
Definition: types.h:592
void emit_label(GVJ_t *job, emit_state_t emit_state, textlabel_t *lp)
Definition: labels.c:222
void gvrender_end_label(GVJ_t *job)
Definition: gvrender.c:435
void * gmalloc(size_t nbytes)
Definition: memory.c:42
int make_html_label(void *obj, textlabel_t *lp)
Definition: htmltable.c:2057
int agerr(agerrlevel_t level, const char *fmt,...)
Definition: agerror.c:141
double fontsize
Definition: types.h:128
void gvrender_begin_label(GVJ_t *job, label_type type)
Definition: gvrender.c:425
Definition: gvcjob.h:271
CGRAPH_API Agraph_t * agroot(void *obj)
Definition: obj.c:169
char * name
Definition: textspan.h:49
char * strdup_and_subst_obj(char *str, void *obj)
Definition: labels.c:451
#define ND_label(n)
Definition: types.h:509
#define LINESPACING
Definition: const.h:73
#define ZALLOC(size, ptr, type, osize)
Definition: memory.h:43
#define GD_gvc(g)
Definition: types.h:358
obj_state_t * obj
Definition: gvcjob.h:278
CGRAPH_API Agraph_t * agraphof(void *obj)
Definition: obj.c:185
CGRAPH_API Agnode_t * agtail(Agedge_t *e)
Definition: edge.c:525
struct textlabel_t::@20::@21 txt
char * str
Definition: textspan.h:59
#define CHAR_BIG5
Definition: const.h:206
#define ED_tail_port(e)
Definition: types.h:600
void free_html_label(htmllabel_t *lp, int root)
Definition: htmltable.c:870
int charset
Definition: types.h:127
pointf pos
Definition: types.h:133
int
Definition: grammar.c:1264
CGRAPH_API Agnode_t * aghead(Agedge_t *e)
Definition: edge.c:533
double y
Definition: geom.h:28
Definition: gvcint.h:70
char * xml_string(char *s)
Definition: labels.c:485
CGRAPH_API char * agnameof(void *)
Definition: id.c:143
#define LT_NONE
Definition: const.h:259
void gvrender_set_pencolor(GVJ_t *job, char *name)
Definition: gvrender.c:464
#define LT_HTML
Definition: const.h:260
void free_textspan(textspan_t *tl, int cnt)
Definition: labels.c:193
char valign
Definition: types.h:141
Definition: grammar.c:79
pointf dimen
Definition: types.h:129
#define AGNODE
Definition: cgraph.h:101
emit_state_t emit_state
Definition: gvcjob.h:201
#define dtinsert(d, o)
Definition: cdt.h:262
#define sub(h, i)
Definition: closest.c:75
void gvrender_textspan(GVJ_t *job, pointf p, textspan_t *span)
Definition: gvrender.c:445
#define NULL
Definition: logic.h:39
#define GD_charset(g)
Definition: types.h:371
double x
Definition: geom.h:28
#define CHAR_LATIN1
Definition: const.h:205
void emit_html_label(GVJ_t *job, htmllabel_t *lp, textlabel_t *tp)
Definition: htmltable.c:747
GVC_t * gvc
Definition: htmlparse.c:87
char * name
Definition: types.h:82
char just
Definition: textspan.h:65
#define LT_RECD
Definition: const.h:261
void(* free_layout)(void *layout)
Definition: textspan.h:62
pointf textspan_size(GVC_t *gvc, textspan_t *span)
Definition: textspan.c:198
agxbuf * str
Definition: htmlparse.c:85
void make_simple_label(GVC_t *gvc, textlabel_t *lp)
Definition: labels.c:51
Agraph_t * root
Definition: cgraph.h:247
#define AGEDGE
Definition: cgraph.h:104
Definition: cgraph.h:388
union textlabel_t::@20 u
pointf space
Definition: types.h:130
#define N_GNEW(n, t)
Definition: agxbuf.c:20
#define FALSE
Definition: cgraph.h:35
int labeledgealigned
Definition: gvcjob.h:244
textfont_t * font
Definition: textspan.h:60
char * xml_string0(char *s, boolean raw)
Definition: labels.c:497
textlabel_t * make_label(void *obj, char *str, int kind, double fontsize, char *fontname, char *fontcolor)
Definition: labels.c:115
#define NEW(t)
Definition: memory.h:35
#define AGRAPH
Definition: cgraph.h:100
void * layout
Definition: textspan.h:61
char * fontname
Definition: types.h:125
#define TRUE
Definition: cgraph.h:38