Graphviz 12.0.1~dev.20240715.2254
Loading...
Searching...
No Matches
labels.c
Go to the documentation of this file.
1
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
13#include <cgraph/agxbuf.h>
14#include <cgraph/alloc.h>
15#include <common/render.h>
16#include <common/htmltable.h>
17#include <limits.h>
18#include <stdbool.h>
19#include <stddef.h>
20
21static char *strdup_and_subst_obj0 (char *str, void *obj, int escBackslash);
22
23static void storeline(GVC_t *gvc, textlabel_t *lp, char *line,
24 char terminator) {
25 pointf size;
26 textspan_t *span;
27 static textfont_t tf;
28 size_t oldsz = lp->u.txt.nspans + 1;
29
30 lp->u.txt.span = gv_recalloc(lp->u.txt.span, oldsz, oldsz + 1,
31 sizeof(textspan_t));
32 span = &lp->u.txt.span[lp->u.txt.nspans];
33 span->str = line;
34 span->just = terminator;
35 if (line && line[0]) {
36 tf.name = lp->fontname;
37 tf.size = lp->fontsize;
38 span->font = dtinsert(gvc->textfont_dt, &tf);
39 size = textspan_size(gvc, span);
40 }
41 else {
42 size.x = 0.0;
43 span->size.y = size.y = (int)(lp->fontsize * LINESPACING);
44 }
45
46 lp->u.txt.nspans++;
47 /* width = max line width */
48 lp->dimen.x = MAX(lp->dimen.x, size.x);
49 /* accumulate height */
50 lp->dimen.y += size.y;
51}
52
53/* compiles <str> into a label <lp> */
55{
56 lp->dimen.x = lp->dimen.y = 0.0;
57 if (*lp->text == '\0')
58 return;
59
60 agxbuf line = {0};
61 for (char c, *p = lp->text; (c = *p++);) {
62 unsigned char byte = (unsigned char) c;
63 /* wingraphviz allows a combination of ascii and big-5. The latter
64 * is a two-byte encoding, with the first byte in 0xA1-0xFE, and
65 * the second in 0x40-0x7e or 0xa1-0xfe. We assume that the input
66 * is well-formed, but check that we don't go past the ending '\0'.
67 */
68 if (lp->charset == CHAR_BIG5 && 0xA1 <= byte && byte <= 0xFE) {
69 agxbputc(&line, c);
70 c = *p++;
71 agxbputc(&line, c);
72 if (!c) /* NB. Protect against unexpected string end here */
73 break;
74 } else {
75 if (c == '\\') {
76 switch (*p) {
77 case 'n':
78 case 'l':
79 case 'r':
80 storeline(gvc, lp, agxbdisown(&line), *p);
81 break;
82 default:
83 agxbputc(&line, *p);
84 }
85 if (*p)
86 p++;
87 /* tcldot can enter real linend characters */
88 } else if (c == '\n') {
89 storeline(gvc, lp, agxbdisown(&line), 'n');
90 } else {
91 agxbputc(&line, c);
92 }
93 }
94 }
95
96 if (agxblen(&line) > 0) {
97 storeline(gvc, lp, agxbdisown(&line), 'n');
98 }
99
100 agxbfree(&line);
101 lp->space = lp->dimen;
102}
103
104/* make_label:
105 * Assume str is freshly allocated for this instance, so it
106 * can be freed in free_label.
107 */
108textlabel_t *make_label(void *obj, char *str, int kind, double fontsize, char *fontname, char *fontcolor)
109{
110 textlabel_t *rv = gv_alloc(sizeof(textlabel_t));
111 graph_t *g = NULL, *sg = NULL;
112 node_t *n = NULL;
113 edge_t *e = NULL;
114 char *s;
115
116 switch (agobjkind(obj)) {
117 case AGRAPH:
118 sg = obj;
119 g = sg->root;
120 break;
121 case AGNODE:
122 n = obj;
123 g = agroot(agraphof(n));
124 break;
125 case AGEDGE:
126 e = obj;
127 g = agroot(agraphof(aghead(e)));
128 break;
129 }
130 rv->fontname = fontname;
131 rv->fontcolor = fontcolor;
132 rv->fontsize = fontsize;
133 rv->charset = GD_charset(g);
134 if (kind & LT_RECD) {
135 rv->text = gv_strdup(str);
136 if (kind & LT_HTML) {
137 rv->html = true;
138 }
139 }
140 else if (kind == LT_HTML) {
141 rv->text = gv_strdup(str);
142 rv->html = true;
143 if (make_html_label(obj, rv)) {
144 switch (agobjkind(obj)) {
145 case AGRAPH:
146 agerr(AGPREV, "in label of graph %s\n",agnameof(sg));
147 break;
148 case AGNODE:
149 agerr(AGPREV, "in label of node %s\n", agnameof(n));
150 break;
151 case AGEDGE:
152 agerr(AGPREV, "in label of edge %s %s %s\n",
153 agnameof(agtail(e)), agisdirected(g)?"->":"--", agnameof(aghead(e)));
154 break;
155 }
156 }
157 }
158 else {
159 assert(kind == LT_NONE);
160 /* This call just processes the graph object based escape sequences. The formatting escape
161 * sequences (\n, \l, \r) are processed in make_simple_label. That call also replaces \\ with \.
162 */
163 rv->text = strdup_and_subst_obj0(str, obj, 0);
164 switch (rv->charset) {
165 case CHAR_LATIN1:
166 s = latin1ToUTF8(rv->text);
167 break;
168 default: /* UTF8 */
169 s = htmlEntityUTF8(rv->text, g);
170 break;
171 }
172 free(rv->text);
173 rv->text = s;
175 }
176 return rv;
177}
178
179/* free_textspan:
180 * Free resources related to textspan_t.
181 * tl is an array of cnt textspan_t's.
182 * It is also assumed that the text stored in the str field
183 * is all stored in one large buffer shared by all of the textspan_t,
184 * so only the first one needs to free its tlp->str.
185 */
186void free_textspan(textspan_t *tl, size_t cnt) {
187 textspan_t* tlp = tl;
188
189 if (!tl) return;
190 for (size_t i = 0; i < cnt; i++) {
191 free(tlp->str);
192 if (tlp->layout && tlp->free_layout)
193 tlp->free_layout (tlp->layout);
194 tlp++;
195 }
196 free(tl);
197}
198
200{
201 if (p) {
202 free(p->text);
203 if (p->html) {
204 if (p->u.html) free_html_label(p->u.html, 1);
205 } else {
207 }
208 free(p);
209 }
210}
211
212void emit_label(GVJ_t * job, emit_state_t emit_state, textlabel_t * lp)
213{
214 obj_state_t *obj = job->obj;
215 pointf p;
216 emit_state_t old_emit_state;
217
218 old_emit_state = obj->emit_state;
219 obj->emit_state = emit_state;
220
221 if (lp->html) {
222 emit_html_label(job, lp->u.html, lp);
223 obj->emit_state = old_emit_state;
224 return;
225 }
226
227 /* make sure that there is something to do */
228 if (lp->u.txt.nspans < 1)
229 return;
230
233
234 /* position for first span */
235 switch (lp->valign) {
236 case 't':
237 p.y = lp->pos.y + lp->space.y / 2.0 - lp->fontsize;
238 break;
239 case 'b':
240 p.y = lp->pos.y - lp->space.y / 2.0 + lp->dimen.y - lp->fontsize;
241 break;
242 case 'c':
243 default:
244 p.y = lp->pos.y + lp->dimen.y / 2.0 - lp->fontsize;
245 break;
246 }
247 if (obj->labeledgealigned)
248 p.y -= lp->pos.y;
249 for (size_t i = 0; i < lp->u.txt.nspans; i++) {
250 switch (lp->u.txt.span[i].just) {
251 case 'l':
252 p.x = lp->pos.x - lp->space.x / 2.0;
253 break;
254 case 'r':
255 p.x = lp->pos.x + lp->space.x / 2.0;
256 break;
257 default:
258 case 'n':
259 p.x = lp->pos.x;
260 break;
261 }
262 gvrender_textspan(job, p, &(lp->u.txt.span[i]));
263
264 /* UL position for next span */
265 p.y -= lp->u.txt.span[i].size.y;
266 }
267
269 obj->emit_state = old_emit_state;
270}
271
272/* strdup_and_subst_obj0:
273 * Replace various escape sequences with the name of the associated
274 * graph object. A double backslash \\ can be used to avoid a replacement.
275 * If escBackslash is true, convert \\ to \; else leave alone. All other dyads
276 * of the form \. are passed through unchanged.
277 */
278static char *strdup_and_subst_obj0 (char *str, void *obj, int escBackslash)
279{
280 char c, *s;
281 char *tp_str = "", *hp_str = "";
282 char *g_str = "\\G", *n_str = "\\N", *e_str = "\\E",
283 *h_str = "\\H", *t_str = "\\T", *l_str = "\\L";
284 bool has_hp = false;
285 bool has_tp = false;
286 int isEdge = 0;
287 textlabel_t *tl;
288 port pt;
289
290 /* prepare substitution strings */
291 switch (agobjkind(obj)) {
292 case AGRAPH:
293 g_str = agnameof(obj);
294 tl = GD_label(obj);
295 if (tl) {
296 l_str = tl->text;
297 }
298 break;
299 case AGNODE:
300 g_str = agnameof(agraphof(obj));
301 n_str = agnameof(obj);
302 tl = ND_label(obj);
303 if (tl) {
304 l_str = tl->text;
305 }
306 break;
307 case AGEDGE:
308 isEdge = 1;
309 g_str = agnameof(agroot(agraphof(agtail(((edge_t *)obj)))));
310 t_str = agnameof(agtail(((edge_t *)obj)));
311 pt = ED_tail_port(obj);
312 if ((tp_str = pt.name))
313 has_tp = *tp_str != '\0';
314 h_str = agnameof(aghead(((edge_t *)obj)));
315 pt = ED_head_port(obj);
316 if ((hp_str = pt.name))
317 has_hp = *hp_str != '\0';
318 tl = ED_label(obj);
319 if (tl) {
320 l_str = tl->text;
321 }
322 if (agisdirected(agroot(agraphof(agtail(((edge_t*)obj))))))
323 e_str = "->";
324 else
325 e_str = "--";
326 break;
327 }
328
329 /* allocate a dynamic buffer that we will use to construct the result */
330 agxbuf buf = {0};
331
332 /* assemble new string */
333 for (s = str; (c = *s++);) {
334 if (c == '\\' && *s != '\0') {
335 switch (c = *s++) {
336 case 'G':
337 agxbput(&buf, g_str);
338 break;
339 case 'N':
340 agxbput(&buf, n_str);
341 break;
342 case 'E':
343 if (isEdge) {
344 agxbput(&buf, t_str);
345 if (has_tp) {
346 agxbprint(&buf, ":%s", tp_str);
347 }
348 agxbprint(&buf, "%s%s", e_str, h_str);
349 if (has_hp) {
350 agxbprint(&buf, ":%s", hp_str);
351 }
352 }
353 break;
354 case 'T':
355 agxbput(&buf, t_str);
356 break;
357 case 'H':
358 agxbput(&buf, h_str);
359 break;
360 case 'L':
361 agxbput(&buf, l_str);
362 break;
363 case '\\':
364 if (escBackslash) {
365 agxbputc(&buf, '\\');
366 break;
367 }
368 /* Fall through */
369 default: /* leave other escape sequences unmodified, e.g. \n \l \r */
370 agxbprint(&buf, "\\%c", c);
371 break;
372 }
373 } else {
374 agxbputc(&buf, c);
375 }
376 }
377
378 /* extract the final string with replacements applied */
379 return agxbdisown(&buf);
380}
381
382/* strdup_and_subst_obj:
383 * Processes graph object escape sequences; also collapses \\ to \.
384 */
385char *strdup_and_subst_obj(char *str, void *obj)
386{
387 return strdup_and_subst_obj0 (str, obj, 1);
388}
Agedge_t * isEdge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *key)
Definition actions.c:446
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:77
static size_t agxbput(agxbuf *xb, const char *s)
append string s into xb
Definition agxbuf.h:249
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:213
static size_t agxblen(const agxbuf *xb)
return number of characters currently stored
Definition agxbuf.h:88
static int agxbputc(agxbuf *xb, char c)
add character to buffer
Definition agxbuf.h:256
static char * agxbdisown(agxbuf *xb)
Definition agxbuf.h:299
Memory allocation wrappers that exit on failure.
static void * gv_recalloc(void *ptr, size_t old_nmemb, size_t new_nmemb, size_t size)
Definition alloc.h:73
static char * gv_strdup(const char *original)
Definition alloc.h:101
static void * gv_alloc(size_t size)
Definition alloc.h:47
#define dtinsert(d, o)
Definition cdt.h:193
char * latin1ToUTF8(char *s)
Converts string from Latin1 encoding to utf8. Also translates HTML entities.
Definition utils.c:1278
char * htmlEntityUTF8(char *s, graph_t *g)
Definition utils.c:1199
#define CHAR_LATIN1
Definition const.h:197
#define CHAR_BIG5
Definition const.h:198
#define LINESPACING
Definition const.h:70
#define LT_RECD
Definition const.h:239
#define LT_NONE
Definition const.h:237
#define LT_HTML
Definition const.h:238
void free(void *)
node NULL
Definition grammar.y:149
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:199
#define agtail(e)
Definition cgraph.h:889
#define aghead(e)
Definition cgraph.h:890
#define ED_head_port(e)
Definition types.h:588
#define ED_label(e)
Definition types.h:589
#define ED_tail_port(e)
Definition types.h:597
int agerr(agerrlevel_t level, const char *fmt,...)
Definition agerror.c:155
@ AGPREV
Definition cgraph.h:858
int agisdirected(Agraph_t *g)
Definition graph.c:179
#define GD_label(g)
Definition types.h:374
#define GD_charset(g)
Definition types.h:367
#define GD_gvc(g)
Definition types.h:355
#define ND_label(n)
Definition types.h:502
Agraph_t * agraphof(void *obj)
Definition obj.c:184
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:158
int agobjkind(void *obj)
Definition obj.c:253
Agraph_t * agroot(void *obj)
Definition obj.c:167
@ AGEDGE
Definition cgraph.h:207
@ AGNODE
Definition cgraph.h:207
@ AGRAPH
Definition cgraph.h:207
@ LABEL_PLAIN
Definition gvcjob.h:38
emit_state_t
Definition gvcjob.h:173
void gvrender_end_label(GVJ_t *job)
Definition gvrender.c:407
void gvrender_textspan(GVJ_t *job, pointf p, textspan_t *span)
Definition gvrender.c:417
void gvrender_begin_label(GVJ_t *job, label_type type)
Definition gvrender.c:397
void gvrender_set_pencolor(GVJ_t *job, char *name)
Definition gvrender.c:436
static Agdesc_t kind
Definition gvpack.cpp:88
GVC_t * gvc
Definition htmlparse.c:99
agxbuf * str
Definition htmlparse.c:97
int make_html_label(void *obj, textlabel_t *lp)
Definition htmltable.c:1987
void free_html_label(htmllabel_t *lp, int root)
Definition htmltable.c:876
void emit_html_label(GVJ_t *job, htmllabel_t *lp, textlabel_t *tp)
Definition htmltable.c:756
char * strdup_and_subst_obj(char *str, void *obj)
Definition labels.c:385
void make_simple_label(GVC_t *gvc, textlabel_t *lp)
Definition labels.c:54
static char * strdup_and_subst_obj0(char *str, void *obj, int escBackslash)
Definition labels.c:278
textlabel_t * make_label(void *obj, char *str, int kind, double fontsize, char *fontname, char *fontcolor)
Definition labels.c:108
void free_textspan(textspan_t *tl, size_t cnt)
Definition labels.c:186
static void storeline(GVC_t *gvc, textlabel_t *lp, char *line, char terminator)
Definition labels.c:23
void free_label(textlabel_t *p)
Definition labels.c:199
void emit_label(GVJ_t *job, emit_state_t emit_state, textlabel_t *lp)
Definition labels.c:212
pointf textspan_size(GVC_t *gvc, textspan_t *span)
Estimates size of a textspan, in points.
Definition textspan.c:79
graph or subgraph
Definition cgraph.h:425
Agraph_t * root
subgraphs - ancestors
Definition cgraph.h:434
Definition gvcint.h:80
Dt_t * textfont_dt
Definition gvcint.h:107
obj_state_t * obj
Definition gvcjob.h:269
emit_state_t emit_state
Definition gvcjob.h:192
unsigned labeledgealigned
Definition gvcjob.h:235
double x
Definition geom.h:29
double y
Definition geom.h:29
Definition types.h:48
char * name
Definition types.h:63
char * name
Definition textspan.h:54
double size
Definition textspan.h:57
pointf pos
Definition types.h:114
char * fontcolor
Definition types.h:107
struct textlabel_t::@81::@82 txt
char * text
Definition types.h:105
int charset
Definition types.h:108
size_t nspans
Definition types.h:118
char valign
Definition types.h:122
textspan_t * span
Definition types.h:117
double fontsize
Definition types.h:109
pointf space
Definition types.h:111
htmllabel_t * html
Definition types.h:120
union textlabel_t::@81 u
char * fontname
Definition types.h:106
pointf dimen
Definition types.h:110
char * str
Definition textspan.h:65
char just
'l' 'n' 'r'
Definition textspan.h:71
void * layout
Definition textspan.h:67
pointf size
Definition textspan.h:70
textfont_t * font
Definition textspan.h:66
void(* free_layout)(void *layout)
Definition textspan.h:68
Definition grammar.c:93
#define MAX(a, b)
Definition write.c:31