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