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