/misc/src/release/graphviz-2.18-1/src/graphviz-2.18/lib/common/labels.c

Go to the documentation of this file.
00001 /* $Id: labels.c,v 1.48 2008/03/06 21:40:02 ellson Exp $ $Revision: 1.48 $ */
00002 /* vim:set shiftwidth=4 ts=8: */
00003 
00004 /**********************************************************
00005 *      This software is part of the graphviz package      *
00006 *                http://www.graphviz.org/                 *
00007 *                                                         *
00008 *            Copyright (c) 1994-2004 AT&T Corp.           *
00009 *                and is licensed under the                *
00010 *            Common Public License, Version 1.0           *
00011 *                      by AT&T Corp.                      *
00012 *                                                         *
00013 *        Information and Software Systems Research        *
00014 *              AT&T Research, Florham Park NJ             *
00015 **********************************************************/
00016 
00017 
00018 #include "render.h"
00019 #include "htmltable.h"
00020 #include <limits.h>
00021 
00022 static void storeline(graph_t *g, textlabel_t *lp, char *line, char terminator)
00023 {
00024     pointf size;
00025     textpara_t *para;
00026     int oldsz = lp->u.txt.nparas + 1;
00027 
00028     lp->u.txt.para = ZALLOC(oldsz + 1, lp->u.txt.para, textpara_t, oldsz);
00029     para = &(lp->u.txt.para[lp->u.txt.nparas]);
00030     para->str = line;
00031     para->just = terminator;
00032     if (line && line[0])
00033         size = textsize(g, para, lp->fontname, lp->fontsize);
00034     else {
00035         size.x = 0.0;
00036         para->height = size.y = (int)(lp->fontsize * LINESPACING);
00037     }
00038 
00039     lp->u.txt.nparas++;
00040     /* width = max line width */
00041     lp->dimen.x = MAX(lp->dimen.x, size.x);
00042     /* accumulate height */
00043     lp->dimen.y += size.y;
00044 }
00045 
00046 /* compiles <str> into a label <lp> and returns its bounding box size.  */
00047 static pointf label_size(graph_t * g, textlabel_t * lp)
00048 {
00049     char c, *p, *line, *lineptr, *str = lp->text;
00050     unsigned char byte = 0x00;
00051     int charset = GD_charset(g);
00052 
00053     lp->dimen.x = lp->dimen.y = 0.0;
00054     if (*str == '\0')
00055         return lp->dimen;
00056 
00057     line = lineptr = NULL;
00058     p = str;
00059     line = lineptr = N_GNEW(strlen(p) + 1, char);
00060     *line = 0;
00061     while ((c = *p++)) {
00062         byte = (unsigned int) c;
00063         /* wingraphviz allows a combination of ascii and big-5. The latter
00064          * is a two-byte encoding, with the first byte in 0xA1-0xFE, and
00065          * the second in 0x40-0x7e or 0xa1-0xfe. We assume that the input
00066          * is well-formed, but check that we don't go past the ending '\0'.
00067          */
00068         if ((charset == CHAR_BIG5) && 0xA1 <= byte && byte <= 0xFE) {
00069             *lineptr++ = c;
00070             c = *p++;
00071             *lineptr++ = c;
00072             if (!c) /* NB. Protect against unexpected string end here */
00073                 break;
00074         } else {
00075             if (c == '\\') {
00076                 switch (*p) {
00077                 case 'n':
00078                 case 'l':
00079                 case 'r':
00080                     *lineptr++ = '\0';
00081                     storeline(g, lp, line, *p);
00082                     line = lineptr;
00083                     break;
00084                 default:
00085                     *lineptr++ = *p;
00086                 }
00087                 if (*p)
00088                     p++;
00089                 /* tcldot can enter real linend characters */
00090             } else if (c == '\n') {
00091                 *lineptr++ = '\0';
00092                 storeline(g, lp, line, 'n');
00093                 line = lineptr;
00094             } else {
00095                 *lineptr++ = c;
00096             }
00097         }
00098     }
00099 
00100     if (line != lineptr) {
00101         *lineptr++ = '\0';
00102         storeline(g, lp, line, 'n');
00103     }
00104 
00105     return lp->dimen;
00106 }
00107 
00108 /* size_label:
00109  * Process label text for size and line breaks.
00110  */ 
00111 void
00112 size_label (graph_t* g, textlabel_t* rv)
00113 {
00114     char *s;
00115 
00116     switch (GD_charset(g->root)) {
00117     case CHAR_LATIN1:
00118         s = latin1ToUTF8(rv->text);
00119         break;
00120     default: /* UTF8 */
00121         s = htmlEntityUTF8(rv->text);
00122         break;
00123     }
00124     free(rv->text);
00125     rv->text = s;
00126     label_size(g, rv);
00127 }
00128 
00129 /* make_label:
00130  * Assume str is freshly allocated for this instance, so it
00131  * can be freed in free_label.
00132  */
00133 textlabel_t *make_label(graph_t *g, int kind, char *str, double fontsize,
00134                         char *fontname, char *fontcolor)
00135 {
00136     textlabel_t *rv = NEW(textlabel_t);
00137 
00138     rv->text = str;
00139     rv->fontname = fontname;
00140     rv->fontcolor = fontcolor;
00141     rv->fontsize = fontsize;
00142     if (kind & LT_HTML)
00143         rv->html = TRUE;
00144     if (kind == LT_NONE)
00145         size_label(g, rv);
00146     return rv;
00147 }
00148 
00149 static void free_textpara(textpara_t * tl)
00150 {
00151     if (tl) {
00152         if (tl->str)
00153             free(tl->str);
00154         if (tl->layout && tl->free_layout)
00155             tl->free_layout (tl->layout);
00156         free(tl);
00157     }
00158 }
00159 
00160 void free_label(textlabel_t * p)
00161 {
00162     if (p) {
00163         free(p->text);
00164         if (p->html) {
00165             free_html_label(p->u.html, 1);
00166         } else {
00167             free_textpara(p->u.txt.para);
00168         }
00169         free(p);
00170     }
00171 }
00172 
00173 void emit_label(GVJ_t * job, emit_state_t emit_state, textlabel_t * lp)
00174 {
00175     obj_state_t *obj = job->obj;
00176     double halfwidth_x, center_x, left_x, right_x;
00177     int i;
00178     pointf p;
00179     emit_state_t old_emit_state;
00180 
00181     old_emit_state = obj->emit_state;
00182     obj->emit_state = emit_state;
00183 
00184     if (lp->html) {
00185         emit_html_label(job, lp->u.html, lp);
00186         return;
00187     }
00188 
00189     /* make sure that there is something to do */
00190     if (lp->u.txt.nparas < 1)
00191         return;
00192 
00193     p.x = lp->p.x;
00194     p.y = lp->p.y;
00195 
00196     /* dimensions of box for label, no padding, adjusted for resizing */
00197     halfwidth_x = (lp->dimen.x + lp->d.x) / 2.0;
00198 
00199     center_x = p.x;
00200     left_x = center_x - halfwidth_x;
00201     right_x = center_x + halfwidth_x;
00202 
00203     /* position for first para */
00204     p.y += (lp->dimen.y + lp->d.y) / 2.0 - lp->fontsize;
00205 
00206     gvrender_begin_context(job);
00207     gvrender_set_pencolor(job, lp->fontcolor);
00208     gvrender_set_font(job, lp->fontname, lp->fontsize);
00209 
00210     for (i = 0; i < lp->u.txt.nparas; i++) {
00211         switch (lp->u.txt.para[i].just) {
00212         case 'l':
00213             p.x = left_x;
00214             break;
00215         case 'r':
00216             p.x = right_x;
00217             break;
00218         default:
00219         case 'n':
00220             p.x = center_x;
00221             break;
00222         }
00223         gvrender_textpara(job, p, &(lp->u.txt.para[i]));
00224 
00225         /* UL position for next para */
00226         p.y -= lp->u.txt.para[i].height;
00227     }
00228 
00229     gvrender_end_context(job);
00230     obj->emit_state = old_emit_state;
00231 }
00232 
00233 char *strdup_and_subst_obj(char *str, void *obj)
00234 {
00235     char c, *s, *p, *t, *newstr;
00236     char *g_str = "\\G", *n_str = "\\N", *e_str = "\\E", *h_str = "\\H", *t_str = "\\T";
00237     int g_len = 2, n_len = 2, e_len = 2, h_len = 2, t_len = 2, newlen = 0;
00238 
00239     /* prepare substitution strings */
00240     switch (agobjkind(obj)) {
00241         case AGGRAPH:
00242             g_str = ((graph_t *)obj)->name;
00243             g_len = strlen(g_str);
00244             break;
00245         case AGNODE:
00246             g_str = ((node_t *)obj)->graph->name;
00247             g_len = strlen(g_str);
00248             n_str = ((node_t *)obj)->name;
00249             n_len = strlen(n_str);
00250             break;
00251         case AGEDGE:
00252             g_str = ((edge_t *)obj)->tail->graph->root->name;
00253             g_len = strlen(g_str);
00254             t_str = ((edge_t *)obj)->tail->name;
00255             t_len = strlen(t_str);
00256             h_str = ((edge_t *)obj)->head->name;
00257             h_len = strlen(h_str);
00258             if (((edge_t *)obj)->tail->graph->root->kind & AGFLAG_DIRECTED)
00259                 e_str = "->";
00260             else
00261                 e_str = "--";
00262             e_len = t_len + 2 + h_len;
00263             break;
00264     }
00265 
00266     /* two passes over str.
00267      *
00268      * first pass prepares substitution strings and computes 
00269      * total length for newstring required from malloc.
00270      */
00271     for (s = str; (c = *s++);) {
00272         if (c == '\\') {
00273             switch (c = *s++) {
00274             case 'G':
00275                 newlen += g_len;
00276                 break;
00277             case 'N':
00278                 newlen += n_len;
00279                 break;
00280             case 'E':
00281                 newlen += e_len;
00282                 break;
00283             case 'H':
00284                 newlen += h_len;
00285                 break;
00286             case 'T':
00287                 newlen += t_len;
00288                 break; 
00289             default:  /* leave other escape sequences unmodified, e.g. \n \l \r */
00290                 newlen += 2;
00291             }
00292         } else {
00293             newlen++;
00294         }
00295     }
00296     /* allocate new string */
00297     newstr = gmalloc(newlen + 1);
00298 
00299     /* second pass over str assembles new string */
00300     for (s = str, p = newstr; (c = *s++);) {
00301         if (c == '\\') {
00302             switch (c = *s++) {
00303             case 'G':
00304                 for (t = g_str; (*p = *t++); p++);
00305                 break;
00306             case 'N':
00307                 for (t = n_str; (*p = *t++); p++);
00308                 break;
00309             case 'E':
00310                 for (t = t_str; (*p = *t++); p++);
00311                 for (t = e_str; (*p = *t++); p++);
00312                 for (t = h_str; (*p = *t++); p++);
00313                 break;
00314             case 'T':
00315                 for (t = t_str; (*p = *t++); p++);
00316                 break;
00317             case 'H':
00318                 for (t = h_str; (*p = *t++); p++);
00319                 break;
00320             default:  /* leave other escape sequences unmodified, e.g. \n \l \r */
00321                 *p++ = '\\';
00322                 *p++ = c;
00323                 break;
00324             }
00325         } else {
00326             *p++ = c;
00327         }
00328     }
00329     *p++ = '\0';
00330     return newstr;
00331 }
00332 
00333 /* return true if *s points to &[A-Za-z]*;      (e.g. &Ccedil; )
00334  *                          or &#[0-9]*;        (e.g. &#38; )
00335  *                          or &#x[0-9a-fA-F]*; (e.g. &#x6C34; )
00336  */
00337 static int xml_isentity(char *s)
00338 {
00339     s++;                        /* already known to be '&' */
00340     if (*s == '#') {
00341         s++;
00342         if (*s == 'x' || *s == 'X') {
00343             s++;
00344             while ((*s >= '0' && *s <= '9')
00345                    || (*s >= 'a' && *s <= 'f')
00346                    || (*s >= 'A' && *s <= 'F'))
00347                 s++;
00348         } else {
00349             while (*s >= '0' && *s <= '9')
00350                 s++;
00351         }
00352     } else {
00353         while ((*s >= 'a' && *s <= 'z')
00354                || (*s >= 'A' && *s <= 'Z'))
00355             s++;
00356     }
00357     if (*s == ';')
00358         return 1;
00359     return 0;
00360 }
00361 
00362 
00363 char *xml_string(char *s)
00364 {
00365     static char *buf = NULL;
00366     static int bufsize = 0;
00367     char *p, *sub, *prev = NULL;
00368     int len, pos = 0;
00369 
00370     if (!buf) {
00371         bufsize = 64;
00372         buf = gmalloc(bufsize);
00373     }
00374 
00375     p = buf;
00376     while (s && *s) {
00377         if (pos > (bufsize - 8)) {
00378             bufsize *= 2;
00379             buf = grealloc(buf, bufsize);
00380             p = buf + pos;
00381         }
00382         /* these are safe even if string is already UTF-8 coded
00383          * since UTF-8 strings won't contain '<' or '>' */
00384         if (*s == '<') {
00385             sub = "&lt;";
00386             len = 4;
00387         } else if (*s == '>') {
00388             sub = "&gt;";
00389             len = 4;
00390         } else if (*s == '"') {
00391             sub = "&quot;";
00392             len = 6;
00393         } else if (*s == '-') { /* can't be used in xml comment strings */
00394             sub = "&#45;";
00395             len = 5;
00396         } else if (*s == '\'') {
00397             sub = "&#39;";
00398             len = 5;
00399         } else if (*s == ' ' && prev && *prev == ' ') {
00400             /* substitute 2nd and subsequent spaces with required_spaces */
00401             sub = "&#160;";  /* inkscape doesn't recognise &nbsp; */
00402             len = 6;
00403         }
00404         /* escape '&' only if not part of a legal entity sequence */
00405         else if (*s == '&' && !(xml_isentity(s))) {
00406             sub = "&amp;";
00407             len = 5;
00408         } else {
00409             sub = s;
00410             len = 1;
00411         }
00412         while (len--) {
00413             *p++ = *sub++;
00414             pos++;
00415         }
00416         prev = s;
00417         s++;
00418     }
00419     *p = '\0';
00420     return buf;
00421 }

Generated on Mon Mar 31 19:03:24 2008 for Graphviz by  doxygen 1.5.1