]> granicus.if.org Git - graphviz/commitdiff
Add support for horizontal and vertical rules in html tables.
authorerg <devnull@localhost>
Fri, 8 Jul 2011 18:52:33 +0000 (18:52 +0000)
committererg <devnull@localhost>
Fri, 8 Jul 2011 18:52:33 +0000 (18:52 +0000)
Note that at present the width of the rules is only 1.
We also allow formatting information about rules to be supplied by
the columns and rows attributes of TABLE. This is limited now to just
specifying all or nothing. These could be extended to accept more
general specifications as used in tabular environment in LaTeX.

lib/common/Makefile.am
lib/common/htmllex.c
lib/common/htmlparse.y
lib/common/htmltable.c
lib/common/htmltable.h
lib/common/intset.c [new file with mode: 0644]
lib/common/intset.h [new file with mode: 0644]

index 196dac9e4bbd0e61465ccad6b5180b9c58b89daf..654e225f6db9d3c36c44fbcb713a151c0eeba422 100644 (file)
@@ -27,12 +27,12 @@ BUILT_SOURCES = colortbl.h ps_font_equiv.h htmlparse.h
 pkginclude_HEADERS = arith.h geom.h color.h types.h textpara.h usershape.h
 noinst_HEADERS = render.h utils.h memory.h \
        geomprocs.h colorprocs.h colortbl.h entities.h globals.h \
-       logic.h const.h macros.h htmllex.h htmltable.h pointset.h
+       logic.h const.h macros.h htmllex.h htmltable.h pointset.h intset.h
 noinst_LTLIBRARIES = libcommon_C.la
 
 libcommon_C_la_SOURCES = arrows.c colxlate.c fontmetrics.c \
        args.c memory.c globals.c htmllex.c htmlparse.y htmltable.c input.c \
-       pointset.c postproc.c routespl.c splines.c psusershape.c \
+       pointset.c intset.c postproc.c routespl.c splines.c psusershape.c \
        timing.c labels.c ns.c shapes.c utils.c geom.c \
        output.c emit.c ps_font_equiv.txt ps_fontmap.txt fontmap.cfg \
        color_names
index 12abc0289636026799328b63e0af72ac5205e1f2..c044137ee9994e6d1fbf70f8b7945728e7badaee 100644 (file)
@@ -229,6 +229,26 @@ static int cellborderfn(htmltbl_t * p, char *v)
     return 0;
 }
 
