svg.c revision a5ae21e4
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
47static void dumpSvgHeader(XtermWidget xw, FILE *fp);
48static void dumpSvgScreen(XtermWidget xw, FILE *fp);
49static void dumpSvgLine(XtermWidget xw, int row, FILE *fp);
50static void dumpSvgFooter(XtermWidget, FILE *fp);
51
52static int rows = 0;
53static int cols = 0;
54static Dimension bw = 0;	/* borderWidth */
55static int ib = 0;		/* internalBorder */
56
57void
58xtermDumpSvg(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
76static void
77dumpSvgHeader(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
101static void
102dumpSvgScreen(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
122static void
123dumpSvgLine(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
280static void
281dumpSvgFooter(XtermWidget xw, FILE *fp)
282{
283    fputs(" </g>\n</svg>\n", fp);
284    xevents(xw);
285}
286