1/* $XTermId: svg.c,v 1.24 2024/12/01 20:27:00 tom Exp $ */ 2 3/* 4 * Copyright 2017-2023,2024 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 != NULL) { 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 (unsigned) (cols * CELLW + 2 * (bw + ib)), 109 (unsigned) (rows * CELLH + 2 * (bw + ib)), 110 PixelToCSSColor(xw, xw->core.border_pixel)); 111 fprintf(fp, " <rect x='%u' y='%u' width='%u' height='%u' fill='%s'/>\n", 112 bw, bw, 113 (unsigned) (MaxCols(s) * CELLW + 2 * ib), 114 (unsigned) (rows * CELLH + 2 * ib), 115 PixelToCSSColor(xw, xw->old_background)); 116 117 for (row = s->top_marg; row <= s->bot_marg; ++row) { 118 fprintf(fp, " <!-- Row %d -->\n", row); 119 dumpSvgLine(xw, row, fp); 120 } 121} 122 123static void 124dumpSvgLine(XtermWidget xw, int row, FILE *fp) 125{ 126 TScreen *s = TScreenOf(xw); 127 int inx = ROW2INX(s, row); 128 LineData *ld = getLineData(s, inx); 129 int col, sal, i; /* sal: same attribute length */ 130 131 if (ld == NULL) 132 return; 133 134 for (col = 0; col < MaxCols(s); col += sal) { 135 XColor fgcolor, bgcolor; 136 137 /* Count how many consecutive cells have the same color & attributes. */ 138 for (sal = 1; col + sal < MaxCols(s); ++sal) { 139#if OPT_ISO_COLORS 140 if (!isSameCColor(ld->color[col], ld->color[col + sal])) 141 break; 142#endif 143 if (ld->attribs[col] != ld->attribs[col + sal]) 144 break; 145 } 146 147 fgcolor.pixel = xw->old_foreground; 148 bgcolor.pixel = xw->old_background; 149#if OPT_ISO_COLORS 150 if (ld->attribs[col] & FG_COLOR) { 151 Pixel fg = extract_fg(xw, ld->color[col], ld->attribs[col]); 152#if OPT_DIRECT_COLOR 153 if (ld->attribs[col] & ATR_DIRECT_FG) 154 fgcolor.pixel = fg; 155 else 156#endif 157 fgcolor.pixel = s->Acolors[fg].value; 158 } 159 if (ld->attribs[col] & BG_COLOR) { 160 Pixel bg = extract_bg(xw, ld->color[col], ld->attribs[col]); 161#if OPT_DIRECT_COLOR 162 if (ld->attribs[col] & ATR_DIRECT_BG) 163 bgcolor.pixel = bg; 164 else 165#endif 166 bgcolor.pixel = s->Acolors[bg].value; 167 } 168#endif 169 170 (void) QueryOneColor(xw, &fgcolor); 171 (void) QueryOneColor(xw, &bgcolor); 172 xevents(xw); 173 174 if (ld->attribs[col] & BLINK) { 175 /* White on red. */ 176 fgcolor.red = fgcolor.green = fgcolor.blue = MAX_U_COLOR; 177 bgcolor.red = MAX_U_COLOR; 178 bgcolor.green = bgcolor.blue = 0u; 179 } 180#if OPT_WIDE_ATTRS 181 if (ld->attribs[col] & ATR_FAINT) { 182 MakeDim(fgcolor.red); 183 MakeDim(fgcolor.green); 184 MakeDim(fgcolor.blue); 185 } 186#endif 187 if (ld->attribs[col] & INVERSE) { 188 XColor tmp = fgcolor; 189 fgcolor = bgcolor; 190 bgcolor = tmp; 191 } 192 193 /* Draw the background rectangle. */ 194 fprintf(fp, " <rect x='%d' y='%d' ", bw + ib + col * CELLW, bw + ib 195 + row * CELLH); 196 fprintf(fp, "height='%d' width='%d' ", CELLH, sal * CELLW); 197 fprintf(fp, "fill='rgb(%.2f%%, %.2f%%, %.2f%%)'/>\n", RGBPCT(bgcolor)); 198 199 /* Now the <text>. */ 200 /* 201 * SVG: Rendering text strings into a given rectangle is a challenge. 202 * Some renderers accept and do the right thing with the 'textLength' 203 * attribute, while others ignore it. The only predictable way to place 204 * (even monospaced) text properly is to do it character by character. 205 */ 206 207 fprintf(fp, " <g"); 208 if (ld->attribs[col] & BOLD) 209 fprintf(fp, " font-weight='bold'"); 210#if OPT_WIDE_ATTRS 211 if (ld->attribs[col] & ATR_ITALIC) 212 fprintf(fp, " font-style='italic'"); 213#endif 214 fprintf(fp, " fill='rgb(%.2f%%, %.2f%%, %.2f%%)'>\n", RGBPCT(fgcolor)); 215 216 for (i = 0; i < sal; ++i) { 217 IChar chr = ld->charData[col + i]; 218 219 if (chr == ' ') 220 continue; 221 fprintf(fp, " <text x='%d' y='%d'>", bw + ib + (col + i) * 222 CELLW, bw + ib + row * CELLH + (CELLH * 3) / 4); 223#if OPT_WIDE_CHARS 224 if (chr > 127) { 225 /* Ignore hidden characters. */ 226 if (chr != HIDDEN_CHAR) { 227 Char temp[10]; 228 *convertToUTF8(temp, chr) = 0; 229 fputs((char *) temp, fp); 230 } 231 } else 232#endif 233 switch (chr) { 234 case 0: 235 fputc(' ', fp); 236 break; 237 case '&': 238 fputs("&", fp); 239 break; 240 case '<': 241 fputs("<", fp); 242 break; 243 case '>': 244 fputs(">", fp); 245 break; 246 default: 247 fputc((int) chr, fp); 248 } 249 fprintf(fp, "</text>\n"); 250 xevents(xw); 251 } 252 fprintf(fp, " </g>\n"); 253 xevents(xw); 254 255#define HLINE(x) \ 256 fprintf(fp, " <line x1='%d' y1='%d' " \ 257 "x2='%d' y2='%d' " \ 258 "stroke='rgb(%.2f%%, %.2f%%, %.2f%%)'/>\n", \ 259 bw + ib + col * CELLW, bw + ib + row * CELLH + CELLH - (x), \ 260 bw + ib + (col + sal) * CELLW, bw + ib + row * CELLH + CELLH - (x), \ 261 RGBPCT(fgcolor)) 262 263 /* Now the line attributes. */ 264 if (ld->attribs[col] & UNDERLINE) { 265 HLINE(4); 266 } 267#if OPT_WIDE_ATTRS 268 if (ld->attribs[col] & ATR_STRIKEOUT) { 269 HLINE(9); 270 } 271 if (ld->attribs[col] & ATR_DBL_UNDER) { 272 HLINE(3); 273 HLINE(1); 274 } 275#endif 276 xevents(xw); 277 } 278 xevents(xw); 279} 280 281static void 282dumpSvgFooter(XtermWidget xw, FILE *fp) 283{ 284 fputs(" </g>\n</svg>\n", fp); 285 xevents(xw); 286} 287