+static int columnsfn(htmltbl_t * p, char *v)
+{
+    if (*v != '*') {
+       agerr(AGWARN, "Unknown value %s for COLUMNS - ignored\n", v);
+       return 1;
+    }
+    p->flags |= HTML_VRULE;
+    return 0;
+}
+
+static int rowsfn(htmltbl_t * p, char *v)
+{
+    if (*v != '*') {
+       agerr(AGWARN, "Unknown value %s for ROWS - ignored\n", v);
+       return 1;
+    }
+    p->flags |= HTML_HRULE;
+    return 0;
+}
+
 static int fixedsizefn(htmldata_t * p, char *v)
 {
     int rv = 0;
@@ -412,11 +432,13 @@ static attr_item tbl_items[] = {
     {"cellpadding", (attrFn) cellpaddingfn},
     {"cellspacing", (attrFn) cellspacingfn},
     {"color", (attrFn) pencolorfn},
+    {"columns", (attrFn) columnsfn},
     {"fixedsize", (attrFn) fixedsizefn},
     {"height", (attrFn) heightfn},
     {"href", (attrFn) hreffn},
     {"id", (attrFn) idfn},
     {"port", (attrFn) portfn},
+    {"rows", (attrFn) rowsfn},
     {"style", (attrFn) stylefn},
     {"target", (attrFn) targetfn},
     {"title", (attrFn) titlefn},
@@ -576,6 +598,10 @@ static void startElement(void *user, const char *name, char **atts)
     } else if (strcasecmp(name, "BR") == 0) {
        mkBR(atts);
        state.tok = T_br;
+    } else if (strcasecmp(name, "HR") == 0) {
+       state.tok = T_hr;
+    } else if (strcasecmp(name, "VR") == 0) {
+       state.tok = T_vr;
     } else if (strcasecmp(name, "IMG") == 0) {
        htmllval.img = mkImg(atts);
        state.tok = T_img;
@@ -616,6 +642,16 @@ static void endElement(void *user, const char *name)
            state.tok = T_BR;
        else
            state.tok = T_end_br;
+    } else if (strcasecmp(name, "HR") == 0) {
+       if (state.tok == T_hr)
+           state.tok = T_HR;
+       else
+           state.tok = T_end_hr;
+    } else if (strcasecmp(name, "VR") == 0) {
+       if (state.tok == T_vr)
+           state.tok = T_VR;
+       else
+           state.tok = T_end_vr;
     } else if (strcasecmp(name, "IMG") == 0) {
        if (state.tok == T_img)
            state.tok = T_IMG;
@@ -774,6 +810,24 @@ static void printTok(int tok)
     char *s;
 
     switch (tok) {
+    case T_VR:
+       s = "T_VR";
+       break;
+    case T_vr:
+       s = "T_vr";
+       break;
+    case T_end_vr:
+       s = "T_end_vr";
+       break;
+    case T_HR:
+       s = "T_HR";
+       break;
+    case T_hr:
+       s = "T_hr";
+       break;
+    case T_end_hr:
+       s = "T_end_hr";
+       break;
     case T_BR:
        s = "T_BR";
        break;
@@ -905,8 +959,10 @@ int htmllex()
        if (endp)
            state.ptr = endp;
     } while (state.tok == 0);
+    /* printTok (state.tok); */
     return state.tok;
 #else
     return EOF;
 #endif
 }
+
index a0851bc2bb54ef2543fcf48bcc4740ee9b194fbf..90dd3eda07bf20f2c79b853eeb617542681a1433 100644 (file)
@@ -252,16 +252,26 @@ mkText(void)
     return hft;
 }
 
+static pitem* lastRow (void)
+{
+  htmltbl_t* tbl = HTMLstate.tblstack;
+  pitem*     sp = dtlast (tbl->u.p.rows);
+  return sp;
+}
+
 /* addRow:
  * Add new cell row to current table.
  */
-static void addRow (void)
+static pitem* addRow (void)
 {
   Dt_t*      dp = dtopen(&cellDisc, Dtqueue);
   htmltbl_t* tbl = HTMLstate.tblstack;
   pitem*     sp = NEW(pitem);
   sp->u.rp = dp;
+  if (tbl->flags & HTML_HRULE)
+    sp->ruled = 1;
   dtinsert (tbl->u.p.rows, sp);
+  return sp;
 }
 
 /* setCell:
@@ -276,6 +286,8 @@ static void setCell (htmlcell_t* cp, void* obj, int kind)
   sp->u.cp = cp;
   dtinsert (row, sp);
   cp->child.kind = kind;
+  if (tbl->flags & HTML_VRULE)
+    cp->ruled = HTML_VRULE;
   
   if(kind == HTML_TEXT)
        cp->child.u.txt = (htmltxt_t*)obj;
@@ -410,11 +422,14 @@ popFont (void)
   htmltbl_t*   tbl;
   htmlfont_t*  font;
   htmlimg_t*   img;
+  pitem*       p;
 }
 
 %token T_end_br T_end_img T_row T_end_row T_html T_end_html
 %token T_end_table T_end_cell T_end_font T_string T_error
 %token T_n_italic T_n_bold T_n_underline T_n_sup T_n_sub
+%token T_HR T_hr T_end_hr
+%token T_VR T_vr T_end_vr
 %token <i> T_BR T_br
 %token <img> T_IMG T_img
 %token <tbl> T_table
@@ -422,9 +437,11 @@ popFont (void)
 %token <font> T_font T_italic T_bold T_underline T_sup T_sub
 
 %type <txt> fonttext
+%type <cell> cell cells
 %type <i> br  
 %type <tbl> table fonttable
 %type <img> image
+%type <p> row rows
 
 %start html
              
@@ -528,27 +545,38 @@ opt_space : string
           | /* empty*/
           ;
 
