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