/* File: svgdraw.c * * This file is part of XSCHEM, * a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit * simulation. * Copyright (C) 1998-2024 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" 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 <= 0.01 ? 0.2 : 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, int fill) { fprintf(fd,"zoom, 1.4*dash/xctx->zoom); if(fill == 0) { fprintf(fd,"style=\"fill:none;\" "); } else if(fill == 2) { fprintf(fd, "style=\"fill-opacity:1.0;\" "); } 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_drawbezier(double *x, double *y, int points) { const double bez_steps = 1.0/32.0; /* divide the t = [0,1] interval into 32 steps */ int b, i; double t; double xp, yp; double x0, x1, x2, y0, y1, y2; i = 0; fprintf(fd, "d=\""); for(b = 0; b < points - 2; b++) { if(points == 3) { /* 3 points: only one bezier */ x0 = x[0]; y0 = y[0]; x1 = x[1]; y1 = y[1]; x2 = x[2]; y2 = y[2]; } else if(b == points - 3) { /* last bezier */ x0 = (x[points - 3] + x[points - 2]) / 2.0; y0 = (y[points - 3] + y[points - 2]) / 2.0; x1 = x[points - 2]; y1 = y[points - 2]; x2 = x[points - 1]; y2 = y[points - 1]; } else if(b == 0) { /* first bezier */ x0 = x[0]; y0 = y[0]; x1 = x[1]; y1 = y[1]; x2 = (x[1] + x[2]) / 2.0; y2 = (y[1] + y[2]) / 2.0; } else { /* beziers in the middle */ x0 = (x[b] + x[b + 1]) / 2.0; y0 = (y[b] + y[b + 1]) / 2.0; x1 = x[b + 1]; y1 = y[b + 1]; x2 = (x[b + 1] + x[b + 2]) / 2.0; y2 = (y[b + 1] + y[b + 2]) / 2.0; } for(t = 0; t <= 1.0; t += bez_steps) { xp = (1 - t) * (1 - t) * x0 + 2 * (1 - t) * t * x1 + t * t * x2; yp = (1 - t) * (1 - t) * y0 + 2 * (1 - t) * t * y1 + t * t * y2; if(i==0) fprintf(fd, "M%g %g", X_TO_SCREEN(xp), Y_TO_SCREEN(yp)); else fprintf(fd, "L%g %g", X_TO_SCREEN(xp), Y_TO_SCREEN(yp)); i++; } } } static void svg_drawpolygon(int c, int what, double *x, double *y, int points, int fill, int dash, int flags, int bus) { double x1,y1,x2,y2; double xx, yy; int bezier, i; polygon_bbox(x, y, points, &x1,&y1,&x2,&y2); x1=X_TO_SCREEN(x1); y1=Y_TO_SCREEN(y1); x2=X_TO_SCREEN(x2); y2=Y_TO_SCREEN(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(bus || fill == 0 || fill == 2) { if(bus) fprintf(fd, "style=\"stroke-width:%g; ", BUS_WIDTH * svg_linew); else fprintf(fd, "style=\""); if(fill == 0) { fprintf(fd,"fill:none;\" "); } else if(fill == 2) { fprintf(fd, "fill-opacity:1.0;\" "); } else { fprintf(fd, "\" "); } } bezier = flags && (points > 2); if(bezier) { svg_drawbezier(x, y, points); } else { 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, int fill, int e_a, int e_b) { double x1,y1,x2,y2; x1=X_TO_SCREEN(rectx1); y1=Y_TO_SCREEN(recty1); x2=X_TO_SCREEN(rectx2); y2=Y_TO_SCREEN(recty2); if( rectclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,&x1,&y1,&x2,&y2) ) { if(e_a != -1) { double rx = (x2 - x1) / 2.0; double ry = (y2 - y1) / 2.0; double cx = (x2 + x1) / 2.0; double cy = (y2 + y1) / 2.0; if(e_b == 360.) { fprintf(fd, "zoom, 1.4*dash/xctx->zoom); if(fill == 0) fprintf(fd, "style=\"fill:none;\" "); else if(fill == 2) fprintf(fd, "style=\"fill-opacity:1.0;\" "); fprintf(fd, "/>\n"); } else { double xx1 = rx * cos(e_a * XSCH_PI / 180.) + cx; double yy1 = -ry * sin(e_a * XSCH_PI / 180.) + cy; double xx2 = rx * cos((e_a + e_b) * XSCH_PI / 180.) + cx; double yy2 = -ry * sin((e_a + e_b) * XSCH_PI / 180.) + cy; int fa = e_b > 180 ? 1 : 0; int fs = e_b > 0 ? 0 : 1; fprintf(fd,"zoom, 1.4*dash/xctx->zoom); if(fill == 0) { fprintf(fd,"style=\"fill:none;\" "); fprintf(fd, "d=\"M%g %g A%g %g 0 %d %d %g %g\"/>\n", xx1, yy1, rx, ry, fa, fs, xx2, yy2); } else if(fill == 2) { fprintf(fd, "style=\"fill-opacity:1.0;\" "); fprintf(fd, "d=\"M%g %g A%g %g 0 %d %d %g %gL%g %gz\"/>\n", xx1, yy1, rx, ry, fa, fs, xx2, yy2, cx, cy); } else { fprintf(fd, "d=\"M%g %g A%g %g 0 %d %d %g %gL%g %gz\"/>\n", xx1, yy1, rx, ry, fa, fs, xx2, yy2, cx, cy); } } } else { svg_xfillrectangle(gc, x1,y1,x2,y2, dash, fill); } } } 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_SCREEN(x); yy=Y_TO_SCREEN(y); rr=r*xctx->mooz; arc_bbox(x, y, r, a, b, &x1,&y1,&x2,&y2); x1=X_TO_SCREEN(x1); y1=Y_TO_SCREEN(y1); x2=X_TO_SCREEN(x2); y2=Y_TO_SCREEN(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_SCREEN(x); yy=Y_TO_SCREEN(y); rr=r*xctx->mooz; arc_bbox(x, y, r, a, b, &x1,&y1,&x2,&y2); x1=X_TO_SCREEN(x1); y1=Y_TO_SCREEN(y1); x2=X_TO_SCREEN(x2); y2=Y_TO_SCREEN(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 == 0) fprintf(fd, "style=\"fill:none;\" "); else if(fillarc == 2) fprintf(fd, "style=\"fill-opacity:1.0;\" "); 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 == 0) { 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); } else if(fillarc == 2) { fprintf(fd, "style=\"fill-opacity:1.0;\" "); fprintf(fd, "d=\"M%g %g A%g %g 0 %d %d %g %gL%g %gz\"/>\n", xx1, yy1, rr, rr, fa, fs, xx2, yy2, xx, yy); } else { fprintf(fd, "d=\"M%g %g A%g %g 0 %d %d %g %gL%g %gz\"/>\n", xx1, yy1, rr, rr, fa, fs, xx2, yy2, xx, yy); } } } } 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_SCREEN(linex1); y1=Y_TO_SCREEN(liney1); x2=X_TO_SCREEN(linex2); y2=Y_TO_SCREEN(liney2); if( clip(&x1,&y1,&x2,&y2) ) { svg_xdrawline(gc, bus, x1, y1, x2, y2, dash); } } static void svg_draw_string_line(int layer, char *s, double x, double y, double size, short rot, short flip, int lineno, double fontheight, double fontascent, double fontdescent, int llength, int no_of_lines, double longest_line) { double ix, iy; short rot1; double line_delta; double lines; char col[20]; if(color_ps) my_snprintf(col, S(col), "#%02x%02x%02x", svg_colors[layer].red, svg_colors[layer].green, svg_colors[layer].blue); else if(tclgetboolvar("dark_colorscheme")) my_snprintf(col, S(col), "#%02x%02x%02x", 255, 255, 255); else my_snprintf(col, S(col), "#%02x%02x%02x", 0, 0, 0); if(s==NULL) return; if(llength==0) return; line_delta = lineno*fontheight; lines = (no_of_lines-1)*fontheight; ix=X_TO_SCREEN(x); iy=Y_TO_SCREEN(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+=longest_line;ix=ix-fontheight+fontascent-lines+line_delta;} else if(rot==2 && flip==0) {iy=iy-fontheight-lines+line_delta+fontascent; ix=ix-longest_line;} else if(rot==3 && flip==0) {ix+=line_delta+fontascent;} else if(rot==0 && flip==1) {ix=ix-longest_line;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+longest_line;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, tclgetvar("svg_font_name"))) 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, short rot, short flip, int hcenter, int vcenter, double x,double y, double xscale, double yscale) { char *tt, *ss, *sss=NULL; double textx1,textx2,texty1,texty2; char c; int lineno=0; double size, height, ascent, descent; int llength=0, no_of_lines; double longest_line; char *estr = NULL; /* expanded string: TABs replaced with spaces */ if(str==NULL) return; estr = my_expand(str, tclgetintvar("tabstop")); size = xscale*52. * cairo_font_scale; height = size*xctx->mooz * 1.147; ascent = size*xctx->mooz * 0.908; descent = size*xctx->mooz * 0.219; text_bbox(estr, xscale, yscale, rot, flip, hcenter, vcenter, x,y, &textx1,&texty1,&textx2,&texty2, &no_of_lines, &longest_line); if(!textclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,textx1,texty1,textx2,texty2)) { my_free(_ALLOC_ID_, &estr); 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(_ALLOC_ID_, &sss, estr); 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, no_of_lines, longest_line); ++lineno; if(c==0) break; *ss='\n'; tt=ss+1; llength=0; } else { ++llength; } ++ss; } my_free(_ALLOC_ID_, &sss); my_free(_ALLOC_ID_, &estr); } static void old_svg_draw_string(int layer, const char *str, short rot, short 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, no_of_lines; double longest_line; char *estr = NULL; if(str==NULL) return; estr = my_expand(str, tclgetintvar("tabstop")); #if HAS_CAIRO==1 text_bbox_nocairo(estr, xscale, yscale, rot, flip, hcenter, vcenter, x,y, &rx1,&ry1,&rx2,&ry2, &no_of_lines, &longest_line); #else text_bbox(estr, xscale, yscale, rot, flip, hcenter, vcenter, x,y, &rx1,&ry1,&rx2,&ry2, &no_of_lines, &longest_line); #endif if(!textclip(xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2,rx1,ry1,rx2,ry2)) { my_free(_ALLOC_ID_, &estr); return; } xscale*=tclgetdoublevar("nocairo_font_xscale") * cairo_font_scale; yscale*=tclgetdoublevar("nocairo_font_yscale") * cairo_font_scale; x=rx1;y=ry1; if(rot&1) {y=ry2;rot=3;} else rot=0; flip = 0; yy=y; while(estr[pos2]) { cc = (unsigned char)estr[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 int svg_embedded_image(xRect *r, double rx1, double ry1, double rx2, double ry2, int rot, int flip) { char *attr = NULL; const char *alpha_str, *filter; size_t attr_len; double x1, y1, x2, y2, w, h, scalex = 1.0, scaley = 1.0; int jpg = 0; char opacity[100]; char transform[150]; double alpha = 1.0; unsigned char *buffer = NULL; size_t buffer_size; double xorig, yorig; /* image anchor point, upper left corner in SVG */ x1=X_TO_SCREEN(rx1); y1=Y_TO_SCREEN(ry1); x2=X_TO_SCREEN(rx2); y2=Y_TO_SCREEN(ry2); /* screen position */ if(RECT_OUTSIDE(x1, y1, x2, y2, xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2)) return 0; if(rot == 1 || rot == 3) { w = fabs(y2 - y1); h = fabs(x2 - x1); } else { h = fabs(y2 - y1); w = fabs(x2 - x1); } if(flip && (rot == 0 || rot == 2)) scalex = -1.0; else if(flip && (rot == 1 || rot == 3)) scalex = -1.0; alpha_str = get_tok_value(r->prop_ptr, "alpha", 0); if(alpha_str[0]) alpha = atof(alpha_str); attr_len = my_strdup2(_ALLOC_ID_, &attr, get_tok_value(r->prop_ptr, "image_data", 0)); buffer = base64_decode(attr, attr_len, &buffer_size); filter = get_tok_value(r->prop_ptr, "filter", 0); if(attr_len > 5) { if(!strncmp(attr, "/9j/", 4)) jpg = 1; else if(!strncmp(attr, "iVBOR", 5)) jpg = 0; else if(my_memmem(buffer, buffer_size, "\n", 0.0, 0.0, w, h, transform, opacity, jpg ? "jpeg" : "png", attr); } else if(jpg == 2) { /* svg */ fprintf(fd, "\n", 0.0, 0.0, w, h, transform, opacity, attr); } my_free(_ALLOC_ID_, &buffer); my_free(_ALLOC_ID_, &attr); return 1; } static void svg_draw_symbol(int c, int n,int layer,short tmp_flip, short rot, double xoffset, double yoffset) /* draws current layer only, should be called within */ { /* a "for(i=0;isym[xctx->inst[n].ptr].type; lvs_ignore=tclgetboolvar("lvs_ignore"); if(xctx->inst[n].ptr == -1) return; if(layer == 0) { xctx->inst[n].flags &= ~IGNORE_INST; /* clear bit */ if( type && strcmp(type, "launcher") && strcmp(type, "logo") && strcmp(type, "probe") && strcmp(type, "architecture") && strcmp(type, "noconn")) { if(skip_instance(n, 1, lvs_ignore)) { xctx->inst[n].flags |= IGNORE_INST; } } } if(shorted_instance(n, lvs_ignore)) { c = PINLAYER; disabled = 2; } else if(xctx->inst[n].flags & IGNORE_INST) { c = GRIDLAYER; disabled = 1; } if(xctx->inst[n].color != -10000) c = get_color(xctx->inst[n].color); if( (xctx->inst[n].flags & HIDE_INST) || ((xctx->inst[n].ptr + xctx->sym)->flags & HIDE_INST) || ((xctx->hide_symbols==1 && (xctx->inst[n].ptr + xctx->sym)->type && !strcmp( (xctx->inst[n].ptr+ xctx->sym)->type, "subcircuit") )) || (xctx->hide_symbols == 2) ) { hide = 1; } else { hide = 0; } if(layer==0) { x1=X_TO_SCREEN(xctx->inst[n].x1); x2=X_TO_SCREEN(xctx->inst[n].x2); y1=Y_TO_SCREEN(xctx->inst[n].y1); y2=Y_TO_SCREEN(xctx->inst[n].y2); if(RECT_OUTSIDE(x1,y1,x2,y2,xctx->areax1,xctx->areay1,xctx->areax2,xctx->areay2)) { xctx->inst[n].flags|=1; return; } else if((xctx->inst[n].x2 - xctx->inst[n].x1) * xctx->mooz < 3 && (xctx->inst[n].y2 - xctx->inst[n].y1) * xctx->mooz < 3) { svg_filledrect(SYMLAYER, xctx->inst[n].xx1, xctx->inst[n].yy1, xctx->inst[n].xx2, xctx->inst[n].yy2, 0, 0, -1, -1); xctx->inst[n].flags|=1; return; } else { xctx->inst[n].flags&=~1; } if(hide) { int color = (disabled==1) ? GRIDLAYER : (disabled == 2) ? PINLAYER : SYMLAYER; svg_filledrect(color, xctx->inst[n].xx1, xctx->inst[n].yy1, xctx->inst[n].xx2, xctx->inst[n].yy2, 2, 0, -1, -1); } } else if(xctx->inst[n].flags&1) { 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); if(layer == cadlayers) goto draw_texts; if( (layer != PINLAYER && !xctx->enable_layer[layer]) ) return; if(!hide) { for(j=0;j< symptr->lines[layer]; ++j) { int dash; line = (symptr->line[layer])[j]; dash = (disabled == 1) ? 3 : line.dash; 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(c, line.bus, x0+x1, y0+y1, x0+x2, y0+y2, dash); } for(j=0;j< symptr->polygons[layer]; ++j) { int dash; int bezier; int bus; polygon =&(symptr->poly[layer])[j]; bus = get_attr_val(get_tok_value(polygon->prop_ptr, "bus", 0)); bezier = !strboolcmp(get_tok_value(polygon->prop_ptr, "bezier", 0), "true"); dash = (disabled == 1) ? 3 : polygon->dash; { /* scope block so we declare some auxiliary arrays for coord transforms. 20171115 */ int k; double *x = my_malloc(_ALLOC_ID_, sizeof(double) * polygon->points); double *y = my_malloc(_ALLOC_ID_, sizeof(double) * polygon->points); for(k=0;kpoints; ++k) { ROTATION(rot, flip, 0.0,0.0,polygon->x[k],polygon->y[k],x[k],y[k]); x[k]+= x0; y[k] += y0; } svg_drawpolygon(c, NOW, x, y, polygon->points, polygon->fill, dash, bezier, bus); my_free(_ALLOC_ID_, &x); my_free(_ALLOC_ID_, &y); } } for(j=0;j< symptr->arcs[layer]; ++j) { int dash; double angle; arc = (symptr->arc[layer])[j]; dash = (disabled == 1) ? 3 : arc.dash; 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(c, arc.fill, x0+x1, y0+y1, arc.r, angle, arc.b, dash); } } /* if(!hide) */ if( (!hide && xctx->enable_layer[layer]) || (hide && layer == PINLAYER && xctx->enable_layer[layer]) ) { for(j=0;j< symptr->rects[layer]; ++j) { int dash; rect = &(symptr->rect[layer])[j]; dash = (disabled == 1) ? 3 : rect->dash; ROTATION(rot, flip, 0.0,0.0,rect->x1,rect->y1,x1,y1); ROTATION(rot, flip, 0.0,0.0,rect->x2,rect->y2,x2,y2); RECTORDER(x1,y1,x2,y2); if(layer == GRIDLAYER && rect->flags & 1024) { double xx1 = x0 + x1; double yy1 = y0 + y1; double xx2 = x0 + x2; double yy2 = y0 + y2; svg_embedded_image(rect, xx1, yy1, xx2, yy2, rot, flip); } else { int ellipse_a = rect->ellipse_a; int ellipse_b = rect->ellipse_b; if(ellipse_a != -1 && ellipse_b != 360) { if(flip) { ellipse_a = 180 - ellipse_a - ellipse_b; } if(rot) { if(rot == 3) { ellipse_a += 90; } else if(rot == 2) { ellipse_a += 180; } else if(rot == 1) { ellipse_a += 270; } ellipse_a %= 360; } } svg_filledrect(c, x0+x1, y0+y1, x0+x2, y0+y2, dash, rect->fill, ellipse_a, ellipse_b); } } } draw_texts: if( xctx->sym_txt && !(xctx->inst[n].flags & HIDE_SYMBOL_TEXTS) && (layer == cadlayers)) { const char *txtptr; if(c != layer) c_for_text = c; else if(xctx->inst[n].flags & PIN_OR_LABEL) c_for_text = TEXTWIRELAYER; else c_for_text = TEXTLAYER; for(j=0;j< symptr->texts; ++j) { double xscale, yscale; get_sym_text_size(n, j, &xscale, &yscale); text = symptr->text[j]; /* if(xscale*FONTWIDTH* xctx->mooz<1) continue; */ if(!xctx->show_hidden_texts && (symptr->text[j].flags & (HIDE_TEXT | HIDE_TEXT_INSTANTIATED))) continue; if( hide && text.txt_ptr && strcmp(text.txt_ptr, "@symname") && strcmp(text.txt_ptr, "@name") ) continue; txtptr= translate(n, text.txt_ptr); ROTATION(rot, flip, 0.0,0.0,text.x0,text.y0,x1,y1); textlayer = c_for_text; /* do not allow custom text color on hilighted instances */ if(disabled == 1) textlayer = GRIDLAYER; else if(disabled == 2) textlayer = PINLAYER; else if( xctx->inst[n].color == -10000) { int lay; get_sym_text_layer(n, j, &lay); if(lay != -1) textlayer = lay; else textlayer = symptr->text[j].layer; } if(textlayer < 0 || textlayer >= cadlayers) textlayer = c_for_text; /* display PINLAYER colored instance texts even if PINLAYER disabled */ if(xctx->inst[n].color == PINLAYER || xctx->enable_layer[textlayer]) { my_snprintf(svg_font_family, S(svg_font_family), tclgetvar("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(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, xscale, 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, xscale, yscale); } } } } static void fill_svg_colors() { char s[200]; /* overflow safe 20161122 */ int i; unsigned int 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(tclgetboolvar("dark_colorscheme")) { if(i == 0) { svg_colors[i].red = 0; svg_colors[i].green = 0; svg_colors[i].blue = 0; } else { svg_colors[i].red = 255; svg_colors[i].green = 255; svg_colors[i].blue = 255; } } else { if(i == 0) { 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; static char lastdir[PATH_MAX] = ""; const char *r, *textfont; int *unused_layer; int color; Hilight_hashentry *entry; if(!lastdir[0]) my_strncpy(lastdir, pwd_dir, S(lastdir)); if(has_x && !xctx->plotfile[0]) { /* tclvareval("tk_getSaveFile -title {Select destination file} -initialfile {", * get_cell(xctx->sch[xctx->currsch], 0) , ".svg} -initialdir {", lastdir, "}", NULL); */ tclvareval("save_file_dialog {Select destination file} *.svg INITIALLOADDIR {", pwd_dir, "/", get_cell(xctx->sch[xctx->currsch], 0), ".svg}", NULL); r = tclresult(); if(r[0]) { my_strncpy(xctx->plotfile, r, S(xctx->plotfile)); tclvareval("file dirname {", xctx->plotfile, "}", NULL); my_strncpy(lastdir, tclresult(), S(lastdir)); } else return; } svg_restore_lw(); svg_colors=my_calloc(_ALLOC_ID_, cadlayers, sizeof(Svg_color)); if(svg_colors==NULL){ fprintf(errfp, "svg_draw(): calloc error\n"); return; } fill_svg_colors(); old_grid=tclgetboolvar("draw_grid"); tclsetvar("draw_grid", "0"); dx=xctx->xrect[0].width; dy=xctx->xrect[0].height; dbg(1, "svg_draw(): dx=%g dy=%g\n", dx, dy); if(xctx->plotfile[0]) { fd=fopen(xctx->plotfile, "w"); if(!fd) { dbg(0, "can not open file: %s\n", xctx->plotfile); return; } } else { fd=fopen("plot.svg", "w"); if(!fd) { dbg(0, "can not open file: %s\n", "plot.svg"); return; } } my_strncpy(xctx->plotfile,"", S(xctx->plotfile)); unused_layer = my_calloc(_ALLOC_ID_, cadlayers, sizeof(int)); #if 0 * /* Determine used layers. Disabled since we want hilight colors */ * for(c=0;ctexts; ++i) * { * textlayer = xctx->text[i].layer; * if(textlayer < 0 || textlayer >= cadlayers) textlayer = TEXTLAYER; * unused_layer[textlayer] = 0; * } * for(c=0;cinst[i].ptr + xctx->sym); * if(xctx->lines[c] || xctx->rects[c] || xctx->arcs[c] || xctx->polygons[c]) unused_layer[c] = 0; * if(xctx->wires) unused_layer[WIRELAYER] = 0; * for(i=0;iinstances; ++i) { * if( (c == PINLAYER || xctx->enable_layer[c]) && symptr->lines[c] ) unused_layer[c] = 0; * if( (c == PINLAYER || xctx->enable_layer[c]) && symptr->polygons[c] ) unused_layer[c] = 0; * if( (c == PINLAYER || xctx->enable_layer[c]) && symptr->arcs[c] ) unused_layer[c] = 0; * if( (c != PINLAYER || xctx->enable_layer[c]) && symptr->rects[c] ) unused_layer[c] = 0; * if( (c==TEXTWIRELAYER && (xctx->inst[i].flags & PIN_OR_LABEL) ) || * (xctx->sym_txt && (c==TEXTLAYER) && !(xctx->inst[i].flags & PIN_OR_LABEL) ) ) * { * int j; * for(j=0;j< symptr->texts; ++j) * { * textlayer = c; * if( !(xctx->inst[i].color == PINLAYER)) { * textlayer = symptr->text[j].layer; * if(textlayer < 0 || textlayer >= cadlayers) textlayer = c; * } * /* display PINLAYER colored instance texts even if PINLAYER disabled */ * if(xctx->inst[i].color == PINLAYER || xctx->enable_layer[textlayer]) { * used_layer[textlayer] = 0; * } * } * } * } * dbg(1, "used_layer[%d] = %d\n", c, used_layer[c]); * } * /* End determine used layer */ #endif fprintf(fd, "\n", dx, dy); fprintf(fd, "\n"); /* background */ fprintf(fd, "\n", 0.0, 0.0, dx, dy); svg_drawgrid(); /* do first graphs as these require draw() which clobbers xctx->inst[n].flags bit 0 */ for(c=0;crects[c]; ++i) { if(xctx->rect[c][i].flags & 1) { /* graph */ xRect *r = &xctx->rect[c][i]; svg_embedded_graph(fd, i, r->x1, r->y1, r->x2, r->y2); } } } for(c=0;clines[c]; ++i) svg_drawline(c, xctx->line[c][i].bus, xctx->line[c][i].x1, xctx->line[c][i].y1, xctx->line[c][i].x2, xctx->line[c][i].y2, xctx->line[c][i].dash); for(i=0;irects[c]; ++i) { if(c == GRIDLAYER && (xctx->rect[c][i].flags & 1) ) { /* graph */ /* do nothing, done above */ } else if(c == GRIDLAYER && (xctx->rect[c][i].flags & 1024) ) { /* image */ xRect *r = &xctx->rect[c][i]; svg_embedded_image(r, r->x1, r->y1, r->x2, r->y2, 0, 0); } else { svg_filledrect(c, xctx->rect[c][i].x1, xctx->rect[c][i].y1, xctx->rect[c][i].x2, xctx->rect[c][i].y2, xctx->rect[c][i].dash, xctx->rect[c][i].fill, xctx->rect[c][i].ellipse_a, xctx->rect[c][i].ellipse_b); } } for(i=0;iarcs[c]; ++i) { svg_drawarc(c, xctx->arc[c][i].fill, xctx->arc[c][i].x, xctx->arc[c][i].y, xctx->arc[c][i].r, xctx->arc[c][i].a, xctx->arc[c][i].b, xctx->arc[c][i].dash); } for(i=0;ipolygons[c]; ++i) { int bezier = !strboolcmp(get_tok_value(xctx->poly[c][i].prop_ptr, "bezier", 0), "true"); int bus = get_attr_val(get_tok_value(xctx->poly[c][i].prop_ptr, "bus", 0)); svg_drawpolygon(c, NOW, xctx->poly[c][i].x, xctx->poly[c][i].y, xctx->poly[c][i].points, xctx->poly[c][i].fill, xctx->poly[c][i].dash, bezier, bus); } for(i=0;iinstances; ++i) { svg_draw_symbol(c,i,c,0,0,0.0,0.0); if(c == cadlayers - 1) { svg_draw_symbol(c + 1 , i, c + 1, 0, 0, 0.0, 0.0); /* ... draw texts */ } } } prepare_netlist_structs(0); /* NEEDED: data was cleared by trim_wires() */ for(i=0;iwires; ++i) { color = WIRELAYER; if(xctx->hilight_nets && (entry=bus_hilight_hash_lookup( xctx->wire[i].node, 0, XLOOKUP))) { color = get_color(entry->value); } svg_drawline(color, xctx->wire[i].bus, xctx->wire[i].x1, xctx->wire[i].y1,xctx->wire[i].x2,xctx->wire[i].y2, 0); } { double x1, y1, x2, y2; Wireentry *wireptr; int i; Iterator_ctx ctx; update_conn_cues(WIRELAYER, 0, 0); /* draw connecting dots */ x1 = X_TO_XSCHEM(xctx->areax1); y1 = Y_TO_XSCHEM(xctx->areay1); x2 = X_TO_XSCHEM(xctx->areax2); y2 = Y_TO_XSCHEM(xctx->areay2); for(init_wire_iterator(&ctx, x1, y1, x2, y2); ( wireptr = wire_iterator_next(&ctx) ) ;) { i = wireptr->n; color = WIRELAYER; if(xctx->hilight_nets && (entry=bus_hilight_hash_lookup( xctx->wire[i].node, 0, XLOOKUP))) { color = get_color(entry->value); } if( xctx->wire[i].end1 >1 ) { svg_drawcircle(color, 1, xctx->wire[i].x1, xctx->wire[i].y1, xctx->cadhalfdotsize, 0, 360); } if( xctx->wire[i].end2 >1 ) { svg_drawcircle(color, 1, xctx->wire[i].x2, xctx->wire[i].y2, xctx->cadhalfdotsize, 0, 360); } } } for(i=0;itexts; ++i) { textlayer = xctx->text[i].layer; if(!xctx->show_hidden_texts && (xctx->text[i].flags & HIDE_TEXT)) continue; if(textlayer < 0 || textlayer >= cadlayers) textlayer = TEXTLAYER; my_snprintf(svg_font_family, S(svg_font_family), tclgetvar("svg_font_name")); my_snprintf(svg_font_style, S(svg_font_style), "normal"); my_snprintf(svg_font_weight, S(svg_font_weight), "normal"); textfont = xctx->text[i].font; if( (textfont && textfont[0])) { my_snprintf(svg_font_family, S(svg_font_family), textfont); } if( xctx->text[i].flags & TEXT_BOLD) my_snprintf(svg_font_weight, S(svg_font_weight), "bold"); if( xctx->text[i].flags & TEXT_ITALIC) my_snprintf(svg_font_style, S(svg_font_style), "italic"); if( xctx->text[i].flags & TEXT_OBLIQUE) my_snprintf(svg_font_style, S(svg_font_style), "oblique"); if(text_svg) svg_draw_string(textlayer, get_text_floater(i), xctx->text[i].rot, xctx->text[i].flip, xctx->text[i].hcenter, xctx->text[i].vcenter, xctx->text[i].x0,xctx->text[i].y0, xctx->text[i].xscale, xctx->text[i].yscale); else old_svg_draw_string(textlayer, get_text_floater(i), xctx->text[i].rot, xctx->text[i].flip, xctx->text[i].hcenter, xctx->text[i].vcenter, xctx->text[i].x0,xctx->text[i].y0, xctx->text[i].xscale, xctx->text[i].yscale); } dbg(1, "svg_draw(): INT_LINE_W(lw)=%d\n",INT_LINE_W(xctx->lw)); fprintf(fd, "\n"); fclose(fd); tclsetboolvar("draw_grid", old_grid); my_free(_ALLOC_ID_, &svg_colors); my_free(_ALLOC_ID_, &unused_layer); Tcl_SetResult(interp,"",TCL_STATIC); }