-rows : row
-     | rows row
+rows : row { $$ = $1; }
+     | rows row { $$ = $2; }
+     | rows HR row { $1->ruled = 1; $$ = $3; }
      ;
 
-row : T_row { addRow (); } cells T_end_row
+row : T_row { addRow (); } cells T_end_row { $$ = lastRow(); }
       ;
 
-cells : cell
-      | cells cell
+cells : cell { $$ = $1; }
+      | cells cell { $$ = $2; }
+      | cells VR cell { $1->ruled |= HTML_VRULE; $$ = $3; }
       ;
 
-cell : T_cell fonttable { setCell($1,$2,HTML_TBL); } T_end_cell
-     | T_cell fonttext { setCell($1,$2,HTML_TEXT); } T_end_cell
-     | T_cell image { setCell($1,$2,HTML_IMAGE); } T_end_cell
-     | T_cell { setCell($1,mkText(),HTML_TEXT); } T_end_cell
+cell : T_cell fonttable { setCell($1,$2,HTML_TBL); } T_end_cell { $$ = $1; }
+     | T_cell fonttext { setCell($1,$2,HTML_TEXT); } T_end_cell { $$ = $1; }
+     | T_cell image { setCell($1,$2,HTML_IMAGE); } T_end_cell { $$ = $1; }
+     | T_cell { setCell($1,mkText(),HTML_TEXT); } T_end_cell { $$ = $1; }
      ;
 
 image  : T_img T_end_img { $$ = $1; }
        | T_IMG { $$ = $1; }
        ;
 
+HR  : T_hr T_end_hr
+    | T_HR
+    ;
+
+VR  : T_vr T_end_vr
+    | T_VR
+    ;
+
+
 %%
 
 /* parseHTML:
index b831b7861f386828066ce4794bc34e1e99bd821c..9c15befcebf01c414f820bb239521776bf689e70 100644 (file)
@@ -36,6 +36,7 @@
 #include "htmltable.h"
 #include "agxbuf.h"
 #include "pointset.h"
+#include "intset.h"
 
 #define DEFAULT_BORDER    1
 #define DEFAULT_CELLPADDING  2
@@ -348,7 +349,73 @@ endAnchor (GVJ_t* job, htmlmap_data_t* save, int openPrev)
 
 /* forward declaration */
 static void emit_html_cell(GVJ_t * job, htmlcell_t * cp, htmlenv_t * env);
