Graphviz 12.0.1~dev.20240715.2254
Loading...
Searching...
No Matches
htmltable.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
14/* Implementation of HTML-like tables.
15 *
16 * The (now purged) CodeGen graphics model, especially with integral coodinates, is
17 * not adequate to handle this as we would like. In particular, it is
18 * difficult to handle notions of adjacency and correct rounding to pixels.
19 * For example, if 2 adjacent boxes bb1.UR.x == bb2.LL.x, the rectangles
20 * may be drawn overlapping. However, if we use bb1.UR.x+1 == bb2.LL.x
21 * there may or may not be a gap between them, even in the same device
22 * depending on their positions. When CELLSPACING > 1, this isn't as much
23 * of a problem.
24 *
25 * We allow negative spacing as a hack to allow overlapping cell boundaries.
26 * For the reasons discussed above, this is difficult to get correct.
27 * This is an important enough case we should extend the table model to
28 * support it correctly. This could be done by allowing a table attribute,
29 * e.g., CELLGRID=n, which sets CELLBORDER=0 and has the border drawing
30 * handled correctly by the table.
31 */
32
33#include <assert.h>
34#include <common/render.h>
35#include <common/htmltable.h>
36#include <cgraph/agxbuf.h>
37#include <cgraph/prisize_t.h>
38#include <common/pointset.h>
39#include <common/intset.h>
40#include <cdt/cdt.h>
41#include <cgraph/alloc.h>
42#include <cgraph/exit.h>
43#include <cgraph/strcasecmp.h>
44#include <cgraph/unreachable.h>
45#include <float.h>
46#include <inttypes.h>
47#include <limits.h>
48#include <math.h>
49#include <stddef.h>
50#include <stdbool.h>
51#include <stdint.h>
52#include <stdio.h>
53
54#define DEFAULT_BORDER 1
55#define DEFAULT_CELLPADDING 2
56#define DEFAULT_CELLSPACING 2
57
58typedef struct {
59 char *url;
60 char *tooltip;
61 char *target;
62 char *id;
67
68#ifdef DEBUG
69static void printCell(htmlcell_t * cp, int ind);
70#endif
71
72/* pushFontInfo:
73 * Replace current font attributes in env with ones from fp,
74 * storing old attributes in savp. We only deal with attributes
75 * set in env. The attributes are restored via popFontInfo.
76 */
77static void
79{
80 if (env->finfo.name) {
81 if (fp->name) {
82 savp->name = env->finfo.name;
83 env->finfo.name = fp->name;
84 } else
85 savp->name = NULL;
86 }
87 if (env->finfo.color) {
88 if (fp->color) {
89 savp->color = env->finfo.color;
90 env->finfo.color = fp->color;
91 } else
92 savp->color = NULL;
93 }
94 if (env->finfo.size >= 0) {
95 if (fp->size >= 0) {
96 savp->size = env->finfo.size;
97 env->finfo.size = fp->size;
98 } else
99 savp->size = -1.0;
100 }
101}
102
103/* popFontInfo:
104 * Restore saved font attributes.
105 * Copy only set values.
106 */
107static void popFontInfo(htmlenv_t * env, textfont_t * savp)
108{
109 if (savp->name)
110 env->finfo.name = savp->name;
111 if (savp->color)
112 env->finfo.color = savp->color;
113 if (savp->size >= 0.0)
114 env->finfo.size = savp->size;
115}
116
117static void
118emit_htextspans(GVJ_t *job, size_t nspans, htextspan_t *spans, pointf p,
119 double halfwidth_x, textfont_t finfo, boxf b, int simple)
120{
121 double center_x, left_x, right_x;
122 textspan_t tl;
123 textfont_t tf;
124 pointf p_ = { 0.0, 0.0 };
125 textspan_t *ti;
126
127 center_x = p.x;
128 left_x = center_x - halfwidth_x;
129 right_x = center_x + halfwidth_x;
130
131 /* Initial p is in center of text block; set initial baseline
132 * to top of text block.
133 */
134 p_.y = p.y + (b.UR.y - b.LL.y) / 2.0;
135
137 for (size_t i = 0; i < nspans; i++) {
138 /* set p.x to leftmost point where the line of text begins */
139 switch (spans[i].just) {
140 case 'l':
141 p.x = left_x;
142 break;
143 case 'r':
144 p.x = right_x - spans[i].size;
145 break;
146 default:
147 case 'n':
148 p.x = center_x - spans[i].size / 2.0;
149 break;
150 }
151 p_.y -= spans[i].lfsize; /* move to current base line */
152
153 ti = spans[i].items;
154 for (size_t j = 0; j < spans[i].nitems; j++) {
155 if (ti->font && ti->font->size > 0)
156 tf.size = ti->font->size;
157 else
158 tf.size = finfo.size;
159 if (ti->font && ti->font->name)
160 tf.name = ti->font->name;
161 else
162 tf.name = finfo.name;
163 if (ti->font && ti->font->color)
164 tf.color = ti->font->color;
165 else
166 tf.color = finfo.color;
167 if (ti->font && ti->font->flags)
168 tf.flags = ti->font->flags;
169 else
170 tf.flags = 0;
171
173
174 tl.str = ti->str;
175 tl.font = &tf;
177 if (simple)
179 else
180 tl.yoffset_centerline = 1;
182 tl.layout = ti->layout;
183 tl.size.x = ti->size.x;
184 tl.size.y = spans[i].lfsize;
185 tl.just = 'l';
186
187 p_.x = p.x;
188 gvrender_textspan(job, p_, &tl);
189 p.x += ti->size.x;
190 ti++;
191 }
192 }
193
195}
196
197static void emit_html_txt(GVJ_t * job, htmltxt_t * tp, htmlenv_t * env)
198{
199 double halfwidth_x;
200 pointf p;
201
202 /* make sure that there is something to do */
203 if (tp->nspans < 1)
204 return;
205
206 halfwidth_x = (tp->box.UR.x - tp->box.LL.x) / 2.0;
207 p.x = env->pos.x + (tp->box.UR.x + tp->box.LL.x) / 2.0;
208 p.y = env->pos.y + (tp->box.UR.y + tp->box.LL.y) / 2.0;
209
210 emit_htextspans(job, tp->nspans, tp->spans, p, halfwidth_x, env->finfo,
211 tp->box, tp->simple);
212}
213
214static void doSide(GVJ_t * job, pointf p, double wd, double ht)
215{
216 boxf BF;
217
218 BF.LL = p;
219 BF.UR.x = p.x + wd;
220 BF.UR.y = p.y + ht;
221 gvrender_box(job, BF, 1);
222}
223
224/* mkPts:
225 * Convert boxf into four corner points
226 * If border is > 1, inset the points by half the border.
227 * It is assumed AF is pointf[4], so the data is store there
228 * and AF is returned.
229 */
230static pointf *mkPts(pointf * AF, boxf b, int border)
231{
232 AF[0] = b.LL;
233 AF[2] = b.UR;
234 if (border > 1) {
235 double delta = (double)border / 2.0;
236 AF[0].x += delta;
237 AF[0].y += delta;
238 AF[2].x -= delta;
239 AF[2].y -= delta;
240 }
241 AF[1].x = AF[2].x;
242 AF[1].y = AF[0].y;
243 AF[3].x = AF[0].x;
244 AF[3].y = AF[2].y;
245
246 return AF;
247}
248
249/* doBorder:
250 * Draw a rectangular border for the box b.
251 * Handles dashed and dotted styles, rounded corners.
252 * Also handles thick lines.
253 * Assume dp->border > 0
254 */
255static void doBorder(GVJ_t * job, htmldata_t * dp, boxf b)
256{
257 pointf AF[7];
258 char *sptr[2];
259 char *color = dp->pencolor ? dp->pencolor : DEFAULT_COLOR;
260 unsigned short sides;
261
263 if (dp->style.dashed || dp->style.dotted) {
264 sptr[0] = sptr[1] = NULL;
265 if (dp->style.dashed)
266 sptr[0] = "dashed";
267 else if (dp->style.dotted)
268 sptr[0] = "dotted";
269 gvrender_set_style(job, sptr);
270 } else
273
274 if (dp->style.rounded)
275 round_corners(job, mkPts(AF, b, dp->border), 4,
276 (graphviz_polygon_style_t){.rounded = true}, 0);
277 else if ((sides = (dp->flags & BORDER_MASK))) {
278 mkPts (AF+1, b, dp->border); /* AF[1-4] has LL=SW,SE,UR=NE,NW */
279 switch (sides) {
280 case BORDER_BOTTOM :
281 gvrender_polyline(job, AF+1, 2);
282 break;
283 case BORDER_RIGHT :
284 gvrender_polyline(job, AF+2, 2);
285 break;
286 case BORDER_TOP :
287 gvrender_polyline(job, AF+3, 2);
288 break;
289 case BORDER_LEFT :
290 AF[0] = AF[4];
291 gvrender_polyline(job, AF, 2);
292 break;
294 gvrender_polyline(job, AF+1, 3);
295 break;
297 gvrender_polyline(job, AF+2, 3);
298 break;
300 AF[5] = AF[1];
301 gvrender_polyline(job, AF+3, 3);
302 break;
304 AF[0] = AF[4];
305 gvrender_polyline(job, AF, 3);
306 break;
308 gvrender_polyline(job, AF+1, 4);
309 break;
311 AF[5] = AF[1];
312 gvrender_polyline(job, AF+2, 4);
313 break;
315 AF[5] = AF[1];
316 AF[6] = AF[2];
317 gvrender_polyline(job, AF+3, 4);
318 break;
320 AF[0] = AF[4];
321 gvrender_polyline(job, AF, 4);
322 break;
324 gvrender_polyline(job, AF+1, 2);
325 gvrender_polyline(job, AF+3, 2);
326 break;
328 AF[0] = AF[4];
329 gvrender_polyline(job, AF, 2);
330 gvrender_polyline(job, AF+2, 2);
331 break;
332 default:
333 break;
334 }
335 } else {
336 if (dp->border > 1) {
337 double delta = (double)dp->border / 2.0;
338 b.LL.x += delta;
339 b.LL.y += delta;
340 b.UR.x -= delta;
341 b.UR.y -= delta;
342 }
343 gvrender_box(job, b, 0);
344 }
345}
346
347/* setFill:
348 * Set up fill values from given color; make pen transparent.
349 * Return type of fill required.
350 */
351static int setFill(GVJ_t *job, char *color, int angle, htmlstyle_t style,
352 char *clrs[2]) {
353 int filled;
354 double frac;
355 if (findStopColor(color, clrs, &frac)) {
356 gvrender_set_fillcolor(job, clrs[0]);
357 if (clrs[1])
358 gvrender_set_gradient_vals(job, clrs[1], angle, frac);
359 else
360 gvrender_set_gradient_vals(job, DEFAULT_COLOR, angle, frac);
361 if (style.radial)
362 filled = RGRADIENT;
363 else
364 filled = GRADIENT;
365 } else {
367 filled = FILL;
368 }
369 gvrender_set_pencolor(job, "transparent");
370 return filled;
371}
372
373/* initAnchor:
374 * Save current map values.
375 * Initialize fields in job->obj pertaining to anchors.
376 * In particular, this also sets the output rectangle.
377 * If there is something to do,
378 * start the anchor and returns 1.
379 * Otherwise, it returns 0.
380 *
381 * FIX: Should we provide a tooltip if none is set, as is done
382 * for nodes, edges, etc. ?
383 */
384static int
386 htmlmap_data_t * save)
387{
388 obj_state_t *obj = job->obj;
389 int changed;
390 char *id;
391 static int anchorId;
392 agxbuf xb = {0};
393
394 save->url = obj->url;
395 save->tooltip = obj->tooltip;
396 save->target = obj->target;
397 save->id = obj->id;
398 save->explicit_tooltip = obj->explicit_tooltip != 0;
399 id = data->id;
400 if (!id || !*id) { /* no external id, so use the internal one */
401 if (!env->objid) {
402 env->objid = gv_strdup(getObjId(job, obj->u.n, &xb));
403 env->objid_set = true;
404 }
405 agxbprint(&xb, "%s_%d", env->objid, anchorId++);
406 id = agxbuse(&xb);
407 }
408 changed =
409 initMapData(job, NULL, data->href, data->title, data->target, id,
410 obj->u.g);
411 agxbfree(&xb);
412
413 if (changed) {
414 if (obj->url || obj->explicit_tooltip) {
415 emit_map_rect(job, b);
417 obj->url, obj->tooltip, obj->target,
418 obj->id);
419 }
420 }
421 return changed;
422}
423
424#define RESET(fld) \
425 if(obj->fld != save->fld) {free(obj->fld); obj->fld = save->fld;}
426
427/* endAnchor:
428 * Pop context pushed by initAnchor.
429 * This is done by ending current anchor, restoring old values and
430 * freeing new.
431 *
432 * NB: We don't save or restore geometric map info. This is because
433 * this preservation of map context is only necessary for SVG-like
434 * systems where graphical items are wrapped in an anchor, and we map
435 * top-down. For ordinary map anchors, this is all done bottom-up, so
436 * the geometric map info at the higher level hasn't been emitted yet.
437 */
438static void endAnchor(GVJ_t * job, htmlmap_data_t * save)
439{
440 obj_state_t *obj = job->obj;
441
442 if (obj->url || obj->explicit_tooltip)
444 RESET(url);
445 RESET(tooltip);
446 RESET(target);
447 RESET(id);
449}
450
451/* forward declaration */
452static void emit_html_cell(GVJ_t * job, htmlcell_t * cp, htmlenv_t * env);
453
454/* emit_html_rules:
455 * place vertical and horizontal lines between adjacent cells and
456 * extend the lines to intersect the rounded table boundary
457 */
458static void
459emit_html_rules(GVJ_t * job, htmlcell_t * cp, htmlenv_t * env, char *color, htmlcell_t* nextc)
460{
461 pointf rule_pt;
462 double rule_length;
463 double base;
464 boxf pts = cp->data.box;
465 pointf pos = env->pos;
466
467 if (!color)
471
472 pts = cp->data.box;
473 pts.LL.x += pos.x;
474 pts.UR.x += pos.x;
475 pts.LL.y += pos.y;
476 pts.UR.y += pos.y;
477
478 //Determine vertical line coordinate and length
479 if ((cp->ruled & HTML_VRULE) && cp->col + cp->colspan < cp->parent->column_count) {
480 if (cp->row == 0) { // first row
481 // extend to center of table border and add half cell spacing
482 base = cp->parent->data.border + cp->parent->data.space / 2;
483 rule_pt.y = pts.LL.y - cp->parent->data.space / 2;
484 } else if (cp->row + cp->rowspan == cp->parent->row_count) { // bottom row
485 // extend to center of table border and add half cell spacing
486 base = cp->parent->data.border + cp->parent->data.space / 2;
487 rule_pt.y = pts.LL.y - cp->parent->data.space / 2 - base;
488 } else {
489 base = 0;
490 rule_pt.y = pts.LL.y - cp->parent->data.space / 2;
491 }
492 rule_pt.x = pts.UR.x + cp->parent->data.space / 2;
493 rule_length = base + pts.UR.y - pts.LL.y + cp->parent->data.space;
494 doSide(job, rule_pt, 0, rule_length);
495 }
496 //Determine the horizontal coordinate and length
497 if ((cp->ruled & HTML_HRULE) && cp->row + cp->rowspan < cp->parent->row_count) {
498 if (cp->col == 0) { // first column
499 // extend to center of table border and add half cell spacing
500 base = cp->parent->data.border + cp->parent->data.space / 2;
501 rule_pt.x = pts.LL.x - base - cp->parent->data.space / 2;
502 if (cp->col + cp->colspan == cp->parent->column_count) // also last column
503 base *= 2;
504 /* incomplete row of cells; extend line to end */
505 else if (nextc && nextc->row != cp->row) {
506 base += cp->parent->data.box.UR.x + pos.x - (pts.UR.x + cp->parent->data.space / 2);
507 }
508 } else if (cp->col + cp->colspan == cp->parent->column_count) { // last column
509 // extend to center of table border and add half cell spacing
510 base = cp->parent->data.border + cp->parent->data.space / 2;
511 rule_pt.x = pts.LL.x - cp->parent->data.space / 2;
512 } else {
513 base = 0;
514 rule_pt.x = pts.LL.x - cp->parent->data.space / 2;
515 /* incomplete row of cells; extend line to end */
516 if (nextc && nextc->row != cp->row) {
517 base += cp->parent->data.box.UR.x + pos.x - (pts.UR.x + cp->parent->data.space / 2);
518 }
519 }
520 rule_pt.y = pts.LL.y - cp->parent->data.space / 2;
521 rule_length = base + pts.UR.x - pts.LL.x + cp->parent->data.space;
522 doSide(job, rule_pt, rule_length, 0);
523 }
524}
525
526static void emit_html_tbl(GVJ_t * job, htmltbl_t * tbl, htmlenv_t * env)
527{
528 boxf pts = tbl->data.box;
529 pointf pos = env->pos;
530 htmlcell_t **cells = tbl->u.n.cells;
531 htmlcell_t *cp;
532 static textfont_t savef;
533 htmlmap_data_t saved;
534 int anchor; /* if true, we need to undo anchor settings. */
535 int doAnchor = (tbl->data.href || tbl->data.target);
536 pointf AF[4];
537
538 if (tbl->font)
539 pushFontInfo(env, tbl->font, &savef);
540
541 pts.LL.x += pos.x;
542 pts.UR.x += pos.x;
543 pts.LL.y += pos.y;
544 pts.UR.y += pos.y;
545
546 if (doAnchor && !(job->flags & EMIT_CLUSTERS_LAST))
547 anchor = initAnchor(job, env, &tbl->data, pts, &saved);
548 else
549 anchor = 0;
550
551 if (!tbl->data.style.invisible) {
552
553 /* Fill first */
554 if (tbl->data.bgcolor) {
555 char *clrs[2];
556 int filled =
557 setFill(job, tbl->data.bgcolor, tbl->data.gradientangle,
558 tbl->data.style, clrs);
559 if (tbl->data.style.rounded) {
560 round_corners(job, mkPts(AF, pts, tbl->data.border), 4,
561 (graphviz_polygon_style_t){.rounded = true}, filled);
562 } else
563 gvrender_box(job, pts, filled);
564 free(clrs[0]);
565 }
566
567 while (*cells) {
568 emit_html_cell(job, *cells, env);
569 cells++;
570 }
571
572 /* Draw table rules and border.
573 * Draw after cells so we can draw over any fill.
574 * At present, we set the penwidth to 1 for rules until we provide the calculations to take
575 * into account wider rules.
576 */
577 cells = tbl->u.n.cells;
578 gvrender_set_penwidth(job, 1.0);
579 while ((cp = *cells++)) {
580 if (cp->ruled)
581 emit_html_rules(job, cp, env, tbl->data.pencolor, *cells);
582 }
583
584 if (tbl->data.border)
585 doBorder(job, &tbl->data, pts);
586
587 }
588
589 if (anchor)
590 endAnchor(job, &saved);
591
592 if (doAnchor && (job->flags & EMIT_CLUSTERS_LAST)) {
593 if (initAnchor(job, env, &tbl->data, pts, &saved))
594 endAnchor(job, &saved);
595 }
596
597 if (tbl->font)
598 popFontInfo(env, &savef);
599}
600
601/* emit_html_img:
602 * The image will be centered in the given box.
603 * Scaling is determined by either the image's scale attribute,
604 * or the imagescale attribute of the graph object being drawn.
605 */
606static void emit_html_img(GVJ_t * job, htmlimg_t * cp, htmlenv_t * env)
607{
608 pointf A[4];
609 boxf bb = cp->box;
610 char *scale;
611
612 bb.LL.x += env->pos.x;
613 bb.LL.y += env->pos.y;
614 bb.UR.x += env->pos.x;
615 bb.UR.y += env->pos.y;
616
617 A[0] = bb.UR;
618 A[2] = bb.LL;
619 A[1].x = A[2].x;
620 A[1].y = A[0].y;
621 A[3].x = A[0].x;
622 A[3].y = A[2].y;
623
624 if (cp->scale)
625 scale = cp->scale;
626 else
627 scale = env->imgscale;
628 assert(cp->src);
629 assert(cp->src[0]);
630 gvrender_usershape(job, cp->src, A, 4, true, scale, "mc");
631}
632
633static void emit_html_cell(GVJ_t * job, htmlcell_t * cp, htmlenv_t * env)
634{
635 htmlmap_data_t saved;
636 boxf pts = cp->data.box;
637 pointf pos = env->pos;
638 int inAnchor, doAnchor = (cp->data.href || cp->data.target);
639 pointf AF[4];
640
641 pts.LL.x += pos.x;
642 pts.UR.x += pos.x;
643 pts.LL.y += pos.y;
644 pts.UR.y += pos.y;
645
646 if (doAnchor && !(job->flags & EMIT_CLUSTERS_LAST))
647 inAnchor = initAnchor(job, env, &cp->data, pts, &saved);
648 else
649 inAnchor = 0;
650
651 if (!cp->data.style.invisible) {
652 if (cp->data.bgcolor) {
653 char *clrs[2];
654 int filled =
655 setFill(job, cp->data.bgcolor, cp->data.gradientangle,
656 cp->data.style, clrs);
657 if (cp->data.style.rounded) {
658 round_corners(job, mkPts(AF, pts, cp->data.border), 4,
659 (graphviz_polygon_style_t){.rounded = true}, filled);
660 } else
661 gvrender_box(job, pts, filled);
662 free(clrs[0]);
663 }
664
665 if (cp->data.border)
666 doBorder(job, &cp->data, pts);
667
668 if (cp->child.kind == HTML_TBL)
669 emit_html_tbl(job, cp->child.u.tbl, env);
670 else if (cp->child.kind == HTML_IMAGE)
671 emit_html_img(job, cp->child.u.img, env);
672 else
673 emit_html_txt(job, cp->child.u.txt, env);
674 }
675
676 if (inAnchor)
677 endAnchor(job, &saved);
678
679 if (doAnchor && (job->flags & EMIT_CLUSTERS_LAST)) {
680 if (initAnchor(job, env, &cp->data, pts, &saved))
681 endAnchor(job, &saved);
682 }
683}
684
685/* allocObj:
686 * Push new obj on stack to be used in common by all
687 * html elements with anchors.
688 * This inherits the type, emit_state, and object of the
689 * parent, as well as the url, explicit, target and tooltip.
690 */
691static void allocObj(GVJ_t * job)
692{
693 obj_state_t *obj;
695
696 obj = push_obj_state(job);
697 parent = obj->parent;
698 obj->type = parent->type;
699 obj->emit_state = parent->emit_state;
700 switch (obj->type) {
701 case NODE_OBJTYPE:
702 obj->u.n = parent->u.n;
703 break;
705 obj->u.g = parent->u.g;
706 break;
707 case CLUSTER_OBJTYPE:
708 obj->u.sg = parent->u.sg;
709 break;
710 case EDGE_OBJTYPE:
711 obj->u.e = parent->u.e;
712 break;
713 default:
714 UNREACHABLE();
715 }
716 obj->url = parent->url;
717 obj->tooltip = parent->tooltip;
718 obj->target = parent->target;
719 obj->explicit_tooltip = parent->explicit_tooltip;
720}
721
722static void freeObj(GVJ_t * job)
723{
724 obj_state_t *obj = job->obj;
725
726 obj->url = NULL;
727 obj->tooltip = NULL;
728 obj->target = NULL;
729 obj->id = NULL;
730 pop_obj_state(job);
731}
732
733static double
735{
736 double sz = 0.0;
737
738 switch (lp->kind) {
739 case HTML_TBL:
740 sz = lp->u.tbl->data.box.UR.y - lp->u.tbl->data.box.LL.y;
741 break;
742 case HTML_IMAGE:
743 sz = lp->u.img->box.UR.y - lp->u.img->box.LL.y;
744 break;
745 case HTML_TEXT:
746 sz = lp->u.txt->box.UR.y - lp->u.txt->box.LL.y;
747 break;
748 default:
749 UNREACHABLE();
750 }
751 return sz;
752}
753
754/* emit_html_label:
755 */
757{
758 htmlenv_t env;
759 pointf p;
760
761 allocObj(job);
762
763 p = tp->pos;
764 switch (tp->valign) {
765 case 't':
766 p.y = tp->pos.y + (tp->space.y - heightOfLbl(lp))/ 2.0 - 1;
767 break;
768 case 'b':
769 p.y = tp->pos.y - (tp->space.y - heightOfLbl(lp))/ 2.0 - 1;
770 break;
771 default:
772 /* no-op */
773 break;
774 }
775 env.pos = p;
776 env.finfo.color = tp->fontcolor;
777 env.finfo.name = tp->fontname;
778 env.finfo.size = tp->fontsize;
779 env.imgscale = agget(job->obj->u.n, "imagescale");
780 env.objid = job->obj->id;
781 env.objid_set = false;
782 if (env.imgscale == NULL || env.imgscale[0] == '\0')
783 env.imgscale = "false";
784 if (lp->kind == HTML_TBL) {
785 htmltbl_t *tbl = lp->u.tbl;
786
787 /* set basic graphics context */
788 /* Need to override line style set by node. */
790 if (tbl->data.pencolor)
792 else
794 emit_html_tbl(job, tbl, &env);
795 } else {
796 emit_html_txt(job, lp->u.txt, &env);
797 }
798 if (env.objid_set)
799 free(env.objid);
800 freeObj(job);
801}
802
804{
805 free(dp->href);
806 free(dp->port);
807 free(dp->target);
808 free(dp->id);
809 free(dp->title);
810 free(dp->bgcolor);
811 free(dp->pencolor);
812}
813
815{
816 htextspan_t *tl;
817 textspan_t *ti;
818
819 if (!t)
820 return;
821
822 tl = t->spans;
823 for (size_t i = 0; i < t->nspans; i++) {
824 ti = tl->items;
825 for (size_t j = 0; j < tl->nitems; j++) {
826 free(ti->str);
827 if (ti->layout && ti->free_layout)
828 ti->free_layout(ti->layout);
829 ti++;
830 }
831 tl++;
832 }
833 free(t->spans);
834 free(t);
835}
836
837static void free_html_img(htmlimg_t * ip)
838{
839 free(ip->src);
840 free(ip);
841}
842
843static void free_html_cell(htmlcell_t * cp)
844{
845 free_html_label(&cp->child, 0);
846 free_html_data(&cp->data);
847 free(cp);
848}
849
850/* free_html_tbl:
851 * If tbl->n_rows is negative, table is in initial state from
852 * HTML parse, with data stored in u.p. Once run through processTbl,
853 * data is stored in u.n and tbl->n_rows is > 0.
854 */
855static void free_html_tbl(htmltbl_t * tbl)
856{
857 htmlcell_t **cells;
858
859 if (tbl->row_count == SIZE_MAX) { // raw, parsed table
860 dtclose(tbl->u.p.rows);
861 } else {
862 cells = tbl->u.n.cells;
863
864 free(tbl->heights);
865 free(tbl->widths);
866 while (*cells) {
867 free_html_cell(*cells);
868 cells++;
869 }
870 free(tbl->u.n.cells);
871 }
872 free_html_data(&tbl->data);
873 free(tbl);
874}
875
876void free_html_label(htmllabel_t * lp, int root)
877{
878 if (lp->kind == HTML_TBL)
879 free_html_tbl(lp->u.tbl);
880 else if (lp->kind == HTML_IMAGE)
881 free_html_img(lp->u.img);
882 else
883 free_html_text(lp->u.txt);
884 if (root)
885 free(lp);
886}
887
888static htmldata_t *portToTbl(htmltbl_t *, char *); /* forward declaration */
889
890static htmldata_t *portToCell(htmlcell_t * cp, char *id)
891{
892 htmldata_t *rv;
893
894 if (cp->data.port && strcasecmp(cp->data.port, id) == 0)
895 rv = &cp->data;
896 else if (cp->child.kind == HTML_TBL)
897 rv = portToTbl(cp->child.u.tbl, id);
898 else
899 rv = NULL;
900
901 return rv;
902}
903
904/* portToTbl:
905 * See if tp or any of its child cells has the given port id.
906 * If true, return corresponding box.
907 */
908static htmldata_t *portToTbl(htmltbl_t * tp, char *id)
909{
910 htmldata_t *rv;
911 htmlcell_t **cells;
912 htmlcell_t *cp;
913
914 if (tp->data.port && strcasecmp(tp->data.port, id) == 0)
915 rv = &tp->data;
916 else {
917 rv = NULL;
918 cells = tp->u.n.cells;
919 while ((cp = *cells++)) {
920 if ((rv = portToCell(cp, id)))
921 break;
922 }
923 }
924
925 return rv;
926}
927
928/* html_port:
929 * See if edge port corresponds to part of the html node.
930 * Assume pname != "".
931 * If successful, return pointer to port's box.
932 * Else return NULL.
933 */
934boxf *html_port(node_t *n, char *pname, unsigned char *sides){
935 htmldata_t *tp;
936 htmllabel_t *lbl = ND_label(n)->u.html;
937 boxf *rv = NULL;
938
939 if (lbl->kind == HTML_TEXT)
940 return NULL;
941
942 tp = portToTbl(lbl->u.tbl, pname);
943 if (tp) {
944 rv = &tp->box;
945 *sides = tp->sides;
946 }
947 return rv;
948
949}
950
951static int size_html_txt(GVC_t *gvc, htmltxt_t * ftxt, htmlenv_t * env)
952{
953 double xsize = 0.0; /* width of text block */
954 double ysize = 0.0; /* height of text block */
955 double lsize; /* height of current line */
956 double mxfsize = 0.0; /* max. font size for the current line */
957 double curbline = 0.0; /* dist. of current base line from top */
958 pointf sz;
959 double width;
960 textspan_t lp;
961 textfont_t tf = {NULL,NULL,NULL,0.0,0,0};
962 double maxoffset, mxysize = 0.0;
963 bool simple = true; // one item per span, same font size/face, no flags
964 double prev_fsize = -1;
965 char* prev_fname = NULL;
966
967 for (size_t i = 0; i < ftxt->nspans; i++) {
968 if (ftxt->spans[i].nitems > 1) {
969 simple = false;
970 break;
971 }
972 if (ftxt->spans[i].items[0].font) {
973 if (ftxt->spans[i].items[0].font->flags) {
974 simple = false;
975 break;
976 }
977 if (ftxt->spans[i].items[0].font->size > 0)
978 tf.size = ftxt->spans[i].items[0].font->size;
979 else
980 tf.size = env->finfo.size;
981 if (ftxt->spans[i].items[0].font->name)
982 tf.name = ftxt->spans[i].items[0].font->name;
983 else
984 tf.name = env->finfo.name;
985 }
986 else {
987 tf.size = env->finfo.size;
988 tf.name = env->finfo.name;
989 }
990 if (i == 0)
991 prev_fsize = tf.size;
992 else if (tf.size != prev_fsize) {
993 simple = false;
994 break;
995 }
996 if (prev_fname == NULL)
997 prev_fname = tf.name;
998 else if (strcmp(tf.name,prev_fname)) {
999 simple = false;
1000 break;
1001 }
1002 }
1003 ftxt->simple = simple;
1004
1005 for (size_t i = 0; i < ftxt->nspans; i++) {
1006 width = 0;
1007 mxysize = maxoffset = mxfsize = 0;
1008 for (size_t j = 0; j < ftxt->spans[i].nitems; j++) {
1009 lp.str =
1010 strdup_and_subst_obj(ftxt->spans[i].items[j].str,
1011 env->obj);
1012 if (ftxt->spans[i].items[j].font) {
1013 if (ftxt->spans[i].items[j].font->flags)
1014 tf.flags = ftxt->spans[i].items[j].font->flags;
1015 else if (env->finfo.flags > 0)
1016 tf.flags = env->finfo.flags;
1017 else
1018 tf.flags = 0;
1019 if (ftxt->spans[i].items[j].font->size > 0)
1020 tf.size = ftxt->spans[i].items[j].font->size;
1021 else
1022 tf.size = env->finfo.size;
1023 if (ftxt->spans[i].items[j].font->name)
1024 tf.name = ftxt->spans[i].items[j].font->name;
1025 else
1026 tf.name = env->finfo.name;
1027 if (ftxt->spans[i].items[j].font->color)
1028 tf.color = ftxt->spans[i].items[j].font->color;
1029 else
1030 tf.color = env->finfo.color;
1031 } else {
1032 tf.size = env->finfo.size;
1033 tf.name = env->finfo.name;
1034 tf.color = env->finfo.color;
1035 tf.flags = env->finfo.flags;
1036 }
1037 lp.font = dtinsert(gvc->textfont_dt, &tf);
1038 sz = textspan_size(gvc, &lp);
1039 free(ftxt->spans[i].items[j].str);
1040 ftxt->spans[i].items[j].str = lp.str;
1041 ftxt->spans[i].items[j].size.x = sz.x;
1042 ftxt->spans[i].items[j].yoffset_layout = lp.yoffset_layout;
1044 ftxt->spans[i].items[j].font = lp.font;
1045 ftxt->spans[i].items[j].layout = lp.layout;
1046 ftxt->spans[i].items[j].free_layout = lp.free_layout;
1047 width += sz.x;
1048 mxfsize = MAX(tf.size, mxfsize);
1049 mxysize = MAX(sz.y, mxysize);
1050 maxoffset = MAX(lp.yoffset_centerline, maxoffset);
1051 }
1052 /* lsize = mxfsize * LINESPACING; */
1053 ftxt->spans[i].size = width;
1054 /* ysize - curbline is the distance from the previous
1055 * baseline to the bottom of the previous line.
1056 * Then, in the current line, we set the baseline to
1057 * be 5/6 of the max. font size. Thus, lfsize gives the
1058 * distance from the previous baseline to the new one.
1059 */
1060 /* ftxt->spans[i].lfsize = 5*mxfsize/6 + ysize - curbline; */
1061 if (simple) {
1062 lsize = mxysize;
1063 if (i == 0)
1064 ftxt->spans[i].lfsize = mxfsize;
1065 else
1066 ftxt->spans[i].lfsize = mxysize;
1067 }
1068 else {
1069 lsize = mxfsize;
1070 if (i == 0)
1071 ftxt->spans[i].lfsize = mxfsize - maxoffset;
1072 else
1073 ftxt->spans[i].lfsize = mxfsize + ysize - curbline - maxoffset;
1074 }
1075 curbline += ftxt->spans[i].lfsize;
1076 xsize = MAX(width, xsize);
1077 ysize += lsize;
1078 }
1079 ftxt->box.UR.x = xsize;
1080 if (ftxt->nspans == 1)
1081 ftxt->box.UR.y = mxysize;
1082 else
1083 ftxt->box.UR.y = ysize;
1084 return 0;
1085}
1086
1087/* forward declarion for recursive usage */
1088static int size_html_tbl(graph_t * g, htmltbl_t * tbl, htmlcell_t * parent,
1089 htmlenv_t * env);
1090
1091/* size_html_img:
1092 */
1093static int size_html_img(htmlimg_t * img, htmlenv_t * env)
1094{
1095 box b;
1096 int rv;
1097
1098 b.LL.x = b.LL.y = 0;
1099 b.UR = gvusershape_size(env->g, img->src);
1100 if (b.UR.x == -1 && b.UR.y == -1) {
1101 rv = 1;
1102 b.UR.x = b.UR.y = 0;
1103 agerrorf("No or improper image file=\"%s\"\n", img->src);
1104 } else {
1105 rv = 0;
1106 GD_has_images(env->g) = true;
1107 }
1108
1109 B2BF(b, img->box);
1110 return rv;
1111}
1112
1113/* size_html_cell:
1114 */
1115static int
1117 htmlenv_t * env)
1118{
1119 int rv;
1120 pointf sz, child_sz;
1121 int margin;
1122
1123 cp->parent = parent;
1124 if (!(cp->data.flags & PAD_SET)) {
1125 if (parent->data.flags & PAD_SET)
1126 cp->data.pad = parent->data.pad;
1127 else
1129 }
1130 if (!(cp->data.flags & BORDER_SET)) {
1131 if (parent->cellborder >= 0)
1132 cp->data.border = (unsigned char)parent->cellborder;
1133 else if (parent->data.flags & BORDER_SET)
1134 cp->data.border = parent->data.border;
1135 else
1137 }
1138
1139 if (cp->child.kind == HTML_TBL) {
1140 rv = size_html_tbl(g, cp->child.u.tbl, cp, env);
1141 child_sz = cp->child.u.tbl->data.box.UR;
1142 } else if (cp->child.kind == HTML_IMAGE) {
1143 rv = size_html_img(cp->child.u.img, env);
1144 child_sz = cp->child.u.img->box.UR;
1145 } else {
1146 rv = size_html_txt(GD_gvc(g), cp->child.u.txt, env);
1147 child_sz = cp->child.u.txt->box.UR;
1148 }
1149
1150 margin = 2 * (cp->data.pad + cp->data.border);
1151 sz.x = child_sz.x + margin;
1152 sz.y = child_sz.y + margin;
1153
1154 if (cp->data.flags & FIXED_FLAG) {
1155 if (cp->data.width && cp->data.height) {
1156 if ((cp->data.width < sz.x || cp->data.height < sz.y) && cp->child.kind != HTML_IMAGE) {
1157 agwarningf("cell size too small for content\n");
1158 rv = 1;
1159 }
1160 sz.x = sz.y = 0;
1161
1162 } else {
1163 agwarningf(
1164 "fixed cell size with unspecified width or height\n");
1165 rv = 1;
1166 }
1167 }
1168 cp->data.box.UR.x = MAX(sz.x, cp->data.width);
1169 cp->data.box.UR.y = MAX(sz.y, cp->data.height);
1170 return rv;
1171}
1172
1173static uint16_t findCol(PointSet *ps, int row, int col, htmlcell_t *cellp) {
1174 int notFound = 1;
1175 int lastc;
1176 int i, j, c;
1177 int end = cellp->colspan - 1;
1178
1179 while (notFound) {
1180 lastc = col + end;
1181 for (c = lastc; c >= col; c--) {
1182 if (isInPS(ps, c, row))
1183 break;
1184 }
1185 if (c >= col) /* conflict : try column after */
1186 col = c + 1;
1187 else
1188 notFound = 0;
1189 }
1190 for (j = col; j < col + cellp->colspan; j++) {
1191 for (i = row; i < row + cellp->rowspan; i++) {
1192 addPS(ps, j, i);
1193 }
1194 }
1195 assert(col >= 0 && col <= UINT16_MAX);
1196 return (uint16_t)col;
1197}
1198
1199/* processTbl:
1200 * Convert parser representation of cells into final form.
1201 * Find column and row positions of cells.
1202 * Recursively size cells.
1203 * Return 1 if problem sizing a cell.
1204 */
1205static int processTbl(graph_t * g, htmltbl_t * tbl, htmlenv_t * env)
1206{
1207 pitem *rp;
1208 pitem *cp;
1209 Dt_t *cdict;
1210 htmlcell_t *cellp;
1211 htmlcell_t **cells;
1212 Dt_t *rows = tbl->u.p.rows;
1213 int rv = 0;
1214 size_t n_rows = 0;
1215 size_t n_cols = 0;
1216 PointSet *ps = newPS();
1217 Dt_t *is = openIntSet();
1218
1219 rp = (pitem *) dtflatten(rows);
1220 size_t cnt = 0;
1221 uint16_t r = 0;
1222 while (rp) {
1223 cdict = rp->u.rp;
1224 cp = (pitem *) dtflatten(cdict);
1225 while (cp) {
1226 cellp = cp->u.cp;
1227 cnt++;
1228 cp = (pitem *)dtlink(cdict, cp);
1229 }
1230 if (rp->ruled) {
1231 addIntSet(is, r + 1);
1232 }
1233 rp = (pitem *)dtlink(rows, rp);
1234 r++;
1235 }
1236
1237 cells = tbl->u.n.cells = gv_calloc(cnt + 1, sizeof(htmlcell_t *));
1238 rp = (pitem *) dtflatten(rows);
1239 r = 0;
1240 while (rp) {
1241 cdict = rp->u.rp;
1242 cp = (pitem *) dtflatten(cdict);
1243 uint16_t c = 0;
1244 while (cp) {
1245 cellp = cp->u.cp;
1246 *cells++ = cellp;
1247 rv |= size_html_cell(g, cellp, tbl, env);
1248 c = findCol(ps, r, c, cellp);
1249 cellp->row = r;
1250 cellp->col = c;
1251 c += cellp->colspan;
1252 n_cols = MAX(c, n_cols);
1253 n_rows = MAX(r + cellp->rowspan, n_rows);
1254 if (inIntSet(is, r + cellp->rowspan))
1255 cellp->ruled |= HTML_HRULE;
1256 cp = (pitem *)dtlink(cdict, cp);
1257 }
1258 rp = (pitem *)dtlink(rows, rp);
1259 r++;
1260 }
1261 tbl->row_count = n_rows;
1262 tbl->column_count = n_cols;
1263 dtclose(rows);
1264 dtclose(is);
1265 freePS(ps);
1266 return rv;
1267}
1268
1269/* Split size x over n pieces with spacing s.
1270 * We subtract s*(n-1) from x, divide by n and
1271 * take the ceiling.
1272 */
1273#define SPLIT(x,n,s) (((x) - ((s)-1)*((n)-1)) / (n))
1274
1275/* sizeLinearArray:
1276 * Determine sizes of rows and columns. The size of a column is the
1277 * maximum width of any cell in it. Similarly for rows.
1278 * A cell spanning columns contributes proportionately to each column
1279 * it is in.
1280 */
1281static void sizeLinearArray(htmltbl_t * tbl)
1282{
1283 htmlcell_t *cp;
1284 htmlcell_t **cells;
1285 int i;
1286
1287 tbl->heights = gv_calloc(tbl->row_count + 1, sizeof(double));
1288 tbl->widths = gv_calloc(tbl->column_count + 1, sizeof(double));
1289
1290 for (cells = tbl->u.n.cells; *cells; cells++) {
1291 cp = *cells;
1292 double ht;
1293 if (cp->rowspan == 1)
1294 ht = cp->data.box.UR.y;
1295 else {
1296 ht = SPLIT(cp->data.box.UR.y, cp->rowspan, tbl->data.space);
1297 ht = fmax(ht, 1);
1298 }
1299 double wd;
1300 if (cp->colspan == 1)
1301 wd = cp->data.box.UR.x;
1302 else {
1303 wd = SPLIT(cp->data.box.UR.x, cp->colspan, tbl->data.space);
1304 wd = fmax(wd, 1);
1305 }
1306 for (i = cp->row; i < cp->row + cp->rowspan; i++) {
1307 tbl->heights[i] = fmax(tbl->heights[i], ht);
1308 }
1309 for (i = cp->col; i < cp->col + cp->colspan; i++) {
1310 tbl->widths[i] = fmax(tbl->widths[i], wd);
1311 }
1312 }
1313}
1314
1315/* closeGraphs:
1316 * Clean up graphs made for setting column and row widths.
1317 */
1318static void closeGraphs(graph_t * rowg, graph_t * colg)
1319{
1320 node_t *n;
1321 for (n = GD_nlist(colg); n; n = ND_next(n)) {
1322 free_list(ND_in(n));
1323 free_list(ND_out(n));
1324 }
1325
1326 agclose(rowg);
1327 agclose(colg);
1328}
1329
1330/* checkChain:
1331 * For each pair of nodes in the node list, add an edge if none exists.
1332 * Assumes node list has nodes ordered correctly.
1333 */
1334static void checkChain(graph_t * g)
1335{
1336 node_t *t;
1337 node_t *h;
1338 edge_t *e;
1339 t = GD_nlist(g);
1340 for (h = ND_next(t); h; h = ND_next(h)) {
1341 if (!agfindedge(g, t, h)) {
1342 e = agedge(g, t, h, NULL, 1);
1343 agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true);
1344 ED_minlen(e) = 0;
1345 elist_append(e, ND_out(t));
1346 elist_append(e, ND_in(h));
1347 }
1348 t = h;
1349 }
1350}
1351
1352/* checkEdge:
1353 * Check for edge in g. If it exists, set its minlen to max of sz and
1354 * current minlen. Else, create it and set minlen to sz.
1355 */
1356static void checkEdge(graph_t *g, node_t *t, node_t *h, double sz) {
1357 edge_t* e;
1358
1359 const int sz_as_int = sz > INT_MAX ? INT_MAX :
1360 sz < INT_MIN ? INT_MIN : (int)sz;
1361
1362 e = agfindedge (g, t, h);
1363 if (e)
1364 ED_minlen(e) = MAX(ED_minlen(e), sz_as_int);
1365 else {
1366 e = agedge(g, t, h, NULL, 1);
1367 agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true);
1368 ED_minlen(e) = sz_as_int;
1369 elist_append(e, ND_out(t));
1370 elist_append(e, ND_in(h));
1371 }
1372}
1373
1374/* makeGraphs:
1375 * Generate dags modeling the row and column constraints.
1376 * If the table has column_count columns, we create the graph
1377 * 0 -> 1 -> 2 -> ... -> column_count
1378 * and if a cell starts in column c with span colspan, with
1379 * width w, we add the edge c -> c+colspan [minlen = w].
1380 * Ditto for rows.
1381 *
1382 */
1383static void makeGraphs(htmltbl_t * tbl, graph_t * rowg, graph_t * colg)
1384{
1385 htmlcell_t *cp;
1386 htmlcell_t **cells;
1387 node_t *t;
1388 node_t *lastn;
1389 node_t *h;
1390 agxbuf value_buffer = {0};
1391
1392 lastn = NULL;
1393 for (size_t i = 0; i <= tbl->column_count; i++) {
1394 agxbprint(&value_buffer, "%" PRISIZE_T, i);
1395 t = agnode(colg, agxbuse(&value_buffer), 1);
1396 agbindrec(t, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true);
1397 alloc_elist(tbl->row_count, ND_in(t));
1398 alloc_elist(tbl->row_count, ND_out(t));
1399 if (lastn) {
1400 ND_next(lastn) = t;
1401 lastn = t;
1402 } else {
1403 lastn = GD_nlist(colg) = t;
1404 }
1405 }
1406 lastn = NULL;
1407 for (size_t i = 0; i <= tbl->row_count; i++) {
1408 agxbprint(&value_buffer, "%" PRISIZE_T, i);
1409 t = agnode(rowg, agxbuse(&value_buffer), 1);
1410 agbindrec(t, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true);
1411 alloc_elist(tbl->column_count, ND_in(t));
1413 if (lastn) {
1414 ND_next(lastn) = t;
1415 lastn = t;
1416 } else {
1417 lastn = GD_nlist(rowg) = t;
1418 }
1419 }
1420
1421 for (cells = tbl->u.n.cells; *cells; cells++) {
1422 cp = *cells;
1423 agxbprint(&value_buffer, "%" PRIu16, cp->col);
1424 t = agfindnode(colg, agxbuse(&value_buffer));
1425 agxbprint(&value_buffer, "%d", cp->col + cp->colspan);
1426 h = agfindnode(colg, agxbuse(&value_buffer));
1427 checkEdge (colg, t, h, cp->data.box.UR.x);
1428
1429 agxbprint(&value_buffer, "%" PRIu16, cp->row);
1430 t = agfindnode(rowg, agxbuse(&value_buffer));
1431 agxbprint(&value_buffer, "%d", cp->row + cp->rowspan);
1432 h = agfindnode(rowg, agxbuse(&value_buffer));
1433 checkEdge (rowg, t, h, cp->data.box.UR.y);
1434 }
1435
1436 agxbfree(&value_buffer);
1437
1438 /* Make sure that 0 <= 1 <= 2 ...k. This implies graph connected. */
1439 checkChain(colg);
1440 checkChain(rowg);
1441}
1442
1443/* setSizes:
1444 * Use rankings to determine cell dimensions. The rank values
1445 * give the coordinate, so to get the width/height, we have
1446 * to subtract the previous value.
1447 */
1448static void setSizes(htmltbl_t * tbl, graph_t * rowg, graph_t * colg)
1449{
1450 int i;
1451 node_t *n;
1452 int prev;
1453
1454 prev = 0;
1455 n = GD_nlist(rowg);
1456 for (i = 0, n = ND_next(n); n; i++, n = ND_next(n)) {
1457 tbl->heights[i] = ND_rank(n) - prev;
1458 prev = ND_rank(n);
1459 }
1460 prev = 0;
1461 n = GD_nlist(colg);
1462 for (i = 0, n = ND_next(n); n; i++, n = ND_next(n)) {
1463 tbl->widths[i] = ND_rank(n) - prev;
1464 prev = ND_rank(n);
1465 }
1466
1467}
1468
1469/* sizeArray:
1470 * Set column and row sizes. Optimize for minimum width and
1471 * height. Where there is slack, try to distribute evenly.
1472 * We do this by encoding cells as edges with min length is
1473 * a dag on a chain. We then run network simplex, using
1474 * LR_balance.
1475 */
1476static void sizeArray(htmltbl_t * tbl)
1477{
1478 graph_t *rowg;
1479 graph_t *colg;
1481
1482 /* Do the 1D cases by hand */
1483 if (tbl->row_count == 1 || tbl->column_count == 1) {
1484 sizeLinearArray(tbl);
1485 return;
1486 }
1487
1488 tbl->heights = gv_calloc(tbl->row_count + 1, sizeof(double));
1489 tbl->widths = gv_calloc(tbl->column_count + 1, sizeof(double));
1490
1491 rowg = agopen("rowg", dir, NULL);
1492 colg = agopen("colg", dir, NULL);
1493 /* Only need GD_nlist */
1494 agbindrec(rowg, "Agraphinfo_t", sizeof(Agraphinfo_t), true); // graph custom data
1495 agbindrec(colg, "Agraphinfo_t", sizeof(Agraphinfo_t), true); // graph custom data
1496 makeGraphs(tbl, rowg, colg);
1497 rank(rowg, 2, INT_MAX);
1498 rank(colg, 2, INT_MAX);
1499 setSizes(tbl, rowg, colg);
1500 closeGraphs(rowg, colg);
1501}
1502
1503static void pos_html_tbl(htmltbl_t *, boxf, unsigned char);
1504
1505/* pos_html_img:
1506 * Place image in cell
1507 * storing allowed space handed by parent cell.
1508 * How this space is used is handled in emit_html_img.
1509 */
1510static void pos_html_img(htmlimg_t * cp, boxf pos)
1511{
1512 cp->box = pos;
1513}
1514
1515/* pos_html_txt:
1516 * Set default alignment.
1517 */
1518static void pos_html_txt(htmltxt_t * ftxt, char c)
1519{
1520 for (size_t i = 0; i < ftxt->nspans; i++) {
1521 if (ftxt->spans[i].just == UNSET_ALIGN) /* unset */
1522 ftxt->spans[i].just = c;
1523 }
1524}
1525
1526/* pos_html_cell:
1527 */
1528static void pos_html_cell(htmlcell_t *cp, boxf pos, unsigned char sides) {
1529 double delx, dely;
1530 pointf oldsz;
1531 boxf cbox;
1532
1533 if (!cp->data.pencolor && cp->parent->data.pencolor)
1535
1536 /* If fixed, align cell */
1537 if (cp->data.flags & FIXED_FLAG) {
1538 oldsz = cp->data.box.UR;
1539 delx = pos.UR.x - pos.LL.x - oldsz.x;
1540 if (delx > 0) {
1541 switch (cp->data.flags & HALIGN_MASK) {
1542 case HALIGN_LEFT:
1543 pos.UR.x = pos.LL.x + oldsz.x;
1544 break;
1545 case HALIGN_RIGHT:
1546 pos.UR.x += delx;
1547 pos.LL.x += delx;
1548 break;
1549 default:
1550 pos.LL.x += delx / 2;
1551 pos.UR.x -= delx / 2;
1552 break;
1553 }
1554 }
1555 dely = pos.UR.y - pos.LL.y - oldsz.y;
1556 if (dely > 0) {
1557 switch (cp->data.flags & VALIGN_MASK) {
1558 case VALIGN_BOTTOM:
1559 pos.UR.y = pos.LL.y + oldsz.y;
1560 break;
1561 case VALIGN_TOP:
1562 pos.UR.y += dely;
1563 pos.LL.y += dely;
1564 break;
1565 default:
1566 pos.LL.y += dely / 2;
1567 pos.UR.y -= dely / 2;
1568 break;
1569 }
1570 }
1571 }
1572 cp->data.box = pos;
1573 cp->data.sides = sides;
1574
1575 /* set up child's position */
1576 cbox.LL.x = pos.LL.x + cp->data.border + cp->data.pad;
1577 cbox.LL.y = pos.LL.y + cp->data.border + cp->data.pad;
1578 cbox.UR.x = pos.UR.x - cp->data.border - cp->data.pad;
1579 cbox.UR.y = pos.UR.y - cp->data.border - cp->data.pad;
1580
1581 if (cp->child.kind == HTML_TBL) {
1582 pos_html_tbl(cp->child.u.tbl, cbox, sides);
1583 } else if (cp->child.kind == HTML_IMAGE) {
1584 /* Note that alignment trumps scaling */
1585 oldsz = cp->child.u.img->box.UR;
1586 delx = cbox.UR.x - cbox.LL.x - oldsz.x;
1587 if (delx > 0) {
1588 switch (cp->data.flags & HALIGN_MASK) {
1589 case HALIGN_LEFT:
1590 cbox.UR.x -= delx;
1591 break;
1592 case HALIGN_RIGHT:
1593 cbox.LL.x += delx;
1594 break;
1595 default:
1596 break;
1597 }
1598 }
1599
1600 dely = cbox.UR.y - cbox.LL.y - oldsz.y;
1601 if (dely > 0) {
1602 switch (cp->data.flags & VALIGN_MASK) {
1603 case VALIGN_BOTTOM:
1604 cbox.UR.y -= dely;
1605 break;
1606 case VALIGN_TOP:
1607 cbox.LL.y += dely;
1608 break;
1609 default:
1610 break;
1611 }
1612 }
1613 pos_html_img(cp->child.u.img, cbox);
1614 } else {
1615 char dfltalign;
1616 int af;
1617
1618 oldsz = cp->child.u.txt->box.UR;
1619 delx = cbox.UR.x - cbox.LL.x - oldsz.x;
1620 /* If the cell is larger than the text block and alignment is
1621 * done at textblock level, the text box is shrunk accordingly.
1622 */
1623 if (delx > 0 && (af = (cp->data.flags & HALIGN_MASK)) != HALIGN_TEXT) {
1624 switch (af) {
1625 case HALIGN_LEFT:
1626 cbox.UR.x -= delx;
1627 break;
1628 case HALIGN_RIGHT:
1629 cbox.LL.x += delx;
1630 break;
1631 default:
1632 cbox.LL.x += delx / 2;
1633 cbox.UR.x -= delx / 2;
1634 break;
1635 }
1636 }
1637
1638 dely = cbox.UR.y - cbox.LL.y - oldsz.y;
1639 if (dely > 0) {
1640 switch (cp->data.flags & VALIGN_MASK) {
1641 case VALIGN_BOTTOM:
1642 cbox.UR.y -= dely;
1643 break;
1644 case VALIGN_TOP:
1645 cbox.LL.y += dely;
1646 break;
1647 default:
1648 cbox.LL.y += dely / 2;
1649 cbox.UR.y -= dely / 2;
1650 break;
1651 }
1652 }
1653 cp->child.u.txt->box = cbox;
1654
1655 /* Set default text alignment
1656 */
1657 switch (cp->data.flags & BALIGN_MASK) {
1658 case BALIGN_LEFT:
1659 dfltalign = 'l';
1660 break;
1661 case BALIGN_RIGHT:
1662 dfltalign = 'r';
1663 break;
1664 default:
1665 dfltalign = 'n';
1666 break;
1667 }
1668 pos_html_txt(cp->child.u.txt, dfltalign);
1669 }
1670}
1671
1672/* pos_html_tbl:
1673 * Position table given its box, then calculate
1674 * the position of each cell. In addition, set the sides
1675 * attribute indicating which external sides of the node
1676 * are accessible to the table.
1677 */
1678static void pos_html_tbl(htmltbl_t *tbl, boxf pos, unsigned char sides) {
1679 int plus;
1680 htmlcell_t **cells = tbl->u.n.cells;
1681 htmlcell_t *cp;
1682 boxf cbox;
1683
1684 if (tbl->u.n.parent && tbl->u.n.parent->data.pencolor
1685 && !tbl->data.pencolor)
1686 tbl->data.pencolor = gv_strdup(tbl->u.n.parent->data.pencolor);
1687
1688 double oldsz = tbl->data.box.UR.x;
1689 double delx = fmax(pos.UR.x - pos.LL.x - oldsz, 0);
1690 oldsz = tbl->data.box.UR.y;
1691 double dely = fmax(pos.UR.y - pos.LL.y - oldsz, 0);
1692
1693 /* If fixed, align box */
1694 if (tbl->data.flags & FIXED_FLAG) {
1695 if (delx > 0) {
1696 switch (tbl->data.flags & HALIGN_MASK) {
1697 case HALIGN_LEFT:
1698 pos.UR.x = pos.LL.x + oldsz;
1699 break;
1700 case HALIGN_RIGHT:
1701 pos.UR.x += delx;
1702 pos.LL.x += delx;
1703 break;
1704 default:
1705 pos.LL.x += delx / 2;
1706 pos.UR.x -= delx / 2;
1707 break;
1708 }
1709 delx = 0;
1710 }
1711 if (dely > 0) {
1712 switch (tbl->data.flags & VALIGN_MASK) {
1713 case VALIGN_BOTTOM:
1714 pos.UR.y = pos.LL.y + oldsz;
1715 break;
1716 case VALIGN_TOP:
1717 pos.LL.y += dely;
1718 pos.UR.y = pos.LL.y + oldsz;
1719 break;
1720 default:
1721 pos.LL.y += dely / 2;
1722 pos.UR.y -= dely / 2;
1723 break;
1724 }
1725 dely = 0;
1726 }
1727 }
1728
1729 /* change sizes to start positions and distribute extra space */
1730 double x = pos.LL.x + tbl->data.border + tbl->data.space;
1731 assert(tbl->column_count <= DBL_MAX);
1732 double extra = delx / (double)tbl->column_count;
1733 plus = ROUND(delx - extra * (double)tbl->column_count);
1734 for (size_t i = 0; i <= tbl->column_count; i++) {
1735 delx = tbl->widths[i] + extra + ((i <= INT_MAX && (int)i < plus) ? 1 : 0);
1736 tbl->widths[i] = x;
1737 x += delx + tbl->data.space;
1738 }
1739 double y = pos.UR.y - tbl->data.border - tbl->data.space;
1740 assert(tbl->row_count <= DBL_MAX);
1741 extra = dely / (double)tbl->row_count;
1742 plus = ROUND(dely - extra * (double)tbl->row_count);
1743 for (size_t i = 0; i <= tbl->row_count; i++) {
1744 dely = tbl->heights[i] + extra + ((i <= INT_MAX && (int)i < plus) ? 1 : 0);
1745 tbl->heights[i] = y;
1746 y -= dely + tbl->data.space;
1747 }
1748
1749 while ((cp = *cells++)) {
1750 unsigned char mask = 0;
1751 if (sides) {
1752 if (cp->col == 0)
1753 mask |= LEFT;
1754 if (cp->row == 0)
1755 mask |= TOP;
1756 if (cp->col + cp->colspan == tbl->column_count)
1757 mask |= RIGHT;
1758 if (cp->row + cp->rowspan == tbl->row_count)
1759 mask |= BOTTOM;
1760 }
1761 cbox.LL.x = tbl->widths[cp->col];
1762 cbox.UR.x = tbl->widths[cp->col + cp->colspan] - tbl->data.space;
1763 cbox.UR.y = tbl->heights[cp->row];
1764 cbox.LL.y = tbl->heights[cp->row + cp->rowspan] + tbl->data.space;
1765 pos_html_cell(cp, cbox, sides & mask);
1766 }
1767
1768 tbl->data.sides = sides;
1769 tbl->data.box = pos;
1770}
1771
1772/* size_html_tbl:
1773 * Determine the size of a table by first determining the
1774 * size of each cell.
1775 */
1776static int
1778 htmlenv_t * env)
1779{
1780 int rv = 0;
1781 static textfont_t savef;
1782
1783 if (tbl->font)
1784 pushFontInfo(env, tbl->font, &savef);
1785 tbl->u.n.parent = parent;
1786 rv = processTbl(g, tbl, env);
1787
1788 /* Set up border and spacing */
1789 if (!(tbl->data.flags & SPACE_SET)) {
1791 }
1792 if (!(tbl->data.flags & BORDER_SET)) {
1793 tbl->data.border = DEFAULT_BORDER;
1794 }
1795
1796 sizeArray(tbl);
1797
1798 assert(tbl->column_count <= DBL_MAX);
1799 double wd = ((double)tbl->column_count + 1) * tbl->data.space + 2 * tbl->data.border;
1800 assert(tbl->row_count <= DBL_MAX);
1801 double ht = ((double)tbl->row_count + 1) * tbl->data.space + 2 * tbl->data.border;
1802 for (size_t i = 0; i < tbl->column_count; i++)
1803 wd += tbl->widths[i];
1804 for (size_t i = 0; i < tbl->row_count; i++)
1805 ht += tbl->heights[i];
1806
1807 if (tbl->data.flags & FIXED_FLAG) {
1808 if (tbl->data.width && tbl->data.height) {
1809 if (tbl->data.width < wd || tbl->data.height < ht) {
1810 agwarningf("table size too small for content\n");
1811 rv = 1;
1812 }
1813 wd = 0;
1814 ht = 0;
1815 } else {
1816 agwarningf(
1817 "fixed table size with unspecified width or height\n");
1818 rv = 1;
1819 }
1820 }
1821 tbl->data.box.UR.x = fmax(wd, tbl->data.width);
1822 tbl->data.box.UR.y = fmax(ht, tbl->data.height);
1823
1824 if (tbl->font)
1825 popFontInfo(env, &savef);
1826 return rv;
1827}
1828
1829static char *nameOf(void *obj, agxbuf * xb)
1830{
1831 Agedge_t *ep;
1832 switch (agobjkind(obj)) {
1833 case AGRAPH:
1834 agxbput(xb, agnameof(obj));
1835 break;
1836 case AGNODE:
1837 agxbput(xb, agnameof(obj));
1838 break;
1839 case AGEDGE:
1840 ep = obj;
1841 agxbput(xb, agnameof(agtail(ep)));
1842 agxbput(xb, agnameof(aghead(ep)));
1843 if (agisdirected(agraphof(aghead(ep))))
1844 agxbput(xb, "->");
1845 else
1846 agxbput(xb, "--");
1847 break;
1848 }
1849 return agxbuse(xb);
1850}
1851
1852#ifdef DEBUG
1853void indent(int i)
1854{
1855 while (i--)
1856 fprintf(stderr, " ");
1857}
1858
1859void printBox(boxf b)
1860{
1861 fprintf(stderr, "(%f,%f)(%f,%f)", b.LL.x, b.LL.y, b.UR.x, b.UR.y);
1862}
1863
1864void printImage(htmlimg_t * ip, int ind)
1865{
1866 indent(ind);
1867 fprintf(stderr, "img: %s\n", ip->src);
1868}
1869
1870void printTxt(htmltxt_t * txt, int ind)
1871{
1872 indent(ind);
1873 fprintf(stderr, "txt spans = %" PRISIZE_T " \n", txt->nspans);
1874 for (size_t i = 0; i < txt->nspans; i++) {
1875 indent(ind + 1);
1876 fprintf(stderr, "[%" PRISIZE_T "] %" PRISIZE_T " items\n", i,
1877 txt->spans[i].nitems);
1878 for (size_t j = 0; j < txt->spans[i].nitems; j++) {
1879 indent(ind + 2);
1880 fprintf(stderr, "[%" PRISIZE_T "] (%f,%f) \"%s\" ",
1881 j, txt->spans[i].items[j].size.x,
1882 txt->spans[i].items[j].size.y,
1883 txt->spans[i].items[j].str);
1884 if (txt->spans[i].items[j].font)
1885 fprintf(stderr, "font %s color %s size %f\n",
1886 txt->spans[i].items[j].font->name,
1887 txt->spans[i].items[j].font->color,
1888 txt->spans[i].items[j].font->size);
1889 else
1890 fprintf(stderr, "\n");
1891 }
1892 }
1893}
1894
1895void printData(htmldata_t * dp)
1896{
1897 unsigned char flags = dp->flags;
1898 char c;
1899
1900 fprintf(stderr, "s%d(%d) ", dp->space, (flags & SPACE_SET ? 1 : 0));
1901 fprintf(stderr, "b%d(%d) ", dp->border, (flags & BORDER_SET ? 1 : 0));
1902 fprintf(stderr, "p%d(%d) ", dp->pad, (flags & PAD_SET ? 1 : 0));
1903 switch (flags & HALIGN_MASK) {
1904 case HALIGN_RIGHT:
1905 c = 'r';
1906 break;
1907 case HALIGN_LEFT:
1908 c = 'l';
1909 break;
1910 default:
1911 c = 'n';
1912 break;
1913 }
1914 fprintf(stderr, "%c", c);
1915 switch (flags & VALIGN_MASK) {
1916 case VALIGN_TOP:
1917 c = 't';
1918 break;
1919 case VALIGN_BOTTOM:
1920 c = 'b';
1921 break;
1922 default:
1923 c = 'c';
1924 break;
1925 }
1926 fprintf(stderr, "%c ", c);
1927 printBox(dp->box);
1928}
1929
1930void printTbl(htmltbl_t * tbl, int ind)
1931{
1932 htmlcell_t **cells = tbl->u.n.cells;
1933 indent(ind);
1934 fprintf(stderr, "tbl (%p) %" PRISIZE_T " %" PRISIZE_T " ", tbl, tbl->column_count, tbl->row_count);
1935 printData(&tbl->data);
1936 fputs("\n", stderr);
1937 while (*cells)
1938 printCell(*cells++, ind + 1);
1939}
1940
1941static void printCell(htmlcell_t * cp, int ind)
1942{
1943 indent(ind);
1944 fprintf(stderr, "cell %" PRIu16 " %" PRIu16 " %" PRIu16 " %" PRIu16 " ", cp->colspan,
1945 cp->colspan, cp->rowspan, cp->col, cp->row);
1946 printData(&cp->data);
1947 fputs("\n", stderr);
1948 switch (cp->child.kind) {
1949 case HTML_TBL:
1950 printTbl(cp->child.u.tbl, ind + 1);
1951 break;
1952 case HTML_TEXT:
1953 printTxt(cp->child.u.txt, ind + 1);
1954 break;
1955 case HTML_IMAGE:
1956 printImage(cp->child.u.img, ind + 1);
1957 break;
1958 default:
1959 break;
1960 }
1961}
1962
1963void printLbl(htmllabel_t * lbl)
1964{
1965 if (lbl->kind == HTML_TBL)
1966 printTbl(lbl->u.tbl, 0);
1967 else
1968 printTxt(lbl->u.txt, 0);
1969}
1970#endif /* DEBUG */
1971
1972static char *getPenColor(void *obj)
1973{
1974 char *str;
1975
1976 if ((str = agget(obj, "pencolor")) != 0 && str[0])
1977 return str;
1978 else if ((str = agget(obj, "color")) != 0 && str[0])
1979 return str;
1980 else
1981 return NULL;
1982}
1983
1984/* make_html_label:
1985 * Return non-zero if problem parsing HTML. In this case, use object name.
1986 */
1987int make_html_label(void *obj, textlabel_t * lp)
1988{
1989 int rv;
1990 double wd2, ht2;
1991 graph_t *g;
1993 htmlenv_t env;
1994 char *s;
1995
1996 env.obj = obj;
1997 switch (agobjkind(obj)) {
1998 case AGRAPH:
1999 env.g = ((Agraph_t *) obj)->root;
2000 break;
2001 case AGNODE:
2002 env.g = agraphof(obj);
2003 break;
2004 case AGEDGE:
2005 env.g = agraphof(aghead(((Agedge_t *) obj)));
2006 break;
2007 }
2008 g = env.g->root;
2009
2010 env.finfo.size = lp->fontsize;
2011 env.finfo.name = lp->fontname;
2012 env.finfo.color = lp->fontcolor;
2013 env.finfo.flags = 0;
2014 lbl = parseHTML(lp->text, &rv, &env);
2015 if (!lbl) {
2016 if (rv == 3) {
2017 // fatal error; `parseHTML` will have printed detail of it
2018 lp->html = false;
2019 lp->text = gv_strdup(lp->text);
2020 return rv;
2021 }
2022 /* Parse of label failed; revert to simple text label */
2023 agxbuf xb = {0};
2024 lp->html = false;
2025 lp->text = gv_strdup(nameOf(obj, &xb));
2026 switch (lp->charset) {
2027 case CHAR_LATIN1:
2028 s = latin1ToUTF8(lp->text);
2029 break;
2030 default: /* UTF8 */
2031 s = htmlEntityUTF8(lp->text, env.g);
2032 break;
2033 }
2034 free(lp->text);
2035 lp->text = s;
2036 make_simple_label(GD_gvc(g), lp);
2037 agxbfree(&xb);
2038 return rv;
2039 }
2040
2041 if (lbl->kind == HTML_TBL) {
2042 if (!lbl->u.tbl->data.pencolor && getPenColor(obj))
2044 rv |= size_html_tbl(g, lbl->u.tbl, NULL, &env);
2045 wd2 = lbl->u.tbl->data.box.UR.x / 2;
2046 ht2 = lbl->u.tbl->data.box.UR.y / 2;
2047 boxf b = {{-wd2, -ht2}, {wd2, ht2}};
2048 pos_html_tbl(lbl->u.tbl, b, BOTTOM | RIGHT | TOP | LEFT);
2049 lp->dimen.x = b.UR.x - b.LL.x;
2050 lp->dimen.y = b.UR.y - b.LL.y;
2051 } else {
2052 rv |= size_html_txt(GD_gvc(g), lbl->u.txt, &env);
2053 wd2 = lbl->u.txt->box.UR.x / 2;
2054 ht2 = lbl->u.txt->box.UR.y / 2;
2055 boxf b = {{-wd2, -ht2}, {wd2, ht2}};
2056 lbl->u.txt->box = b;
2057 lp->dimen.x = b.UR.x - b.LL.x;
2058 lp->dimen.y = b.UR.y - b.LL.y;
2059 }
2060
2061 lp->u.html = lbl;
2062
2063 /* If the label is a table, replace label text because this may
2064 * be used for the title and alt fields in image maps.
2065 */
2066 if (lbl->kind == HTML_TBL) {
2067 free(lp->text);
2068 lp->text = gv_strdup("<TABLE>");
2069 }
2070
2071 return rv;
2072}
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 char * agxbuse(agxbuf *xb)
Definition agxbuf.h:286
Memory allocation wrappers that exit on failure.
static char * gv_strdup(const char *original)
Definition alloc.h:101
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
#define ROUND(f)
Definition arith.h:48
container data types API
CDT_API Dtlink_t * dtflatten(Dt_t *)
Definition dtflatten.c:10
#define dtlink(d, e)
Definition cdt.h:183
#define dtinsert(d, o)
Definition cdt.h:193
CDT_API int dtclose(Dt_t *)
Definition dtclose.c:8
#define parent(i)
Definition closest.c:78
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 LEFT
Definition const.h:120
#define DEFAULT_COLOR
Definition const.h:48
#define RIGHT
Definition const.h:118
#define GRADIENT
Definition const.h:232
#define BOTTOM
Definition const.h:117
#define RGRADIENT
Definition const.h:233
#define TOP
Definition const.h:119
int initMapData(GVJ_t *job, char *lbl, char *url, char *tooltip, char *target, char *id, void *gobj)
Definition emit.c:151
obj_state_t * push_obj_state(GVJ_t *job)
Definition emit.c:95
void emit_map_rect(GVJ_t *job, boxf b)
Definition emit.c:648
void pop_obj_state(GVJ_t *job)
Definition emit.c:119
char * getObjId(GVJ_t *job, void *obj, agxbuf *xb)
Use id of root graph if any, plus kind and internal id of object.
Definition emit.c:198
bool findStopColor(char *colorlist, char *clrs[2], double *frac)
Definition emit.c:4038
#define A(n, t)
Definition expr.h:76
static int flags
Definition gc.c:61
#define B2BF(b, bf)
Definition geom.h:75
static pointf scale(double c, pointf p)
Definition geomprocs.h:130
#define FILL
Definition gmlparse.c:400
void free(void *)
#define SIZE_MAX
Definition gmlscan.c:347
#define UINT16_MAX
Definition gmlscan.c:340
node NULL
Definition grammar.y:149
simple
Definition grammar.y:142
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:199
char * agget(void *obj, char *name)
Definition attr.c:442
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:260
#define ED_minlen(e)
Definition types.h:592
#define agtail(e)
Definition cgraph.h:889
#define agfindedge(g, t, h)
Definition types.h:609
#define aghead(e)
Definition cgraph.h:890
void agwarningf(const char *fmt,...)
Definition agerror.c:173
void agerrorf(const char *fmt,...)
Definition agerror.c:165
int agisdirected(Agraph_t *g)
Definition graph.c:179
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:96
#define GD_has_images(g)
Definition types.h:369
#define GD_nlist(g)
Definition types.h:393
Agdesc_t Agstrictdirected
strict directed. A strict graph cannot have multi-edges or self-arcs.
Definition graph.c:274
Agraph_t * agopen(char *name, Agdesc_t desc, Agdisc_t *disc)
creates a new graph with the given name and kind
Definition graph.c:44
#define GD_gvc(g)
Definition types.h:355
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:147
#define ND_rank(n)
Definition types.h:523
#define ND_next(n)
Definition types.h:510
#define ND_label(n)
Definition types.h:502
#define agfindnode(g, n)
Definition types.h:611
#define ND_in(n)
Definition types.h:501
#define ND_out(n)
Definition types.h:515
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
@ AGEDGE
Definition cgraph.h:207
@ AGNODE
Definition cgraph.h:207
@ AGRAPH
Definition cgraph.h:207
void * agbindrec(void *obj, const char *name, unsigned int recsize, int move_to_front)
attaches a new record of the given size to the object
Definition rec.c:88
static uint64_t id
Definition gv2gml.c:42
static void indent(int ix)
Definition gv2gml.c:96
#define EMIT_CLUSTERS_LAST
Definition gvcjob.h:84
@ LABEL_HTML
Definition gvcjob.h:38
@ CLUSTER_OBJTYPE
Definition gvcjob.h:168
@ EDGE_OBJTYPE
Definition gvcjob.h:168
@ ROOTGRAPH_OBJTYPE
Definition gvcjob.h:168
@ NODE_OBJTYPE
Definition gvcjob.h:168
static void color(Agraph_t *g)
Definition gvcolor.c:128
void gvrender_end_label(GVJ_t *job)
Definition gvrender.c:407
void gvrender_usershape(GVJ_t *job, char *name, pointf *AF, size_t n, bool filled, char *imagescale, char *imagepos)
Definition gvrender.c:673
void gvrender_set_style(GVJ_t *job, char **s)
Definition gvrender.c:484
void gvrender_set_fillcolor(GVJ_t *job, char *name)
Definition gvrender.c:453
void gvrender_polyline(GVJ_t *job, pointf *AF, size_t n)
Definition gvrender.c:599
point gvusershape_size(graph_t *g, char *name)
void gvrender_box(GVJ_t *job, boxf BF, int filled)
Definition gvrender.c:568
void gvrender_set_gradient_vals(GVJ_t *job, char *stopcolor, int angle, double frac)
Definition gvrender.c:470
void gvrender_begin_anchor(GVJ_t *job, char *href, char *tooltip, char *target, char *id)
Definition gvrender.c:376
void gvrender_end_anchor(GVJ_t *job)
Definition gvrender.c:387
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_penwidth(GVJ_t *job, double penwidth)
Definition gvrender.c:801
void gvrender_set_pencolor(GVJ_t *job, char *name)
Definition gvrender.c:436
htmllabel_t * parseHTML(char *txt, int *warn, htmlenv_t *env)
Definition htmlparse.c:2431
GVC_t * gvc
Definition htmlparse.c:99
agxbuf * str
Definition htmlparse.c:97
htmllabel_t * lbl
Definition htmlparse.c:93
$2 u p prev
Definition htmlparse.y:495
$2 u p rows
Definition htmlparse.y:496
rows row
Definition htmlparse.y:524
static void free_html_img(htmlimg_t *ip)
Definition htmltable.c:837
static void endAnchor(GVJ_t *job, htmlmap_data_t *save)
Definition htmltable.c:438
static int size_html_tbl(graph_t *g, htmltbl_t *tbl, htmlcell_t *parent, htmlenv_t *env)
Definition htmltable.c:1777
static void pos_html_cell(htmlcell_t *cp, boxf pos, unsigned char sides)
Definition htmltable.c:1528
static void pushFontInfo(htmlenv_t *env, textfont_t *fp, textfont_t *savp)
Definition htmltable.c:78
static void pos_html_txt(htmltxt_t *ftxt, char c)
Definition htmltable.c:1518
#define RESET(fld)
Definition htmltable.c:424
static char * nameOf(void *obj, agxbuf *xb)
Definition htmltable.c:1829
static void closeGraphs(graph_t *rowg, graph_t *colg)
Definition htmltable.c:1318
static void allocObj(GVJ_t *job)
Definition htmltable.c:691
boxf * html_port(node_t *n, char *pname, unsigned char *sides)
Definition htmltable.c:934
static void pos_html_tbl(htmltbl_t *, boxf, unsigned char)
Definition htmltable.c:1678
static double heightOfLbl(htmllabel_t *lp)
Definition htmltable.c:734
int make_html_label(void *obj, textlabel_t *lp)
Definition htmltable.c:1987
static void emit_html_img(GVJ_t *job, htmlimg_t *cp, htmlenv_t *env)
Definition htmltable.c:606
static void pos_html_img(htmlimg_t *cp, boxf pos)
Definition htmltable.c:1510
#define DEFAULT_CELLSPACING
Definition htmltable.c:56
#define DEFAULT_CELLPADDING
Definition htmltable.c:55
static int size_html_cell(graph_t *g, htmlcell_t *cp, htmltbl_t *parent, htmlenv_t *env)
Definition htmltable.c:1116
static void doBorder(GVJ_t *job, htmldata_t *dp, boxf b)
Definition htmltable.c:255
#define SPLIT(x, n, s)
Definition htmltable.c:1273
static pointf * mkPts(pointf *AF, boxf b, int border)
Definition htmltable.c:230
static void freeObj(GVJ_t *job)
Definition htmltable.c:722
static void setSizes(htmltbl_t *tbl, graph_t *rowg, graph_t *colg)
Definition htmltable.c:1448
static int processTbl(graph_t *g, htmltbl_t *tbl, htmlenv_t *env)
Definition htmltable.c:1205
static htmldata_t * portToTbl(htmltbl_t *, char *)
Definition htmltable.c:908
static void emit_html_txt(GVJ_t *job, htmltxt_t *tp, htmlenv_t *env)
Definition htmltable.c:197
static htmldata_t * portToCell(htmlcell_t *cp, char *id)
Definition htmltable.c:890
void free_html_text(htmltxt_t *t)
Definition htmltable.c:814
static void free_html_cell(htmlcell_t *cp)
Definition htmltable.c:843
static void emit_html_tbl(GVJ_t *job, htmltbl_t *tbl, htmlenv_t *env)
Definition htmltable.c:526
static void popFontInfo(htmlenv_t *env, textfont_t *savp)
Definition htmltable.c:107
static void makeGraphs(htmltbl_t *tbl, graph_t *rowg, graph_t *colg)
Definition htmltable.c:1383
static int size_html_txt(GVC_t *gvc, htmltxt_t *ftxt, htmlenv_t *env)
Definition htmltable.c:951
void free_html_label(htmllabel_t *lp, int root)
Definition htmltable.c:876
static void emit_html_cell(GVJ_t *job, htmlcell_t *cp, htmlenv_t *env)
Definition htmltable.c:633
static uint16_t findCol(PointSet *ps, int row, int col, htmlcell_t *cellp)
Definition htmltable.c:1173
void emit_html_label(GVJ_t *job, htmllabel_t *lp, textlabel_t *tp)
Definition htmltable.c:756
static void emit_htextspans(GVJ_t *job, size_t nspans, htextspan_t *spans, pointf p, double halfwidth_x, textfont_t finfo, boxf b, int simple)
Definition htmltable.c:118
static int initAnchor(GVJ_t *job, htmlenv_t *env, htmldata_t *data, boxf b, htmlmap_data_t *save)
Definition htmltable.c:385
void free_html_data(htmldata_t *dp)
Definition htmltable.c:803
static void sizeLinearArray(htmltbl_t *tbl)
Definition htmltable.c:1281
static int setFill(GVJ_t *job, char *color, int angle, htmlstyle_t style, char *clrs[2])
Definition htmltable.c:351
static void doSide(GVJ_t *job, pointf p, double wd, double ht)
Definition htmltable.c:214
#define DEFAULT_BORDER
Definition htmltable.c:54
static void checkChain(graph_t *g)
Definition htmltable.c:1334
static void sizeArray(htmltbl_t *tbl)
Definition htmltable.c:1476
static void checkEdge(graph_t *g, node_t *t, node_t *h, double sz)
Definition htmltable.c:1356
static void emit_html_rules(GVJ_t *job, htmlcell_t *cp, htmlenv_t *env, char *color, htmlcell_t *nextc)
Definition htmltable.c:459
static void free_html_tbl(htmltbl_t *tbl)
Definition htmltable.c:855
static int size_html_img(htmlimg_t *img, htmlenv_t *env)
Definition htmltable.c:1093
static char * getPenColor(void *obj)
Definition htmltable.c:1972
#define PAD_SET
Definition htmltable.h:32
#define HTML_TEXT
Definition htmltable.h:102
#define HTML_HRULE
Definition htmltable.h:106
#define BORDER_RIGHT
Definition htmltable.h:39
#define HTML_VRULE
Definition htmltable.h:105
#define HTML_IMAGE
Definition htmltable.h:103
#define HTML_TBL
Definition htmltable.h:101
#define BORDER_TOP
Definition htmltable.h:38
#define HALIGN_TEXT
Definition htmltable.h:27
#define UNSET_ALIGN
Definition htmltable.h:43
#define HALIGN_LEFT
Definition htmltable.h:25
#define VALIGN_BOTTOM
Definition htmltable.h:29
#define BALIGN_MASK
Definition htmltable.h:36
#define BALIGN_RIGHT
Definition htmltable.h:34
#define HALIGN_MASK
Definition htmltable.h:26
#define BALIGN_LEFT
Definition htmltable.h:35
#define BORDER_BOTTOM
Definition htmltable.h:40
#define SPACE_SET
Definition htmltable.h:33
#define VALIGN_MASK
Definition htmltable.h:30
#define BORDER_SET
Definition htmltable.h:31
#define BORDER_LEFT
Definition htmltable.h:37
#define BORDER_MASK
Definition htmltable.h:41
#define HALIGN_RIGHT
Definition htmltable.h:24
#define VALIGN_TOP
Definition htmltable.h:28
#define FIXED_FLAG
Definition htmltable.h:23
int inIntSet(Dt_t *is, size_t v)
Definition intset.c:56
void addIntSet(Dt_t *is, size_t v)
Definition intset.c:49
Dt_t * openIntSet(void)
Definition intset.c:44
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 int * ps
Definition lu.c:51
#define delta
Definition maze.c:133
int rank(graph_t *g, int balance, int maxiter)
Definition ns.c:997
void addPS(PointSet *ps, double x, double y)
Definition pointset.c:71
PointSet * newPS(void)
Definition pointset.c:53
void freePS(PointSet *ps)
Definition pointset.c:58
int isInPS(PointSet *ps, double x, double y)
Definition pointset.c:84
point containers PointSet and PointMap
static void printData(object_t *objs, size_t n_objs, xlabel_t *lbls, size_t n_lbls, label_params_t *params)
Definition postproc.c:213
#define PRISIZE_T
PRIu64 alike for printing size_t
Definition prisize_t.h:27
void round_corners(GVJ_t *job, pointf *AF, size_t sides, graphviz_polygon_style_t style, int filled)
Handle some special graphical cases, such as rounding the shape, adding diagonals at corners,...
Definition shapes.c:706
pointf textspan_size(GVC_t *gvc, textspan_t *span)
Estimates size of a textspan, in points.
Definition textspan.c:79
platform abstraction for case-insensitive string functions
graph descriptor
Definition cgraph.h:284
graph or subgraph
Definition cgraph.h:425
Agraph_t * root
subgraphs - ancestors
Definition cgraph.h:434
Definition gvcint.h:80
char ** defaultlinestyle
Definition gvcint.h:148
Dt_t * textfont_dt
Definition gvcint.h:107
int flags
Definition gvcjob.h:299
obj_state_t * obj
Definition gvcjob.h:269
GVC_t * gvc
Definition gvcjob.h:263
Definition cdt.h:104
Definition geom.h:39
point LL
Definition geom.h:39
point UR
Definition geom.h:39
Definition geom.h:41
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
Definition legal.c:50
double lfsize
Definition htmltable.h:56
size_t nitems
Definition htmltable.h:53
textspan_t * items
Definition htmltable.h:52
double size
Definition htmltable.h:55
uint16_t rowspan
Definition htmltable.h:145
htmllabel_t child
Definition htmltable.h:148
uint16_t colspan
Definition htmltable.h:144
htmltbl_t * parent
Definition htmltable.h:149
unsigned char ruled
Definition htmltable.h:150
uint16_t col
Definition htmltable.h:146
uint16_t row
Definition htmltable.h:147
htmldata_t data
Definition htmltable.h:143
char * bgcolor
Definition htmltable.h:86
unsigned char border
Definition htmltable.h:90
char * target
Definition htmltable.h:83
char * id
Definition htmltable.h:85
signed char space
Definition htmltable.h:89
unsigned short width
Definition htmltable.h:94
unsigned short height
Definition htmltable.h:95
int gradientangle
Definition htmltable.h:88
char * port
Definition htmltable.h:82
unsigned short flags
Definition htmltable.h:93
unsigned char sides
Definition htmltable.h:92
char * href
Definition htmltable.h:81
unsigned char pad
Definition htmltable.h:91
char * pencolor
Definition htmltable.h:87
htmlstyle_t style
Definition htmltable.h:96
char * title
Definition htmltable.h:84
graph_t * g
Definition htmltable.h:171
textfont_t finfo
Definition htmltable.h:169
pointf pos
Definition htmltable.h:168
bool objid_set
Definition htmltable.h:174
char * objid
Definition htmltable.h:173
char * imgscale
Definition htmltable.h:172
void * obj
Definition htmltable.h:170
char * scale
Definition htmltable.h:69
char * src
Definition htmltable.h:68
boxf box
Definition htmltable.h:67
htmltxt_t * txt
Definition htmltable.h:136
union htmllabel_t::@78 u
htmltbl_t * tbl
Definition htmltable.h:135
htmlimg_t * img
Definition htmltable.h:137
bool explicit_tooltip
Definition htmltable.c:63
char * tooltip
Definition htmltable.c:60
char * target
Definition htmltable.c:61
bool dashed
Definition htmltable.h:77
bool dotted
Definition htmltable.h:76
bool rounded
Definition htmltable.h:74
bool radial
Definition htmltable.h:73
bool invisible
Definition htmltable.h:75
union htmltbl_t::@75 u
textfont_t * font
Definition htmltable.h:128
size_t row_count
number of rows
Definition htmltable.h:126
double * widths
widths of the columns
Definition htmltable.h:125
struct htmltbl_t::@75::@76 n
struct htmltbl_t::@75::@77 p
htmlcell_t ** cells
Definition htmltable.h:116
htmldata_t data
Definition htmltable.h:112
Dt_t * rows
Definition htmltable.h:120
htmlcell_t * parent
Definition htmltable.h:115
double * heights
heights of the rows
Definition htmltable.h:124
size_t column_count
number of columns
Definition htmltable.h:127
boxf box
Definition htmltable.h:63
size_t nspans
Definition htmltable.h:61
char simple
Definition htmltable.h:62
htextspan_t * spans
Definition htmltable.h:60
union obj_state_s::@92 u
graph_t * g
Definition gvcjob.h:186
edge_t * e
Definition gvcjob.h:189
char * tooltip
Definition gvcjob.h:216
char * url
Definition gvcjob.h:210
unsigned explicit_tooltip
Definition gvcjob.h:226
char * target
Definition gvcjob.h:221
obj_type type
Definition gvcjob.h:184
node_t * n
Definition gvcjob.h:188
graph_t * sg
Definition gvcjob.h:187
char * id
Definition gvcjob.h:211
emit_state_t emit_state
Definition gvcjob.h:192
obj_state_t * parent
Definition gvcjob.h:182
unsigned char ruled
Definition htmltable.h:164
Dt_t * rp
Definition htmltable.h:161
union pitem::@79 u
htmlcell_t * cp
Definition htmltable.h:162
Definition geom.h:27
int y
Definition geom.h:27
int x
Definition geom.h:27
double x
Definition geom.h:29
double y
Definition geom.h:29
char * color
Definition textspan.h:55
char * name
Definition textspan.h:54
PostscriptAlias * postscript_alias
Definition textspan.h:56
unsigned int flags
Definition textspan.h:58
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
char valign
Definition types.h:122
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
double yoffset_layout
Definition textspan.h:69
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
double yoffset_centerline
Definition textspan.h:69
void(* free_layout)(void *layout)
Definition textspan.h:68
#define free_list(L)
Definition types.h:272
#define elist_append(item, L)
Definition types.h:261
#define alloc_elist(n, L)
Definition types.h:267
Definition grammar.c:93
#define UNREACHABLE()
Definition unreachable.h:30
#define MAX(a, b)
Definition write.c:31