1 /*----------------------------------------------------------------------*/
3 /* Copyright (c) 2009 Tim Edwards, Open Circuit Design */
4 /*----------------------------------------------------------------------*/
23 #include <X11/Intrinsic.h>
24 #include <X11/StringDefs.h>
31 /*----------------------------------------------------------------------*/
33 /*----------------------------------------------------------------------*/
39 #include "colordefs.h"
42 /*----------------------------------------------------------------------*/
43 /* Function prototype declarations */
44 /*----------------------------------------------------------------------*/
45 #include "prototypes.h"
47 void SVGDrawString(labelptr
, int, objinstptr
);
49 /*----------------------------------------------------------------------*/
50 /* External Variable definitions */
51 /*----------------------------------------------------------------------*/
54 extern Pixmap STIPPLE
[8];
55 extern XCWindowData
*areawin
;
56 extern Globaldata xobjs
;
57 extern colorindex
*colorlist
;
58 extern int number_colors
;
59 extern fontinfo
*fonts
;
60 extern short fontcount
;
62 /*----------------------------------------------------------------------*/
63 /* The output file is a global variable used by all routines. */
64 /*----------------------------------------------------------------------*/
68 /*----------------------------------------------------------------------*/
69 /*----------------------------------------------------------------------*/
71 void svg_printcolor(int passcolor
, char *prefix
)
73 if ((passcolor
< number_colors
) && (passcolor
!= DEFAULTCOLOR
)) {
74 fprintf(svgf
, "%s\"#%02x%02x%02x\" ",
76 (colorlist
[passcolor
].color
.red
>> 8),
77 (colorlist
[passcolor
].color
.green
>> 8),
78 (colorlist
[passcolor
].color
.blue
>> 8));
82 /*----------------------------------------------------------------------*/
83 /* Since we can't do stipples in SVG, and since the whole stipple thing */
84 /* was only put in because we can't (easily) do transparency effects */
85 /* in X11, we convert stipples to transparency when the stipple is a */
86 /* mask, and do a color blend with white when the stipple is opaque. */
88 /* Blend amount is 1 (almost original color) to 7 (almost white) */
89 /*----------------------------------------------------------------------*/
91 void svg_blendcolor(int passcolor
, char *prefix
, int amount
)
93 int i
, bred
, bgreen
, bblue
;
95 if (passcolor
!= DEFAULTCOLOR
) {
96 for (i
= 0; i
< number_colors
; i
++) {
97 if (colorlist
[i
].color
.pixel
== passcolor
) break;
99 if (i
< number_colors
) {
100 bred
= colorlist
[i
].color
.red
>> 8;
101 bgreen
= colorlist
[i
].color
.green
>> 8;
102 bblue
= colorlist
[i
].color
.blue
>> 8;
106 bred
= bgreen
= bblue
= 0;
108 bred
= ((bred
* amount
) + (255 * (8 - amount
))) >> 3;
109 bgreen
= ((bgreen
* amount
) + (255 * (8 - amount
))) >> 3;
110 bblue
= ((bblue
* amount
) + (255 * (8 - amount
))) >> 3;
112 fprintf(svgf
, "%s\"#%02x%02x%02x\" ", prefix
, bred
, bgreen
, bblue
);
115 /*----------------------------------------------------------------------*/
116 /* Fill and/or draw a border around an element */
117 /*----------------------------------------------------------------------*/
119 void svg_stroke(int passcolor
, short style
, float width
)
122 short minwidth
, solidpart
, shade
;
124 tmpwidth
= UTopTransScale(xobjs
.pagelist
[areawin
->page
]->wirewidth
* width
);
125 minwidth
= max(1, (short)tmpwidth
);
127 if (style
& FILLED
|| (!(style
& FILLED
) && style
& OPAQUE
)) {
128 if ((style
& FILLSOLID
) == FILLSOLID
) {
129 svg_printcolor(passcolor
, "fill=");
131 else if (!(style
& FILLED
)) {
132 fprintf(svgf
, "fill=\"white\" ");
135 shade
= 1 + ((style
& FILLSOLID
) >> 5);
136 if (style
& OPAQUE
) {
137 svg_blendcolor(passcolor
, "fill=", shade
);
140 svg_printcolor(passcolor
, "fill=");
141 fprintf(svgf
, "fill-opacity=\"%g\" ", (float)shade
/ 8);
146 fprintf(svgf
, "fill=\"none\" ");
148 if (!(style
& NOBORDER
)) {
149 /* set up dots or dashes */
150 if (style
& DASHED
) solidpart
= 4 * minwidth
;
151 else if (style
& DOTTED
) solidpart
= minwidth
;
152 if (style
& (DASHED
| DOTTED
)) {
153 fprintf(svgf
, "style=\"stroke-dasharray:%d,%d\" ", solidpart
, 4 * minwidth
);
155 fprintf(svgf
, "stroke-width=\"%g\" ", tmpwidth
);
156 fprintf(svgf
, "stroke-linecap=\"butt\" ");
157 if (style
& SQUARECAP
)
158 fprintf(svgf
, "stroke-linejoin=\"miter\" ");
160 fprintf(svgf
, "stroke-linejoin=\"bevel\" ");
163 fprintf(svgf
, "stroke-width=\"%g\" ", tmpwidth
);
164 if (style
& SQUARECAP
) {
165 fprintf(svgf
, "stroke-linejoin=\"miter\" ");
166 fprintf(svgf
, "stroke-linecap=\"projecting\" ");
169 fprintf(svgf
, "stroke-linejoin=\"bevel\" ");
170 fprintf(svgf
, "stroke-linecap=\"round\" ");
173 svg_printcolor(passcolor
, "stroke=");
176 fprintf(svgf
, "stroke=\"none\" ");
177 fprintf(svgf
, "/>\n");
180 /*----------------------------------------------------------------------*/
181 /* Finish a path and fill and/or stroke */
182 /*----------------------------------------------------------------------*/
184 void svg_strokepath(int passcolor
, short style
, float width
)
186 /* Finish the path, closing if necessary */
187 if (!(style
& UNCLOSED
))
188 fprintf(svgf
, "z\" ");
190 fprintf(svgf
, "\" ");
192 svg_stroke(passcolor
, style
, width
);
195 /*-------------------------------------------------------------------------*/
197 void SVGCreateImages(int page
)
207 char *fname
, outname
[128], *pptr
;
212 /* Check which images are used on this page */
213 glist
= (short *)malloc(xobjs
.images
* sizeof(short));
214 for (i
= 0; i
< xobjs
.images
; i
++) glist
[i
] = 0;
215 count_graphics(xobjs
.pagelist
[page
]->pageinst
->thisobject
, glist
);
217 for (i
= 0; i
< xobjs
.images
; i
++) {
218 if (glist
[i
] == 0) continue;
219 img
= xobjs
.imagelist
+ i
;
221 /* Generate a PPM file, then convert it to PNG */
223 fname
= tmpnam(NULL
);
224 ppf
= fopen(fname
, "w");
227 width
= xcImageGetWidth(img
->image
);
228 height
= xcImageGetWidth(img
->image
);
229 fprintf(ppf
, "P6 %d %d 255\n", width
, height
);
230 for (y
= 0; y
< height
; y
++) {
231 for (x
= 0; x
< width
; x
++) {
233 xcImageGetPixel(img
->image
, x
, y
, &r
, &g
, &b
);
234 fwrite(&r
, 1, 1, ppf
);
235 fwrite(&g
, 1, 1, ppf
);
236 fwrite(&b
, 1, 1, ppf
);
242 /* Run "convert" to make this into a png file */
244 strcpy(outname
, img
->filename
);
245 if ((pptr
= strrchr(outname
, '.')) != NULL
)
246 strcpy(pptr
, ".png");
248 strcat(outname
, ".png");
251 if ((pid
= vfork()) == 0) {
252 execlp("convert", "convert", fname
, outname
, NULL
);
253 exit(0); /* not reached */
255 waitpid(pid
, NULL
, 0);
258 _spawnl(_P_WAIT
, GM_EXEC
, GM_EXEC
, "convert", fname
, outname
, NULL
);
261 Fprintf(stdout
, "Generated standalone PNG image file %s\n", outname
);
266 /*-------------------------------------------------------------------------*/
268 void SVGDrawGraphic(graphicptr gp
)
273 char outname
[128], *pptr
;
276 int width
= xcImageGetWidth(gp
->source
);
277 int height
= xcImageGetHeight(gp
->source
);
279 for (i
= 0; i
< xobjs
.images
; i
++) {
280 img
= xobjs
.imagelist
+ i
;
281 if (img
->image
== gp
->source
)
284 if (i
== xobjs
.images
) return;
286 strcpy(outname
, img
->filename
);
287 if ((pptr
= strrchr(outname
, '.')) != NULL
)
288 strcpy(pptr
, ".png");
290 strcat(outname
, ".png");
293 UPreMultCTM(DCTM
, gp
->position
, gp
->scale
, gp
->rotation
);
294 corner
.x
= -(width
>> 1);
295 corner
.y
= (height
>> 1);
296 UTransformbyCTM(DCTM
, &corner
, &ppt
, 1);
299 tscale
= gp
->scale
* UTopScale();
300 rotation
= gp
->rotation
+ UTopRotation();
301 if (rotation
>= 360.0) rotation
-= 360.0;
302 else if (rotation
< 0.0) rotation
+= 360.0;
304 fprintf(svgf
, "<image transform=\"translate(%d,%d) scale(%g) rotate(%f)\"\n",
305 ppt
.x
, ppt
.y
, tscale
, rotation
);
306 fprintf(svgf
, " width=\"%dpx\" height=\"%dpx\"", width
, height
);
307 fprintf(svgf
, " xlink:href=\"%s\">\n", outname
);
308 fprintf(svgf
, "</image>\n");
311 /*-------------------------------------------------------------------------*/
313 void SVGDrawSpline(splineptr thespline
, int passcolor
)
317 UTransformbyCTM(DCTM
, thespline
->ctrl
, tmppoints
, 4);
319 fprintf(svgf
, "<path d=\"M%d,%d C%d,%d %d,%d %d,%d ",
320 tmppoints
[0].x
, tmppoints
[0].y
,
321 tmppoints
[1].x
, tmppoints
[1].y
,
322 tmppoints
[2].x
, tmppoints
[2].y
,
323 tmppoints
[3].x
, tmppoints
[3].y
);
324 svg_strokepath(passcolor
, thespline
->style
, thespline
->width
);
327 /*-------------------------------------------------------------------------*/
329 void SVGDrawPolygon(polyptr thepoly
, int passcolor
)
332 XPoint
*tmppoints
= (pointlist
) malloc(thepoly
->number
* sizeof(XPoint
));
334 UTransformbyCTM(DCTM
, thepoly
->points
, tmppoints
, thepoly
->number
);
336 fprintf(svgf
, "<path ");
337 if (thepoly
->style
& BBOX
) fprintf(svgf
, "visibility=\"hidden\" ");
338 fprintf(svgf
, "d=\"M%d,%d L", tmppoints
[0].x
, tmppoints
[0].y
);
339 for (i
= 1; i
< thepoly
->number
; i
++) {
340 fprintf(svgf
, "%d,%d ", tmppoints
[i
].x
, tmppoints
[i
].y
);
343 svg_strokepath(passcolor
, thepoly
->style
, thepoly
->width
);
347 /*-------------------------------------------------------------------------*/
349 void SVGDrawArc(arcptr thearc
, int passcolor
)
355 radius
[0] = UTopTransScale(thearc
->radius
);
356 radius
[1] = UTopTransScale(thearc
->yaxis
);
358 tarc
= (thearc
->angle2
- thearc
->angle1
);
360 UTransformbyCTM(DCTM
, &(thearc
->position
), endpoints
, 1);
361 fprintf(svgf
, "<ellipse cx=\"%d\" cy=\"%d\" rx=\"%d\" ry=\"%d\" ",
362 endpoints
[0].x
, endpoints
[0].y
, radius
[0], radius
[1]);
363 svg_stroke(passcolor
, thearc
->style
, thearc
->width
);
366 UfTransformbyCTM(DCTM
, thearc
->points
, endpoints
, 1);
367 UfTransformbyCTM(DCTM
, thearc
->points
+ thearc
->number
- 1, endpoints
+ 1, 1);
369 /* When any arc is flipped, the direction of travel reverses. */
370 fprintf(svgf
, "<path d=\"M%d,%d A%d,%d 0 %d,%d %d,%d ",
371 endpoints
[0].x
, endpoints
[0].y
,
372 radius
[0], radius
[1],
373 ((tarc
> 180) ? 1 : 0),
374 (((DCTM
->a
* DCTM
->e
) >= 0) ? 1 : 0),
375 endpoints
[1].x
, endpoints
[1].y
);
376 svg_strokepath(passcolor
, thearc
->style
, thearc
->width
);
380 /*-------------------------------------------------------------------------*/
382 void SVGDrawPath(pathptr thepath
, int passcolor
)
384 XPoint
*tmppoints
= (pointlist
) malloc(sizeof(XPoint
));
390 fprintf(svgf
, "<path d=\"");
392 for (genpath
= thepath
->plist
; genpath
< thepath
->plist
+ thepath
->parts
;
394 switch(ELEMENTTYPE(*genpath
)) {
396 thepoly
= TOPOLY(genpath
);
397 tmppoints
= (pointlist
) realloc(tmppoints
, thepoly
->number
* sizeof(XPoint
));
398 UTransformbyCTM(DCTM
, thepoly
->points
, tmppoints
, thepoly
->number
);
400 fprintf(svgf
, "M%d,%d ", tmppoints
[0].x
, tmppoints
[0].y
);
404 for (i
= 1; i
< thepoly
->number
; i
++) {
405 fprintf(svgf
, "%d,%d ", tmppoints
[i
].x
, tmppoints
[i
].y
);
409 thespline
= TOSPLINE(genpath
);
410 tmppoints
= (pointlist
) realloc(tmppoints
, 4 * sizeof(XPoint
));
411 UTransformbyCTM(DCTM
, thespline
->ctrl
, tmppoints
, 4);
413 fprintf(svgf
, "M%d,%d ", tmppoints
[0].x
, tmppoints
[0].y
);
416 fprintf(svgf
, "C%d,%d %d,%d %d,%d ",
417 tmppoints
[1].x
, tmppoints
[1].y
,
418 tmppoints
[2].x
, tmppoints
[2].y
,
419 tmppoints
[3].x
, tmppoints
[3].y
);
423 svg_strokepath(passcolor
, thepath
->style
, thepath
->width
);
427 /*----------------------------------------------------------------------*/
428 /* Main recursive object instance drawing routine. */
429 /* context is the instance information passed down from above */
430 /* theinstance is the object instance to be drawn */
431 /* level is the level of recursion */
432 /* passcolor is the inherited color value passed to object */
433 /*----------------------------------------------------------------------*/
435 void SVGDrawObject(objinstptr theinstance
, short level
, int passcolor
, pushlistptr
*stack
)
439 int defaultcolor
= passcolor
;
440 int curcolor
= passcolor
;
442 objectptr theobject
= theinstance
->thisobject
;
444 /* All parts are given in the coordinate system of the object, unless */
445 /* this is the top-level object, in which they will be interpreted as */
446 /* relative to the screen. */
450 if (stack
) push_stack(stack
, theinstance
, NULL
);
452 UPreMultCTM(DCTM
, theinstance
->position
, theinstance
->scale
,
453 theinstance
->rotation
);
455 /* make parameter substitutions */
456 psubstitute(theinstance
);
458 /* draw all of the elements */
460 tmpwidth
= UTopTransScale(xobjs
.pagelist
[areawin
->page
]->wirewidth
);
462 /* Here---set a default style using "g" like PostScript "gsave" */
463 /* stroke-width = tmpwidth, stroke = passcolor */
465 /* guard against plist being regenerated during a redraw by the */
466 /* expression parameter mechanism (should that be prohibited?) */
468 for (thispart
= 0; thispart
< theobject
->parts
; thispart
++) {
469 areagen
= theobject
->plist
+ thispart
;
470 if ((*areagen
)->type
& DRAW_HIDE
) continue;
472 if (defaultcolor
!= DOFORALL
) {
473 if ((*areagen
)->color
!= curcolor
) {
474 if ((*areagen
)->color
== DEFAULTCOLOR
)
475 curcolor
= defaultcolor
;
477 curcolor
= (*areagen
)->color
;
481 switch(ELEMENTTYPE(*areagen
)) {
483 if (level
== 0 || !((TOPOLY(areagen
))->style
& BBOX
))
484 SVGDrawPolygon(TOPOLY(areagen
), curcolor
);
488 SVGDrawSpline(TOSPLINE(areagen
), curcolor
);
492 SVGDrawArc(TOARC(areagen
), curcolor
);
496 SVGDrawPath(TOPATH(areagen
), curcolor
);
500 SVGDrawGraphic(TOGRAPHIC(areagen
));
504 if (areawin
->editinplace
&& stack
&& (TOOBJINST(areagen
)
505 == areawin
->topinstance
)) {
506 /* If stack matches areawin->stack, then don't */
507 /* draw because it would be redundant. */
508 pushlistptr alist
= *stack
, blist
= areawin
->stack
;
509 while (alist
&& blist
) {
510 if (alist
->thisinst
!= blist
->thisinst
) break;
514 if ((!alist
) || (!blist
)) break;
516 SVGDrawObject(TOOBJINST(areagen
), level
+ 1, curcolor
, stack
);
520 if (level
== 0 || TOLABEL(areagen
)->pin
== False
||
521 (TOLABEL(areagen
)->anchor
& PINVISIBLE
))
522 SVGDrawString(TOLABEL(areagen
), curcolor
, theinstance
);
528 if (stack
) pop_stack(stack
);
531 /*----------------------------------------------------------------------*/
533 #define addlinepoint(pointlist, numvals, xval, yval) \
534 { if (!numvals) pointlist = (XPoint *)malloc(sizeof(XPoint)); \
535 else pointlist = (XPoint *)realloc(pointlist, (numvals + 1) * sizeof(XPoint)); \
536 pointlist[numvals].x = xval; \
537 pointlist[numvals++].y = -yval; \
540 /*----------------------------------------------------------------------*/
541 /* Draw an entire string, including parameter substitutions */
542 /*----------------------------------------------------------------------*/
544 void SVGDrawString(labelptr drawlabel
, int passcolor
, objinstptr localinst
)
548 short fstyle
, ffont
, tmpanchor
, baseline
, deltay
;
549 int pos
, defaultcolor
, curcolor
, scolor
;
550 short oldx
, oldfont
, oldstyle
;
552 float tmpscale
= 1.0, natscale
= 1.0;
555 TextLinesInfo tlinfo
;
556 short *tabstops
= NULL
;
557 short tabno
, numtabs
= 0, group
= 0;
559 int open_text
, open_span
, open_decor
;
560 XPoint
*decorations
= NULL
;
563 char *symbol_html_encoding
[] = {
564 " ", "!", "∀", "#", "∃", "%", "&", "?", "(", ")",
565 "*", "+", ",", "−", ".", "/", "0", "1", "2", "3", "4",
566 "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "≅",
567 "Α", "Β", "Χ", "Δ", "Ε", "Φ",
568 "Γ", "Η", "Ι", "ϑ", "Κ", "Λ",
569 "Μ", "Ν", "Ο", "Π", "Θ", "Ρ",
570 "Σ", "Τ", "Υ", "σ", "Ω", "Ξ",
571 "Ψ", "Ζ", "[", "∴", "]", "⊥", "_",
572 "‾", "α", "β", "χ", "δ", "ε",
573 "φ", "γ", "η", "ι", "φ", "κ",
574 "λ", "μ", "ν", "ο", "π", "θ",
575 "ρ", "σ", "τ", "υ", "ω", "ω",
576 "ξ", "ψ", "ζ", "{", "|", "}", "~", "", "", "",
577 "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
578 "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
579 "ϒ", "′", "≤", "⁄", "∞", "ƒ",
580 "♣", "♦", "♥", "♠", "↔",
581 "←", "↑", "→", "↓", "°", "±",
582 "″", "≥", "×", "∝", "∂", "•",
583 "÷", "≠", "≡", "≅", "…"
586 /* Standard encoding vector, in HTML, from character 161 to 255 */
587 u_int standard_html_encoding
[] = {
588 161, 162, 163, 8725, 165, 131, 167, 164, 146, 147, 171, 8249,
589 8250, 64256, 64258, 0, 8211, 8224, 8225, 183, 0, 182, 8226,
590 8218, 8222, 8221, 187, 8230, 8240, 0, 191, 0, 96, 180, 710,
591 126, 713, 728, 729, 168, 0, 730, 184, 0, 733, 731, 711, 8212,
592 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 508, 0,
593 170, 0, 0, 0, 0, 321, 216, 338, 186, 0, 0, 0, 0, 0, 230, 0,
594 0, 0, 185, 0, 0, 322, 248, 339, 223};
596 if (fontcount
== 0) return;
598 /* Don't draw temporary labels from schematic capture system */
599 if (drawlabel
->string
->type
!= FONT_NAME
) return;
601 if (passcolor
== DOSUBSTRING
)
602 defaultcolor
= curcolor
= drawlabel
->color
;
604 defaultcolor
= curcolor
= passcolor
;
606 if (defaultcolor
!= DOFORALL
) {
607 if (drawlabel
->color
!= DEFAULTCOLOR
)
608 curcolor
= drawlabel
->color
;
610 curcolor
= defaultcolor
;
613 /* calculate the transformation matrix for this object */
614 /* in natural units of the alphabet vectors */
615 /* (conversion to window units) */
617 /* Labels don't rotate in Firefox, so use <g> record for transform */
620 UPreMultCTM(DCTM
, drawlabel
->position
, drawlabel
->scale
, drawlabel
->rotation
);
622 /* check for flip invariance; recompute CTM and anchoring if necessary */
623 tmpanchor
= flipadjust(drawlabel
->anchor
);
625 /* Note that the Y-scale is inverted or text comes out upside-down. But we */
626 /* need to adjust to the Y baseline. */
628 fprintf(svgf
, "<g transform=\"matrix(%4g %4g %4g %4g %3g %3g)\" ",
629 DCTM
->a
, DCTM
->d
, -(DCTM
->b
), -(DCTM
->e
), DCTM
->c
, DCTM
->f
);
631 svg_printcolor(passcolor
, "fill=");
632 fprintf(svgf
, ">\n");
634 /* "natural" (unscaled) length */
636 tlinfo
.tbreak
= NULL
;
637 tlinfo
.padding
= NULL
;
638 tmpext
= ULength(drawlabel
, localinst
, &tlinfo
);
640 newpoint
.x
= (tmpanchor
& NOTLEFT
?
641 (tmpanchor
& RIGHT
? -tmpext
.maxwidth
: -tmpext
.maxwidth
>> 1) : 0);
642 newpoint
.y
= (tmpanchor
& NOTBOTTOM
?
643 (tmpanchor
& TOP
? -tmpext
.ascent
: -(tmpext
.ascent
+ tmpext
.base
) >> 1)
646 /* Pinlabels have an additional offset spacing to pad */
647 /* them from the circuit point to which they attach. */
649 if (drawlabel
->pin
) {
650 pinadjust(tmpanchor
, &(newpoint
.x
), &(newpoint
.y
), 1);
654 baseline
= newpoint
.y
;
661 /* Adjust for center or right justification */
662 if (tlinfo
.padding
!= NULL
) {
663 if (tmpanchor
& JUSTIFYRIGHT
)
664 newpoint
.x
+= tlinfo
.padding
[linenum
];
665 else if (tmpanchor
& TEXTCENTERED
)
666 newpoint
.x
+= 0.5 * tlinfo
.padding
[linenum
];
670 for (strptr
= drawlabel
->string
; strptr
!= NULL
;
671 strptr
= nextstringpart(strptr
, localinst
)) {
673 /* All segments other than text cancel any */
674 /* existing overline/underline in effect. */
676 if (strptr
->type
!= TEXT_STRING
)
679 switch(strptr
->type
) {
681 while (open_span
> 0) {
682 fprintf(svgf
, "</tspan>");
685 while (open_text
> 0) {
686 fprintf(svgf
, "</text>");
690 addlinepoint(decorations
, nvals
, newpoint
.x
, group
);
697 while (open_span
> 0) {
698 fprintf(svgf
, "</tspan>");
701 while (open_text
> 0) {
702 fprintf(svgf
, "</text>");
706 addlinepoint(decorations
, nvals
, newpoint
.x
, group
);
723 while (open_span
> 1) {
724 fprintf(svgf
, "</tspan>");
728 addlinepoint(decorations
, nvals
, newpoint
.x
, group
);
733 /* These do not need to be handled */
739 /* These are not handled yet, but should be */
747 /* deal with each text segment type */
749 switch(strptr
->type
) {
752 if (strptr
->data
.font
< fontcount
) {
753 ffont
= strptr
->data
.font
;
754 fstyle
= 0; /* style reset by font change */
755 if (baseline
== newpoint
.y
) { /* set top-level font and style */
760 fprintf(svgf
, "<text stroke=\"none\" ");
761 fprintf(svgf
, "font-family=");
762 if (issymbolfont(ffont
))
763 fprintf(svgf
, "\"Times\" ");
764 else if (!strncmp(fonts
[ffont
].family
, "Times", 5))
765 fprintf(svgf
, "\"Times\" ");
767 fprintf(svgf
, "\"%s\" ", fonts
[ffont
].family
);
769 if (fonts
[ffont
].flags
& 0x1)
770 fprintf(svgf
, " font-weight=\"bold\" ");
771 if (fonts
[ffont
].flags
& 0x2) {
772 if (issansfont(ffont
))
773 fprintf(svgf
, " font-style=\"oblique\" ");
775 fprintf(svgf
, " font-style=\"italic\" ");
777 olinerise
= (issansfont(ffont
)) ? 7 : 4;
779 if (strptr
->type
== FONT_SCALE
) {
780 tmpscale
= natscale
* strptr
->data
.scale
;
781 if (baseline
== newpoint
.y
) /* reset top-level scale */
787 /* Actual scale taken care of by transformation matrix */
788 fprintf(svgf
, "font-size=\"%g\" >", tmpscale
* 40);
789 fprintf(svgf
, "<tspan x=\"%d\" y=\"%d\">", newpoint
.x
, -newpoint
.y
);
795 newpoint
.x
+= strptr
->data
.kern
[0];
796 newpoint
.y
+= strptr
->data
.kern
[1];
797 fprintf(svgf
, "<text dx=\"%d\" dy=\"%d\">",
798 strptr
->data
.kern
[0], strptr
->data
.kern
[1]);
803 if (defaultcolor
!= DOFORALL
) {
804 if (strptr
->data
.color
!= DEFAULTCOLOR
)
805 curcolor
= colorlist
[strptr
->data
.color
].color
.pixel
;
807 curcolor
= DEFAULTCOLOR
;
812 case TABBACKWARD
: /* find first tab value with x < xtotal */
813 for (tabno
= numtabs
- 1; tabno
>= 0; tabno
--) {
814 if (tabstops
[tabno
] < newpoint
.x
) {
815 newpoint
.x
= tabstops
[tabno
];
819 fprintf(svgf
, "<tspan x=\"%d\">", newpoint
.x
);
823 case TABFORWARD
: /* find first tab value with x > xtotal */
824 for (tabno
= 0; tabno
< numtabs
; tabno
++) {
825 if (tabstops
[tabno
] > newpoint
.x
) {
826 newpoint
.x
= tabstops
[tabno
];
830 fprintf(svgf
, "<tspan x=\"%d\">", newpoint
.x
);
836 if (tabstops
== NULL
) tabstops
= (short *)malloc(sizeof(short));
837 else tabstops
= (short *)realloc(tabstops
, numtabs
* sizeof(short));
838 tabstops
[numtabs
- 1] = newpoint
.x
;
839 /* Force a tab at this point so that the output aligns */
840 /* to our computation of the position, not its own. */
841 fprintf(svgf
, "<tspan x=\"%d\">", newpoint
.x
);
846 tmpscale
= natscale
= 1.0;
847 baseline
-= BASELINE
;
848 newpoint
.y
= baseline
;
851 if (tlinfo
.padding
!= NULL
) {
852 if (tmpanchor
& JUSTIFYRIGHT
)
853 newpoint
.x
+= tlinfo
.padding
[linenum
];
854 else if (tmpanchor
& TEXTCENTERED
)
855 newpoint
.x
+= 0.5 * tlinfo
.padding
[linenum
];
859 fprintf(svgf
, "<tspan x=\"%d\" y=\"%d\">", newpoint
.x
, -newpoint
.y
);
864 natscale
*= SUBSCALE
;
866 deltay
= (short)((TEXTHEIGHT
>> 1) * natscale
);
867 newpoint
.y
-= deltay
;
868 fprintf(svgf
, "<tspan dy=\"%d\" font-size=\"%g\">", deltay
,
874 natscale
*= SUBSCALE
;
876 deltay
= (short)(TEXTHEIGHT
* natscale
);
877 newpoint
.y
+= deltay
;
878 fprintf(svgf
, "<tspan dy=\"%d\" font-size=\"%g\">", -deltay
,
884 tmpscale
= natscale
= 1.0;
885 ffont
= oldfont
; /* revert to top-level font and style */
887 newpoint
.y
= baseline
;
888 fprintf(svgf
, "<tspan y=\"%d\">", baseline
);
894 group
= newpoint
.y
- 6;
895 addlinepoint(decorations
, nvals
, newpoint
.x
, group
);
900 if (strptr
->nextpart
!= NULL
&& strptr
->nextpart
->type
== TEXT_STRING
) {
905 for (textptr
= strptr
->nextpart
->data
.string
;
906 textptr
&& *textptr
!= '\0'; textptr
++) {
907 charptr
= fonts
[ffont
].encoding
[*(u_char
*)textptr
];
908 tmpheight
= (int)((float)charptr
->bbox
.height
909 * fonts
[ffont
].scale
);
910 if (group
< tmpheight
) group
= (short)tmpheight
;
913 group
+= olinerise
+ newpoint
.y
;
914 addlinepoint(decorations
, nvals
, newpoint
.x
, group
);
922 case HALFSPACE
: case QTRSPACE
: {
924 objectptr drawchar
= fonts
[ffont
].encoding
[(u_char
)32];
925 addx
= (drawchar
->bbox
.lowerleft
.x
+ drawchar
->bbox
.width
) *
927 addx
>>= ((strptr
->type
== HALFSPACE
) ? 1 : 2);
929 fprintf(svgf
, "<tspan dx=\"%d\">", addx
);
935 textptr
= strptr
->data
.string
;
937 if (issymbolfont(ffont
)) {
938 for (; *textptr
!= '\0'; textptr
++)
939 if (((u_char
)(*textptr
) >= 32) && ((u_char
)(*textptr
) < 158))
940 fprintf(svgf
, "%s", symbol_html_encoding
[(*textptr
) - 32]);
943 /* Handle "&" and non-ASCII characters in the text */
944 if (isisolatin1(ffont
)) {
945 for (; *textptr
!= '\0'; textptr
++) {
947 fprintf(svgf
, "&");
948 else if ((u_char
)(*textptr
) >= 128)
949 fprintf(svgf
, "&#%d;", (int)((u_char
)*textptr
));
950 else if ((u_char
)(*textptr
) >= 32)
951 fprintf(svgf
, "%c", *textptr
);
955 for (; *textptr
!= '\0'; textptr
++) {
957 fprintf(svgf
, "&");
958 else if ((u_char
)(*textptr
) >= 161)
959 fprintf(svgf
, "&#%d;",
960 standard_html_encoding
[(u_char
)(*textptr
)
962 else if ((u_char
)(*textptr
) >= 32 && (u_char
)(*textptr
) < 161)
963 fprintf(svgf
, "%c", *textptr
);
969 /* Compute the new X position */
971 for (textptr
= strptr
->data
.string
; *textptr
!= '\0'; textptr
++) {
972 objectptr drawchar
= fonts
[ffont
].encoding
[(u_char
)(*textptr
)];
973 short addx
= (drawchar
->bbox
.lowerleft
.x
+ drawchar
->bbox
.width
) *
981 while (open_span
> 0) {
982 fprintf(svgf
, "</tspan>");
985 while (open_text
> 0) {
986 fprintf(svgf
, "</text>");
989 fprintf(svgf
, "\n</text>");
993 if (tabstops
!= NULL
) free(tabstops
);
994 if (tlinfo
.padding
!= NULL
) free(tlinfo
.padding
);
996 /* If there were decorations (underlines, overlines), generate them */
998 if (decorations
!= NULL
) {
1001 addlinepoint(decorations
, nvals
, newpoint
.x
, group
);
1003 for (i
= 0; i
< nvals
; i
+= 2) {
1004 fprintf(svgf
, "\n<line stroke-width=\"2\" stroke-linecap=\"square\" "
1005 "x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" />",
1006 decorations
[i
].x
, decorations
[i
].y
, decorations
[i
+ 1].x
,
1007 decorations
[i
+ 1].y
);
1011 fprintf(svgf
, "</g>\n");
1014 /*----------------------------------------------------------------------*/
1015 /* Write the SVG file output */
1016 /*----------------------------------------------------------------------*/
1018 #define PMARGIN 6 /* Pixel margin around drawing */
1021 OutputSVG(char *filename
, Boolean fullscale
)
1026 float outwidth
, outheight
, cscale
;
1028 svgf
= fopen(filename
, "w");
1030 Fprintf(stderr
, "Cannot open file %s for writing.\n", filename
);
1034 /* Generate external image files, if necessary */
1035 SVGCreateImages(areawin
->page
);
1037 /* Save the number of selections and set it to zero while we do the */
1038 /* object drawing. */
1040 savesel
= areawin
->selects
;
1041 areawin
->selects
= 0;
1042 pinst
= xobjs
.pagelist
[areawin
->page
]->pageinst
;
1044 UPushCTM(); /* Save the top-level graphics state */
1046 /* This is like UMakeWCTM()---it inverts the whole picture so that */
1047 /* The origin is at the top left, and all data points fit in a box */
1048 /* at (0, 0) to the object (width, height) */
1052 DCTM
->c
= -pinst
->bbox
.lowerleft
.x
;
1055 DCTM
->f
= pinst
->bbox
.lowerleft
.y
+ pinst
->bbox
.height
;
1057 fprintf(svgf
, "<svg xmlns=\"http://www.w3.org/2000/svg\"\n");
1058 fprintf(svgf
, " xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
1059 fprintf(svgf
, " version=\"1.1\"\n");
1060 fprintf(svgf
, " id=\"%s\" ", pinst
->thisobject
->name
);
1063 fprintf(svgf
, "width=\"100%%\" height=\"100%%\" ");
1066 cscale
= getpsscale(xobjs
.pagelist
[areawin
->page
]->outscale
, areawin
->page
);
1067 cstyle
= xobjs
.pagelist
[areawin
->page
]->coordstyle
;
1069 outwidth
= toplevelwidth(pinst
, NULL
) * cscale
;
1070 outwidth
/= (cstyle
== CM
) ? IN_CM_CONVERT
: 72.0;
1071 outheight
= toplevelheight(pinst
, NULL
) * cscale
;
1072 outheight
/= (cstyle
== CM
) ? IN_CM_CONVERT
: 72.0;
1074 /* Set display height to that specified in the output properties (in inches) */
1075 fprintf(svgf
, "width=\"%.3g%s\" height=\"%.3g%s\" ",
1076 outwidth
, (cstyle
== CM
) ? "cm" : "in",
1077 outheight
, (cstyle
== CM
) ? "cm" : "in");
1079 fprintf(svgf
, " viewBox=\"%d %d %d %d\">\n",
1080 -PMARGIN
, -PMARGIN
, pinst
->bbox
.width
+ PMARGIN
,
1081 pinst
->bbox
.height
+ PMARGIN
);
1083 fprintf(svgf
, "<desc>\n");
1084 fprintf(svgf
, "XCircuit Version %2.1f\n", PROG_VERSION
);
1085 fprintf(svgf
, "File \"%s\" Page %d\n", xobjs
.pagelist
[areawin
->page
]->filename
,
1087 fprintf(svgf
, "</desc>\n");
1089 /* Set default color to black */
1090 fprintf(svgf
, "<g stroke=\"black\">\n");
1092 if (areawin
->hierstack
) free_stack(&areawin
->hierstack
);
1093 SVGDrawObject(areawin
->topinstance
, TOPLEVEL
, FOREGROUND
, &areawin
->hierstack
);
1094 if (areawin
->hierstack
) free_stack(&areawin
->hierstack
);
1096 /* restore the selection list (if any) */
1097 areawin
->selects
= savesel
;
1099 fprintf(svgf
, "</g>\n</svg>\n");
1102 UPopCTM(); /* Restore the top-level graphics state */
1107 /*----------------------------------------------------------------------*/
1108 /* The TCL command-line for the SVG file write routine. */
1109 /*----------------------------------------------------------------------*/
1111 int xctcl_svg(ClientData clientData
, Tcl_Interp
*interp
,
1112 int objc
, Tcl_Obj
*CONST objv
[])
1114 char filename
[128], *pptr
;
1115 Boolean fullscale
= 0;
1119 /* Argument "-full" forces full scale (not scaled per page output settings) */
1121 lastarg
= Tcl_GetString(objv
[objc
- 1]);
1122 if (lastarg
[0] == '-') {
1123 if (!strncmp(lastarg
+ 1, "full", 4))
1126 Tcl_SetResult(interp
, "Unknown option.\n", NULL
);
1135 /* If there is a non-option argument, use it for the output filename */
1136 sprintf(filename
, Tcl_GetString(objv
[1]));
1138 else if (xobjs
.pagelist
[areawin
->page
]->pageinst
->thisobject
->name
== NULL
)
1139 sprintf(filename
, xobjs
.pagelist
[areawin
->page
]->filename
);
1141 sprintf(filename
, xobjs
.pagelist
[areawin
->page
]->pageinst
->thisobject
->name
);
1143 pptr
= strrchr(filename
, '.');
1145 sprintf(pptr
+ 1, "svg");
1146 else if (strcmp(filename
+ strlen(filename
) - 3, "svg"))
1147 strcat(filename
, ".svg");
1149 OutputSVG(filename
, fullscale
);
1150 Fprintf(stdout
, "Saved page as SVG format file \"%s\"\n", filename
);
1151 return XcTagCallback(interp
, objc
, objv
);
1154 #endif /* TCL_WRAPPER */
1156 /*-------------------------------------------------------------------------*/