1 /* $XTermId: svg.c,v 1.21 2021/09/19 18:22:57 tom Exp $ */
2 
3 /*
4  * Copyright 2017-2020,2021	Thomas E. Dickey
5  * Copyright 2015-2016,2017	Jens Schweikhardt
6  *
7  * All Rights Reserved
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a copy
10  * of this software and associated documentation files (the "Software"), to
11  * deal in the Software without restriction, including without limitation the
12  * rights to use, copy, modify, merge, publish, distribute, sublicense,
13  * and/or sell copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included in
17  * all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22  * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
23  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  * OTHER DEALINGS IN THE SOFTWARE.
26  *
27  * Except as contained in this notice, the name(s) of the above copyright
28  * holders shall not be used in advertising or otherwise to promote the sale,
29  * use or other dealings in this Software without prior written
30  * authorization.
31  */
32 
33 #include <xterm.h>
34 #include <version.h>
35 
36 #define MakeDim(color) \
37 	color = (unsigned short) ((2 * (unsigned) color) / 3)
38 
39 #define RGBPCT(c) \
40  	((double)c.red   / 655.35), \
41 	((double)c.green / 655.35), \
42 	((double)c.blue  / 655.35)
43 
44 #define CELLW 10
45 #define CELLH 20
46 
47 static void dumpSvgHeader(XtermWidget xw, FILE *fp);
48 static void dumpSvgScreen(XtermWidget xw, FILE *fp);
49 static void dumpSvgLine(XtermWidget xw, int row, FILE *fp);
50 static void dumpSvgFooter(XtermWidget, FILE *fp);
51 
52 static int rows = 0;
53 static int cols = 0;
54 static Dimension bw = 0;	/* borderWidth */
55 static int ib = 0;		/* internalBorder */
56 
57 void
xtermDumpSvg(XtermWidget xw)58 xtermDumpSvg(XtermWidget xw)
59 {
60     char *saveLocale;
61     FILE *fp;
62 
63     TRACE(("xtermDumpSvg...\n"));
64     saveLocale = xtermSetLocale(LC_NUMERIC, "C");
65     fp = create_printfile(xw, ".svg");
66     if (fp != 0) {
67 	dumpSvgHeader(xw, fp);
68 	dumpSvgScreen(xw, fp);
69 	dumpSvgFooter(xw, fp);
70 	fclose(fp);
71     }
72     xtermResetLocale(LC_NUMERIC, saveLocale);
73     TRACE(("...xtermDumpSvg done\n"));
74 }
75 
76 static void
dumpSvgHeader(XtermWidget xw,FILE * fp)77 dumpSvgHeader(XtermWidget xw, FILE *fp)
78 {
79     TScreen *s = TScreenOf(xw);
80 
81     rows = s->bot_marg - s->top_marg + 1;
82     cols = MaxCols(s);
83     bw = BorderWidth(xw);
84     ib = s->border;
85 
86     fputs("<?xml version='1.0' encoding='UTF-8'?>\n", fp);
87     fputs("<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN'\n", fp);
88     fputs("  'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>\n", fp);
89     fputs("<svg xmlns='http://www.w3.org/2000/svg'\n", fp);
90     fputs(" version='1.1' baseProfile='full'\n", fp);
91     fprintf(fp, " viewBox='0 0 %d %d'>\n", 2 * (bw + ib) + cols * CELLW, 2 *
92 	    (bw + ib) +
93 	    rows * CELLH);
94     fprintf(fp, " <desc>%s Screen Dump</desc>\n", xtermVersion());
95     fprintf(fp,
96 	    " <g font-size='%.2f' font-family='monospace, monospace'>\n",
97 	    0.80 * CELLH);
98     xevents(xw);
99 }
100 
101 static void
dumpSvgScreen(XtermWidget xw,FILE * fp)102 dumpSvgScreen(XtermWidget xw, FILE *fp)
103 {
104     TScreen *s = TScreenOf(xw);
105     int row;
106 
107     fprintf(fp, "  <rect x='0' y='0' width='%u' height='%u' fill='%s'/>\n",
108 	    cols * CELLW + 2 * (bw + ib), rows * CELLH + 2 * (bw + ib),
109 	    PixelToCSSColor(xw, xw->core.border_pixel));
110     fprintf(fp, "  <rect x='%u' y='%u' width='%u' height='%u' fill='%s'/>\n",
111 	    bw, bw,
112 	    MaxCols(s) * CELLW + 2 * ib,
113 	    (unsigned) (rows * CELLH + 2 * ib),
114 	    PixelToCSSColor(xw, xw->old_background));
115 
116     for (row = s->top_marg; row <= s->bot_marg; ++row) {
117 	fprintf(fp, "  <!-- Row %d -->\n", row);
118 	dumpSvgLine(xw, row, fp);
119     }
120 }
121 
122 static void
dumpSvgLine(XtermWidget xw,int row,FILE * fp)123 dumpSvgLine(XtermWidget xw, int row, FILE *fp)
124 {
125     TScreen *s = TScreenOf(xw);
126     int inx = ROW2INX(s, row);
127     LineData *ld = getLineData(s, inx);
128     int col, sal, i;		/* sal: same attribute length */
129 
130     if (ld == 0)
131 	return;
132 
133     for (col = 0; col < MaxCols(s); col += sal) {
134 	XColor fgcolor, bgcolor;
135 
136 	/* Count how many consecutive cells have the same color & attributes. */
137 	for (sal = 1; col + sal < MaxCols(s); ++sal) {
138 #if OPT_ISO_COLORS
139 	    if (!isSameCColor(ld->color[col], ld->color[col + sal]))
140 		break;
141 #endif
142 	    if (ld->attribs[col] != ld->attribs[col + sal])
143 		break;
144 	}
145 
146 	fgcolor.pixel = xw->old_foreground;
147 	bgcolor.pixel = xw->old_background;
148 #if OPT_ISO_COLORS
149 	if (ld->attribs[col] & FG_COLOR) {
150 	    Pixel fg = extract_fg(xw, ld->color[col], ld->attribs[col]);
151 #if OPT_DIRECT_COLOR
152 	    if (ld->attribs[col] & ATR_DIRECT_FG)
153 		fgcolor.pixel = fg;
154 	    else
155 #endif
156 		fgcolor.pixel = s->Acolors[fg].value;
157 	}
158 	if (ld->attribs[col] & BG_COLOR) {
159 	    Pixel bg = extract_bg(xw, ld->color[col], ld->attribs[col]);
160 #if OPT_DIRECT_COLOR
161 	    if (ld->attribs[col] & ATR_DIRECT_BG)
162 		bgcolor.pixel = bg;
163 	    else
164 #endif
165 		bgcolor.pixel = s->Acolors[bg].value;
166 	}
167 #endif
168 
169 	(void) QueryOneColor(xw, &fgcolor);
170 	(void) QueryOneColor(xw, &bgcolor);
171 	xevents(xw);
172 
173 	if (ld->attribs[col] & BLINK) {
174 	    /* White on red. */
175 	    fgcolor.red = fgcolor.green = fgcolor.blue = MAX_U_COLOR;
176 	    bgcolor.red = MAX_U_COLOR;
177 	    bgcolor.green = bgcolor.blue = 0u;
178 	}
179 #if OPT_WIDE_ATTRS
180 	if (ld->attribs[col] & ATR_FAINT) {
181 	    MakeDim(fgcolor.red);
182 	    MakeDim(fgcolor.green);
183 	    MakeDim(fgcolor.blue);
184 	}
185 #endif
186 	if (ld->attribs[col] & INVERSE) {
187 	    XColor tmp = fgcolor;
188 	    fgcolor = bgcolor;
189 	    bgcolor = tmp;
190 	}
191 
192 	/* Draw the background rectangle. */
193 	fprintf(fp, "  <rect x='%d' y='%d' ", bw + ib + col * CELLW, bw + ib
194 		+ row * CELLH);
195 	fprintf(fp, "height='%d' width='%d' ", CELLH, sal * CELLW);
196 	fprintf(fp, "fill='rgb(%.2f%%, %.2f%%, %.2f%%)'/>\n", RGBPCT(bgcolor));
197 
198 	/* Now the <text>. */
199 	/*
200 	 * SVG: Rendering text strings into a given rectangle is a challenge.
201 	 * Some renderers accept and do the right thing with the 'textLength'
202 	 * attribute, while others ignore it. The only predictable way to place
203 	 * (even monospaced) text properly is to do it character by character.
204 	 */
205 
206 	fprintf(fp, "  <g");
207 	if (ld->attribs[col] & BOLD)
208 	    fprintf(fp, " font-weight='bold'");
209 #if OPT_WIDE_ATTRS
210 	if (ld->attribs[col] & ATR_ITALIC)
211 	    fprintf(fp, " font-style='italic'");
212 #endif
213 	fprintf(fp, " fill='rgb(%.2f%%, %.2f%%, %.2f%%)'>\n", RGBPCT(fgcolor));
214 
215 	for (i = 0; i < sal; ++i) {
216 	    IChar chr = ld->charData[col + i];
217 
218 	    if (chr == ' ')
219 		continue;
220 	    fprintf(fp, "   <text x='%d' y='%d'>", bw + ib + (col + i) *
221 		    CELLW, bw + ib + row * CELLH + (CELLH * 3) / 4);
222 #if OPT_WIDE_CHARS
223 	    if (chr > 127) {
224 		/* Ignore hidden characters. */
225 		if (chr != HIDDEN_CHAR) {
226 		    Char temp[10];
227 		    *convertToUTF8(temp, chr) = 0;
228 		    fputs((char *) temp, fp);
229 		}
230 	    } else
231 #endif
232 		switch (chr) {
233 		case 0:
234 		    fputc(' ', fp);
235 		    break;
236 		case '&':
237 		    fputs("&amp;", fp);
238 		    break;
239 		case '<':
240 		    fputs("&lt;", fp);
241 		    break;
242 		case '>':
243 		    fputs("&gt;", fp);
244 		    break;
245 		default:
246 		    fputc((int) chr, fp);
247 		}
248 	    fprintf(fp, "</text>\n");
249 	    xevents(xw);
250 	}
251 	fprintf(fp, "  </g>\n");
252 	xevents(xw);
253 
254 #define HLINE(x) \
255   fprintf(fp, "  <line x1='%d' y1='%d' " \
256                       "x2='%d' y2='%d' " \
257                   "stroke='rgb(%.2f%%, %.2f%%, %.2f%%)'/>\n", \
258     bw + ib + col * CELLW,         bw + ib + row * CELLH + CELLH - (x), \
259     bw + ib + (col + sal) * CELLW, bw + ib + row * CELLH + CELLH - (x), \
260     RGBPCT(fgcolor))
261 
262 	/* Now the line attributes. */
263 	if (ld->attribs[col] & UNDERLINE) {
264 	    HLINE(4);
265 	}
266 #if OPT_WIDE_ATTRS
267 	if (ld->attribs[col] & ATR_STRIKEOUT) {
268 	    HLINE(9);
269 	}
270 	if (ld->attribs[col] & ATR_DBL_UNDER) {
271 	    HLINE(3);
272 	    HLINE(1);
273 	}
274 #endif
275 	xevents(xw);
276     }
277     xevents(xw);
278 }
279 
280 static void
dumpSvgFooter(XtermWidget xw,FILE * fp)281 dumpSvgFooter(XtermWidget xw, FILE *fp)
282 {
283     fputs(" </g>\n</svg>\n", fp);
284     xevents(xw);
285 }
286