From bf183f0d2034db3ddd04d59e3d69c631a5f98d23 Mon Sep 17 00:00:00 2001 From: Stefan Schippers Date: Wed, 18 Nov 2020 18:29:14 +0100 Subject: [PATCH] Option (default now) to export svg images using the svg element. This makes generated SVGs much smaller and in most cases faster to render. --- src/draw.c | 27 +-- src/globals.c | 4 +- src/scheduler.c | 14 ++ src/svgdraw.c | 243 +++++++++++++++++--- src/xinit.c | 1 + src/xschem.h | 4 +- src/xschem.tcl | 4 +- src/xschemrc | 3 +- xschem_library/devices/ngspice_get_expr.sym | 4 +- xschem_library/examples/mos_power_ampli.sch | 26 +-- 10 files changed, 259 insertions(+), 71 deletions(-) diff --git a/src/draw.c b/src/draw.c index 3d2f877c..d93b983b 100644 --- a/src/draw.c +++ b/src/draw.c @@ -252,8 +252,8 @@ int set_text_custom_font(xText *txt) #ifdef HAS_CAIRO -void cairo_draw_string_line(cairo_t *cairo_ctx, char *s, - double x, double y, double size, int rot, int flip, +static void cairo_draw_string_line(cairo_t *cairo_ctx, char *s, + double x, double y, int rot, int flip, int lineno, double fontheight, double fontascent, double fontdescent, int llength) { double ix, iy; @@ -261,7 +261,6 @@ void cairo_draw_string_line(cairo_t *cairo_ctx, char *s, cairo_text_extents_t ext; int line_delta; int line_offset; - double xadvance; double lines; double vc; /* 20171121 vert correct */ /*int rx1, ry1, rx2, ry2, save_rot, save_flip; */ @@ -269,14 +268,10 @@ void cairo_draw_string_line(cairo_t *cairo_ctx, char *s, if(s==NULL) return; if(llength==0) return; cairo_text_extents(cairo_ctx, s, &ext); - xadvance = ext.x_advance > ext.width? ext.x_advance : ext.width; line_delta = lineno*fontheight*cairo_font_line_spacing; lines = (cairo_lines-1)*fontheight*cairo_font_line_spacing; - line_offset=cairo_longest_line-xadvance; - - /* 20171215 NO! will skip drawing of long strings */ - /* if(xadvance>14000) return; */ /* too big: close to short overflow */ + line_offset=cairo_longest_line; ix=X_TO_SCREEN(x); iy=Y_TO_SCREEN(y); @@ -287,13 +282,13 @@ void cairo_draw_string_line(cairo_t *cairo_ctx, char *s, vc = cairo_vert_correct*xctx->mooz; /* converted to device (pixel) space */ if( rot==0 && flip==0) {iy+=line_delta+fontascent-vc;} - else if(rot==1 && flip==0) {iy+=xadvance+line_offset;ix=ix-fontheight+fontascent+vc-lines+line_delta;} - else if(rot==2 && flip==0) {iy=iy-fontheight-lines+line_delta+fontascent+vc; ix=ix-xadvance-line_offset;} + else if(rot==1 && flip==0) {iy+=line_offset;ix=ix-fontheight+fontascent+vc-lines+line_delta;} + else if(rot==2 && flip==0) {iy=iy-fontheight-lines+line_delta+fontascent+vc; ix=ix-line_offset;} else if(rot==3 && flip==0) {ix+=line_delta+fontascent-vc;} - else if(rot==0 && flip==1) {ix=ix-xadvance-line_offset;iy+=line_delta+fontascent-vc;} + else if(rot==0 && flip==1) {ix=ix-line_offset;iy+=line_delta+fontascent-vc;} else if(rot==1 && flip==1) {ix=ix-fontheight+line_delta-lines+fontascent+vc;} else if(rot==2 && flip==1) {iy=iy-fontheight-lines+line_delta+fontascent+vc;} - else if(rot==3 && flip==1) {iy=iy+xadvance+line_offset;ix+=line_delta+fontascent-vc;} + else if(rot==3 && flip==1) {iy=iy+line_offset;ix+=line_delta+fontascent-vc;} cairo_save(cairo_ctx); cairo_translate(cairo_ctx, ix, iy); @@ -328,7 +323,7 @@ void draw_string(int layer, int what, const char *str, int rot, int flip, int hc if(!textclip(areax1,areay1,areax2,areay2,textx1,texty1,textx2,texty2)) { return; } - + if(hcenter) { if(rot == 0 && flip == 0 ) { x=textx1;} if(rot == 1 && flip == 0 ) { y=texty1;} @@ -362,6 +357,8 @@ void draw_string(int layer, int what, const char *str, int rot, int flip, int hc cairo_set_font_size (cairo_ctx, size*xctx->mooz); cairo_set_font_size (cairo_save_ctx, size*xctx->mooz); cairo_font_extents(cairo_ctx, &fext); + dbg(1, "draw_string(): size * mooz=%g height=%g ascent=%g descent=%g\n", + size * xctx->mooz, fext.height, fext.ascent, fext.descent); llength=0; my_strdup2(73, &sss, str); tt=ss=sss; @@ -370,9 +367,9 @@ void draw_string(int layer, int what, const char *str, int rot, int flip, int hc if(c=='\n' || c==0) { *ss='\0'; /*fprintf(errfp, "cairo_draw_string(): tt=%s, longest line: %d\n", tt, cairo_longest_line); */ - if(draw_window) cairo_draw_string_line(cairo_ctx, tt, x, y, size, rot, flip, + if(draw_window) cairo_draw_string_line(cairo_ctx, tt, x, y, rot, flip, lineno, fext.height, fext.ascent, fext.descent, llength); - if(draw_pixmap) cairo_draw_string_line(cairo_save_ctx, tt, x, y, size, rot, flip, + if(draw_pixmap) cairo_draw_string_line(cairo_save_ctx, tt, x, y, rot, flip, lineno, fext.height, fext.ascent, fext.descent, llength); lineno++; if(c==0) break; diff --git a/src/globals.c b/src/globals.c index b9cf2576..ad982a96 100644 --- a/src/globals.c +++ b/src/globals.c @@ -156,6 +156,7 @@ int fill=1; /* filled rectangles */ int draw_pixmap=1; /* use pixmap for double buffer */ int draw_window=0; int draw_grid=1; +int text_svg=1; /* use svg element for text instead of xschem's internal vector font */ double cadgrid = CADGRID; double cadhalfdotsize = CADHALFDOTSIZE; int change_lw=0; /* allow change xctx->lw */ @@ -232,7 +233,8 @@ int batch_mode = 0; /* no tcl console if set; batch mode */ int hide_symbols = 0; /* draw only a bounding box for component instances and @symname, @name texts */ int show_pin_net_names = 0; -char cairo_font_name[1024]="Monospace"; +char cairo_font_name[1024]="Sans Serif"; +char svg_font_name[1024]="Sans Serif"; int cairo_longest_line; int cairo_lines; double cairo_font_scale=1.0; /* default: 1.0, allows to adjust font size */ diff --git a/src/scheduler.c b/src/scheduler.c index 7e14f5a0..b4741d2d 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -1734,6 +1734,12 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg else Tcl_AppendResult(interp, "0",NULL); } + else if(!strcmp(argv[2],"text_svg")) { + if( text_svg != 0 ) + Tcl_AppendResult(interp, "1",NULL); + else + Tcl_AppendResult(interp, "0",NULL); + } else if(!strcmp(argv[2],"sym_txt")) { if( sym_txt != 0 ) Tcl_AppendResult(interp, "1",NULL); @@ -1897,6 +1903,11 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg else if(!strcmp(argv[1],"set") && argc==4) { + if(!strcmp(argv[2],"svg_font_name")) { + if( strlen(argv[3]) < sizeof(svg_font_name) ) { + my_strncpy(svg_font_name, argv[3], S(svg_font_name)); + } + } #ifdef HAS_CAIRO if(!strcmp(argv[2],"cairo_font_name")) { if( strlen(argv[3]) < sizeof(cairo_font_name) ) { @@ -2016,6 +2027,9 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg else if(!strcmp(argv[2],"draw_grid")) { draw_grid=atoi(argv[3]); } + else if(!strcmp(argv[2],"text_svg")) { + text_svg=atoi(argv[3]); + } else if(!strcmp(argv[2],"sym_txt")) { sym_txt=atoi(argv[3]); } diff --git a/src/svgdraw.c b/src/svgdraw.c index 19aa30cd..6751ae8d 100644 --- a/src/svgdraw.c +++ b/src/svgdraw.c @@ -34,13 +34,15 @@ typedef struct { } 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 Svg_color svg_stroke; static void svg_restore_lw(void) { - svg_linew = xctx->lw*0.7; + svg_linew = xctx->lw*1.2; } static void set_svg_colors(unsigned int pixel) @@ -99,6 +101,7 @@ static void svg_drawpolygon(int c, int what, double *x, double *y, int points, i fprintf(fd, "zoom, 1.4*dash/xctx->zoom); if(fill) { + /* if(fill_type[c] == 2) fprintf(fd, "fill-opacity=\"0.5\" "); */ fprintf(fd,"style=\"fill:#%02x%02x%02x;\" d=\"", svg_stroke.red, svg_stroke.green, svg_stroke.blue); } else { fprintf(fd,"style=\"fill:none;\" d=\""); @@ -146,7 +149,7 @@ static void svg_drawcircle(int gc, int fillarc, double x,double y,double r,doubl fprintf(fd, "\n", xx, yy, rr, svg_stroke.red, svg_stroke.green, svg_stroke.blue, - svg_stroke.red, svg_stroke.green, svg_stroke.blue, svg_linew/4); + svg_stroke.red, svg_stroke.green, svg_stroke.blue, svg_linew); } } @@ -213,15 +216,133 @@ static void svg_drawline(int gc, double linex1,double liney1,double linex2,doubl } } -/* TODO use regular svg text: - I qgv SVG - I love SVG - */ -static void svg_draw_string(int layer, const char *str, - int rot, int flip, int hcenter, int vcenter, - double x1,double y1, - double xscale, double yscale) +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,"", + col, svg_font_family, size*xctx->mooz, svg_font_weight, svg_font_style, ix, iy, rot1*90); + + 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.; + if(size*xctx->mooz<3.0) return; /* too small */ + if(size*xctx->mooz>1600) return; /* too big */ + + 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(areax1,areay1,areax2,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(73, &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(1157, &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; @@ -229,17 +350,17 @@ static void svg_draw_string(int layer, const char *str, if(str==NULL) return; #ifdef HAS_CAIRO - text_bbox_nocairo(str, xscale, yscale, rot, flip, hcenter, vcenter, x1,y1, &rx1,&ry1,&rx2,&ry2); + 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, x1,y1, &rx1,&ry1,&rx2,&ry2); + 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(areax1,areay1,areax2,areay2,rx1,ry1,rx2,ry2)) return; - x1=rx1;y1=ry1; - if(rot&1) {y1=ry2;rot=3;} + x=rx1;y=ry1; + if(rot&1) {y=ry2;rot=3;} else rot=0; - flip = 0; yy=y1; + flip = 0; yy=y; while(str[pos2]) { cc = (unsigned char)str[pos2++]; @@ -253,12 +374,12 @@ static void svg_draw_string(int layer, const char *str, a = pos*(FONTWIDTH+FONTWHITESPACE); for(i=0;iinst[n].ptr == -1) return; if( (layer != PINLAYER && !enable_layer[layer]) ) return; @@ -340,6 +463,7 @@ static void svg_draw_symbol(int n,int layer,int tmp_flip, int rot, 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]; @@ -404,11 +528,34 @@ static void svg_draw_symbol(int n,int layer,int tmp_flip, int rot, 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]) { - 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); + 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); } } } @@ -453,11 +600,10 @@ void svg_draw(void) { double dx, dy; int c,i, textlayer; - int filledrect; int old_grid; int modified_save; char *tmpstring=NULL; - const char *r; + const char *r, *textfont; if(!plotfile[0]) { @@ -500,14 +646,14 @@ void svg_draw(void) fprintf(fd, "