1
/*----------------------------------------------------------------------*/
3
/* Copyright (c) 2009 Tim Edwards, Open Circuit Design */
4
/*----------------------------------------------------------------------*/
22
#include <X11/Intrinsic.h>
23
#include <X11/StringDefs.h>
26
/*----------------------------------------------------------------------*/
28
/*----------------------------------------------------------------------*/
34
#include "colordefs.h"
37
/*----------------------------------------------------------------------*/
38
/* Function prototype declarations */
39
/*----------------------------------------------------------------------*/
40
#include "prototypes.h"
42
void SVGDrawString(labelptr, int, objinstptr);
44
/*----------------------------------------------------------------------*/
45
/* External Variable definitions */
46
/*----------------------------------------------------------------------*/
49
extern Pixmap STIPPLE[8];
50
extern XCWindowData *areawin;
51
extern Globaldata xobjs;
52
extern int *appcolors;
53
extern colorindex *colorlist;
54
extern int number_colors;
55
extern fontinfo *fonts;
56
extern short fontcount;
58
/*----------------------------------------------------------------------*/
59
/* The output file is a global variable used by all routines. */
60
/*----------------------------------------------------------------------*/
64
/*----------------------------------------------------------------------*/
65
/*----------------------------------------------------------------------*/
67
void svg_printcolor(int passcolor, char *prefix)
70
if (passcolor != DEFAULTCOLOR) {
71
for (i = 0; i < number_colors; i++) {
72
if (colorlist[i].color.pixel == passcolor) break;
74
if (i < number_colors) {
75
fprintf(svgf, "%s\"#%02x%02x%02x\" ",
77
(colorlist[i].color.red >> 8),
78
(colorlist[i].color.green >> 8),
79
(colorlist[i].color.blue >> 8));
84
/*----------------------------------------------------------------------*/
85
/* Since we can't do stipples in SVG, and since the whole stipple thing */
86
/* was only put in because we can't (easily) do transparency effects */
87
/* in X11, we convert stipples to transparency when the stipple is a */
88
/* mask, and do a color blend with white when the stipple is opaque. */
90
/* Blend amount is 1 (almost original color) to 7 (almost white) */
91
/*----------------------------------------------------------------------*/
93
void svg_blendcolor(int passcolor, char *prefix, int amount)
95
int i, bred, bgreen, bblue;
97
if (passcolor != DEFAULTCOLOR) {
98
for (i = 0; i < number_colors; i++) {
99
if (colorlist[i].color.pixel == passcolor) break;
101
if (i < number_colors) {
102
bred = colorlist[i].color.red >> 8;
103
bgreen = colorlist[i].color.green >> 8;
104
bblue = colorlist[i].color.blue >> 8;
108
bred = bgreen = bblue = 0;
110
bred = ((bred * amount) + (255 * (8 - amount))) >> 3;
111
bgreen = ((bgreen * amount) + (255 * (8 - amount))) >> 3;
112
bblue = ((bblue * amount) + (255 * (8 - amount))) >> 3;
114
fprintf(svgf, "%s\"#%02x%02x%02x\" ", prefix, bred, bgreen, bblue);
117
/*----------------------------------------------------------------------*/
118
/* Fill and/or draw a border around an element */
119
/*----------------------------------------------------------------------*/
121
void svg_stroke(int passcolor, short style, float width)
124
short minwidth, solidpart, shade;
126
tmpwidth = UTopTransScale(xobjs.pagelist[areawin->page]->wirewidth * width);
127
minwidth = max(1, (short)tmpwidth);
129
if (style & FILLED || (!(style & FILLED) && style & OPAQUE)) {
130
if ((style & FILLSOLID) == FILLSOLID) {
131
svg_printcolor(passcolor, "fill=");
133
else if (!(style & FILLED)) {
134
fprintf(svgf, "fill=\"white\" ");
137
shade = 1 + ((style & FILLSOLID) >> 5);
138
if (style & OPAQUE) {
139
svg_blendcolor(passcolor, "fill=", shade);
142
svg_printcolor(passcolor, "fill=");
143
fprintf(svgf, "fill-opacity=\"%g\" ", (float)shade / 8);
148
fprintf(svgf, "fill=\"none\" ");
150
if (!(style & NOBORDER)) {
151
/* set up dots or dashes */
152
if (style & DASHED) solidpart = 4 * minwidth;
153
else if (style & DOTTED) solidpart = minwidth;
154
if (style & (DASHED | DOTTED)) {
155
fprintf(svgf, "style=\"stroke-dasharray:%d,%d\" ", solidpart, 4 * minwidth);
157
fprintf(svgf, "stroke-width=\"%g\" ", tmpwidth);
158
fprintf(svgf, "stroke-linecap=\"butt\" ");
159
if (style & SQUARECAP)
160
fprintf(svgf, "stroke-linejoin=\"miter\" ");
162
fprintf(svgf, "stroke-linejoin=\"bevel\" ");
165
fprintf(svgf, "stroke-width=\"%g\" ", tmpwidth);
166
if (style & SQUARECAP) {
167
fprintf(svgf, "stroke-linejoin=\"miter\" ");
168
fprintf(svgf, "stroke-linecap=\"projecting\" ");
171
fprintf(svgf, "stroke-linejoin=\"bevel\" ");
172
fprintf(svgf, "stroke-linecap=\"round\" ");
175
svg_printcolor(passcolor, "stroke=");
178
fprintf(svgf, "stroke=\"none\" ");
179
fprintf(svgf, "/>\n");
182
/*----------------------------------------------------------------------*/
183
/* Finish a path and fill and/or stroke */
184
/*----------------------------------------------------------------------*/
186
void svg_strokepath(int passcolor, short style, float width)
188
/* Finish the path, closing if necessary */
189
if (!(style & UNCLOSED))
190
fprintf(svgf, "z\" ");
192
fprintf(svgf, "\" ");
194
svg_stroke(passcolor, style, width);
197
/*-------------------------------------------------------------------------*/
199
void SVGCreateImages(int page)
209
char *fname, outname[128], *pptr;
214
/* Check which images are used on this page */
215
glist = (short *)malloc(xobjs.images * sizeof(short));
216
for (i = 0; i < xobjs.images; i++) glist[i] = 0;
217
count_graphics(xobjs.pagelist[page]->pageinst->thisobject, glist);
219
for (i = 0; i < xobjs.images; i++) {
220
if (glist[i] == 0) continue;
221
img = xobjs.imagelist + i;
223
/* Generate a PPM file, then convert it to PNG */
225
fname = tmpnam(NULL);
226
ppf = fopen(fname, "w");
228
fprintf(ppf, "P6 %d %d 255\n", img->image->width, img->image->height);
229
for (y = 0; y < img->image->height; y++) {
230
for (x = 0; x < img->image->width; x++) {
231
pixel.i = XGetPixel(img->image, x, y);
232
fwrite(&pixel.b[2], 1, 1, ppf);
233
fwrite(&pixel.b[1], 1, 1, ppf);
234
fwrite(&pixel.b[0], 1, 1, ppf);
240
/* Run "convert" to make this into a png file */
242
strcpy(outname, img->filename);
243
if ((pptr = strrchr(outname, '.')) != NULL)
244
strcpy(pptr, ".png");
246
strcat(outname, ".png");
249
if ((pid = vfork()) == 0) {
250
execlp("convert", "convert", fname, outname, NULL);
251
exit(0); /* not reached */
253
waitpid(pid, NULL, 0);
256
_spawnl(_P_WAIT, GM_EXEC, GM_EXEC, "convert", fname, outname, NULL);
260
Fprintf(stdout, "Generated standalone PNG image file %s\n", outname);
265
/*-------------------------------------------------------------------------*/
267
void SVGDrawGraphic(graphicptr gp)
272
char outname[128], *pptr;
276
for (i = 0; i < xobjs.images; i++) {
277
img = xobjs.imagelist + i;
278
if (img->image == gp->source)
281
if (i == xobjs.images) return;
283
strcpy(outname, img->filename);
284
if ((pptr = strrchr(outname, '.')) != NULL)
285
strcpy(pptr, ".png");
287
strcat(outname, ".png");
290
UPreMultCTM(DCTM, gp->position, gp->scale, gp->rotation);
291
corner.x = -(gp->source->width >> 1);
292
corner.y = (gp->source->height >> 1);
293
UTransformbyCTM(DCTM, &corner, &ppt, 1);
296
tscale = gp->scale * UTopScale();
297
rotation = gp->rotation + UTopRotation();
298
if (rotation >= 360) rotation -= 360;
299
else if (rotation < 0) rotation += 360;
301
fprintf(svgf, "<image transform=\"translate(%d,%d) scale(%g) rotate(%d)\"\n",
302
ppt.x, ppt.y, tscale, rotation);
303
fprintf(svgf, " width=\"%dpx\" height=\"%dpx\"",
304
gp->source->width, gp->source->height);
305
fprintf(svgf, " xlink:href=\"%s\">\n", outname);
306
fprintf(svgf, "</image>\n");
309
/*-------------------------------------------------------------------------*/
311
void SVGDrawSpline(splineptr thespline, int passcolor)
315
UTransformbyCTM(DCTM, thespline->ctrl, tmppoints, 4);
317
fprintf(svgf, "<path d=\"M%d,%d C%d,%d %d,%d %d,%d ",
318
tmppoints[0].x, tmppoints[0].y,
319
tmppoints[1].x, tmppoints[1].y,
320
tmppoints[2].x, tmppoints[2].y,
321
tmppoints[3].x, tmppoints[3].y);
322
svg_strokepath(passcolor, thespline->style, thespline->width);
325
/*-------------------------------------------------------------------------*/
327
void SVGDrawPolygon(polyptr thepoly, int passcolor)
330
XPoint *tmppoints = (pointlist) malloc(thepoly->number * sizeof(XPoint));
332
UTransformbyCTM(DCTM, thepoly->points, tmppoints, thepoly->number);
334
fprintf(svgf, "<path ");
335
if (thepoly->style & BBOX) fprintf(svgf, "visibility=\"hidden\" ");
336
fprintf(svgf, "d=\"M%d,%d L", tmppoints[0].x, tmppoints[0].y);
337
for (i = 1; i < thepoly->number; i++) {
338
fprintf(svgf, "%d,%d ", tmppoints[i].x, tmppoints[i].y);
341
svg_strokepath(passcolor, thepoly->style, thepoly->width);
345
/*-------------------------------------------------------------------------*/
347
void SVGDrawArc(arcptr thearc, int passcolor)
353
radius[0] = UTopTransScale(thearc->radius);
354
radius[1] = UTopTransScale(thearc->yaxis);
356
tarc = (thearc->angle2 - thearc->angle1);
358
UTransformbyCTM(DCTM, &(thearc->position), endpoints, 1);
359
fprintf(svgf, "<ellipse cx=\"%d\" cy=\"%d\" rx=\"%d\" ry=\"%d\" ",
360
endpoints[0].x, endpoints[0].y, radius[0], radius[1]);
361
svg_stroke(passcolor, thearc->style, thearc->width);
364
UfTransformbyCTM(DCTM, thearc->points, endpoints, 1);
365
UfTransformbyCTM(DCTM, thearc->points + thearc->number - 1, endpoints + 1, 1);
367
/* When any arc is flipped, the direction of travel reverses. */
368
fprintf(svgf, "<path d=\"M%d,%d A%d,%d 0 %d,%d %d,%d ",
369
endpoints[0].x, endpoints[0].y,
370
radius[0], radius[1],
371
((tarc > 180) ? 1 : 0),
372
(((DCTM->a * DCTM->e) >= 0) ? 1 : 0),
373
endpoints[1].x, endpoints[1].y);
374
svg_strokepath(passcolor, thearc->style, thearc->width);
378
/*-------------------------------------------------------------------------*/
380
void SVGDrawPath(pathptr thepath, int passcolor)
382
XPoint *tmppoints = (pointlist) malloc(sizeof(XPoint));
388
fprintf(svgf, "<path d=\"");
390
for (genpath = thepath->plist; genpath < thepath->plist + thepath->parts;
392
switch(ELEMENTTYPE(*genpath)) {
394
thepoly = TOPOLY(genpath);
395
tmppoints = (pointlist) realloc(tmppoints, thepoly->number * sizeof(XPoint));
396
UTransformbyCTM(DCTM, thepoly->points, tmppoints, thepoly->number);
398
fprintf(svgf, "M%d,%d ", tmppoints[0].x, tmppoints[0].y);
402
for (i = 1; i < thepoly->number; i++) {
403
fprintf(svgf, "%d,%d ", tmppoints[i].x, tmppoints[i].y);
407
thespline = TOSPLINE(genpath);
408
tmppoints = (pointlist) realloc(tmppoints, 4 * sizeof(XPoint));
409
UTransformbyCTM(DCTM, thespline->ctrl, tmppoints, 4);
411
fprintf(svgf, "M%d,%d ", tmppoints[0].x, tmppoints[0].y);
414
fprintf(svgf, "C%d,%d %d,%d %d,%d ",
415
tmppoints[1].x, tmppoints[1].y,
416
tmppoints[2].x, tmppoints[2].y,
417
tmppoints[3].x, tmppoints[3].y);
421
svg_strokepath(passcolor, thepath->style, thepath->width);
425
/*----------------------------------------------------------------------*/
426
/* Main recursive object instance drawing routine. */
427
/* context is the instance information passed down from above */
428
/* theinstance is the object instance to be drawn */
429
/* level is the level of recursion */
430
/* passcolor is the inherited color value passed to object */
431
/*----------------------------------------------------------------------*/
433
void SVGDrawObject(objinstptr theinstance, short level, int passcolor, pushlistptr *stack)
437
int defaultcolor = passcolor;
438
int curcolor = passcolor;
440
objectptr theobject = theinstance->thisobject;
442
/* All parts are given in the coordinate system of the object, unless */
443
/* this is the top-level object, in which they will be interpreted as */
444
/* relative to the screen. */
448
if (stack) push_stack(stack, theinstance, NULL);
450
UPreMultCTM(DCTM, theinstance->position, theinstance->scale,
451
theinstance->rotation);
453
/* make parameter substitutions */
454
psubstitute(theinstance);
456
/* draw all of the elements */
458
tmpwidth = UTopTransScale(xobjs.pagelist[areawin->page]->wirewidth);
460
/* Here---set a default style using "g" like PostScript "gsave" */
461
/* stroke-width = tmpwidth, stroke = passcolor */
463
/* guard against plist being regenerated during a redraw by the */
464
/* expression parameter mechanism (should that be prohibited?) */
466
for (thispart = 0; thispart < theobject->parts; thispart++) {
467
areagen = theobject->plist + thispart;
468
if ((*areagen)->type & DRAW_HIDE) continue;
470
if (defaultcolor != DOFORALL) {
471
if ((*areagen)->color != curcolor) {
472
if ((*areagen)->color == DEFAULTCOLOR)
473
curcolor = defaultcolor;
475
curcolor = (*areagen)->color;
479
switch(ELEMENTTYPE(*areagen)) {
481
if (level == 0 || !((TOPOLY(areagen))->style & BBOX))
482
SVGDrawPolygon(TOPOLY(areagen), curcolor);
486
SVGDrawSpline(TOSPLINE(areagen), curcolor);
490
SVGDrawArc(TOARC(areagen), curcolor);
494
SVGDrawPath(TOPATH(areagen), curcolor);
498
SVGDrawGraphic(TOGRAPHIC(areagen));
502
if (areawin->editinplace && stack && (TOOBJINST(areagen)
503
== areawin->topinstance)) {
504
/* If stack matches areawin->stack, then don't */
505
/* draw because it would be redundant. */
506
pushlistptr alist = *stack, blist = areawin->stack;
507
while (alist && blist) {
508
if (alist->thisinst != blist->thisinst) break;
512
if ((!alist) || (!blist)) break;
514
SVGDrawObject(TOOBJINST(areagen), level + 1, curcolor, stack);
518
if (level == 0 || TOLABEL(areagen)->pin == False ||
519
(TOLABEL(areagen)->justify & PINVISIBLE))
520
SVGDrawString(TOLABEL(areagen), curcolor, theinstance);
526
if (stack) pop_stack(stack);
529
/*----------------------------------------------------------------------*/
531
#define addlinepoint(pointlist, numvals, xval, yval) \
532
{ if (!numvals) pointlist = (XPoint *)malloc(sizeof(XPoint)); \
533
else pointlist = (XPoint *)realloc(pointlist, (numvals + 1) * sizeof(XPoint)); \
534
pointlist[numvals].x = xval; \
535
pointlist[numvals++].y = -yval; \
538
/*----------------------------------------------------------------------*/
539
/* Draw an entire string, including parameter substitutions */
540
/*----------------------------------------------------------------------*/
542
void SVGDrawString(labelptr drawlabel, int passcolor, objinstptr localinst)
546
short fstyle, ffont, tmpjust, baseline, deltay;
547
int pos, defaultcolor, curcolor, scolor;
548
short oldx, oldfont, oldstyle;
550
float tmpscale = 1.0, natscale = 1.0;
553
short *tabstops = NULL;
554
short tabno, numtabs = 0, group = 0;
555
int open_text, open_span, open_decor;
556
XPoint *decorations = NULL;
559
char *symbol_html_encoding[] = {
560
" ", "!", "∀", "#", "∃", "%", "&", "?", "(", ")",
561
"*", "+", ",", "−", ".", "/", "0", "1", "2", "3", "4",
562
"5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "≅",
563
"Α", "Β", "Χ", "Δ", "Ε", "Φ",
564
"Γ", "Η", "Ι", "ϑ", "Κ", "Λ",
565
"Μ", "Ν", "Ο", "Π", "Θ", "Ρ",
566
"Σ", "Τ", "Υ", "σ", "Ω", "Ξ",
567
"Ψ", "Ζ", "[", "∴", "]", "⊥", "_",
568
"‾", "α", "β", "χ", "δ", "ε",
569
"φ", "γ", "η", "ι", "φ", "κ",
570
"λ", "μ", "ν", "ο", "π", "θ",
571
"ρ", "σ", "τ", "υ", "ω", "ω",
572
"ξ", "ψ", "ζ", "{", "|", "}", "~", "", "", "",
573
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
574
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
575
"ϒ", "′", "≤", "⁄", "∞", "ƒ",
576
"♣", "♦", "♥", "♠", "↔",
577
"←", "↑", "→", "↓", "°", "±",
578
"″", "≥", "×", "∝", "∂", "•",
579
"÷", "≠", "≡", "≅", "…"
582
/* Standard encoding vector, in HTML, from character 161 to 255 */
583
u_int standard_html_encoding[] = {
584
161, 162, 163, 8725, 165, 131, 167, 164, 146, 147, 171, 8249,
585
8250, 64256, 64258, 0, 8211, 8224, 8225, 183, 0, 182, 8226,
586
8218, 8222, 8221, 187, 8230, 8240, 0, 191, 0, 96, 180, 710,
587
126, 713, 728, 729, 168, 0, 730, 184, 0, 733, 731, 711, 8212,
588
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 508, 0,
589
170, 0, 0, 0, 0, 321, 216, 338, 186, 0, 0, 0, 0, 0, 230, 0,
590
0, 0, 185, 0, 0, 322, 248, 339, 223};
592
if (fontcount == 0) return;
594
/* Don't draw temporary labels from schematic capture system */
595
if (drawlabel->string->type != FONT_NAME) return;
597
if (passcolor == DOSUBSTRING)
598
defaultcolor = curcolor = drawlabel->color;
600
defaultcolor = curcolor = passcolor;
602
if (defaultcolor != DOFORALL) {
603
if (drawlabel->color != DEFAULTCOLOR)
604
curcolor = drawlabel->color;
606
curcolor = defaultcolor;
609
/* calculate the transformation matrix for this object */
610
/* in natural units of the alphabet vectors */
611
/* (conversion to window units) */
613
/* Labels don't rotate in Firefox, so use <g> record for transform */
616
UPreMultCTM(DCTM, drawlabel->position, drawlabel->scale, drawlabel->rotation);
618
/* check for flip invariance; recompute CTM and justification if necessary */
619
tmpjust = flipadjust(drawlabel->justify);
621
/* Note that the Y-scale is inverted or text comes out upside-down. But we */
622
/* need to adjust to the Y baseline. */
624
fprintf(svgf, "<g transform=\"matrix(%4g %4g %4g %4g %3g %3g)\" ",
625
DCTM->a, DCTM->d, -(DCTM->b), -(DCTM->e), DCTM->c, DCTM->f);
627
svg_printcolor(passcolor, "fill=");
628
fprintf(svgf, ">\n");
630
/* "natural" (unscaled) length */
631
tmpext = ULength(drawlabel, localinst, 0, NULL);
633
newpoint.x = (tmpjust & NOTLEFT ?
634
(tmpjust & RIGHT ? -tmpext.width : -tmpext.width >> 1) : 0);
635
newpoint.y = (tmpjust & NOTBOTTOM ?
636
(tmpjust & TOP ? -tmpext.ascent : -(tmpext.ascent + tmpext.base) >> 1)
639
/* Pinlabels have an additional offset spacing to pad */
640
/* them from the circuit point to which they attach. */
642
if (drawlabel->pin) {
643
pinadjust(tmpjust, &(newpoint.x), &(newpoint.y), 1);
647
baseline = newpoint.y;
653
for (strptr = drawlabel->string; strptr != NULL;
654
strptr = nextstringpart(strptr, localinst)) {
656
/* All segments other than text cancel any */
657
/* existing overline/underline in effect. */
659
if (strptr->type != TEXT_STRING)
662
switch(strptr->type) {
664
while (open_span > 0) {
665
fprintf(svgf, "</tspan>");
668
while (open_text > 0) {
669
fprintf(svgf, "</text>");
673
addlinepoint(decorations, nvals, newpoint.x, group);
680
while (open_span > 0) {
681
fprintf(svgf, "</tspan>");
684
while (open_text > 0) {
685
fprintf(svgf, "</text>");
689
addlinepoint(decorations, nvals, newpoint.x, group);
706
while (open_span > 1) {
707
fprintf(svgf, "</tspan>");
711
addlinepoint(decorations, nvals, newpoint.x, group);
716
/* These do not need to be handled */
722
/* These are not handled yet, but should be */
730
/* deal with each text segment type */
732
switch(strptr->type) {
735
if (strptr->data.font < fontcount) {
736
ffont = strptr->data.font;
737
fstyle = 0; /* style reset by font change */
738
if (baseline == newpoint.y) { /* set top-level font and style */
743
fprintf(svgf, "<text stroke=\"none\" ");
744
fprintf(svgf, "font-family=");
745
if (issymbolfont(ffont))
746
fprintf(svgf, "\"Times\" ");
747
else if (!strncmp(fonts[ffont].family, "Times", 5))
748
fprintf(svgf, "\"Times\" ");
750
fprintf(svgf, "\"%s\" ", fonts[ffont].family);
752
if (fonts[ffont].flags & 0x1)
753
fprintf(svgf, " font-weight=\"bold\" ");
754
if (fonts[ffont].flags & 0x2) {
755
if (issansfont(ffont))
756
fprintf(svgf, " font-style=\"oblique\" ");
758
fprintf(svgf, " font-style=\"italic\" ");
760
olinerise = (issansfont(ffont)) ? 7 : 4;
762
if (strptr->type == FONT_SCALE) {
763
tmpscale = natscale * strptr->data.scale;
764
if (baseline == newpoint.y) /* reset top-level scale */
770
/* Actual scale taken care of by transformation matrix */
771
fprintf(svgf, "font-size=\"%g\" >", tmpscale * 40);
772
fprintf(svgf, "<tspan x=\"%d\" y=\"%d\">", newpoint.x, -newpoint.y);
778
newpoint.x += strptr->data.kern[0];
779
newpoint.y += strptr->data.kern[1];
780
fprintf(svgf, "<text dx=\"%d\" dy=\"%d\">",
781
strptr->data.kern[0], strptr->data.kern[1]);
786
if (defaultcolor != DOFORALL) {
787
if (strptr->data.color != DEFAULTCOLOR)
788
curcolor = colorlist[strptr->data.color].color.pixel;
790
curcolor = DEFAULTCOLOR;
795
case TABBACKWARD: /* find first tab value with x < xtotal */
796
for (tabno = numtabs - 1; tabno >= 0; tabno--) {
797
if (tabstops[tabno] < newpoint.x) {
798
newpoint.x = tabstops[tabno];
802
fprintf(svgf, "<tspan x=\"%d\">", newpoint.x);
806
case TABFORWARD: /* find first tab value with x > xtotal */
807
for (tabno = 0; tabno < numtabs; tabno++) {
808
if (tabstops[tabno] > newpoint.x) {
809
newpoint.x = tabstops[tabno];
813
fprintf(svgf, "<tspan x=\"%d\">", newpoint.x);
819
if (tabstops == NULL) tabstops = (short *)malloc(sizeof(short));
820
else tabstops = (short *)realloc(tabstops, numtabs * sizeof(short));
821
tabstops[numtabs - 1] = newpoint.x;
822
/* Force a tab at this point so that the output aligns */
823
/* to our computation of the position, not its own. */
824
fprintf(svgf, "<tspan x=\"%d\">", newpoint.x);
829
tmpscale = natscale = 1.0;
830
baseline -= BASELINE;
831
newpoint.y = baseline;
833
fprintf(svgf, "<tspan x=\"%d\" y=\"%d\">", newpoint.x, -newpoint.y);
838
natscale *= SUBSCALE;
840
deltay = (short)((TEXTHEIGHT >> 1) * natscale);
841
newpoint.y -= deltay;
842
fprintf(svgf, "<tspan dy=\"%d\" font-size=\"%g\">", deltay,
848
natscale *= SUBSCALE;
850
deltay = (short)(TEXTHEIGHT * natscale);
851
newpoint.y += deltay;
852
fprintf(svgf, "<tspan dy=\"%d\" font-size=\"%g\">", -deltay,
858
tmpscale = natscale = 1.0;
859
ffont = oldfont; /* revert to top-level font and style */
861
newpoint.y = baseline;
862
fprintf(svgf, "<tspan y=\"%d\">", baseline);
868
group = newpoint.y - 6;
869
addlinepoint(decorations, nvals, newpoint.x, group);
874
if (strptr->nextpart != NULL && strptr->nextpart->type == TEXT_STRING) {
879
for (textptr = strptr->nextpart->data.string;
880
textptr && *textptr != '\0'; textptr++) {
881
charptr = fonts[ffont].encoding[*(u_char *)textptr];
882
tmpheight = (int)((float)charptr->bbox.height
883
* fonts[ffont].scale);
884
if (group < tmpheight) group = (short)tmpheight;
887
group += olinerise + newpoint.y;
888
addlinepoint(decorations, nvals, newpoint.x, group);
896
case HALFSPACE: case QTRSPACE: {
898
objectptr drawchar = fonts[ffont].encoding[(u_char)32];
899
addx = (drawchar->bbox.lowerleft.x + drawchar->bbox.width) *
901
addx >>= ((strptr->type == HALFSPACE) ? 1 : 2);
903
fprintf(svgf, "<tspan dx=\"%d\">", addx);
909
textptr = strptr->data.string;
911
if (issymbolfont(ffont)) {
912
for (; *textptr != '\0'; textptr++)
913
if (((u_char)(*textptr) >= 32) && ((u_char)(*textptr) < 158))
914
fprintf(svgf, "%s", symbol_html_encoding[(*textptr) - 32]);
917
/* Handle "&" and non-ASCII characters in the text */
918
if (isisolatin1(ffont)) {
919
for (; *textptr != '\0'; textptr++) {
921
fprintf(svgf, "&");
922
else if ((u_char)(*textptr) >= 128)
923
fprintf(svgf, "&#%d;", (int)((u_char)*textptr));
924
else if ((u_char)(*textptr) >= 32)
925
fprintf(svgf, "%c", *textptr);
929
for (; *textptr != '\0'; textptr++) {
931
fprintf(svgf, "&");
932
else if ((u_char)(*textptr) >= 161)
933
fprintf(svgf, "&#%d;",
934
standard_html_encoding[(u_char)(*textptr)
936
else if ((u_char)(*textptr) >= 32 && (u_char)(*textptr) < 161)
937
fprintf(svgf, "%c", *textptr);
943
/* Compute the new X position */
945
for (textptr = strptr->data.string; *textptr != '\0'; textptr++) {
946
objectptr drawchar = fonts[ffont].encoding[(u_char)(*textptr)];
947
short addx = (drawchar->bbox.lowerleft.x + drawchar->bbox.width) *
955
while (open_span > 0) {
956
fprintf(svgf, "</tspan>");
959
while (open_text > 0) {
960
fprintf(svgf, "</text>");
963
fprintf(svgf, "\n</text>");
967
if (tabstops != NULL) free(tabstops);
969
/* If there were decorations (underlines, overlines), generate them */
971
if (decorations != NULL) {
974
addlinepoint(decorations, nvals, newpoint.x, group);
976
for (i = 0; i < nvals; i += 2) {
977
fprintf(svgf, "\n<line stroke-width=\"2\" stroke-linecap=\"square\" "
978
"x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" />",
979
decorations[i].x, decorations[i].y, decorations[i + 1].x,
980
decorations[i + 1].y);
984
fprintf(svgf, "</g>\n");
987
/*----------------------------------------------------------------------*/
988
/* Write the SVG file output */
989
/*----------------------------------------------------------------------*/
991
#define PMARGIN 6 /* Pixel margin around drawing */
994
OutputSVG(char *filename, Boolean fullscale)
999
float outwidth, outheight, cscale;
1001
svgf = fopen(filename, "w");
1003
Fprintf(stderr, "Cannot open file %s for writing.\n", filename);
1007
/* Generate external image files, if necessary */
1008
SVGCreateImages(areawin->page);
1010
/* Save the number of selections and set it to zero while we do the */
1011
/* object drawing. */
1013
savesel = areawin->selects;
1014
areawin->selects = 0;
1015
pinst = xobjs.pagelist[areawin->page]->pageinst;
1017
UPushCTM(); /* Save the top-level graphics state */
1019
/* This is like UMakeWCTM()---it inverts the whole picture so that */
1020
/* The origin is at the top left, and all data points fit in a box */
1021
/* at (0, 0) to the object (width, height) */
1025
DCTM->c = -pinst->bbox.lowerleft.x;
1028
DCTM->f = pinst->bbox.lowerleft.y + pinst->bbox.height;
1030
fprintf(svgf, "<svg xmlns=\"http://www.w3.org/2000/svg\"\n");
1031
fprintf(svgf, " xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
1032
fprintf(svgf, " version=\"1.1\"\n");
1033
fprintf(svgf, " id=\"%s\" ", pinst->thisobject->name);
1036
fprintf(svgf, "width=\"100%%\" height=\"100%%\" ");
1039
cscale = getpsscale(xobjs.pagelist[areawin->page]->outscale, areawin->page);
1040
cstyle = xobjs.pagelist[areawin->page]->coordstyle;
1042
outwidth = toplevelwidth(pinst, NULL) * cscale;
1043
outwidth /= (cstyle == CM) ? IN_CM_CONVERT : 72.0;
1044
outheight = toplevelheight(pinst, NULL) * cscale;
1045
outheight /= (cstyle == CM) ? IN_CM_CONVERT : 72.0;
1047
/* Set display height to that specified in the output properties (in inches) */
1048
fprintf(svgf, "width=\"%.3g%s\" height=\"%.3g%s\" ",
1049
outwidth, (cstyle == CM) ? "cm" : "in",
1050
outheight, (cstyle == CM) ? "cm" : "in");
1052
fprintf(svgf, " viewBox=\"%d %d %d %d\">\n",
1053
-PMARGIN, -PMARGIN, pinst->bbox.width + PMARGIN,
1054
pinst->bbox.height + PMARGIN);
1056
fprintf(svgf, "<desc>\n");
1057
fprintf(svgf, "XCircuit Version %2.1f\n", PROG_VERSION);
1058
fprintf(svgf, "File \"%s\" Page %d\n", xobjs.pagelist[areawin->page]->filename,
1060
fprintf(svgf, "</desc>\n");
1062
/* Set default color to black */
1063
fprintf(svgf, "<g stroke=\"black\">\n");
1065
if (areawin->hierstack) free_stack(&areawin->hierstack);
1066
SVGDrawObject(areawin->topinstance, TOPLEVEL, FOREGROUND, &areawin->hierstack);
1067
if (areawin->hierstack) free_stack(&areawin->hierstack);
1069
/* restore the selection list (if any) */
1070
areawin->selects = savesel;
1072
fprintf(svgf, "</g>\n</svg>\n");
1075
UPopCTM(); /* Restore the top-level graphics state */
1080
/*----------------------------------------------------------------------*/
1081
/* The TCL command-line for the SVG file write routine. */
1082
/*----------------------------------------------------------------------*/
1084
int xctcl_svg(ClientData clientData, Tcl_Interp *interp,
1085
int objc, Tcl_Obj *CONST objv[])
1087
char filename[128], *pptr;
1088
Boolean fullscale = 0;
1092
/* Argument "-full" forces full scale (not scaled per page output settings) */
1094
lastarg = Tcl_GetString(objv[objc - 1]);
1095
if (lastarg[0] == '-') {
1096
if (!strncmp(lastarg + 1, "full", 4))
1099
Tcl_SetResult(interp, "Unknown option.\n", NULL);
1108
/* If there is a non-option argument, use it for the output filename */
1109
sprintf(filename, Tcl_GetString(objv[1]));
1111
else if (xobjs.pagelist[areawin->page]->pageinst->thisobject->name == NULL)
1112
sprintf(filename, xobjs.pagelist[areawin->page]->filename);
1114
sprintf(filename, xobjs.pagelist[areawin->page]->pageinst->thisobject->name);
1116
pptr = strrchr(filename, '.');
1118
sprintf(pptr + 1, "svg");
1119
else if (strcmp(filename + strlen(filename) - 3, "svg"))
1120
strcat(filename, ".svg");
1122
OutputSVG(filename, fullscale);
1123
Fprintf(stdout, "Saved page as SVG format file \"%s\"\n", filename);
1124
return XcTagCallback(interp, objc, objv);
1127
#endif /* TCL_WRAPPER */
1129
/*-------------------------------------------------------------------------*/