/* File: svgdraw.c * * This file is part of XSCHEM, * a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit * simulation. * Copyright (C) 1998-2020 Stefan Frederik Schippers * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "xschem.h" #define X_TO_SVG(x) ( (x+xctx->xorigin)* xctx->mooz ) #define Y_TO_SVG(y) ( (y+xctx->yorigin)* xctx->mooz ) static FILE *fd; typedef struct { int red; int green; int blue; } Svg_color; static Svg_color *svg_colors; static char svg_font_weight[80] = "normal"; /* normal, bold, bolder, lighter */ static char svg_font_family[80] = "Sans Serif"; /* serif, monospace, Helvetica, Arial */ static char svg_font_style[80] = "normal"; /* normal, italic, oblique */ static double svg_linew; /* current width of lines / rectangles */ static void svg_restore_lw(void) { svg_linew = xctx->lw*1.2; } static void svg_xdrawline(int layer, int bus, double x1, double y1, double x2, double y2, int dash) { fprintf(fd,"zoom, 1.4*dash/xctx->zoom); if(bus) fprintf(fd, "style=\"stroke-width:%g;\" ", BUS_WIDTH * svg_linew); fprintf(fd,"d=\"M%g %gL%g %g\"/>\n", x1, y1, x2, y2); } static void svg_xdrawpoint(int layer, double x1, double y1) { fprintf(fd,"\n", x1, y1, x1+1.0, y1, x1+1.0, y1+1.0, x1, y1+1.0, x1, y1); } static void svg_xfillrectangle(int layer, double x1, double y1, double x2, double y2, int dash) { fprintf(fd,"zoom, 1.4*dash/xctx->zoom); fprintf(fd,"d=\"M%g %gL%g %gL%g %gL%g %gL%g %gz\"/>\n", x1, y1, x2, y1, x2, y2, x1, y2, x1, y1); } static void svg_drawpolygon(int c, int what, double *x, double *y, int points, int fill, int dash) { double x1,y1,x2,y2; double xx, yy; int i; polygon_bbox(x, y, points, &x1,&y1,&x2,&y2); x1=X_TO_SVG(x1); y1=Y_TO_SVG(y1); x2=X_TO_SVG(x2); y2=Y_TO_SVG(y2); if( !rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&x1,&y1,&x2,&y2) ) { return; } fprintf(fd, "zoom, 1.4*dash/xctx->zoom); if(!fill) { fprintf(fd,"style=\"fill:none;\" "); } fprintf(fd, "d=\""); for(i=0;i\n"); */ fprintf(fd, "\"/>\n"); } static void svg_filledrect(int gc, double rectx1,double recty1,double rectx2,double recty2, int dash) { double x1,y1,x2,y2; x1=X_TO_SVG(rectx1); y1=Y_TO_SVG(recty1); x2=X_TO_SVG(rectx2); y2=Y_TO_SVG(recty2); if( rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&x1,&y1,&x2,&y2) ) { svg_xfillrectangle(gc, x1,y1,x2,y2, dash); } } static void svg_drawcircle(int gc, int fillarc, double x,double y,double r,double a, double b) { double xx,yy,rr; double x1, y1, x2, y2; xx=X_TO_SVG(x); yy=Y_TO_SVG(y); rr=r*xctx->mooz; arc_bbox(x, y, r, a, b, &x1,&y1,&x2,&y2); x1=X_TO_SVG(x1); y1=Y_TO_SVG(y1); x2=X_TO_SVG(x2); y2=Y_TO_SVG(y2); if( rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&x1,&y1,&x2,&y2) ) { fprintf(fd, "\n", gc, xx, yy, rr); } } static void svg_drawarc(int gc, int fillarc, double x,double y,double r,double a, double b, int dash) { double xx,yy,rr; double x1, y1, x2, y2; double xx1, yy1, xx2, yy2; int fs, fa; xx=X_TO_SVG(x); yy=Y_TO_SVG(y); rr=r*xctx->mooz; arc_bbox(x, y, r, a, b, &x1,&y1,&x2,&y2); x1=X_TO_SVG(x1); y1=Y_TO_SVG(y1); x2=X_TO_SVG(x2); y2=Y_TO_SVG(y2); if( rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&x1,&y1,&x2,&y2) ) { if(b == 360.) { fprintf(fd, "zoom, 1.4*dash/xctx->zoom); if(!fillarc) fprintf(fd, "style=\"fill:none;\""); fprintf(fd, "/>\n"); } else { xx1 = rr * cos(a * XSCH_PI / 180.) + xx; yy1 = -rr * sin(a * XSCH_PI / 180.) + yy; xx2 = rr * cos((a + b) * XSCH_PI / 180.) + xx; yy2 = -rr * sin((a + b) * XSCH_PI / 180.) + yy; fa = b > 180 ? 1 : 0; fs = b > 0 ? 0 : 1; fprintf(fd,"zoom, 1.4*dash/xctx->zoom); if(!fillarc) fprintf(fd,"style=\"fill:none;\" "); fprintf(fd, "d=\"M%g %g A%g %g 0 %d %d %g %g\"/>\n", xx1, yy1, rr, rr, fa, fs, xx2, yy2); } } } static void svg_drawline(int gc, int bus, double linex1,double liney1,double linex2,double liney2, int dash) { double x1,y1,x2,y2; x1=X_TO_SVG(linex1); y1=Y_TO_SVG(liney1); x2=X_TO_SVG(linex2); y2=Y_TO_SVG(liney2); if( clip(&x1,&y1,&x2,&y2) ) { svg_xdrawline(gc, bus, x1, y1, x2, y2, dash); } } static double textx1,textx2,texty1,texty2; static void svg_draw_string_line(int layer, char *s, double x, double y, double size, int rot, int flip, int lineno, double fontheight, double fontascent, double fontdescent, int llength) { double ix, iy; int rot1; int line_delta; int line_offset; double lines; char col[20]; my_snprintf(col, S(col), "#%02x%02x%02x", xcolor_array[layer].red >> 8, xcolor_array[layer].green >> 8, xcolor_array[layer].blue >> 8); if(s==NULL) return; if(llength==0) return; line_delta = lineno*fontheight; lines = (cairo_lines-1)*fontheight; line_offset=cairo_longest_line; ix=X_TO_SVG(x); iy=Y_TO_SVG(y); if(rot&1) { rot1=3; } else rot1=0; if( rot==0 && flip==0) {iy+=line_delta+fontascent;} else if(rot==1 && flip==0) {iy+=line_offset;ix=ix-fontheight+fontascent-lines+line_delta;} else if(rot==2 && flip==0) {iy=iy-fontheight-lines+line_delta+fontascent; ix=ix-line_offset;} else if(rot==3 && flip==0) {ix+=line_delta+fontascent;} else if(rot==0 && flip==1) {ix=ix-line_offset;iy+=line_delta+fontascent;} else if(rot==1 && flip==1) {ix=ix-fontheight+line_delta-lines+fontascent;} else if(rot==2 && flip==1) {iy=iy-fontheight-lines+line_delta+fontascent;} else if(rot==3 && flip==1) {iy=iy+line_offset;ix+=line_delta+fontascent;} fprintf(fd,"mooz); if(strcmp(svg_font_weight, "normal")) fprintf(fd, "font-weight=\"%s\" ", svg_font_weight); if(strcmp(svg_font_style, "normal")) fprintf(fd, "font-style=\"%s\" ", svg_font_style); if(strcmp(svg_font_family, "Sans Serif")) fprintf(fd, "style=\"font-family:%s;\" ", svg_font_family); if(rot1) fprintf(fd, "transform=\"translate(%g, %g) rotate(%d)\" ", ix, iy, rot1*90); else fprintf(fd, "transform=\"translate(%g, %g)\" ", ix, iy); fprintf(fd, ">"); while(*s) { switch(*s) { case '<': fputs("<", fd); break; case '>': fputs(">", fd); break; case '&': fputs("&", fd); break; default: fputc(*s, fd); } s++; } fprintf(fd, "\n"); } static void svg_draw_string(int layer, const char *str, int rot, int flip, int hcenter, int vcenter, double x,double y, double xscale, double yscale) { char *tt, *ss, *sss=NULL; char c; int lineno=0; double size, height, ascent, descent; int llength=0; if(str==NULL || !has_x ) return; size = xscale*52.; height = size*xctx->mooz * 1.147; ascent = size*xctx->mooz * 0.908; descent = size*xctx->mooz * 0.219; text_bbox(str, xscale, yscale, rot, flip, hcenter, vcenter, x,y, &textx1,&texty1,&textx2,&texty2); if(!textclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,textx1,texty1,textx2,texty2)) { return; } if(hcenter) { if(rot == 0 && flip == 0 ) { x=textx1;} if(rot == 1 && flip == 0 ) { y=texty1;} if(rot == 2 && flip == 0 ) { x=textx2;} if(rot == 3 && flip == 0 ) { y=texty2;} if(rot == 0 && flip == 1 ) { x=textx2;} if(rot == 1 && flip == 1 ) { y=texty2;} if(rot == 2 && flip == 1 ) { x=textx1;} if(rot == 3 && flip == 1 ) { y=texty1;} } if(vcenter) { if(rot == 0 && flip == 0 ) { y=texty1;} if(rot == 1 && flip == 0 ) { x=textx2;} if(rot == 2 && flip == 0 ) { y=texty2;} if(rot == 3 && flip == 0 ) { x=textx1;} if(rot == 0 && flip == 1 ) { y=texty1;} if(rot == 1 && flip == 1 ) { x=textx2;} if(rot == 2 && flip == 1 ) { y=texty2;} if(rot == 3 && flip == 1 ) { x=textx1;} } llength=0; my_strdup2(465, &sss, str); tt=ss=sss; for(;;) { c=*ss; if(c=='\n' || c==0) { *ss='\0'; svg_draw_string_line(layer, tt, x, y, size, rot, flip, lineno, height, ascent, descent, llength); lineno++; if(c==0) break; *ss='\n'; tt=ss+1; llength=0; } else { llength++; } ss++; } my_free(1154, &sss); } static void old_svg_draw_string(int layer, const char *str, int rot, int flip, int hcenter, int vcenter, double x,double y, double xscale, double yscale) { double a,yy,curr_x1,curr_y1,curr_x2,curr_y2,rx1,rx2,ry1,ry2; int pos=0,cc,pos2=0; int i; if(str==NULL) return; #ifdef HAS_CAIRO text_bbox_nocairo(str, xscale, yscale, rot, flip, hcenter, vcenter, x,y, &rx1,&ry1,&rx2,&ry2); #else text_bbox(str, xscale, yscale, rot, flip, hcenter, vcenter, x,y, &rx1,&ry1,&rx2,&ry2); #endif xscale*=nocairo_font_xscale; yscale*=nocairo_font_yscale; if(!textclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,rx1,ry1,rx2,ry2)) return; x=rx1;y=ry1; if(rot&1) {y=ry2;rot=3;} else rot=0; flip = 0; yy=y; while(str[pos2]) { cc = (unsigned char)str[pos2++]; if(cc>127) cc= '?'; if(cc=='\n') { yy+=(FONTHEIGHT+FONTDESCENT+FONTWHITESPACE)* yscale; pos=0; continue; } a = pos*(FONTWIDTH+FONTWHITESPACE); for(i=0;imooz; while(deltaxorigin* xctx->mooz;y = xctx->yorigin* xctx->mooz; if(y>xctx->areay1 && yareay2) { svg_xdrawline(GRIDLAYER, 0, xctx->areax1+1,(int)y, xctx->areax2-1, (int)y, 0); } if(x>xctx->areax1 && xareax2) { svg_xdrawline(GRIDLAYER, 0, (int)x,xctx->areay1+1, (int)x, xctx->areay2-1, 0); } tmp = floor((xctx->areay1+1)/delta)*delta-fmod(-xctx->yorigin* xctx->mooz,delta); for(x=floor((xctx->areax1+1)/delta)*delta-fmod(-xctx->xorigin* xctx->mooz,delta);xareax2;x+=delta) { for(y=tmp;yareay2;y+=delta) { svg_xdrawpoint(GRIDLAYER, (int)(x), (int)(y)); } } } static void svg_draw_symbol(int n,int layer,int tmp_flip, int rot, double xoffset, double yoffset) /* draws current layer only, should be called within */ { /* a "for(i=0;iinst[n].ptr == -1) return; if( (layer != PINLAYER && !enable_layer[layer]) ) return; if(layer==0) { x1=X_TO_SVG(xctx->inst[n].x1); x2=X_TO_SVG(xctx->inst[n].x2); y1=Y_TO_SVG(xctx->inst[n].y1); y2=Y_TO_SVG(xctx->inst[n].y2); if(OUTSIDE(x1,y1,x2,y2,xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2)) { xctx->inst[n].flags|=1; return; } else xctx->inst[n].flags&=~1; /* following code handles different text color for labels/pins 06112002 */ } else if(xctx->inst[n].flags&1) { dbg(1, "draw_symbol(): skippinginst %d\n", n); return; } flip = xctx->inst[n].flip; if(tmp_flip) flip = !flip; rot = (xctx->inst[n].rot + rot ) & 0x3; x0=xctx->inst[n].x0 + xoffset; y0=xctx->inst[n].y0 + yoffset; symptr = (xctx->inst[n].ptr+ xctx->sym); for(j=0;j< (xctx->inst[n].ptr+ xctx->sym)->lines[layer];j++) { line = ((xctx->inst[n].ptr+ xctx->sym)->line[layer])[j]; ROTATION(rot, flip, 0.0,0.0,line.x1,line.y1,x1,y1); ROTATION(rot, flip, 0.0,0.0,line.x2,line.y2,x2,y2); ORDER(x1,y1,x2,y2); svg_drawline(layer, line.bus, x0+x1, y0+y1, x0+x2, y0+y2, line.dash); } for(j=0;j< (xctx->inst[n].ptr+ xctx->sym)->polygons[layer];j++) { polygon = ((xctx->inst[n].ptr+ xctx->sym)->poly[layer])[j]; { /* scope block so we declare some auxiliary arrays for coord transforms. 20171115 */ int k; double *x = my_malloc(417, sizeof(double) * polygon.points); double *y = my_malloc(418, sizeof(double) * polygon.points); for(k=0;kinst[n].ptr+ xctx->sym)->arcs[layer];j++) { double angle; arc = ((xctx->inst[n].ptr+ xctx->sym)->arc[layer])[j]; if(flip) { angle = 270.*rot+180.-arc.b-arc.a; } else { angle = arc.a+rot*270.; } angle = fmod(angle, 360.); if(angle<0.) angle+=360.; ROTATION(rot, flip, 0.0,0.0,arc.x,arc.y,x1,y1); svg_drawarc(layer, arc.fill, x0+x1, y0+y1, arc.r, angle, arc.b, arc.dash); } if( (layer != PINLAYER || enable_layer[layer]) ) for(j=0;j< (xctx->inst[n].ptr+ xctx->sym)->rects[layer];j++) { box = ((xctx->inst[n].ptr+ xctx->sym)->rect[layer])[j]; ROTATION(rot, flip, 0.0,0.0,box.x1,box.y1,x1,y1); ROTATION(rot, flip, 0.0,0.0,box.x2,box.y2,x2,y2); RECTORDER(x1,y1,x2,y2); svg_filledrect(layer, x0+x1, y0+y1, x0+x2, y0+y2, box.dash); } if( (layer==TEXTWIRELAYER && !(xctx->inst[n].flags&2) ) || (sym_txt && (layer==TEXTLAYER) && (xctx->inst[n].flags&2) ) ) { const char *txtptr; for(j=0;j< (xctx->inst[n].ptr+ xctx->sym)->texts;j++) { text = (xctx->inst[n].ptr+ xctx->sym)->text[j]; /* if(text.xscale*FONTWIDTH* xctx->mooz<1) continue; */ txtptr= translate(n, text.txt_ptr); ROTATION(rot, flip, 0.0,0.0,text.x0,text.y0,x1,y1); textlayer = layer; if( !(layer == PINLAYER && (xctx->inst[n].flags & 4))) { textlayer = (xctx->inst[n].ptr+ xctx->sym)->text[j].layer; if(textlayer < 0 || textlayer >= cadlayers) textlayer = layer; } my_snprintf(svg_font_family, S(svg_font_family), svg_font_name); my_snprintf(svg_font_style, S(svg_font_style), "normal"); my_snprintf(svg_font_weight, S(svg_font_weight), "normal"); textfont = symptr->text[j].font; if( (textfont && textfont[0])) { my_snprintf(svg_font_family, S(svg_font_family), textfont); } if( symptr->text[j].flags & TEXT_BOLD) my_snprintf(svg_font_weight, S(svg_font_weight), "bold"); if( symptr->text[j].flags & TEXT_ITALIC) my_snprintf(svg_font_style, S(svg_font_style), "italic"); if( symptr->text[j].flags & TEXT_OBLIQUE) my_snprintf(svg_font_style, S(svg_font_style), "oblique"); if((layer == PINLAYER && xctx->inst[n].flags & 4) || enable_layer[textlayer]) { if(text_svg) svg_draw_string(textlayer, txtptr, (text.rot + ( (flip && (text.rot & 1) ) ? rot+2 : rot) ) & 0x3, flip^text.flip, text.hcenter, text.vcenter, x0+x1, y0+y1, text.xscale, text.yscale); else old_svg_draw_string(textlayer, txtptr, (text.rot + ( (flip && (text.rot & 1) ) ? rot+2 : rot) ) & 0x3, flip^text.flip, text.hcenter, text.vcenter, x0+x1, y0+y1, text.xscale, text.yscale); } } } Tcl_SetResult(interp,"",TCL_STATIC); } static void fill_svg_colors() { char s[200]; /* overflow safe 20161122 */ unsigned int i,c; if(debug_var>=1) { tcleval( "puts $svg_colors"); } for(i=0;i> 16; svg_colors[i].green = (c & 0x00ff00) >> 8; svg_colors[i].blue = (c & 0x0000ff); } else if(dark_colorscheme) { svg_colors[i].red = 255; svg_colors[i].green = 255; svg_colors[i].blue = 255; } else { svg_colors[i].red = 0; svg_colors[i].green = 0; svg_colors[i].blue = 0; } if(debug_var>=1) { fprintf(errfp, "svg_colors: %d %d %d\n", svg_colors[i].red, svg_colors[i].green, svg_colors[i].blue); } } } void svg_draw(void) { double dx, dy; int c,i, textlayer; int old_grid; int modified_save; char *tmpstring=NULL; const char *r, *textfont; if(!plotfile[0]) { my_strdup(61, &tmpstring, "tk_getSaveFile -title {Select destination file} -initialdir [pwd]"); tcleval(tmpstring); r = tclresult(); my_free(963, &tmpstring); if(r[0]) my_strncpy(plotfile, r, S(plotfile)); else return; } svg_restore_lw(); svg_colors=my_calloc(419, cadlayers, sizeof(Svg_color)); if(svg_colors==NULL){ fprintf(errfp, "svg_draw(): calloc error\n");tcleval( "exit"); } fill_svg_colors(); old_grid=draw_grid; draw_grid=0; dx=xctx->areax2-xctx->areax1; dy=xctx->areay2-xctx->areay1; dbg(1, "svg_draw(): dx=%g dy=%g\n", dx, dy); modified_save=xctx->modified; push_undo(); trim_wires(); /* 20161121 add connection boxes on wires but undo at end */ if(plotfile[0]) fd=fopen(plotfile, "w"); else fd=fopen("plot.svg", "w"); my_strncpy(plotfile,"", S(plotfile)); fprintf(fd, "\n", dx, dy); fprintf(fd, "\n"); fclose(fd); draw_grid=old_grid; my_free(964, &svg_colors); pop_undo(0); xctx->modified=modified_save; }