|
Graphviz
2.29.20120524.0446
|
00001 /* $Id$Revision: */ 00002 /* vim:set shiftwidth=4 ts=8: */ 00003 00004 /************************************************************************* 00005 * Copyright (c) 2011 AT&T Intellectual Property 00006 * All rights reserved. This program and the accompanying materials 00007 * are made available under the terms of the Eclipse Public License v1.0 00008 * which accompanies this distribution, and is available at 00009 * http://www.eclipse.org/legal/epl-v10.html 00010 * 00011 * Contributors: See CVS logs. Details at http://www.graphviz.org/ 00012 *************************************************************************/ 00013 00014 00015 /* Implementation of HTML-like tables. 00016 * 00017 * The (now purged) CodeGen graphics model, especially with integral coodinates, is 00018 * not adequate to handle this as we would like. In particular, it is 00019 * difficult to handle notions of adjacency and correct rounding to pixels. 00020 * For example, if 2 adjacent boxes bb1.UR.x == bb2.LL.x, the rectangles 00021 * may be drawn overlapping. However, if we use bb1.UR.x+1 == bb2.LL.x 00022 * there may or may not be a gap between them, even in the same device 00023 * depending on their positions. When CELLSPACING > 1, this isn't as much 00024 * of a problem. 00025 * 00026 * We allow negative spacing as a hack to allow overlapping cell boundaries. 00027 * For the reasons discussed above, this is difficult to get correct. 00028 * This is an important enough case we should extend the table model to 00029 * support it correctly. This could be done by allowing a table attribute, 00030 * e.g., CELLGRID=n, which sets CELLBORDER=0 and has the border drawing 00031 * handled correctly by the table. 00032 */ 00033 00034 #include <assert.h> 00035 #include "render.h" 00036 #include "htmltable.h" 00037 #include "agxbuf.h" 00038 #include "pointset.h" 00039 #include "intset.h" 00040 00041 #define DEFAULT_BORDER 1 00042 #define DEFAULT_CELLPADDING 2 00043 #define DEFAULT_CELLSPACING 2 00044 00045 typedef struct { 00046 pointf pos; 00047 htmlfont_t finfo; 00048 void *obj; 00049 graph_t *g; 00050 char* imgscale; 00051 char* objid; 00052 boolean objid_set; 00053 } htmlenv_t; 00054 00055 typedef struct { 00056 char *url; 00057 char *tooltip; 00058 char *target; 00059 char *id; 00060 boolean explicit_tooltip; 00061 point LL; 00062 point UR; 00063 } htmlmap_data_t; 00064 00065 #ifdef DEBUG 00066 static void printCell(htmlcell_t * cp, int ind); 00067 #endif 00068 00069 /* pushFontInfo: 00070 * Replace current font attributes in env with ones from fp, 00071 * storing old attributes in savp. We only deal with attributes 00072 * set in env. The attributes are restored via popFontInfo. 00073 */ 00074 static void 00075 pushFontInfo(htmlenv_t * env, htmlfont_t * fp, htmlfont_t * savp) 00076 { 00077 if (env->finfo.name) { 00078 if (fp->name) { 00079 savp->name = env->finfo.name; 00080 env->finfo.name = fp->name; 00081 } else 00082 savp->name = NULL; 00083 } 00084 if (env->finfo.color) { 00085 if (fp->color) { 00086 savp->color = env->finfo.color; 00087 env->finfo.color = fp->color; 00088 } else 00089 savp->color = NULL; 00090 } 00091 if (env->finfo.size >= 0) { 00092 if (fp->size >= 0) { 00093 savp->size = env->finfo.size; 00094 env->finfo.size = fp->size; 00095 } else 00096 savp->size = -1.0; 00097 } 00098 } 00099 00100 /* popFontInfo: 00101 * Restore saved font attributes. 00102 * Copy only set values. 00103 */ 00104 static void popFontInfo(htmlenv_t * env, htmlfont_t * savp) 00105 { 00106 if (savp->name) 00107 env->finfo.name = savp->name; 00108 if (savp->color) 00109 env->finfo.color = savp->color; 00110 if (savp->size >= 0.0) 00111 env->finfo.size = savp->size; 00112 } 00113 00114 static void 00115 emit_htextparas(GVJ_t* job, int nparas, htextpara_t* paras, pointf p, 00116 double halfwidth_x, htmlfont_t finfo, boxf b) 00117 { 00118 int i,j; 00119 double center_x, left_x, right_x, fsize_; 00120 char *fname_ , *fcolor_; 00121 textpara_t tl; 00122 pointf p_ = {0.0, 0.0}; 00123 textpara_t* ti; 00124 00125 center_x = p.x; 00126 left_x = center_x - halfwidth_x; 00127 right_x = center_x + halfwidth_x; 00128 00129 /* Initial p is in center of text block; set initial baseline 00130 * to top of text block. 00131 */ 00132 p_.y = p.y + (b.UR.y-b.LL.y)/2.0; 00133 00134 gvrender_begin_label(job, LABEL_HTML); 00135 for(i=0; i<nparas; i++) { 00136 /* set p.x to leftmost point where the line of text begins */ 00137 switch (paras[i].just) { 00138 case 'l': 00139 p.x = left_x; 00140 break; 00141 case 'r': 00142 p.x = right_x - paras[i].size; 00143 break; 00144 default: 00145 case 'n': 00146 p.x = center_x - paras[i].size/2.0; 00147 break; 00148 } 00149 p_.y -= paras[i].lfsize; /* move to current base line */ 00150 00151 ti = paras[i].items; 00152 for(j=0; j<paras[i].nitems; j++) { 00153 if (ti->font && (ti->font->size > 0)) 00154 fsize_ = ti->font->size; 00155 else 00156 fsize_ = finfo.size; 00157 if (ti->font && ti->font->name) 00158 fname_ = ti->font->name; 00159 else 00160 fname_ = finfo.name; 00161 if (ti->font && ti->font->color) 00162 fcolor_ = ti->font->color; 00163 else 00164 fcolor_ = finfo.color; 00165 00166 gvrender_set_pencolor(job, fcolor_); 00167 00168 tl.str = ti->str; 00169 tl.fontname = fname_; 00170 tl.fontsize = fsize_; 00171 tl.font = ti->font; 00172 tl.yoffset_layout = ti->yoffset_layout; 00173 /* tl.yoffset_centerline = ti->yoffset_centerline; */ 00174 tl.yoffset_centerline = 1; 00175 tl.postscript_alias = ti->postscript_alias; 00176 tl.layout = ti->layout; 00177 tl.width = ti->size; 00178 tl.height = paras[i].lfsize; 00179 tl.just = 'l'; 00180 00181 p_.x = p.x; 00182 gvrender_textpara(job, p_, &tl); 00183 p.x += ti->size; 00184 ti++; 00185 } 00186 } 00187 00188 gvrender_end_label(job); 00189 } 00190 00191 static void 00192 emit_html_txt(GVJ_t* job, htmltxt_t* tp, htmlenv_t* env) 00193 { 00194 double halfwidth_x; 00195 pointf p; 00196 00197 /* make sure that there is something to do */ 00198 if (tp->nparas < 1) 00199 return; 00200 00201 halfwidth_x = ((double) (tp->box.UR.x - tp->box.LL.x)) / 2.0; 00202 p.x = env->pos.x + ((double) (tp->box.UR.x + tp->box.LL.x)) / 2.0; 00203 p.y = env->pos.y + ((double) (tp->box.UR.y + tp->box.LL.y)) / 2.0; 00204 00205 emit_htextparas(job, tp->nparas, tp->paras, p, halfwidth_x, env->finfo, tp->box); 00206 } 00207 00208 static void doSide(GVJ_t * job, pointf p, double wd, double ht) 00209 { 00210 boxf BF; 00211 00212 BF.LL = p; 00213 BF.UR.x = p.x + wd; 00214 BF.UR.y = p.y + ht; 00215 gvrender_box(job, BF, 1); 00216 } 00217 00218 /* doBorder: 00219 * Draw rectangle of width border inside rectangle given 00220 * by box. If border is 1, we call use a single call to gvrender_polygon. 00221 * (We have set linewidth to 1 below.) Otherwise, we use four separate 00222 * filled rectangles. We could use a richer graphics model, as things 00223 * can go wrong when cell spacing and borders are small. 00224 * We decrement the border value by 1, as typically a filled rectangle 00225 * from x to x+border will all pixels from x to x+border, and thus have 00226 * width border+1. 00227 */ 00228 static void doBorder(GVJ_t * job, char *color, int border, boxf BF) 00229 { 00230 pointf pt; 00231 double wd, ht; 00232 00233 if (!color) 00234 color = DEFAULT_COLOR; 00235 gvrender_set_fillcolor(job, color); 00236 gvrender_set_pencolor(job, color); 00237 00238 if (border == 1) { 00239 gvrender_box(job, BF, 0); 00240 } else { 00241 border--; 00242 ht = BF.UR.y - BF.LL.y; 00243 wd = BF.UR.x - BF.LL.x; 00244 doSide(job, BF.LL, border, ht); 00245 pt.x = BF.LL.x; 00246 pt.y = BF.UR.y; 00247 doSide(job, pt, wd, -border); 00248 doSide(job, BF.UR, -border, -ht); 00249 pt.x = BF.UR.x; 00250 pt.y = BF.LL.y; 00251 doSide(job, pt, -wd, border); 00252 } 00253 } 00254 00255 /* setFill: 00256 * Set up fill values from given color; make pen transparent. 00257 * Return type of fill required. 00258 */ 00259 static int 00260 setFill (GVJ_t* job, char* color, int angle, int style, char* clrs[2]) 00261 { 00262 int filled; 00263 if (findStopColor (color, clrs)) { 00264 gvrender_set_fillcolor(job, clrs[0]); 00265 if (clrs[1]) 00266 gvrender_set_gradient_vals(job,clrs[1],angle); 00267 else 00268 gvrender_set_gradient_vals(job,DEFAULT_COLOR,angle); 00269 if (style & RADIAL) 00270 filled = RGRADIENT; 00271 else 00272 filled = GRADIENT; 00273 } 00274 else { 00275 gvrender_set_fillcolor(job, color); 00276 filled = FILL; 00277 } 00278 gvrender_set_pencolor(job, "transparent"); 00279 return filled; 00280 } 00281 00282 /* initAnchor: 00283 * Save current map values 00284 * Initialize fields in job->obj pertaining to anchors. 00285 * In particular, this also sets the output rectangle. 00286 * If there is something to do, close current anchor if 00287 * necessary, start the anchor and returns 1. 00288 * Otherwise, it returns 0. 00289 * 00290 * FIX: Should we provide a tooltip if none is set, as is done 00291 * for nodes, edges, etc. ? 00292 */ 00293 static int 00294 initAnchor (GVJ_t* job, htmlenv_t* env, htmldata_t* data, boxf b, htmlmap_data_t* save, 00295 int closePrev) 00296 { 00297 obj_state_t *obj = job->obj; 00298 int changed; 00299 char* id; 00300 static int anchorId; 00301 int internalId = 0; 00302 agxbuf xb; 00303 char intbuf[30]; /* hold 64-bit decimal integer */ 00304 unsigned char buf[SMALLBUF]; 00305 00306 save->url = obj->url; 00307 save->tooltip = obj->tooltip; 00308 save->target = obj->target; 00309 save->id = obj->id; 00310 save->explicit_tooltip = obj->explicit_tooltip; 00311 id = data->id; 00312 if (!id || !*id) { /* no external id, so use the internal one */ 00313 agxbinit(&xb, SMALLBUF, buf); 00314 if (!env->objid) { 00315 env->objid = strdup (getObjId (job, obj->u.n, &xb)); 00316 env->objid_set = 1; 00317 } 00318 agxbput (&xb, env->objid); 00319 sprintf (intbuf, "_%d", anchorId++); 00320 agxbput (&xb, intbuf); 00321 id = agxbuse (&xb); 00322 internalId = 1; 00323 } 00324 changed = initMapData (job, NULL, data->href, data->title, data->target, id, obj->u.g); 00325 if (internalId) 00326 agxbfree (&xb); 00327 00328 if (changed) { 00329 if (closePrev && (save->url || save->explicit_tooltip)) 00330 gvrender_end_anchor(job); 00331 if (obj->url || obj->explicit_tooltip) { 00332 emit_map_rect(job, b); 00333 gvrender_begin_anchor(job, 00334 obj->url, obj->tooltip, obj->target, obj->id); 00335 } 00336 } 00337 return changed; 00338 } 00339 00340 #define RESET(fld) \ 00341 if(obj->fld != save->fld) {free(obj->fld); obj->fld = save->fld;} 00342 00343 /* endAnchor: 00344 * Pop context pushed by initAnchor. 00345 * This is done by ending current anchor, restoring old values and 00346 * freeing new, and reopening previous anchor if necessary. 00347 * 00348 * NB: We don't save or restore geometric map info. This is because 00349 * this preservation of map context is only necessary for SVG-like 00350 * systems where graphical items are wrapped in an anchor, and we map 00351 * top-down. For ordinary map anchors, this is all done bottom-up, so 00352 * the geometric map info at the higher level hasn't been emitted yet. 00353 */ 00354 static void 00355 endAnchor (GVJ_t* job, htmlmap_data_t* save, int openPrev) 00356 { 00357 obj_state_t *obj = job->obj; 00358 00359 if (obj->url || obj->explicit_tooltip) 00360 gvrender_end_anchor(job); 00361 RESET(url); 00362 RESET(tooltip); 00363 RESET(target); 00364 RESET(id); 00365 obj->explicit_tooltip = save->explicit_tooltip; 00366 if (openPrev && (obj->url || obj->explicit_tooltip)) 00367 gvrender_begin_anchor(job, 00368 obj->url, obj->tooltip, obj->target, obj->id); 00369 } 00370 00371 /* forward declaration */ 00372 static void emit_html_cell(GVJ_t * job, htmlcell_t * cp, htmlenv_t * env); 00373 00374 /* emit_html_rules: 00375 * place vertical and horizontal lines between adjacent cells and 00376 * extend the lines to intersect the rounded table boundary 00377 */ 00378 static void 00379 emit_html_rules(GVJ_t * job, htmlcell_t * cp, htmlenv_t * env, char *color) 00380 { 00381 pointf rule_pt; 00382 double rule_length; 00383 unsigned char base; 00384 boxf pts = cp->data.box; 00385 pointf pos = env->pos; 00386 00387 if (!color) 00388 color = DEFAULT_COLOR; 00389 gvrender_set_fillcolor(job, color); 00390 gvrender_set_pencolor(job, color); 00391 00392 pts = cp->data.box; 00393 pts.LL.x += pos.x; 00394 pts.UR.x += pos.x; 00395 pts.LL.y += pos.y; 00396 pts.UR.y += pos.y; 00397 00398 //Determine vertical line coordinate and length 00399 if ((cp->ruled & HTML_VRULE) && (cp->col + cp->cspan < cp->parent->cc)) { 00400 if(cp->row == 0) { // first row 00401 // extend to center of table border and add half cell spacing 00402 base = cp->parent->data.border + cp->parent->data.space/2; 00403 rule_pt.y = pts.LL.y - cp->parent->data.space/2; 00404 } 00405 else if(cp->row + cp->rspan == cp->parent->rc){ // bottom row 00406 // extend to center of table border and add half cell spacing 00407 base = cp->parent->data.border + cp->parent->data.space/2; 00408 rule_pt.y = pts.LL.y - cp->parent->data.space/2 - base; 00409 } 00410 else { 00411 base = 0; 00412 rule_pt.y = pts.LL.y - cp->parent->data.space/2; 00413 } 00414 rule_pt.x = pts.UR.x + cp->parent->data.space/2; 00415 rule_length = base + pts.UR.y - pts.LL.y + cp->parent->data.space; 00416 doSide(job,rule_pt,0,rule_length); 00417 } 00418 00419 //Determine the horizontal coordinate and length 00420 if ((cp->ruled & HTML_HRULE) && (cp->row + cp->rspan < cp->parent->rc)) { 00421 if(cp->col == 0) { // first column 00422 // extend to center of table border and add half cell spacing 00423 base = cp->parent->data.border + cp->parent->data.space/2; 00424 rule_pt.x = pts.LL.x - base - cp->parent->data.space/2; 00425 if(cp->col + cp->cspan == cp->parent->cc) // also last column 00426 base *= 2; 00427 } 00428 else if(cp->col + cp->cspan == cp->parent->cc){ // last column 00429 // extend to center of table border and add half cell spacing 00430 base = cp->parent->data.border + cp->parent->data.space/2; 00431 rule_pt.x = pts.LL.x - cp->parent->data.space/2; 00432 } 00433 else { 00434 base = 0; 00435 rule_pt.x = pts.LL.x - cp->parent->data.space/2; 00436 } 00437 rule_pt.y = pts.LL.y - cp->parent->data.space/2; 00438 rule_length = base + pts.UR.x - pts.LL.x + cp->parent->data.space; 00439 doSide(job,rule_pt,rule_length,0); 00440 } 00441 } 00442 00443 static void 00444 emit_html_tbl(GVJ_t * job, htmltbl_t * tbl, htmlenv_t * env) 00445 { 00446 boxf pts = tbl->data.box; 00447 pointf pos = env->pos; 00448 htmlcell_t **cells = tbl->u.n.cells; 00449 htmlcell_t *cp; 00450 static htmlfont_t savef; 00451 htmlmap_data_t saved; 00452 int anchor; /* if true, we need to undo anchor settings. */ 00453 int doAnchor = (tbl->data.href || tbl->data.target); 00454 pointf AF[4]; 00455 00456 if (tbl->font) 00457 pushFontInfo(env, tbl->font, &savef); 00458 00459 pts.LL.x += pos.x; 00460 pts.UR.x += pos.x; 00461 pts.LL.y += pos.y; 00462 pts.UR.y += pos.y; 00463 00464 if (doAnchor && !(job->flags & EMIT_CLUSTERS_LAST)) 00465 anchor = initAnchor(job, env, &tbl->data, pts, &saved, 1); 00466 else 00467 anchor = 0; 00468 /* Set up rounded style */ 00469 if (tbl->data.style & ROUNDED) { 00470 AF[0] = pts.LL; 00471 AF[2] = pts.UR; 00472 if (tbl->data.border) { 00473 double delta = ((double)tbl->data.border)/2.0; 00474 AF[0].x += delta; 00475 AF[0].y += delta; 00476 AF[2].x -= delta; 00477 AF[2].y -= delta; 00478 } 00479 AF[1].x = AF[2].x; 00480 AF[1].y = AF[0].y; 00481 AF[3].x = AF[0].x; 00482 AF[3].y = AF[2].y; 00483 } 00484 00485 /* Fill first */ 00486 if (tbl->data.bgcolor) { 00487 char* clrs[2]; 00488 int filled = setFill (job, tbl->data.bgcolor, tbl->data.gradientangle, tbl->data.style, clrs); 00489 if (tbl->data.style & ROUNDED){ 00490 round_corners (job, AF, 4, tbl->data.style, filled); 00491 } 00492 else 00493 gvrender_box(job, pts, filled); 00494 free (clrs[0]); 00495 } 00496 00497 while (*cells) { 00498 emit_html_cell(job, *cells, env); 00499 cells++; 00500 } 00501 00502 /* Draw table rules and border. 00503 * Draw after cells so we can draw over any fill. 00504 * At present, we set the penwidth to 1 for rules until we provide the calculations to take 00505 * into account wider rules. 00506 */ 00507 cells = tbl->u.n.cells; 00508 gvrender_set_penwidth(job, 1.0); 00509 while ((cp = *cells++)){ 00510 if (cp->ruled) emit_html_rules(job, cp, env, tbl->data.pencolor); 00511 } 00512 00513 if (tbl->data.border) { 00514 if (tbl->data.style & ROUNDED) { 00515 char* color = (tbl->data.pencolor ? tbl->data.pencolor : DEFAULT_COLOR); 00516 gvrender_set_penwidth(job, tbl->data.border); 00517 gvrender_set_pencolor(job, color); 00518 round_corners (job, AF, 4, tbl->data.style, 0); 00519 } 00520 else 00521 doBorder(job, tbl->data.pencolor, tbl->data.border, pts); 00522 } 00523 00524 if (anchor) 00525 endAnchor (job, &saved, 1); 00526 00527 if (doAnchor && (job->flags & EMIT_CLUSTERS_LAST)) { 00528 if (initAnchor(job, env, &tbl->data, pts, &saved, 0)) 00529 endAnchor (job, &saved, 0); 00530 } 00531 00532 if (tbl->font) 00533 popFontInfo(env, &savef); 00534 } 00535 00536 /* emit_html_img: 00537 * The image will be centered in the given box. 00538 * Scaling is determined by either the image's scale attribute, 00539 * or the imagescale attribute of the graph object being drawn. 00540 */ 00541 static void 00542 emit_html_img(GVJ_t * job, htmlimg_t * cp, htmlenv_t * env) 00543 { 00544 pointf A[4]; 00545 boxf bb = cp->box; 00546 char* scale; 00547 00548 bb.LL.x += env->pos.x; 00549 bb.LL.y += env->pos.y; 00550 bb.UR.x += env->pos.x; 00551 bb.UR.y += env->pos.y; 00552 00553 A[0] = bb.UR; 00554 A[2] = bb.LL; 00555 A[1].x = A[2].x; 00556 A[1].y = A[0].y; 00557 A[3].x = A[0].x; 00558 A[3].y = A[2].y; 00559 00560 if (cp->scale) 00561 scale = cp->scale; 00562 else 00563 scale = env->imgscale; 00564 gvrender_usershape(job, cp->src, A, 4, TRUE, scale); 00565 } 00566 00567 static void 00568 emit_html_cell(GVJ_t * job, htmlcell_t * cp, htmlenv_t * env) 00569 { 00570 htmlmap_data_t saved; 00571 boxf pts = cp->data.box; 00572 pointf pos = env->pos; 00573 int inAnchor, doAnchor = (cp->data.href || cp->data.target); 00574 00575 pts.LL.x += pos.x; 00576 pts.UR.x += pos.x; 00577 pts.LL.y += pos.y; 00578 pts.UR.y += pos.y; 00579 00580 if (doAnchor && !(job->flags & EMIT_CLUSTERS_LAST)) 00581 inAnchor = initAnchor(job, env, &cp->data, pts, &saved, 1); 00582 else 00583 inAnchor = 0; 00584 00585 if (cp->data.bgcolor) { 00586 char* clrs[2]; 00587 int filled = setFill (job, cp->data.bgcolor, cp->data.gradientangle, cp->data.style, clrs); 00588 gvrender_box(job, pts, filled); 00589 free (clrs[0]); 00590 } 00591 00592 if (cp->data.border) 00593 doBorder(job, cp->data.pencolor, cp->data.border, pts); 00594 00595 if (cp->child.kind == HTML_TBL) 00596 emit_html_tbl(job, cp->child.u.tbl, env); 00597 else if (cp->child.kind == HTML_IMAGE) 00598 emit_html_img(job, cp->child.u.img, env); 00599 else 00600 emit_html_txt(job, cp->child.u.txt, env); 00601 00602 if (inAnchor) 00603 endAnchor (job, &saved, 1); 00604 00605 if (doAnchor && (job->flags & EMIT_CLUSTERS_LAST)) { 00606 if (initAnchor(job, env, &cp->data, pts, &saved, 0)) 00607 endAnchor (job, &saved, 0); 00608 } 00609 } 00610 00611 /* allocObj: 00612 * Push new obj on stack to be used in common by all 00613 * html elements with anchors. 00614 * This inherits the type, emit_state, and object of the 00615 * parent, as well as the url, explicit, target and tooltip. 00616 */ 00617 static void 00618 allocObj (GVJ_t * job) 00619 { 00620 obj_state_t *obj; 00621 obj_state_t *parent; 00622 00623 obj = push_obj_state(job); 00624 parent = obj->parent; 00625 obj->type = parent->type; 00626 obj->emit_state = parent->emit_state; 00627 switch (obj->type) { 00628 case NODE_OBJTYPE : 00629 obj->u.n = parent->u.n; 00630 break; 00631 case ROOTGRAPH_OBJTYPE : 00632 obj->u.g = parent->u.g; 00633 break; 00634 case CLUSTER_OBJTYPE : 00635 obj->u.sg = parent->u.sg; 00636 break; 00637 case EDGE_OBJTYPE : 00638 obj->u.e = parent->u.e; 00639 break; 00640 } 00641 obj->url = parent->url; 00642 obj->tooltip = parent->tooltip; 00643 obj->target = parent->target; 00644 obj->explicit_tooltip = parent->explicit_tooltip; 00645 } 00646 00647 static void 00648 freeObj (GVJ_t * job) 00649 { 00650 obj_state_t *obj = job->obj; 00651 00652 obj->url = NULL; 00653 obj->tooltip = NULL; 00654 obj->target = NULL; 00655 obj->id = NULL; 00656 pop_obj_state(job); 00657 } 00658 00659 /* emit_html_label: 00660 */ 00661 void 00662 emit_html_label(GVJ_t * job, htmllabel_t * lp, textlabel_t * tp) 00663 { 00664 htmlenv_t env; 00665 00666 allocObj (job); 00667 env.pos = tp->pos; 00668 env.finfo.color = tp->fontcolor; 00669 env.finfo.name = tp->fontname; 00670 env.finfo.size = tp->fontsize; 00671 env.finfo.size = tp->fontsize; 00672 env.imgscale = agget (job->obj->u.n, "imagescale"); 00673 env.objid = job->obj->id; 00674 env.objid_set = 0; 00675 if ((env.imgscale == NULL) || (env.imgscale[0] == '\0')) 00676 env.imgscale = "false"; 00677 if (lp->kind == HTML_TBL) { 00678 htmltbl_t *tbl = lp->u.tbl; 00679 00680 /* set basic graphics context */ 00681 /* Need to override line style set by node. */ 00682 gvrender_set_style(job, job->gvc->defaultlinestyle); 00683 if (tbl->data.pencolor) 00684 gvrender_set_pencolor(job, tbl->data.pencolor); 00685 else 00686 gvrender_set_pencolor(job, DEFAULT_COLOR); 00687 emit_html_tbl(job, tbl, &env); 00688 } else { 00689 emit_html_txt(job, lp->u.txt, &env); 00690 } 00691 if (env.objid_set) 00692 free (env.objid); 00693 freeObj (job); 00694 } 00695 00696 void free_html_font(htmlfont_t * fp) 00697 { 00698 fp->cnt--; 00699 if (fp->cnt == 0) { 00700 if (fp->name) 00701 free(fp->name); 00702 if (fp->color) 00703 free(fp->color); 00704 free(fp); 00705 } 00706 } 00707 00708 void free_html_data(htmldata_t * dp) 00709 { 00710 free(dp->href); 00711 free(dp->port); 00712 free(dp->target); 00713 free(dp->id); 00714 free(dp->title); 00715 free(dp->bgcolor); 00716 free(dp->pencolor); 00717 } 00718 00719 void free_html_text(htmltxt_t* t) 00720 { 00721 htextpara_t *tl; 00722 textpara_t *ti; 00723 int i, j; 00724 00725 if (!t) return; 00726 00727 tl = t->paras; 00728 for (i = 0; i < t->nparas; i++) { 00729 ti = tl->items; 00730 for (j = 0; j < tl->nitems; j++) { 00731 if (ti->str) free (ti->str); 00732 if (ti->font) free_html_font(ti->font); 00733 if (ti->layout && ti->free_layout) ti->free_layout (ti->layout); 00734 ti++; 00735 } 00736 tl++; 00737 } 00738 if (t->paras) free(t->paras); 00739 free(t); 00740 } 00741 00742 void free_html_img(htmlimg_t * ip) 00743 { 00744 free(ip->src); 00745 free(ip); 00746 } 00747 00748 static void free_html_cell(htmlcell_t * cp) 00749 { 00750 free_html_label(&cp->child, 0); 00751 free_html_data(&cp->data); 00752 free(cp); 00753 } 00754 00755 /* free_html_tbl: 00756 * If tbl->n_rows is negative, table is in initial state from 00757 * HTML parse, with data stored in u.p. Once run through processTbl, 00758 * data is stored in u.n and tbl->n_rows is > 0. 00759 */ 00760 static void free_html_tbl(htmltbl_t * tbl) 00761 { 00762 htmlcell_t **cells; 00763 00764 if (tbl->rc == -1) { 00765 dtclose(tbl->u.p.rows); 00766 } else { 00767 cells = tbl->u.n.cells; 00768 00769 free(tbl->heights); 00770 free(tbl->widths); 00771 while (*cells) { 00772 free_html_cell(*cells); 00773 cells++; 00774 } 00775 free(tbl->u.n.cells); 00776 } 00777 if (tbl->font) 00778 free_html_font(tbl->font); 00779 free_html_data(&tbl->data); 00780 free(tbl); 00781 } 00782 00783 void free_html_label(htmllabel_t * lp, int root) 00784 { 00785 if (lp->kind == HTML_TBL) 00786 free_html_tbl(lp->u.tbl); 00787 else if (lp->kind == HTML_IMAGE) 00788 free_html_img(lp->u.img); 00789 else 00790 free_html_text(lp->u.txt); 00791 if (root) 00792 free(lp); 00793 } 00794 00795 static htmldata_t* portToTbl(htmltbl_t *, char *); /* forward declaration */ 00796 00797 static htmldata_t* portToCell(htmlcell_t * cp, char *id) 00798 { 00799 htmldata_t* rv; 00800 00801 if (cp->data.port && (strcasecmp(cp->data.port, id) == 0)) 00802 rv = &cp->data; 00803 else if (cp->child.kind == HTML_TBL) 00804 rv = portToTbl(cp->child.u.tbl, id); 00805 else 00806 rv = NULL; 00807 00808 return rv; 00809 } 00810 00811 /* portToTbl: 00812 * See if tp or any of its child cells has the given port id. 00813 * If true, return corresponding box. 00814 */ 00815 static htmldata_t* 00816 portToTbl(htmltbl_t* tp, char* id) 00817 { 00818 htmldata_t* rv; 00819 htmlcell_t** cells; 00820 htmlcell_t* cp; 00821 00822 if (tp->data.port && (strcasecmp(tp->data.port, id) == 0)) 00823 rv = &tp->data; 00824 else { 00825 rv = NULL; 00826 cells = tp->u.n.cells; 00827 while ((cp = *cells++)) { 00828 if ((rv = portToCell(cp, id))) 00829 break; 00830 } 00831 } 00832 00833 return rv; 00834 } 00835 00836 /* html_port: 00837 * See if edge port corresponds to part of the html node. 00838 * Assume pname != "". 00839 * If successful, return pointer to port's box. 00840 * Else return NULL. 00841 */ 00842 boxf *html_port(node_t * n, char *pname, int* sides) 00843 { 00844 htmldata_t* tp; 00845 htmllabel_t* lbl = ND_label(n)->u.html; 00846 boxf* rv = NULL; 00847 00848 if (lbl->kind == HTML_TEXT) 00849 return NULL; 00850 00851 tp = portToTbl(lbl->u.tbl, pname); 00852 if (tp) { 00853 rv = &tp->box; 00854 *sides = tp->sides; 00855 } 00856 return rv; 00857 00858 } 00859 00860 /* html_path: 00861 * Return a box in a table containing the given endpoint. 00862 * If the top flow is text (no internal structure), return 00863 * the box of the flow 00864 * Else return the box of the subtable containing the point. 00865 * Because of spacing, the point might not be in any subtable. 00866 * In that case, return the top flow's box. 00867 * Note that box[0] must contain the edge point. Additional boxes 00868 * move out to the boundary. 00869 * 00870 * At present, unimplemented, since the label may be inside a 00871 * non-box node and we need to figure out what this means. 00872 */ 00873 int html_path(node_t * n, port* p, int side, boxf * rv, int *k) 00874 { 00875 #ifdef UNIMPL 00876 point p; 00877 tbl_t *info; 00878 tbl_t *t; 00879 boxf b; 00880 int i; 00881 00882 info = (tbl_t *) ND_shape_info(n); 00883 assert(info->tbls); 00884 info = info->tbls[0]; /* top-level flow */ 00885 assert(IS_FLOW(info)); 00886 00887 b = info->box; 00888 if (info->tbl) { 00889 info = info->tbl; 00890 if (pt == 1) 00891 p = ED_tail_port(e).p; 00892 else 00893 p = ED_head_port(e).p; 00894 p = flip_pt(p, GD_rankdir(n->graph)); /* move p to node's coordinate system */ 00895 for (i = 0; (t = info->tbls[i]) != 0; i++) 00896 if (INSIDE(p, t->box)) { 00897 b = t->box; 00898 break; 00899 } 00900 } 00901 00902 /* move box into layout coordinate system */ 00903 if (GD_flip(n->graph)) 00904 b = flip_trans_box(b, ND_coord_i(n)); 00905 else 00906 b = move_box(b, ND_coord_i(n)); 00907 00908 *k = 1; 00909 *rv = b; 00910 if (pt == 1) 00911 return BOTTOM; 00912 else 00913 return TOP; 00914 #endif 00915 return 0; 00916 } 00917 00918 static int 00919 size_html_txt(graph_t *g, htmltxt_t* ftxt, htmlenv_t* env) 00920 { 00921 double xsize = 0.0; /* width of text block */ 00922 double ysize = 0.0; /* height of text block */ 00923 double fsize; 00924 double lsize; /* height of current line */ 00925 double mxfsize = 0.0; /* max. font size for the current line */ 00926 double curbline = 0.0; /* dist. of current base line from top */ 00927 pointf sz; 00928 int i, j, w, width; 00929 char *fname; 00930 textpara_t lp; 00931 htmlfont_t lhf; 00932 double maxoffset; 00933 00934 lp.font = &lhf; 00935 for (i = 0; i < ftxt->nparas; i++) { 00936 width = w = 0; 00937 maxoffset = mxfsize = 0; 00938 for (j = 0; j < ftxt->paras[i].nitems; j++) { 00939 lp.str = strdup_and_subst_obj (ftxt->paras[i].items[j].str, env->obj); 00940 if (ftxt->paras[i].items[j].font) { 00941 if(ftxt->paras[i].items[j].font->flags) 00942 lp.font->flags = ftxt->paras[i].items[j].font->flags; 00943 else if(env->finfo.flags > 0) 00944 lp.font->flags = env->finfo.flags; 00945 else 00946 lp.font->flags = 0; 00947 if (ftxt->paras[i].items[j].font->size > 0) 00948 fsize = ftxt->paras[i].items[j].font->size; 00949 else 00950 fsize = env->finfo.size; 00951 if (ftxt->paras[i].items[j].font->name) 00952 fname = ftxt->paras[i].items[j].font->name; 00953 else 00954 fname = env->finfo.name; 00955 } else { 00956 fsize = env->finfo.size; 00957 fname = env->finfo.name; 00958 lp.font->flags = 0; 00959 } 00960 sz = textsize(g, &lp, fname, fsize); 00961 free (ftxt->paras[i].items[j].str); 00962 ftxt->paras[i].items[j].str = lp.str; 00963 ftxt->paras[i].items[j].size = sz.x; 00964 ftxt->paras[i].items[j].yoffset_layout = lp.yoffset_layout; 00965 ftxt->paras[i].items[j].yoffset_centerline = lp.yoffset_centerline; 00966 ftxt->paras[i].items[j].postscript_alias = lp.postscript_alias; 00967 ftxt->paras[i].items[j].layout = lp.layout; 00968 ftxt->paras[i].items[j].free_layout = lp.free_layout; 00969 width += sz.x; 00970 mxfsize = MAX(fsize, mxfsize); 00971 maxoffset = MAX(lp.yoffset_centerline, maxoffset); 00972 } 00973 /* lsize = mxfsize * LINESPACING; */ 00974 lsize = mxfsize; 00975 ftxt->paras[i].size = (double) width; 00976 /* ysize - curbline is the distance from the previous 00977 * baseline to the bottom of the previous line. 00978 * Then, in the current line, we set the baseline to 00979 * be 5/6 of the max. font size. Thus, lfsize gives the 00980 * distance from the previous baseline to the new one. 00981 */ 00982 /* ftxt->paras[i].lfsize = 5*mxfsize/6 + ysize - curbline; */ 00983 ftxt->paras[i].lfsize = mxfsize + ysize - curbline - maxoffset; 00984 curbline += ftxt->paras[i].lfsize; 00985 xsize = MAX(width, xsize); 00986 ysize += lsize; 00987 } 00988 ftxt->box.UR.x = xsize; 00989 if (ftxt->nparas == 1) 00990 ftxt->box.UR.y = (int) (mxfsize); 00991 else 00992 ftxt->box.UR.y = (int) (ysize); 00993 return 0; 00994 } 00995 00996 /* forward declarion for recursive usage */ 00997 static int size_html_tbl(graph_t *g, htmltbl_t * tbl, htmlcell_t * parent, 00998 htmlenv_t * env); 00999 01000 /* size_html_img: 01001 */ 01002 static int size_html_img(htmlimg_t * img, htmlenv_t * env) 01003 { 01004 box b; 01005 int rv; 01006 01007 b.LL.x = b.LL.y = 0; 01008 b.UR = gvusershape_size(env->g, img->src); 01009 if ((b.UR.x == -1) && (b.UR.y == -1)) { 01010 rv = 1; 01011 b.UR.x = b.UR.y = 0; 01012 agerr(AGERR, "No or improper image file=\"%s\"\n", img->src); 01013 } else { 01014 rv = 0; 01015 GD_has_images(env->g) = TRUE; 01016 } 01017 01018 B2BF(b, img->box); 01019 return rv; 01020 } 01021 01022 /* size_html_cell: 01023 */ 01024 static int 01025 size_html_cell(graph_t *g, htmlcell_t * cp, htmltbl_t * parent, htmlenv_t * env) 01026 { 01027 int rv; 01028 pointf sz, child_sz; 01029 int margin; 01030 01031 cp->parent = parent; 01032 if (!(cp->data.flags & PAD_SET)) { 01033 if (parent->data.flags & PAD_SET) 01034 cp->data.pad = parent->data.pad; 01035 else 01036 cp->data.pad = DEFAULT_CELLPADDING; 01037 } 01038 if (!(cp->data.flags & BORDER_SET)) { 01039 if (parent->cb >= 0) 01040 cp->data.border = parent->cb; 01041 else if (parent->data.flags & BORDER_SET) 01042 cp->data.border = parent->data.border; 01043 else 01044 cp->data.border = DEFAULT_BORDER; 01045 } 01046 01047 if (cp->child.kind == HTML_TBL) { 01048 rv = size_html_tbl(g, cp->child.u.tbl, cp, env); 01049 child_sz = cp->child.u.tbl->data.box.UR; 01050 } else if (cp->child.kind == HTML_IMAGE) { 01051 rv = size_html_img(cp->child.u.img, env); 01052 child_sz = cp->child.u.img->box.UR; 01053 } else { 01054 rv = size_html_txt(g, cp->child.u.txt, env); 01055 child_sz = cp->child.u.txt->box.UR; 01056 } 01057 01058 margin = 2 * (cp->data.pad + cp->data.border); 01059 sz.x = child_sz.x + margin; 01060 sz.y = child_sz.y + margin; 01061 01062 if (cp->data.flags & FIXED_FLAG) { 01063 if (cp->data.width && cp->data.height) { 01064 if ((cp->data.width < sz.x) || (cp->data.height < sz.y)) { 01065 agerr(AGWARN, "cell size too small for content\n"); 01066 rv = 1; 01067 } 01068 sz.x = sz.y = 0; 01069 01070 } else { 01071 agerr(AGWARN, 01072 "fixed cell size with unspecified width or height\n"); 01073 rv = 1; 01074 } 01075 } 01076 cp->data.box.UR.x = MAX(sz.x, cp->data.width); 01077 cp->data.box.UR.y = MAX(sz.y, cp->data.height); 01078 return rv; 01079 } 01080 01081 static int findCol(PointSet * ps, int row, int col, htmlcell_t * cellp) 01082 { 01083 int notFound = 1; 01084 int lastc; 01085 int i, j, c; 01086 int end = cellp->cspan - 1; 01087 01088 while (notFound) { 01089 lastc = col + end; 01090 for (c = lastc; c >= col; c--) { 01091 if (isInPS(ps, c, row)) 01092 break; 01093 } 01094 if (c >= col) /* conflict : try column after */ 01095 col = c + 1; 01096 else 01097 notFound = 0; 01098 } 01099 for (j = col; j < col + cellp->cspan; j++) { 01100 for (i = row; i < row + cellp->rspan; i++) { 01101 addPS(ps, j, i); 01102 } 01103 } 01104 return col; 01105 } 01106 01107 /* processTbl: 01108 * Convert parser representation of cells into final form. 01109 * Find column and row positions of cells. 01110 * Recursively size cells. 01111 * Return 1 if problem sizing a cell. 01112 */ 01113 static int processTbl(graph_t *g, htmltbl_t * tbl, htmlenv_t * env) 01114 { 01115 pitem *rp; 01116 pitem *cp; 01117 Dt_t *cdict; 01118 int r, c, cnt; 01119 htmlcell_t *cellp; 01120 htmlcell_t **cells; 01121 Dt_t *rows = tbl->u.p.rows; 01122 int rv = 0; 01123 int n_rows = 0; 01124 int n_cols = 0; 01125 PointSet *ps = newPS(); 01126 Dt_t* is = openIntSet(); 01127 01128 rp = (pitem *) dtflatten(rows); 01129 cnt = 0; 01130 r = 0; 01131 while (rp) { 01132 cdict = rp->u.rp; 01133 cp = (pitem *) dtflatten(cdict); 01134 while (cp) { 01135 cellp = cp->u.cp; 01136 cnt++; 01137 cp = (pitem *) dtlink(cdict, (Dtlink_t *) cp); 01138 } 01139 if (rp->ruled) { 01140 addIntSet (is, r+1); 01141 } 01142 rp = (pitem *) dtlink(rows, (Dtlink_t *) rp); 01143 r++; 01144 } 01145 01146 cells = tbl->u.n.cells = N_NEW(cnt + 1, htmlcell_t *); 01147 rp = (pitem *) dtflatten(rows); 01148 r = 0; 01149 while (rp) { 01150 cdict = rp->u.rp; 01151 cp = (pitem *) dtflatten(cdict); 01152 c = 0; 01153 while (cp) { 01154 cellp = cp->u.cp; 01155 *cells++ = cellp; 01156 rv |= size_html_cell(g, cellp, tbl, env); 01157 c = findCol(ps, r, c, cellp); 01158 cellp->row = r; 01159 cellp->col = c; 01160 c += cellp->cspan; 01161 n_cols = MAX(c, n_cols); 01162 n_rows = MAX(r + cellp->rspan, n_rows); 01163 if (inIntSet (is, r+cellp->rspan)) cellp->ruled |= HTML_HRULE; 01164 cp = (pitem *) dtlink(cdict, (Dtlink_t *) cp); 01165 } 01166 rp = (pitem *) dtlink(rows, (Dtlink_t *) rp); 01167 r++; 01168 } 01169 tbl->rc = n_rows; 01170 tbl->cc = n_cols; 01171 dtclose(rows); 01172 dtclose(is); 01173 freePS(ps); 01174 return rv; 01175 } 01176 01177 /* Split size x over n pieces with spacing s. 01178 * We substract s*(n-1) from x, divide by n and 01179 * take the ceiling. 01180 */ 01181 #define SPLIT(x,n,s) (((x) - ((s)-1)*((n)-1)) / (n)) 01182 01183 /* sizeLinearArray: 01184 * Determine sizes of rows and columns. The size of a column is the 01185 * maximum width of any cell in it. Similarly for rows. 01186 * A cell spanning columns contributes proportionately to each column 01187 * it is in. 01188 */ 01189 void sizeLinearArray(htmltbl_t * tbl) 01190 { 01191 htmlcell_t *cp; 01192 htmlcell_t **cells; 01193 int wd, ht, i, x, y; 01194 01195 tbl->heights = N_NEW(tbl->rc + 1, int); 01196 tbl->widths = N_NEW(tbl->cc + 1, int); 01197 01198 for (cells = tbl->u.n.cells; *cells; cells++) { 01199 cp = *cells; 01200 if (cp->rspan == 1) 01201 ht = cp->data.box.UR.y; 01202 else { 01203 ht = SPLIT(cp->data.box.UR.y, cp->rspan, tbl->data.space); 01204 ht = MAX(ht, 1); 01205 } 01206 if (cp->cspan == 1) 01207 wd = cp->data.box.UR.x; 01208 else { 01209 wd = SPLIT(cp->data.box.UR.x, cp->cspan, tbl->data.space); 01210 wd = MAX(wd, 1); 01211 } 01212 for (i = cp->row; i < cp->row + cp->rspan; i++) { 01213 y = tbl->heights[i]; 01214 tbl->heights[i] = MAX(y, ht); 01215 } 01216 for (i = cp->col; i < cp->col + cp->cspan; i++) { 01217 x = tbl->widths[i]; 01218 tbl->widths[i] = MAX(x, wd); 01219 } 01220 } 01221 } 01222 01223 static char *nnames[] = { 01224 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", 01225 "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", 01226 }; 01227 01228 /* nToName: 01229 * Convert int to its decimal string representation. 01230 */ 01231 char *nToName(int c) 01232 { 01233 static char name[100]; 01234 01235 if (c < sizeof(nnames) / sizeof(char *)) 01236 return nnames[c]; 01237 01238 sprintf(name, "%d", c); 01239 return name; 01240 } 01241 01242 /* closeGraphs: 01243 * Clean up graphs made for setting column and row widths. 01244 */ 01245 static void closeGraphs(graph_t * rowg, graph_t * colg) 01246 { 01247 node_t *n; 01248 for (n = GD_nlist(colg); n; n = ND_next(n)) { 01249 free_list(ND_in(n)); 01250 free_list(ND_out(n)); 01251 } 01252 01253 agclose(rowg); 01254 agclose(colg); 01255 } 01256 01257 static void checkChain(graph_t * g) 01258 { 01259 node_t *t; 01260 node_t *h; 01261 edge_t *e; 01262 t = GD_nlist(g); 01263 for (h = ND_next(t); h; h = ND_next(h)) { 01264 if (!agfindedge(g, t, h)) { 01265 #ifdef WITH_CGRAPH 01266 e = agedge(g, t, h, NULL, 1); 01267 agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), TRUE); 01268 #else 01269 e = agedge(g, t, h); 01270 #endif 01271 ED_minlen(e) = 0; 01272 elist_append(e, ND_out(t)); 01273 elist_append(e, ND_in(h)); 01274 } 01275 t = h; 01276 } 01277 } 01278 01279 /* makeGraphs: 01280 * Generate dags modeling the row and column constraints. 01281 * If the table has cc columns, we create the graph 01282 * 0 -> 1 -> 2 -> ... -> cc 01283 * and if a cell starts in column c with span cspan, with 01284 * width w, we add the edge c -> c+cspan [minlen = w]. 01285 * 01286 * We might simplify the graph by removing multiedges, 01287 * using the max minlen, but will affect the balancing? 01288 */ 01289 void makeGraphs(htmltbl_t * tbl, graph_t * rowg, graph_t * colg) 01290 { 01291 htmlcell_t *cp; 01292 htmlcell_t **cells; 01293 node_t *t; 01294 node_t *lastn; 01295 node_t *h; 01296 edge_t *e; 01297 int i; 01298 int* minc; 01299 int* minr; 01300 01301 lastn = NULL; 01302 for (i = 0; i <= tbl->cc; i++) { 01303 #ifdef WITH_CGRAPH 01304 t = agnode(colg, nToName(i),1); 01305 agbindrec(t, "Agnodeinfo_t", sizeof(Agnodeinfo_t), TRUE); 01306 #else 01307 t = agnode(colg, nToName(i)); 01308 #endif 01309 alloc_elist(tbl->rc, ND_in(t)); 01310 alloc_elist(tbl->rc, ND_out(t)); 01311 if (lastn) { 01312 ND_next(lastn) = t; 01313 lastn = t; 01314 } else { 01315 lastn = GD_nlist(colg) = t; 01316 } 01317 } 01318 lastn = NULL; 01319 for (i = 0; i <= tbl->rc; i++) { 01320 #ifdef WITH_CGRAPH 01321 t = agnode(rowg, nToName(i),1); 01322 agbindrec(t, "Agnodeinfo_t", sizeof(Agnodeinfo_t), TRUE); 01323 #else 01324 t = agnode(rowg, nToName(i)); 01325 #endif 01326 alloc_elist(tbl->cc, ND_in(t)); 01327 alloc_elist(tbl->cc, ND_out(t)); 01328 if (lastn) { 01329 ND_next(lastn) = t; 01330 lastn = t; 01331 } else { 01332 lastn = GD_nlist(rowg) = t; 01333 } 01334 } 01335 minr = N_NEW(tbl->rc, int); 01336 minc = N_NEW(tbl->cc, int); 01337 for (cells = tbl->u.n.cells; *cells; cells++) { 01338 int x, y, c, r; 01339 cp = *cells; 01340 x = (cp->data.box.UR.x + (cp->cspan-1))/cp->cspan; 01341 for (c = 0; c < cp->cspan; c++) 01342 minc[cp->col + c] = MAX(minc[cp->col + c],x); 01343 y = (cp->data.box.UR.y + (cp->rspan-1))/cp->rspan; 01344 for (r = 0; r < cp->rspan; r++) 01345 minr[cp->row + r] = MAX(minr[cp->row + r],y); 01346 } 01347 for (cells = tbl->u.n.cells; *cells; cells++) { 01348 int x, y, c, r; 01349 cp = *cells; 01350 t = agfindnode(colg, nToName(cp->col)); 01351 h = agfindnode(colg, nToName(cp->col + cp->cspan)); 01352 #ifdef WITH_CGRAPH 01353 e = agedge(colg, t, h, NULL, 1); 01354 agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), TRUE); 01355 #else 01356 e = agedge(colg, t, h); 01357 #endif 01358 x = 0; 01359 for (c = 0; c < cp->cspan; c++) 01360 x += minc[cp->col + c]; 01361 ED_minlen(e) = x; 01362 /* ED_minlen(e) = cp->data.box.UR.x; */ 01363 #if (DEBUG==2) 01364 fprintf(stderr, "col edge %s -> %s %d\n", t->name, h->name, 01365 ED_minlen(e)); 01366 #endif 01367 elist_append(e, ND_out(t)); 01368 elist_append(e, ND_in(h)); 01369 01370 t = agfindnode(rowg, nToName(cp->row)); 01371 h = agfindnode(rowg, nToName(cp->row + cp->rspan)); 01372 #ifdef WITH_CGRAPH 01373 e = agedge(rowg, t, h, NULL, 1); 01374 agbindrec(e, "Agedgeinfo_t", sizeof(Agedgeinfo_t), TRUE); 01375 #else 01376 e = agedge(rowg, t, h); 01377 #endif 01378 y = 0; 01379 for (r = 0; r < cp->rspan; r++) 01380 y += minr[cp->row + r]; 01381 ED_minlen(e) = y; 01382 /* ED_minlen(e) = cp->data.box.UR.y; */ 01383 #if (DEBUG==2) 01384 fprintf(stderr, "row edge %s -> %s %d\n", agnameof(t), agnameof(h), 01385 ED_minlen(e)); 01386 #endif 01387 elist_append(e, ND_out(t)); 01388 elist_append(e, ND_in(h)); 01389 } 01390 01391 /* Make sure that 0 <= 1 <= 2 ...k. This implies graph connected. */ 01392 checkChain(colg); 01393 checkChain(rowg); 01394 01395 free (minc); 01396 free (minr); 01397 } 01398 01399 /* setSizes: 01400 * Use rankings to determine cell dimensions. The rank values 01401 * give the coordinate, so to get the width/height, we have 01402 * to subtract the previous value. 01403 */ 01404 void setSizes(htmltbl_t * tbl, graph_t * rowg, graph_t * colg) 01405 { 01406 int i; 01407 node_t *n; 01408 int prev; 01409 01410 prev = 0; 01411 n = GD_nlist(rowg); 01412 for (i = 0, n = ND_next(n); n; i++, n = ND_next(n)) { 01413 tbl->heights[i] = ND_rank(n) - prev; 01414 prev = ND_rank(n); 01415 } 01416 prev = 0; 01417 n = GD_nlist(colg); 01418 for (i = 0, n = ND_next(n); n; i++, n = ND_next(n)) { 01419 tbl->widths[i] = ND_rank(n) - prev; 01420 prev = ND_rank(n); 01421 } 01422 01423 } 01424 01425 /* sizeArray: 01426 * Set column and row sizes. Optimize for minimum width and 01427 * height. Where there is slack, try to distribute evenly. 01428 * We do this by encoding cells as edges with min length is 01429 * a dag on a chain. We then run network simplex, using 01430 * LR_balance. 01431 */ 01432 void sizeArray(htmltbl_t * tbl) 01433 { 01434 graph_t *rowg; 01435 graph_t *colg; 01436 01437 /* Do the 1D cases by hand */ 01438 if ((tbl->rc == 1) || (tbl->cc == 1)) { 01439 sizeLinearArray(tbl); 01440 return; 01441 } 01442 01443 tbl->heights = N_NEW(tbl->rc + 1, int); 01444 tbl->widths = N_NEW(tbl->cc + 1, int); 01445 01446 #ifdef WITH_CGRAPH 01447 rowg = agopen("rowg", Agdirected,NIL(Agdisc_t *)); 01448 colg = agopen("colg", Agdirected,NIL(Agdisc_t *)); 01449 /* Only need GD_nlist */ 01450 agbindrec(rowg, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE); // graph custom data 01451 agbindrec(colg, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE); // graph custom data 01452 #else 01453 rowg = agopen("rowg", AGDIGRAPH); 01454 colg = agopen("colg", AGDIGRAPH); 01455 #endif 01456 makeGraphs(tbl, rowg, colg); 01457 rank(rowg, 2, INT_MAX); 01458 rank(colg, 2, INT_MAX); 01459 setSizes(tbl, rowg, colg); 01460 closeGraphs(rowg, colg); 01461 } 01462 01463 static void pos_html_tbl(htmltbl_t *, boxf, int); /* forward declaration */ 01464 01465 /* pos_html_img: 01466 * Place image in cell 01467 * storing allowed space handed by parent cell. 01468 * How this space is used is handled in emit_html_img. 01469 */ 01470 static void pos_html_img(htmlimg_t * cp, boxf pos) 01471 { 01472 cp->box = pos; 01473 } 01474 01475 /* pos_html_txt: 01476 * Set default alignment. 01477 */ 01478 static void 01479 pos_html_txt(htmltxt_t* ftxt, char c) 01480 { 01481 int i; 01482 01483 for (i = 0; i < ftxt->nparas; i++) { 01484 if (ftxt->paras[i].just == UNSET_ALIGN) /* unset */ 01485 ftxt->paras[i].just = c; 01486 } 01487 } 01488 01489 /* pos_html_cell: 01490 */ 01491 static void pos_html_cell(htmlcell_t * cp, boxf pos, int sides) 01492 { 01493 double delx, dely; 01494 pointf oldsz; 01495 boxf cbox; 01496 01497 if (!cp->data.pencolor && cp->parent->data.pencolor) 01498 cp->data.pencolor = strdup(cp->parent->data.pencolor); 01499 01500 /* If fixed, align cell */ 01501 if (cp->data.flags & FIXED_FLAG) { 01502 oldsz = cp->data.box.UR; 01503 delx = (pos.UR.x - pos.LL.x) - oldsz.x; 01504 if (delx > 0) { 01505 switch (cp->data.flags & HALIGN_MASK) { 01506 case HALIGN_LEFT: 01507 pos.UR.x = pos.LL.x + oldsz.x; 01508 break; 01509 case HALIGN_RIGHT: 01510 pos.UR.x += delx; 01511 pos.LL.x += delx; 01512 break; 01513 default: 01514 pos.LL.x += delx / 2; 01515 pos.UR.x -= delx / 2; 01516 break; 01517 } 01518 } 01519 dely = (pos.UR.y - pos.LL.y) - oldsz.y; 01520 if (dely > 0) { 01521 switch (cp->data.flags & VALIGN_MASK) { 01522 case VALIGN_BOTTOM: 01523 pos.UR.y = pos.LL.y + oldsz.y; 01524 break; 01525 case VALIGN_TOP: 01526 pos.UR.y += dely; 01527 pos.LL.y += dely; 01528 break; 01529 default: 01530 pos.LL.y += dely / 2; 01531 pos.UR.y -= dely / 2; 01532 break; 01533 } 01534 } 01535 } 01536 cp->data.box = pos; 01537 cp->data.sides = sides; 01538 01539 /* set up child's position */ 01540 cbox.LL.x = pos.LL.x + cp->data.border + cp->data.pad; 01541 cbox.LL.y = pos.LL.y + cp->data.border + cp->data.pad; 01542 cbox.UR.x = pos.UR.x - cp->data.border - cp->data.pad; 01543 cbox.UR.y = pos.UR.y - cp->data.border - cp->data.pad; 01544 01545 if (cp->child.kind == HTML_TBL) { 01546 pos_html_tbl(cp->child.u.tbl, cbox, sides); 01547 } else if (cp->child.kind == HTML_IMAGE) { 01548 /* Note that alignment trumps scaling */ 01549 oldsz = cp->child.u.img->box.UR; 01550 delx = (cbox.UR.x - cbox.LL.x) - oldsz.x; 01551 if (delx > 0) { 01552 switch (cp->data.flags & HALIGN_MASK) { 01553 case HALIGN_LEFT: 01554 cbox.UR.x -= delx; 01555 break; 01556 case HALIGN_RIGHT: 01557 cbox.LL.x += delx; 01558 break; 01559 } 01560 } 01561 01562 dely = (cbox.UR.y - cbox.LL.y) - oldsz.y; 01563 if (dely > 0) { 01564 switch (cp->data.flags & VALIGN_MASK) { 01565 case VALIGN_BOTTOM: 01566 cbox.UR.y -= dely; 01567 break; 01568 case VALIGN_TOP: 01569 cbox.LL.y += dely; 01570 break; 01571 } 01572 } 01573 pos_html_img(cp->child.u.img, cbox); 01574 } else { 01575 char dfltalign; 01576 int af; 01577 01578 oldsz = cp->child.u.txt->box.UR; 01579 delx = (cbox.UR.x - cbox.LL.x) - oldsz.x; 01580 /* If the cell is larger than the text block and alignment is 01581 * done at textblock level, the text box is shrunk accordingly. 01582 */ 01583 if ((delx > 0)&&((af=(cp->data.flags & HALIGN_MASK)) != HALIGN_TEXT)) { 01584 switch (af) { 01585 case HALIGN_LEFT: 01586 cbox.UR.x -= delx; 01587 break; 01588 case HALIGN_RIGHT: 01589 cbox.LL.x += delx; 01590 break; 01591 default: 01592 cbox.LL.x += delx / 2; 01593 cbox.UR.x -= delx / 2; 01594 break; 01595 } 01596 } 01597 01598 dely = (cbox.UR.y - cbox.LL.y) - oldsz.y; 01599 if (dely > 0) { 01600 switch (cp->data.flags & VALIGN_MASK) { 01601 case VALIGN_BOTTOM: 01602 cbox.UR.y -= dely; 01603 break; 01604 case VALIGN_TOP: 01605 cbox.LL.y += dely; 01606 break; 01607 default: 01608 cbox.LL.y += dely / 2; 01609 cbox.UR.y -= dely / 2; 01610 break; 01611 } 01612 } 01613 cp->child.u.txt->box = cbox; 01614 01615 /* Set default text alignment 01616 */ 01617 switch (cp->data.flags & BALIGN_MASK) { 01618 case BALIGN_LEFT: 01619 dfltalign = 'l'; 01620 break; 01621 case BALIGN_RIGHT: 01622 dfltalign = 'r'; 01623 break; 01624 default: 01625 dfltalign = 'n'; 01626 break; 01627 } 01628 pos_html_txt (cp->child.u.txt, dfltalign); 01629 } 01630 } 01631 01632 /* pos_html_tbl: 01633 * Position table given its box, then calculate 01634 * the position of each cell. In addition, set the sides 01635 * attribute indicating which external sides of the node 01636 * are accessible to the table. 01637 */ 01638 static void pos_html_tbl(htmltbl_t * tbl, boxf pos, int sides) 01639 { 01640 int x, y, delx, dely, oldsz; 01641 int i, extra, plus; 01642 htmlcell_t **cells = tbl->u.n.cells; 01643 htmlcell_t *cp; 01644 boxf cbox; 01645 01646 if (tbl->u.n.parent && tbl->u.n.parent->data.pencolor && !tbl->data.pencolor) 01647 tbl->data.pencolor = strdup (tbl->u.n.parent->data.pencolor); 01648 01649 oldsz = tbl->data.box.UR.x; 01650 delx = (pos.UR.x - pos.LL.x) - oldsz; 01651 assert(delx >= 0); 01652 oldsz = tbl->data.box.UR.y; 01653 dely = (pos.UR.y - pos.LL.y) - oldsz; 01654 assert(dely >= 0); 01655 01656 /* If fixed, align box */ 01657 if (tbl->data.flags & FIXED_FLAG) { 01658 if (delx > 0) { 01659 switch (tbl->data.flags & HALIGN_MASK) { 01660 case HALIGN_LEFT: 01661 pos.UR.x = pos.LL.x + oldsz; 01662 break; 01663 case HALIGN_RIGHT: 01664 pos.UR.x += delx; 01665 pos.LL.x += delx; 01666 break; 01667 default: 01668 pos.LL.x += delx / 2; 01669 pos.UR.x -= delx / 2; 01670 break; 01671 } 01672 delx = 0; 01673 } 01674 if (dely > 0) { 01675 switch (tbl->data.flags & VALIGN_MASK) { 01676 case VALIGN_BOTTOM: 01677 pos.UR.y = pos.LL.y + oldsz; 01678 break; 01679 case VALIGN_TOP: 01680 pos.UR.y += dely; 01681 pos.LL.y += dely; 01682 break; 01683 default: 01684 pos.LL.y += dely / 2; 01685 pos.UR.y -= dely / 2; 01686 break; 01687 } 01688 dely = 0; 01689 } 01690 } 01691 01692 /* change sizes to start positions and distribute extra space */ 01693 x = pos.LL.x + tbl->data.border + tbl->data.space; 01694 extra = delx / (tbl->cc); 01695 plus = ROUND(delx - extra * (tbl->cc)); 01696 for (i = 0; i <= tbl->cc; i++) { 01697 delx = tbl->widths[i] + extra + (i < plus ? 1 : 0); 01698 tbl->widths[i] = x; 01699 x += delx + tbl->data.space; 01700 } 01701 y = pos.UR.y - tbl->data.border - tbl->data.space; 01702 extra = dely / (tbl->rc); 01703 plus = ROUND(dely - extra * (tbl->rc)); 01704 for (i = 0; i <= tbl->rc; i++) { 01705 dely = tbl->heights[i] + extra + (i < plus ? 1 : 0); 01706 tbl->heights[i] = y; 01707 y -= dely + tbl->data.space; 01708 } 01709 01710 while ((cp = *cells++)) { 01711 int mask = 0; 01712 if (sides) { 01713 if (cp->col == 0) mask |= LEFT; 01714 if (cp->row == 0) mask |= TOP; 01715 if (cp->col + cp->cspan == tbl->cc) mask |= RIGHT; 01716 if (cp->row + cp->rspan == tbl->rc) mask |= BOTTOM; 01717 } 01718 cbox.LL.x = tbl->widths[cp->col]; 01719 cbox.UR.x = tbl->widths[cp->col + cp->cspan] - tbl->data.space; 01720 cbox.UR.y = tbl->heights[cp->row]; 01721 cbox.LL.y = tbl->heights[cp->row + cp->rspan] + tbl->data.space; 01722 pos_html_cell(cp, cbox, sides & mask); 01723 } 01724 01725 tbl->data.sides = sides; 01726 tbl->data.box = pos; 01727 } 01728 01729 /* size_html_tbl: 01730 * Determine the size of a table by first determining the 01731 * size of each cell. 01732 */ 01733 static int 01734 size_html_tbl(graph_t *g, htmltbl_t * tbl, htmlcell_t * parent, htmlenv_t * env) 01735 { 01736 int i, wd, ht; 01737 int rv = 0; 01738 static htmlfont_t savef; 01739 01740 if (tbl->font) 01741 pushFontInfo(env, tbl->font, &savef); 01742 tbl->u.n.parent = parent; 01743 rv = processTbl(g, tbl, env); 01744 01745 /* Set up border and spacing */ 01746 if (!(tbl->data.flags & SPACE_SET)) { 01747 tbl->data.space = DEFAULT_CELLSPACING; 01748 } 01749 if (!(tbl->data.flags & BORDER_SET)) { 01750 tbl->data.border = DEFAULT_BORDER; 01751 } 01752 01753 sizeArray(tbl); 01754 01755 wd = (tbl->cc + 1) * tbl->data.space + 2 * tbl->data.border; 01756 ht = (tbl->rc + 1) * tbl->data.space + 2 * tbl->data.border; 01757 for (i = 0; i < tbl->cc; i++) 01758 wd += tbl->widths[i]; 01759 for (i = 0; i < tbl->rc; i++) 01760 ht += tbl->heights[i]; 01761 01762 if (tbl->data.flags & FIXED_FLAG) { 01763 if (tbl->data.width && tbl->data.height) { 01764 if ((tbl->data.width < wd) || (tbl->data.height < ht)) { 01765 agerr(AGWARN, "table size too small for content\n"); 01766 rv = 1; 01767 } 01768 wd = ht = 0; 01769 } else { 01770 agerr(AGWARN, 01771 "fixed table size with unspecified width or height\n"); 01772 rv = 1; 01773 } 01774 } 01775 tbl->data.box.UR.x = MAX(wd, tbl->data.width); 01776 tbl->data.box.UR.y = MAX(ht, tbl->data.height); 01777 01778 if (tbl->font) 01779 popFontInfo(env, &savef); 01780 return rv; 01781 } 01782 01783 static char *nameOf(void *obj, agxbuf * xb) 01784 { 01785 Agedge_t *ep; 01786 switch (agobjkind(obj)) { 01787 #ifndef WITH_CGRAPH 01788 case AGGRAPH: 01789 #else 01790 case AGRAPH: 01791 #endif 01792 agxbput(xb, agnameof(((Agraph_t *) obj))); 01793 break; 01794 case AGNODE: 01795 agxbput(xb, agnameof(((Agnode_t *) obj))); 01796 break; 01797 case AGEDGE: 01798 ep = (Agedge_t *) obj; 01799 agxbput(xb, agnameof(agtail(ep))); 01800 agxbput(xb, agnameof(aghead(ep))); 01801 if (agisdirected(agraphof(aghead(ep)))) 01802 agxbput(xb, "->"); 01803 else 01804 agxbput(xb, "--"); 01805 break; 01806 } 01807 return agxbuse(xb); 01808 } 01809 01810 #ifdef DEBUG 01811 void indent(int i) 01812 { 01813 while (i--) 01814 fprintf(stderr, " "); 01815 } 01816 01817 void printBox(boxf b) 01818 { 01819 fprintf(stderr, "(%f,%f)(%f,%f)", b.LL.x, b.LL.y, b.UR.x, b.UR.y); 01820 } 01821 01822 void printImage(htmlimg_t *ip, int ind) 01823 { 01824 indent(ind); 01825 fprintf(stderr, "img: %s\n", ip->src); 01826 } 01827 01828 void printTxt(htmltxt_t * txt, int ind) 01829 { 01830 int i, j; 01831 01832 indent(ind); 01833 fprintf (stderr, "txt paras = %d \n", txt->nparas); 01834 for (i = 0; i < txt->nparas; i++) { 01835 indent(ind+1); 01836 fprintf (stderr, "[%d] %d items\n", i, txt->paras[i].nitems); 01837 for (j = 0; j < txt->paras[i].nitems; j++) { 01838 indent(ind+2); 01839 fprintf (stderr, "[%d] (%f) \"%s\" ", 01840 j, txt->paras[i].items[j].size, 01841 txt->paras[i].items[j].str); 01842 if (txt->paras[i].items[j].font) 01843 fprintf (stderr, "font %s color %s size %f\n", 01844 txt->paras[i].items[j].font->name, 01845 txt->paras[i].items[j].font->color, 01846 txt->paras[i].items[j].font->size); 01847 else fprintf (stderr, "\n"); 01848 } 01849 } 01850 } 01851 01852 void printData(htmldata_t * dp) 01853 { 01854 unsigned char flags = dp->flags; 01855 char c; 01856 01857 fprintf(stderr, "s%d(%d) ", dp->space, (flags & SPACE_SET ? 1 : 0)); 01858 fprintf(stderr, "b%d(%d) ", dp->border, (flags & BORDER_SET ? 1 : 0)); 01859 fprintf(stderr, "p%d(%d) ", dp->pad, (flags & PAD_SET ? 1 : 0)); 01860 switch (flags & HALIGN_MASK) { 01861 case HALIGN_RIGHT: 01862 c = 'r'; 01863 break; 01864 case HALIGN_LEFT: 01865 c = 'l'; 01866 break; 01867 default: 01868 c = 'n'; 01869 break; 01870 } 01871 fprintf(stderr, "%c", c); 01872 switch (flags & VALIGN_MASK) { 01873 case VALIGN_TOP: 01874 c = 't'; 01875 break; 01876 case VALIGN_BOTTOM: 01877 c = 'b'; 01878 break; 01879 default: 01880 c = 'c'; 01881 break; 01882 } 01883 fprintf(stderr, "%c ", c); 01884 printBox(dp->box); 01885 } 01886 01887 void printTbl(htmltbl_t * tbl, int ind) 01888 { 01889 htmlcell_t **cells = tbl->u.n.cells; 01890 indent(ind); 01891 fprintf(stderr, "tbl (%p) %d %d ", tbl, tbl->cc, tbl->rc); 01892 printData(&tbl->data); 01893 fputs("\n", stderr); 01894 while (*cells) 01895 printCell(*cells++, ind + 1); 01896 } 01897 01898 static void printCell(htmlcell_t * cp, int ind) 01899 { 01900 indent(ind); 01901 fprintf(stderr, "cell %d %d %d %d ", cp->cspan, cp->rspan, cp->col, 01902 cp->row); 01903 printData(&cp->data); 01904 fputs("\n", stderr); 01905 switch (cp->child.kind) { 01906 case HTML_TBL: 01907 printTbl(cp->child.u.tbl, ind + 1); 01908 break; 01909 case HTML_TEXT: 01910 printTxt(cp->child.u.txt, ind + 1); 01911 break; 01912 case HTML_IMAGE: 01913 printImage(cp->child.u.img, ind + 1); 01914 break; 01915 default: 01916 break; 01917 } 01918 } 01919 01920 void printLbl(htmllabel_t * lbl) 01921 { 01922 if (lbl->kind == HTML_TBL) 01923 printTbl(lbl->u.tbl, 0); 01924 else 01925 printTxt(lbl->u.txt, 0); 01926 } 01927 #endif /* DEBUG */ 01928 01929 static char *getPenColor(void *obj) 01930 { 01931 char *str; 01932 01933 if (((str = agget(obj, "pencolor")) != 0) && str[0]) 01934 return str; 01935 else if (((str = agget(obj, "color")) != 0) && str[0]) 01936 return str; 01937 else 01938 return NULL; 01939 } 01940 01941 /* make_html_label: 01942 * Return non-zero if problem parsing HTML. In this case, use object name. 01943 */ 01944 int make_html_label(void *obj, textlabel_t * lp) 01945 { 01946 int rv; 01947 double wd2, ht2; 01948 boxf box; 01949 graph_t *g; 01950 htmllabel_t *lbl; 01951 htmlenv_t env; 01952 char *s; 01953 01954 env.obj = obj; 01955 switch (agobjkind(obj)) { 01956 #ifdef WITH_CGRAPH 01957 case AGRAPH: 01958 #else 01959 case AGGRAPH: 01960 #endif 01961 env.g = ((Agraph_t *) obj)->root; 01962 break; 01963 case AGNODE: 01964 env.g = agraphof(((Agnode_t *) obj)); 01965 break; 01966 case AGEDGE: 01967 env.g = agraphof(aghead (((Agedge_t *) obj))); 01968 break; 01969 } 01970 g = env.g->root; 01971 01972 env.finfo.size = lp->fontsize; 01973 env.finfo.name = lp->fontname; 01974 env.finfo.color = lp->fontcolor; 01975 env.finfo.flags = 0; 01976 lbl = parseHTML(lp->text, &rv, GD_charset(env.g)); 01977 if (!lbl) { 01978 /* Parse of label failed; revert to simple text label */ 01979 agxbuf xb; 01980 unsigned char buf[SMALLBUF]; 01981 agxbinit(&xb, SMALLBUF, buf); 01982 lp->html = FALSE; 01983 lp->text = strdup(nameOf(obj, &xb)); 01984 switch (lp->charset) { 01985 case CHAR_LATIN1: 01986 s = latin1ToUTF8(lp->text); 01987 break; 01988 default: /* UTF8 */ 01989 s = htmlEntityUTF8(lp->text, env.g); 01990 break; 01991 } 01992 free(lp->text); 01993 lp->text = s; 01994 make_simple_label(g, lp); 01995 agxbfree(&xb); 01996 return rv; 01997 } 01998 01999 if (lbl->kind == HTML_TBL) { 02000 if (! lbl->u.tbl->data.pencolor && getPenColor(obj)) 02001 lbl->u.tbl->data.pencolor = strdup(getPenColor(obj)); 02002 rv |= size_html_tbl(g, lbl->u.tbl, NULL, &env); 02003 wd2 = (lbl->u.tbl->data.box.UR.x + 1) / 2; 02004 ht2 = (lbl->u.tbl->data.box.UR.y + 1) / 2; 02005 box = boxfof(-wd2, -ht2, wd2, ht2); 02006 pos_html_tbl(lbl->u.tbl, box, BOTTOM | RIGHT | TOP | LEFT); 02007 lp->dimen.x = box.UR.x - box.LL.x; 02008 lp->dimen.y = box.UR.y - box.LL.y; 02009 } else { 02010 rv |= size_html_txt(g, lbl->u.txt, &env); 02011 wd2 = (lbl->u.txt->box.UR.x + 1) / 2; 02012 ht2 = (lbl->u.txt->box.UR.y + 1) / 2; 02013 box = boxfof(-wd2, -ht2, wd2, ht2); 02014 lbl->u.txt->box = box; 02015 lp->dimen.x = box.UR.x - box.LL.x; 02016 lp->dimen.y = box.UR.y - box.LL.y; 02017 } 02018 02019 lp->u.html = lbl; 02020 02021 /* If the label is a table, replace label text because this may 02022 * be used for the title and alt fields in image maps. 02023 */ 02024 if (lbl->kind == HTML_TBL) { 02025 free (lp->text); 02026 lp->text = strdup ("<TABLE>"); 02027 } 02028 02029 return rv; 02030 } 02031
1.7.5