-static void emit_html_rules(GVJ_t * job, htmlcell_t * cp, htmlenv_t * env, char *fillc);
+
+/* emit_html_rules:
+ * place vertical and horizontal lines between adjacent cells and
+ * extend the lines to intersect the rounded table boundary 
+ */
+static void
+emit_html_rules(GVJ_t * job, htmlcell_t * cp, htmlenv_t * env, char *color)
+{
+    pointf rule_pt;
+    double rule_length;
+    unsigned char base;
+    boxf pts = cp->data.box;
+    pointf pos = env->pos;
+    
+    if (!color)
+       color = DEFAULT_COLOR;
+    gvrender_set_fillcolor(job, color);
+    gvrender_set_pencolor(job, color);
+
+    pts = cp->data.box;
+    pts.LL.x += pos.x;
+    pts.UR.x += pos.x;
+    pts.LL.y += pos.y;
+    pts.UR.y += pos.y;
+
+    //Determine vertical line coordinate and length
+    if ((cp->ruled & HTML_VRULE) && (cp->col + cp->cspan < cp->parent->cc)) {
+       if(cp->row == 0) {  // first row
+           // extend to table border and add half cell spacing
+           base = cp->parent->data.border + cp->parent->data.space/2;
+           rule_pt.y = pts.LL.y - cp->parent->data.space/2; 
+       }
+       else if(cp->row + cp->rspan == cp->parent->rc){  // bottom row
+           // extend to table border and add half cell spacing
+           base = cp->parent->data.border + cp->parent->data.space/2;
+           rule_pt.y = pts.LL.y - cp->parent->data.space/2 - base;
+       }
+       else {
+           base = 0;
+           rule_pt.y = pts.LL.y - cp->parent->data.space/2;
+       }
+       rule_pt.x = pts.UR.x + cp->parent->data.space/2;
+       rule_length = base + pts.UR.y - pts.LL.y + cp->parent->data.space;
+       doSide(job,rule_pt,0,rule_length);
+    }
+
+    //Determine the horizontal coordinate and length
+    if ((cp->ruled & HTML_HRULE) && (cp->row + cp->rspan < cp->parent->rc)) {
+       if(cp->col == 0) { // first column 
+           // extend to table border and add half cell spacing
+           base = cp->parent->data.border + cp->parent->data.space/2;
+           rule_pt.x = pts.LL.x - base - cp->parent->data.space/2;
+       }
+       else if(cp->col + cp->cspan == cp->parent->cc){  // last column
+           // extend to table border and add half cell spacing
+           base = cp->parent->data.border + cp->parent->data.space/2;
+           rule_pt.x = pts.LL.x - cp->parent->data.space/2;
+       }
+       else {
+           base = 0;
+           rule_pt.x = pts.LL.x - cp->parent->data.space/2;
+       }
+       rule_pt.y = pts.LL.y - cp->parent->data.space/2;
+       rule_length = base + pts.UR.x - pts.LL.x + cp->parent->data.space;
+       doSide(job,rule_pt,rule_length,0);
+    }
+}
 
 static void
 emit_html_tbl(GVJ_t * job, htmltbl_t * tbl, htmlenv_t * env)
@@ -356,6 +423,7 @@ emit_html_tbl(GVJ_t * job, htmltbl_t * tbl, htmlenv_t * env)
     boxf pts = tbl->data.box;
     pointf pos = env->pos;
     htmlcell_t **cells = tbl->u.n.cells;
+    htmlcell_t *cp;
     static htmlfont_t savef;
     htmlmap_data_t saved;
     int anchor; /* if true, we need to undo anchor settings. */
@@ -394,12 +462,9 @@ emit_html_tbl(GVJ_t * job, htmltbl_t * tbl, htmlenv_t * env)
            doBorder(job, tbl->data.pencolor, tbl->data.border, pts);
     }
     //render table rules
-#if 0
-    while (*cells){
-       emit_html_rules(job, *cells, env, tbl->data.bgcolor);
-       cells++;
+    while ((cp = *cells++)){
+       if (cp->ruled) emit_html_rules(job, cp, env, tbl->data.bgcolor);
     }
-#endif
     cells = tbl->u.n.cells;
 
     while (*cells) {
@@ -491,73 +556,6 @@ emit_html_cell(GVJ_t * job, htmlcell_t * cp, htmlenv_t * env)
     }
 }
 
