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