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("&", fp);
238 break;
239 case '<':
240 fputs("<", fp);
241 break;
242 case '>':
243 fputs(">", 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