-/* emit_html_rules:
- * place vertical and horizontal lines between adjacent cells and
- * extend the lines to intersect the rounded table boundary 
- */
-static void
-emit_html_rules(GVJ_t * job, htmlcell_t * cp, htmlenv_t * env, char *color)
-{
-    pointf rule_pt;
-    double rule_length;
-    unsigned char base;
-    boxf pts = cp->data.box;
-    pointf pos = env->pos;
-    
-    if (!color)
-       color = DEFAULT_COLOR;
-    gvrender_set_fillcolor(job, color);
-    gvrender_set_pencolor(job, color);
-
-    pts = cp->data.box;
-    pts.LL.x += pos.x;
-    pts.UR.x += pos.x;
-    pts.LL.y += pos.y;
-    pts.UR.y += pos.y;
-
-    //Determine vertical line coordinate and length
-    if (cp->col + cp->cspan < cp->parent->cc){
-       if(cp->row == 0) {  // first row
-           // extend to table border and add half cell spacing
-           base = cp->parent->data.border + cp->parent->data.space/2;
-           rule_pt.y = pts.LL.y - cp->parent->data.space/2; 
-       }
-       else if(cp->row + cp->rspan == cp->parent->rc){  // bottom row
-           // extend to table border and add half cell spacing
-           base = cp->parent->data.border + cp->parent->data.space/2;
-           rule_pt.y = pts.LL.y - cp->parent->data.space/2 - base;
-       }
-       else {
-           base = 0;
-           rule_pt.y = pts.LL.y - cp->parent->data.space/2;
-       }
-       rule_pt.x = pts.UR.x + cp->parent->data.space/2;
-       rule_length = base + pts.UR.y - pts.LL.y + cp->parent->data.space;
-       doSide(job,rule_pt,0,rule_length);
-    }
-    //Determine the horizontal coordinate and length
-    if(cp->row + cp->rspan < cp->parent->rc){
-       if(cp->col == 0) { // first column 
-           // extend to table border and add half cell spacing
-           base = cp->parent->data.border + cp->parent->data.space/2;
-           rule_pt.x = pts.LL.x - base - cp->parent->data.space/2;
-       }
-       else if(cp->col + cp->cspan == cp->parent->cc){  // last column
-           // extend to table border and add half cell spacing
-           base = cp->parent->data.border + cp->parent->data.space/2;
-           rule_pt.x = pts.LL.x - cp->parent->data.space/2;
-       }
-       else {
-           base = 0;
-           rule_pt.x = pts.LL.x - cp->parent->data.space/2;
-       }
-       rule_pt.y = pts.LL.y - cp->parent->data.space/2;
-       rule_length = base + pts.UR.x - pts.LL.x + cp->parent->data.space;
-       doSide(job,rule_pt,rule_length,0);
-    }
-
-}
-
 /* allocObj:
  * Push new obj on stack to be used in common by all 
  * html elements with anchors.
@@ -704,7 +702,7 @@ static void free_html_cell(htmlcell_t * cp)
 
 /* free_html_tbl:
  * If tbl->n_rows is negative, table is in initial state from
- * HTML parse, with data stored in u.p. Once run through processTable,
+ * HTML parse, with data stored in u.p. Once run through processTbl,
  * data is stored in u.n and tbl->n_rows is > 0.
  */
 static void free_html_tbl(htmltbl_t * tbl)
@@ -1073,9 +1071,11 @@ static int processTbl(graph_t *g, htmltbl_t * tbl, htmlenv_t * env)
     int n_rows = 0;
     int n_cols = 0;
     PointSet *ps = newPS();
+    Dt_t* is = openIntSet();
 
     rp = (pitem *) dtflatten(rows);
     cnt = 0;
+    r = 0;
     while (rp) {
        cdict = rp->u.rp;
        cp = (pitem *) dtflatten(cdict);
@@ -1084,7 +1084,11 @@ static int processTbl(graph_t *g, htmltbl_t * tbl, htmlenv_t * env)
            cnt++;
            cp = (pitem *) dtlink(cdict, (Dtlink_t *) cp);
        }
+       if (rp->ruled) {
+           addIntSet (is, r+1);
+       }
        rp = (pitem *) dtlink(rows, (Dtlink_t *) rp);
+       r++;
     }
 
     cells = tbl->u.n.cells = N_NEW(cnt + 1, htmlcell_t *);
@@ -1104,6 +1108,7 @@ static int processTbl(graph_t *g, htmltbl_t * tbl, htmlenv_t * env)
            c += cellp->cspan;
            n_cols = MAX(c, n_cols);
            n_rows = MAX(r + cellp->rspan, n_rows);
+           if (inIntSet (is, r+cellp->rspan)) cellp->ruled |= HTML_HRULE;
            cp = (pitem *) dtlink(cdict, (Dtlink_t *) cp);
        }
        rp = (pitem *) dtlink(rows, (Dtlink_t *) rp);
@@ -1112,6 +1117,7 @@ static int processTbl(graph_t *g, htmltbl_t * tbl, htmlenv_t * env)
     tbl->rc = n_rows;
     tbl->cc = n_cols;
     dtclose(rows);
