/*
This software may only be used by you under license from AT&T Corp.
("AT&T"). A copy of AT&T's Source Code Agreement is available at
AT&T's Internet website having the URL:
<http://www.research.att.com/sw/tools/graphviz/license/source.html>
If you received this software without first entering into a license
with AT&T, you have an infringing copy of this software and cannot use
it without violating AT&T's intellectual property rights.
*/
#pragma prototyped
/*
* graphics code generator
*/
#include "render.h"
#include "xbuf.h"
#include "utils.h"
int Obj;
static int N_pages = 1; /* w.r.t. unrotated coords */
static int Page; /* w.r.t. unrotated coords */
static int Layer,Nlayers;
static char **LayerID;
static point First,Major,Minor;
static point Pages;
static box PB; /* drawable region in device coords */
static pointf GP; /* graph page size, in graph coords */
static box CB; /* current page box, in graph coords */
static point PFC; /* device page box for centering */
static double Deffontsize;
static char *Deffontname;
static char Layerdelims[] = ":\t ";
static attrsym_t* G_peripheries;
static int write_edge_test(Agraph_t *g, Agedge_t *e) {
Agraph_t *subg;
int c;
for(c = 1; c <= GD_n_cluster(g); c++) {
subg = GD_clust(g)[c];
if(agcontains(subg, e)) return FALSE;
}
return TRUE;
}
static int write_node_test(Agraph_t *g, Agnode_t *n) {
Agraph_t *subg;
int c;
for(c = 1; c <= GD_n_cluster(g); c++) {
subg = GD_clust(g)[c];
if(agcontains(subg, n)) return FALSE;
}
return TRUE;
}
void emit_reset(Agraph_t* g)
{
Agnode_t *n;
N_pages = 1;
Page = 0;
Layer = Nlayers = 0;
LayerID = (char **) 0;
First.x = First.y = 0;
Major.x = Major.y = 0;
Minor.x = Minor.y = 0;
Pages.x = Pages.y = 0;
PB.LL.x = PB.LL.y = PB.UR.x = PB.UR.y = 0;
GP.x = GP.y = 0;
CB.LL.x = CB.LL.y = CB.UR.x = CB.UR.y = 0;
PFC.x = PFC.y = 0;
Deffontsize = 0;
Deffontname = (char *) 0;
for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
ND_state(n) = 0;
}
if(CodeGen) CodeGen->reset();
}
void emit_graph(graph_t* g, int flags)
{
point curpage;
graph_t *subg;
node_t *n;
edge_t *e;
int c;
char *str;
G_peripheries = agfindattr(g,"peripheries");
emit_header(g);
if (flags & EMIT_COLORS) {
CodeGen->set_fillcolor(DEFAULT_FILL);
if (((str = agget(g,"bgcolor")) != 0) && str[0])
CodeGen->set_fillcolor(str);
if (((str = agget(g,"fontcolor")) != 0) && str[0])
CodeGen->set_pencolor(str);
for (c = 1; c <= GD_n_cluster(g); c++) {
subg = GD_clust(g)[c];
if (((str = agget(subg,"color")) != 0) && str[0])
CodeGen->set_pencolor(str);
if (((str = agget(subg,"fillcolor")) != 0) && str[0])
CodeGen->set_fillcolor(str);
if (((str = agget(subg,"fontcolor")) != 0) && str[0])
CodeGen->set_pencolor(str);
}
for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
if (((str = agget(n,"color")) != 0) && str[0])
CodeGen->set_pencolor(str);
if (((str = agget(n,"fillcolor")) != 0) && str[0])
CodeGen->set_fillcolor(str);
if (((str = agget(n,"fontcolor")) != 0) && str[0])
CodeGen->set_pencolor(str);
for (e = agfstout(g,n); e; e = agnxtout(g,e)) {
if (((str = agget(e,"color")) != 0) && str[0])
CodeGen->set_pencolor(str);
if (((str = agget(e,"fontcolor")) != 0) && str[0])
CodeGen->set_pencolor(str);
}
}
}
Layer = 1;
do {
if (Nlayers > 0) emit_layer(Layer);
for (curpage = First; validpage(curpage); curpage = pageincr(curpage)) {
Obj = NONE;
setup_page(g,curpage);
if (GD_label(g)) emit_label(GD_label(g),g);
Obj = CLST;
/* when drawing, lay clusters down before nodes and edges */
if (!(flags & EMIT_CLUSTERS_LAST)) {
emit_clusters(g,flags);
}
if (flags & EMIT_SORTED) {
/* output all nodes, then all edges */
Obj = NODE;
CodeGen->begin_nodes();
for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
emit_node(n);
}
CodeGen->end_nodes();
Obj = EDGE;
CodeGen->begin_edges();
for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
for (e = agfstout(g,n); e; e = agnxtout(g,e)) {
emit_edge(e);
}
}
CodeGen->end_edges();
}
else if (flags & EMIT_EDGE_SORTED) {
/* output all edges, then all nodes */
Obj = EDGE;
CodeGen->begin_edges();
for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
for (e = agfstout(g,n); e; e = agnxtout(g,e)) {
emit_edge(e);
}
}
CodeGen->end_edges();
Obj = NODE;
CodeGen->begin_nodes();
for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
emit_node(n);
}
CodeGen->end_nodes();
}
else if(flags & EMIT_PREORDER) {
Obj = NODE;
CodeGen->begin_nodes();
for(n = agfstnode(g); n; n = agnxtnode(g, n)) {
if(write_node_test(g, n))
emit_node(n);
}
CodeGen->end_nodes();
Obj = EDGE;
CodeGen->begin_edges();
for(n = agfstnode(g); n; n = agnxtnode(g, n)) {
for(e = agfstout(g, n); e; e = agnxtout(g, e)) {
if(write_edge_test(g, e))
emit_edge(e);
}
}
CodeGen->end_edges();
}
else {
/* output in breadth first graph walk order */
for (n = agfstnode(g); n; n = agnxtnode(g,n)) {
Obj = NODE;
emit_node(n);
for (e = agfstout(g,n); e; e = agnxtout(g,e)) {
Obj = NODE;
emit_node(e->head);
Obj = EDGE;
emit_edge(e);
}
}
}
/* when mapping, detect events on clusters after nodes and edges */
if (flags & EMIT_CLUSTERS_LAST) {
emit_clusters(g,flags);
}
Obj = NONE;
CodeGen->end_page();
}
Layer++;
} while (Layer <= Nlayers);
emit_trailer();
}
void emit_eof(void)
{
if (Page > 0) CodeGen->end_job();
}
void emit_clusters(graph_t* g, int flags)
{
int i,c,filled;
graph_t *subg;
point A[4];
char *str,**style;
node_t *n;
edge_t *e;
for (c = 1; c <= GD_n_cluster(g); c++) {
subg = GD_clust(g)[c];
if (clust_in_layer(subg) == FALSE) continue;
/* when mapping, detect events on clusters after sub_clusters */
if (flags & EMIT_CLUSTERS_LAST) {
emit_clusters(subg,flags);
}
Obj = CLST;
CodeGen->begin_cluster(subg);
CodeGen->begin_context();
filled = FALSE;
if (((str = agget(subg,"style")) != 0) && str[0]) {
CodeGen->set_style(style = parse_style(str));
for (i = 0; style[i]; i++)
if (strcmp(style[i],"filled")==0) {filled = TRUE; break;}
}
if (((str = agget(subg,"pencolor")) != 0) && str[0])
CodeGen->set_pencolor(str);
else if (((str = agget(subg,"color")) != 0) && str[0])
CodeGen->set_pencolor(str);
/* bgcolor is supported for backward compatability */
else if (((str = agget(subg,"bgcolor")) != 0) && str[0])
CodeGen->set_pencolor(str);
str = 0;
if (((str = agget(subg,"fillcolor")) != 0) && str[0])
CodeGen->set_fillcolor(str);
else if (((str = agget(subg,"color")) != 0) && str[0])
CodeGen->set_fillcolor(str);
/* bgcolor is supported for backward compatability */
else if (((str = agget(subg,"bgcolor")) != 0) && str[0]) {
filled = TRUE;
CodeGen->set_fillcolor(str);
}
A[0] = GD_bb(subg).LL;
A[2] = GD_bb(subg).UR;
A[1].x = A[2].x; A[1].y = A[0].y;
A[3].x = A[0].x; A[3].y = A[2].y;
if (late_int(subg,G_peripheries,1,0)) {
CodeGen->polygon(A,4,filled);
}
else if (filled) {
CodeGen->set_pencolor(str);
CodeGen->polygon(A,4,filled);
}
if (GD_label(subg)) emit_label(GD_label(subg),subg);
if(flags & EMIT_PREORDER) {
for(n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
Obj = NODE;
emit_node(n);
for(e = agfstout(subg, n); e; e = agnxtout(subg, e)) {
Obj = EDGE;
emit_edge(e);
}
}
Obj = NONE;
}
CodeGen->end_context();
CodeGen->end_cluster();
/* when drawing, lay down clusters before sub_clusters */
if (!(flags & EMIT_CLUSTERS_LAST)) {
emit_clusters(subg,flags);
}
}
}
void emit_node(node_t* n)
{
if (ND_shape(n) == NULL) return;
if (node_in_layer(n->graph,n) && node_in_CB(n) && (ND_state(n) != Page)) {
ND_shape(n)->codefn(n);
ND_state(n) = Page;
}
}
void emit_edge(edge_t* e)
{
int i;
char *color,*style;
char **styles = 0;
char **sp;
bezier bz;
boolean saved = FALSE;
double scale;
double theta;
char *p;
if ((edge_in_CB(e) == FALSE) || (edge_in_layer(e->head->graph,e) == FALSE))
return;
CodeGen->begin_edge(e);
style = late_string(e,E_style,"");
/* We shortcircuit drawing an invisible edge because the arrowhead
* code resets the style to solid, and most of the code generators
* (except PostScript) won't honor a previous style of invis.
*/
if (style[0]) {
styles = parse_style(style);
sp = styles;
while ((p = *sp++)) {
if (streq(p, "invis")) {
CodeGen->end_edge();
return;
}
}
}
color = late_string(e,E_color,"");
scale = late_double(e,E_arrowsz,1.0,0.0);
if (color[0] || styles) {
CodeGen->begin_context();
if (styles) CodeGen->set_style(styles);
if (color[0]) {
CodeGen->set_pencolor(color);
CodeGen->set_fillcolor(color);
}
saved = TRUE;
}
if (ED_spl(e)) {
for (i = 0; i < ED_spl(e)->size; i++) {
bz = ED_spl(e)->list[i];
if (NOT(codegen_bezier_has_arrows())) {
CodeGen->beziercurve(bz.list,bz.size,FALSE,FALSE);
/* CodeGen->beziercurve(bz.list,bz.size,bz.sflag,bz.eflag); */
/* vladimir: added sflag/eflag, arrowheads are made here */
if (bz.sflag) {
/* dds: open style has sp == list[0] */
if ((bz.sflag == ARR_OPEN) || (bz.sflag == ARR_HALFOPEN))
theta = atan2pt(bz.list[1],bz.list[0]);
else
theta = atan2pt(bz.list[0],bz.sp);
arrow_gen(bz.sp, DEGREES(theta), scale, bz.sflag);
}
if (bz.eflag) {
/* dds: open style has ep == list[size-1] */
if ((bz.eflag == ARR_OPEN) || (bz.eflag == ARR_HALFOPEN))
theta = atan2pt(bz.list[bz.size-2],bz.list[bz.size-1]);
else
theta = atan2pt(bz.list[bz.size-1],bz.ep);
arrow_gen(bz.ep, DEGREES(theta), scale, bz.eflag);
}
} else
CodeGen->beziercurve(bz.list,bz.size,bz.sflag,bz.eflag);
}
}
if (ED_label(e)) {
emit_label(ED_label(e),e->tail->graph);
if (mapbool(late_string(e,E_decorate,"false")) && ED_spl(e))
emit_attachment(ED_label(e),ED_spl(e));
}
if (ED_head_label(e)) emit_label(ED_head_label(e),e->tail->graph); /* vladimir */
if (ED_tail_label(e)) emit_label(ED_tail_label(e),e->tail->graph); /* vladimir */
if (saved) CodeGen->end_context();
CodeGen->end_edge();
}
int node_in_CB(node_t* n)
{
box nb;
if (N_pages == 1) return TRUE;
nb.LL.x = ND_coord_i(n).x - ND_lw_i(n);
nb.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2;
nb.UR.x = ND_coord_i(n).x + ND_rw_i(n);
nb.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2;
return rect_overlap(CB,nb);
}
int node_in_layer(graph_t* g, node_t* n)
{
char *pn,*pe;
edge_t *e;
if (Nlayers <= 0) return TRUE;
pn = late_string(n,N_layer,"");
if (selectedlayer(pn)) return TRUE;
if (pn[0]) return FALSE; /* Only check edges if pn = "" */
if ((e = agfstedge(g,n)) == NULL) return TRUE;
for (e = agfstedge(g,n); e; e = agnxtedge(g,e,n)) {
pe = late_string(e,E_layer,"");
if ((pe[0] == '\0') || selectedlayer(pe)) return TRUE;
}
return FALSE;
}
int edge_in_layer(graph_t* g, edge_t* e)
{
char *pe,*pn;
int cnt;
if (Nlayers <= 0) return TRUE;
pe = late_string(e,E_layer,"");
if (selectedlayer(pe)) return TRUE;
if (pe[0]) return FALSE;
for (cnt = 0; cnt < 2; cnt++) {
pn = late_string(cnt < 1? e->tail:e->head,N_layer,"");
if ((pn[0] == '\0') || selectedlayer(pn)) return TRUE;
}
return FALSE;
}
int clust_in_layer(graph_t* subg)
{
char *pg;
node_t *n;
if (Nlayers <= 0) return TRUE;
pg = late_string(subg,agfindattr(subg,"layer"),"");
if (selectedlayer(pg)) return TRUE;
if (pg[0]) return FALSE;
for (n = agfstnode(subg); n; n = agnxtnode(subg,n))
if (node_in_layer(subg,n)) return TRUE;
return FALSE;
}
int edge_in_CB(edge_t* e)
{
int i,j,np;
bezier bz;
point *p,pp,sz;
box b;
textlabel_t *lp;
if (N_pages == 1) return TRUE;
if (ED_spl(e) == NULL) return FALSE;
for (i = 0; i < ED_spl(e)->size; i++) {
bz = ED_spl(e)->list[i];
np = bz.size;
p = bz.list;
pp = p[0];
for (j = 0; j < np; j++) {
if (rect_overlap(CB,mkbox(pp,p[j]))) return TRUE;
pp = p[j];
}
}
if ((lp = ED_label(e)) == NULL) return FALSE;
sz = cvt2pt(lp->dimen);
b.LL.x = lp->p.x - sz.x / 2; b.UR.x = lp->p.x + sz.x / 2;
b.LL.y = lp->p.y - sz.y / 2; b.UR.y = lp->p.y + sz.y / 2;
return rect_overlap(CB,b);
}
void emit_attachment(textlabel_t* lp, splines* spl)
{
point sz,A[3];
char *s;
for (s = lp->text; *s; s++) if (isspace(*s) == FALSE) break;
if (*s == 0) return;
sz = cvt2pt(lp->dimen);
A[0] = pointof(lp->p.x + sz.x/2,lp->p.y - sz.y/2);
A[1] = pointof(A[0].x - sz.x, A[0].y);
A[2] = dotneato_closest(spl,lp->p);
CodeGen->polyline(A,3);
}
void emit_header(graph_t* g)
{
char *user;
setup_graph(g);
if (Page == 0) {
user = username();
CodeGen->begin_job(Output_file,g,Lib,user,Info,Pages);
}
CodeGen->begin_graph(g,PB,PFC);
}
void emit_trailer(void)
{
CodeGen->end_graph();
}
point pagecode(char c)
{
point rv;
rv.x = rv.y = 0;
switch(c) {
case 'T': First.y = Pages.y - 1; rv.y = -1; break;
case 'B': rv.y = 1; break;
case 'L': rv.x = 1; break;
case 'R': First.x = Pages.x - 1; rv.x = -1; break;
}
return rv;
}
static void set_pagedir(graph_t* g)
{
char *str;
Major.x = Major.y = Minor.x = Minor.y = 0;
str = agget(g,"pagedir");
if (str && str[0]) {
Major = pagecode(str[0]);
Minor = pagecode(str[1]);
}
if ((abs(Major.x + Minor.x) != 1) || (abs(Major.y + Minor.y) != 1)) {
Major.x = 0; Major.y = 1; Minor.x = 1; Minor.y = 0;
First.x = First.y = 0;
if (str) agerr (AGWARN, "pagedir=%s ignored\n",str);
}
}
int validpage(point page)
{
return ((page.x >= 0) && (page.x < Pages.x)
&& (page.y >= 0) && (page.y < Pages.y));
}
int layerindex(char* tok)
{
int i;
for (i = 1; i <= Nlayers; i++)
if (streq(tok,LayerID[i])) return i;
return -1;
}
int is_natural_number(char* str)
{
while (*str) if (NOT(isdigit(*str++))) return FALSE;
return TRUE;
}
int layer_index(char* str, int all)
{
int i;
if (streq(str,"all")) return all;
if (is_natural_number(str)) return atoi(str);
if (LayerID)
for (i = 1; i <= Nlayers; i++)
if (streq(str,LayerID[i])) return i;
return -1;
}
int selectedlayer(char* spec)
{
int n0,n1;
unsigned char buf[SMALLBUF];
char *w0, *w1;
xbuf xb;
int rval = FALSE;
xbinit (&xb, SMALLBUF, buf);
xbput(&xb,spec);
w1 = w0 = strtok(xbuse(&xb),Layerdelims);
if (w0) w1 = strtok(NULL,Layerdelims);
switch((w0 != NULL) + (w1 != NULL)) {
case 0:
rval = FALSE;
break;
case 1:
n0 = layer_index(w0,Layer);
rval = (n0 == Layer);
break;
case 2:
n0 = layer_index(w0,0);
n1 = layer_index(w1,Nlayers);
if ((n0 < 0) || (n1 < 0)) rval = TRUE;
else if (n0 > n1) {
int t = n0;
n0 = n1;
n1 = t;}
rval = BETWEEN(n0,Layer,n1);
break;
}
xbfree (&xb);
return rval;
}
point
pageincr(point page)
{
page = add_points(page,Minor);
if (validpage(page) == FALSE) {
if (Major.y) page.x = First.x;
else page.y = First.y;
page = add_points(page,Major);
}
return page;
}
static point exch_xy(point p)
{
int t;
t = p.x; p.x = p.y; p.y = t;
return p;
}
static pointf exch_xyf(pointf p)
{
double t;
t = p.x; p.x = p.y; p.y = t;
return p;
}
/* this isn't a pretty sight... */
void setup_graph(graph_t* g)
{
double xscale,yscale,scale;
char *p;
point PFCLM; /* page for centering less margins */
point DS; /* device drawable region for a page of the graph */
assert((GD_bb(g).LL.x == 0) && (GD_bb(g).LL.y == 0));
if (LayerID) free(LayerID);
if ((p = agget(g,"layers")) != 0) Nlayers = parse_layers(p);
else {LayerID = NULL; Nlayers = 0;}
/* determine final drawing size and scale to apply. */
/* N.B. magnification could be allowed someday in the next conditional */
/* N.B. size given by user is not rotated by landscape mode */
if ((GD_drawing(g)->size.x > 0) /* was given by user... */
&& ((GD_drawing(g)->size.x < GD_bb(g).UR.x) /* drawing is too big... */
|| (GD_drawing(g)->size.y < GD_bb(g).UR.y))) {
xscale = ((double)GD_drawing(g)->size.x) / GD_bb(g).UR.x;
yscale = ((double)GD_drawing(g)->size.y) / GD_bb(g).UR.y;
scale = MIN(xscale,yscale);
GD_drawing(g)->scale = scale;
GD_drawing(g)->size.x = scale * GD_bb(g).UR.x;
GD_drawing(g)->size.y = scale * GD_bb(g).UR.y;
}
else { /* go with "natural" size of layout */
GD_drawing(g)->size = GD_bb(g).UR;
scale = GD_drawing(g)->scale = 1.0;
}
/* determine pagination */
PB.LL = GD_drawing(g)->margin;
if ((GD_drawing(g)->page.x > 0) && (GD_drawing(g)->page.y > 0)) {
/* page was set by user */
point tp;
PFC = GD_drawing(g)->page;
PFCLM.x = PFC.x - 2*PB.LL.x; PFCLM.y = PFC.y - 2*PB.LL.y;
GP.x = PFCLM.x ; GP.y = PFCLM.y; /* convert to double */
if (GD_drawing(g)->landscape) GP = exch_xyf(GP);
GP.x = GP.x / scale; GP.y = GP.y / scale;
/* we don't want graph page to exceed its bounding box */
GP.x = MIN(GP.x,GD_bb(g).UR.x); GP.y = MIN(GP.y,GD_bb(g).UR.y);
Pages.x = (GP.x > 0) ? ceil( ((double)GD_bb(g).UR.x) / GP.x) : 1;
Pages.y = (GP.y > 0) ? ceil( ((double)GD_bb(g).UR.y) / GP.y) : 1;
N_pages = Pages.x * Pages.y;
/* find the drawable size in device coords */
tp = GD_drawing(g)->size;
if (GD_drawing(g)->landscape) tp = exch_xy(tp);
DS.x = MIN(tp.x,PFCLM.x);
DS.y = MIN(tp.y,PFCLM.y);
}
else {
/* page not set by user, assume default when centering,
but allow infinite page for any other interpretation */
GP.x = GD_bb(g).UR.x; GP.y = GD_bb(g).UR.y;
PFC.x = DEFAULT_PAGEWD; PFC.y = DEFAULT_PAGEHT;
PFCLM.x = PFC.x - 2*PB.LL.x; PFCLM.y = PFC.y - 2*PB.LL.y;
DS = GD_drawing(g)->size;
if (GD_drawing(g)->landscape) DS = exch_xy(DS);
Pages.x = Pages.y = N_pages = 1;
}
set_pagedir(g);
/* determine page box including centering */
if (GD_drawing(g)->centered) {
point extra;
if ((extra.x = PFCLM.x - DS.x) < 0) extra.x = 0;
if ((extra.y = PFCLM.y - DS.y) < 0) extra.y = 0;
PB.LL.x += extra.x / 2; PB.LL.y += extra.y / 2;
}
PB.UR = add_points(PB.LL,DS);
Deffontname = late_nnstring(g->proto->n,N_fontname,DEFAULT_FONTNAME);
Deffontsize = late_double(g->proto->n,N_fontsize,DEFAULT_FONTSIZE,MIN_FONTSIZE);
}
void emit_background(graph_t* g, point LL, point UR)
{
char *str;
point A[4];
if (((str = agget(g,"bgcolor")) != 0) && str[0]) {
A[0].x = A[1].x = LL.x - GD_drawing(g)->margin.x;
A[2].x = A[3].x = UR.x + GD_drawing(g)->margin.x;
A[1].y = A[2].y = UR.y + GD_drawing(g)->margin.y;
A[3].y = A[0].y = LL.y - GD_drawing(g)->margin.y;
CodeGen->set_fillcolor(str);
CodeGen->set_pencolor(str);
CodeGen->polygon(A,4,TRUE); /* filled */
}
}
/* even if this makes you cringe, at least it's short */
void setup_page(graph_t* g, point page)
{
point offset;
int theta;
Page++;
theta = (GD_drawing(g)->landscape ? 90 : 0);
/* establish current box in graph coordinates */
CB.LL.x = page.x * GP.x; CB.LL.y = page.y * GP.y;
CB.UR.x = CB.LL.x + GP.x; CB.UR.y = CB.LL.y + GP.y;
/* establish offset to be applied, in graph coordinates */
if (GD_drawing(g)->landscape == FALSE) offset = pointof(-CB.LL.x,-CB.LL.y);
else { offset.x = (page.y + 1) * GP.y; offset.y = -page.x * GP.x; }
CodeGen->begin_page(g,page,GD_drawing(g)->scale,theta,offset);
emit_background(g, CB.LL, CB.UR);
emit_defaults(g);
}
void emit_label(textlabel_t* lp, Agraph_t *g)
{
int i, linespacing, left_x, center_x, right_x, width_x;
point p;
/* make sure that there is something to do */
if (lp->nlines < 1) return;
g = g->root;
/* dimensions of box for label */
width_x = ROUND(POINTS(lp->dimen.x));
center_x = lp->p.x;
left_x = center_x - width_x / 2;
right_x = center_x + width_x / 2;
/* set linespacing */
linespacing = (int)(lp->fontsize * LINESPACING);
/* position for first line */
p.y = lp->p.y
+ (linespacing * (lp->nlines -1) / 2) /* cl of topline */
- lp->fontsize / 3.0 ; /* cl to baseline */
CodeGen->begin_context();
CodeGen->set_pencolor(lp->fontcolor);
CodeGen->set_font(lp->fontname,lp->fontsize*GD_drawing(g)->font_scale_adj);
for (i = 0; i < lp->nlines; i++) {
switch(lp->line[i].just) {
case 'l':
p.x = left_x;
break;
case 'r':
p.x = right_x;
break;
default:
case 'n':
p.x = center_x;
break;
}
CodeGen->textline(p,&(lp->line[i]));
/* position for next line */
p.y -= linespacing;
}
CodeGen->end_context();
}
void emit_defaults(graph_t* g)
{
CodeGen->set_pencolor(DEFAULT_COLOR);
CodeGen->set_fillcolor(DEFAULT_COLOR);
CodeGen->set_font(Deffontname,Deffontsize);
}
int parse_layers(char* p)
{
int ntok,c;
char *pcopy,*tok;
ntok = strccnt(p,':')+1;
pcopy = strdup(p);
if (LayerID) free(LayerID);
LayerID = N_NEW(ntok+2,char*);
c = 1;
for (tok = strtok(pcopy,Layerdelims); tok; tok = strtok(NULL,Layerdelims))
LayerID[c++] = tok;
return ntok;
}
void emit_layer(int n)
{
char buf[BUFSIZ],*fake[2];
char null = 0;
if (LayerID) {
/* create compatible char array structure for set_style call */
sprintf(buf,"setlayer%c%d%c%d%c",null,n,null,Nlayers,null);
fake[0] = buf;
fake[1] = NULL;
CodeGen->set_style(fake);
}
}
static int style_delim(int c)
{
switch (c) {
case '(': case ')': case ',': case '\0': return TRUE;
default : return FALSE;
}
}
#define SID 1
static int
style_token(char** s, xbuf *xb)
{
char* p = *s;
int token;
char c;
while (*p && (isspace(*p) || (*p ==','))) p++;
switch (*p) {
case '\0': token = 0; break;
case '(': case ')': token = *p++; break;
default:
token = SID;
while (!style_delim(c = *p)) {
xbputc(xb,c);
p++;
}
}
*s = p;
return token;
}
#define FUNLIMIT 64
static unsigned char outbuf[SMALLBUF];
static xbuf ps_xb;
static void
cleanup()
{
xbfree(&ps_xb);
}
char **
parse_style(char* s)
{
static char* parse[FUNLIMIT];
static int first = 1;
int fun = 0;
boolean in_parens = FALSE;
unsigned char buf[SMALLBUF];
char* p;
int c;
xbuf xb;
if (first) {
xbinit (&ps_xb, SMALLBUF, outbuf);
atexit(cleanup);
first = 0;
}
xbinit (&xb, SMALLBUF, buf);
p = s;
while ((c = style_token(&p,&xb)) != 0) {
switch (c) {
case '(':
if (in_parens) {
agerr(AGERR, "nesting not allowed in style: %s\n",s);
parse[0] = (char*)0;
xbfree (&xb);
return parse;
}
in_parens = TRUE;
break;
case ')':
if (in_parens == FALSE) {
agerr(AGERR, "unmatched ')' in style: %s\n",s);
parse[0] = (char*)0;
xbfree (&xb);
return parse;
}
in_parens = FALSE;
break;
default:
if (in_parens == FALSE) {
if (fun == FUNLIMIT-1) {
agerr(AGWARN, "truncating style '%s'\n", s);
parse[fun] = (char*)0;
xbfree (&xb);
return parse;
}
xbputc(&ps_xb, '\0'); /* terminate previous */
parse[fun++] = xbnext(&ps_xb);
}
xbput (&ps_xb, xbuse(&xb));
xbputc(&ps_xb, '\0');
}
}
if (in_parens) {
agerr(AGERR, "unmatched '(' in style: %s\n",s);
parse[0] = (char*)0;
xbfree (&xb);
return parse;
}
parse[fun] = (char*)0;
xbfree (&xb);
xbuse (&ps_xb); /* adds final '\0' to buffer */
return parse;
}
static struct cg_s {
codegen_t *cg;
char *name;
int id;
}
gens[] = {
{&PS_CodeGen,"ps",POSTSCRIPT},
{&PS_CodeGen,"ps2",PDF},
{&HPGL_CodeGen,"hpgl",HPGL},
{&HPGL_CodeGen,"pcl",PCL},
{&MIF_CodeGen,"mif",MIF},
{&PIC_CodeGen,"pic",PIC_format},
{&GD_CodeGen,"gd",GD},
#ifdef HAVE_LIBZ
{&GD_CodeGen,"gd2",GD2},
#endif
#ifdef WITH_GIF
{&GD_CodeGen,"gif",GIF},
#endif
#ifdef HAVE_LIBJPEG
{&GD_CodeGen,"jpg",JPEG},
{&GD_CodeGen,"jpeg",JPEG},
#endif
#ifdef HAVE_LIBPNG
#ifdef HAVE_LIBZ
{&GD_CodeGen,"png",PNG},
#endif
#endif
{&GD_CodeGen,"wbmp",WBMP},
#ifdef HAVE_LIBXPM
{&GD_CodeGen,"xbm",XBM},
#endif
{&ISMAP_CodeGen,"ismap",ISMAP},
{&IMAP_CodeGen,"imap",IMAP},
{&CMAP_CodeGen,"cmap",CMAP},
#ifdef HAVE_LIBPNG
{&VRML_CodeGen,"vrml",VRML},
#endif
{&VTX_CodeGen,"vtx",VTX},
{&MP_CodeGen,"mp",METAPOST},
{&FIG_CodeGen,"fig",FIG},
{&SVG_CodeGen,"svg",SVG},
#ifdef HAVE_LIBZ
{&SVG_CodeGen,"svgz",SVGZ},
{&DIA_CodeGen,"dia",DIA},
#endif
{(codegen_t*)0,"dot",ATTRIBUTED_DOT},
{(codegen_t*)0,"canon",CANONICAL_DOT},
{(codegen_t*)0,"plain",PLAIN},
{(codegen_t*)0,"plain-ext",PLAIN_EXT},
{&XDot_CodeGen,"xdot",EXTENDED_DOT},
{(codegen_t*)0,(char*)0,0}
};
int lang_select(char* str, int warn)
{
struct cg_s *p;
for (p = gens; p->name; p++) {
if (strcasecmp(str,p->name) == 0) {
CodeGen = p->cg;
return p->id;
}
}
if (warn) {
agerr(AGWARN, "language %s not recognized, use one of:\n",str);
for (p = gens; p->name; p++) agerr(AGPREV, " %s",p->name);
agerr(AGPREV, "\n");
}
return ATTRIBUTED_DOT;
}
FILE *
file_select(char* str)
{
FILE *rv;
rv = fopen(str,"wb");
if (rv == NULL) { perror(str); exit(1); }
return rv;
}
void use_library(char* name)
{
static int cnt = 0;
if (name) {
Lib = ALLOC(cnt+2,Lib,char*);
Lib[cnt++] = name;
Lib[cnt] = NULL;
}
}
|