+    dtclose(is);
     freePS(ps);
     return rv;
 }
index 7185d8470e6bae6d9fd7284e510fc9504920adec..f0d3cc9d3184ab4ffb2be9066be323c57df6f03d 100644 (file)
@@ -84,6 +84,9 @@ extern "C" {
 #define HTML_TEXT 2
 #define HTML_IMAGE 3
 
+#define HTML_VRULE 1
+#define HTML_HRULE 2
+
     typedef struct htmlcell_t htmlcell_t;
     typedef struct htmltbl_t htmltbl_t;
        
@@ -106,6 +109,7 @@ extern "C" {
        int cc;                 /* number of columns */
        htmlfont_t *font;       /* font info */
        unsigned char style;
+       unsigned char flags;
     };
 
     struct htmllabel_t {
@@ -125,6 +129,7 @@ extern "C" {
        unsigned short row;
        htmllabel_t child;
        htmltbl_t *parent;
+       unsigned char ruled;
     };
 
 /* During parsing, table contents are stored as rows of cells.
@@ -138,6 +143,7 @@ extern "C" {
            Dt_t *rp;
            htmlcell_t *cp;
        } u;
+       unsigned char ruled;
     } pitem;
        
     extern htmllabel_t *parseHTML(char *, int *, int);
diff --git a/lib/common/intset.c b/lib/common/intset.c
new file mode 100644 (file)
index 0000000..4409370
--- /dev/null
@@ -0,0 +1,76 @@
+/* $Id$Revision: */
+/* vim:set shiftwidth=4 ts=8: */
+
+/*************************************************************************
+ * Copyright (c) 2011 AT&T Intellectual Property 
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: See CVS logs. Details at http://www.graphviz.org/
+ *************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stddef.h>
+#include <intset.h>
+#include <memory.h>
+
+static Void_t*
+mkIntItem(Dt_t* d,intitem* obj,Dtdisc_t* disc)
+{ 
+    intitem* np = NEW(intitem);
+    np->id = obj->id;
+    return (Void_t*)np;
+}
+
+static void
+freeIntItem(Dt_t* d,intitem* obj,Dtdisc_t* disc)
+{
+    free (obj);
+}
+
+static int
+cmpid(Dt_t* d, int* key1, int* key2, Dtdisc_t* disc)
+{
+  if (*key1 > *key2) return 1;
+  else if (*key1 < *key2) return -1;
+  else return 0;
+}   
+
+static Dtdisc_t intSetDisc = {
+    offsetof(intitem,id),
+    sizeof(int),
+    offsetof(intitem,link),
+    (Dtmake_f)mkIntItem,
+    (Dtfree_f)freeIntItem,
+    (Dtcompar_f)cmpid,
+    0,
+    0,
+    0
+};
+
+Dt_t* 
+openIntSet (void)
+{
+    return dtopen(&intSetDisc,Dtoset);
+}
+
+void 
+addIntSet (Dt_t* is, int v)
+{
+    intitem obj;
+
+    obj.id = v;
+    dtinsert(is, &obj);
+}
+
+int 
+inIntSet (Dt_t* is, int v)
+{
+    return (dtmatch (is, &v) != 0);
+}
+
diff --git a/lib/common/intset.h b/lib/common/intset.h
new file mode 100644 (file)
index 0000000..433f897
--- /dev/null
@@ -0,0 +1,27 @@
+/* $Id$Revision: */
+/* vim:set shiftwidth=4 ts=8: */
+
+/*************************************************************************
+ * Copyright (c) 2011 AT&T Intellectual Property 
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: See CVS logs. Details at http://www.graphviz.org/
+ *************************************************************************/
+
+#ifndef INTSET_H
+#define INTSET_H
+
+#include <cdt.h>
+
+typedef struct {
+    int       id;
+    Dtlink_t  link;
+} intitem;
+
+extern Dt_t* openIntSet (void);
+extern void addIntSet (Dt_t*, int);
+extern int inIntSet (Dt_t*, int);
+#endif